MySql常见问题总结

简单的总结了下Mysql的知识点

  • 存储索引
  • 索引
  • 缓存
  • 事务
  • 锁机制

存储引擎

MyISAM

MyISAM是MySQL的默认数据库引擎(5.5版之前),由早期的 ISAM (Indexed Sequential Access Method:有索引的顺序访问方法)所改良。虽然性能极佳,而且提供了大量的特性,包括全文索引、压缩、空间函数等,但MyISAM不支持事务和行级锁,而且最大的缺陷就是崩溃后无法安全恢复。不过,5.5版本之后,MySQL引入了InnoDB(另一种数据库引擎)。

两者有不同的使用场景,比如:MyISAM更适合读密集的表,而InnoDB更适合写密集的的表。 在数据库做主从分离的情况下,经常选择InnoDB作为主库的存储引擎。

MyISAM特点

  • 不支持行锁(MyISAM只有表锁),读取时对需要读到的所有表加锁,写入时则对表加排他锁;

  • 不支持事务

  • 不支持外键

  • 不支持崩溃后的安全恢复

  • 在表有读取查询的同时,支持往表中插入新纪录

  • 支持BLOB和TEXT的前500个字符索引,支持全文索引

  • 支持延迟更新索引,极大地提升了写入性能

对于不会进行修改的表,支持 压缩表 ,极大地减少了磁盘空间的占用

补充概念

Mysql的行锁和表锁( 锁是计算机协调多个进程或纯线程并发访问某一资源的机制) 表级锁: 每次操作锁住整张表。开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低; 行级锁: 每次操作锁住一行数据。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高;

InnoDB

InnoDB是MySQL的默认数据库引擎(5.5版之后),2006年五月时由甲骨文公司并购。与传统的ISAM与MyISAM相比,InnoDB的最大特色就是支持了ACID兼容的事务(Transaction)功能。

InnoDB特点

  • 支持行锁,采用MVCC来支持高并发,有可能死锁
  • 支持事务
  • 支持外键
  • 支持崩溃后的安全恢复
  • 不支持全文索引

二者的对比与总结

对比

  • count运算上的区别: 因为MyISAM缓存有表meta-data(行数等),因此在做COUNT(*)时对于一个结构很好的查询是不需要消耗多少资源的。而对于InnoDB来说,则没有这种缓存。
  • 是否支持事务和崩溃后的安全恢复: MyISAM 强调的是性能,每次查询具有原子性,其执行数度比InnoDB类型更快,但是不提供事务支持。但是InnoDB 提供事务支持事务,外部键等高级数据库功能。 具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。
  • 是否支持外键: MyISAM不支持,而InnoDB支持。

总结

MyISAM更适合读密集的表,而InnoDB更适合写密集的的表。 在数据库做主从分离的情况下,经常选择MyISAM作为主库的存储引擎。

一般来说,如果需要事务支持,并且有较高的并发读取频率(MyISAM的表锁的粒度太大,所以当该表写并发量较高时,要等待的查询就会很多了),InnoDB是不错的选择。如果你的数据量很大(MyISAM支持压缩特性可以减少磁盘的空间占用),而且不需要支持事务时,MyISAM是最好的选择。

对比项 MyISAM InnoDB
主外键 不支持 支持
事务 不支持 支持
行表锁 表锁,不适合高并发 行锁,适合高并发
缓存 只缓存索引,不缓存真实数据 不仅缓存索引,还要缓存真实数据
表空间
关注点 性能 事务
默认安装 Y Y

索引

Mysql索引使用的数据结构主要有BTree索引哈希索引 。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。

  Mysql的BTree索引使用的是B数中的B+Tree,但对于主要的两种存储引擎的实现方式是不同的。

  MyISAM: B+Tree叶节点的data域存放的是数据记录的地址。在索引检索的时候,首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其 data 域的值,然后以 data 域的值为地址读取相应的数据记录。这被称为“非聚簇索引”。

  InnoDB: 其数据文件本身就是索引文件。相比MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按B+Tree组织的一个索引结构,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。这被称为“聚簇索引(或聚集索引)”。而其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方。在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。 因此,在设计表的时候,不建议使用过长的字段作为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。

查询缓存的使用

my.cnf加入以下配置,重启Mysql开启查询缓存

1
2
query_cache_type=1
query_cache_size=600000

Mysql执行以下命令也可以开启查询缓存

1
2
set global  query_cache_type=1;
set global query_cache_size=600000;

如上,开启查询缓存后在同样的查询条件以及数据情况下,会直接在缓存中返回结果。这里的查询条件包括查询本身、当前要查询的数据库、客户端协议版本号等一些可能影响结果的信息。因此任何两个查询在任何字符上的不同都会导致缓存不命中。此外,如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、Mysql库中的系统表,其查询结果也不会被缓存。

缓存建立之后,Mysql的查询缓存系统会跟踪查询中涉及的每张表,如果这些表(数据或结构)发生变化,那么和这张表相关的所有缓存数据都将失效。

缓存虽然能够提升数据库的查询性能,但是缓存同时也带来了额外的开销,每次查询后都要做一次缓存操作,失效后还要销毁。 因此,开启缓存查询要谨慎,尤其对于写密集的应用来说更是如此。如果开启,要注意合理控制缓存空间大小,一般来说其大小设置为几十MB比较合适。此外,还可以通过sql_cache和sql_no_cache来控制某个查询语句是否需要缓存:

1
select sql_no_cache count(*) from usr;

事务机制

关系性数据库需要遵循ACID规则,具体内容如下:

  1. 原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
  2. 一致性: 执行事务前后,数据库从一个一致性状态转换到另一个一致性状态。
  3. 隔离性: 并发访问数据库时,一个用户的事物不被其他事务所干扰,各并发事务之间数据库是独立的;
  4. 持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库 发生故障也不应该对其有任何影响。

为了达到上述事务特性,数据库定义了几种不同的事务隔离级别:

  • READ_UNCOMMITTED(未提交读): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
  • READ_COMMITTED(提交读): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • REPEATABLE_READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • SERIALIZABLE(串行): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

锁机制与InnoDB锁算法

MyISAM和InnoDB存储引擎使用的锁:

  • MyISAM采用表级锁(table-level locking)。

  • InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁

    表级锁和行级锁对比:

    • 表级锁: Mysql中锁定 粒度最大 的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁。
    • 行级锁: Mysql中锁定 粒度最小 的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。

    详细内容可以参考: Mysql锁机制简单了解一下

InnoDB存储引擎的锁的算法有三种:

  • Record lock:单个行记录上的锁

  • Gap lock:间隙锁,锁定一个范围,不包括记录本身

  • Next-key lock:record+gap 锁定一个范围,包含记录本身

    相关知识点:

    1. innodb对于行的查询使用next-key lock
    2. Next-locking keying为了解决Phantom Problem幻读问题
    3. 当查询的索引含有唯一属性时,将next-key lock降级为record key
    4. Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生
    5. 有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock) A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1
0%