使用哈希索引时,存储引擎会对索引列的值进行哈希运算,并将计算出的哈希值和指向该行数据的指针存储在索引中,因此它更适用于等值查询,而不是范围查询,同样也不能用于优化排序和分组等操作。在建立哈希索引时,需要选取选择性比较高的列,即列上的数据不容易重复 (如身份证号),这样可以尽量避免哈希冲突。因为哈希索引并不需要存储索引列的数据,所以其结构比较紧凑,对应的查询速度也比较快。
InnoDB 引擎有一个名为 “自适应哈希索引 (adaptive hash index)” 的功能,当某些索引值被频繁使用时,它会在内存中基于 B+ tree 索引再创建一个哈希索引,从而让 B-Tree 索引具备哈希索引快速查找的优点。
2.4 索引的优点索引极大减少了服务器需要扫描的数据量;
索引可以帮助服务器避免排序和临时表;
索引可以将随机 IO 转换为顺序 IO。
2.5 使用策略在查询时,应该避免在索引列上使用函数或者表达式。
对于多列索引,应该按照使用频率由高到低的顺序建立联合索引。
尽量避免创建冗余的索引。如存在索引 (A,B),接着又创建了索引 A,因为索引 A 是索引 (A,B) 的前缀索引,从而出现冗余。
建立索引时,应该考虑查询时候的排序和分组的需求。只有当索引列的顺序和 ORDER BY 字句的顺序完全一致,并且遵循同样的升序或降序规则时候,MySQL 才会使用索引来对结果做排序。
三、锁 3.1 共享锁与排它锁InnoDB 存储引擎支持以下两种标准的行级锁:
共享锁 (S Lock,又称读锁) :允许加锁事务读取数据;
排它锁 (X Lock,又称写锁) :允许加锁事务删除或者修改数据。
排它锁和共享锁的兼容情况如下:
X XX 不兼容 不兼容
S 不兼容 兼容
3.2 意向共享锁与意向排它锁
为了说明意向锁的作用,这里先引入一个案例:假设事务 A 利用 S 锁锁住了表中的某一行,让其只能读不能写。之后事务 B 尝试申请整个表的写锁,如果事务 B 申请成功,那么理论上它就应该能修改表中的任意一行,这与事务 A 持有的行锁是冲突的。想要解决这个问题,数据库必须知道表中某一行已经被锁定,从而在事务 B 尝试申请整个表的写锁时阻塞它。想要知道表中某一行被锁定,可以对表的每一行进行遍历,这种方式可行但是性能比较差,所以 InnoDB 引入了意向锁。
意向共享锁 (IS Lock) :当前表中某行或者某几行数据存在共享锁;
意向排它锁 (LX Lock) :当前表中某行或者某几行数据存在排它锁。
按照意向锁的规则,当上面的事务 A 给表中的某一行加 S 锁时,会同时给表加上 IS 锁,之后事务 B 尝试获取表的 X 锁时,由于 X 锁与 IS 锁并不兼容,所以事务 B 会被阻塞。
X IX S ISX 不兼容 不兼容 不兼容 不兼容
IX 不兼容 兼容 不兼容 兼容
S 不兼容 不兼容 兼容 兼容
IS 不兼容 兼容 兼容 兼容
3.3 一致性读
1. 一致性非锁定读
一致非锁定读 (consistent nonlocking read) 是指在 InnoDB 存储引擎下,如果将要读取的行正在执行 DELETE 或 UPDATE 操作,此时不必去等待行上锁的释放,而是去读取 undo 日志上该行的快照数据,具体如下:
在 READ COMMITTED 事务隔离级别下,读取被锁定行的最新一份快照数据;
在 REPEATABLE READ 事务隔离级别下,读取事务开始时所处版本的数据。
基于多版本并发控制和一致性非锁定读,可以避免获取锁的等待,从而提高并发访问下的性能。
2. 一致性锁定读
一致性锁定读则允许用户按照自己的需求在进行 SELECT 操作时手动加锁,通常有以下两种方式:
SELECT ... FOR SHARE:在读取行上加 S 锁;
SELECT ... FOR UPDATE:在读取行上加 X 锁。
3.4 锁的算法InnoDB 存储引擎支持以下三种锁的算法:
Record Lock:行锁,用于锁定单个行记录。示例如下:
-- 利用行锁可以防止其他事务更新或删除该行 SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;