1. B-Tree索引(创建多索引时,索引列的顺序非常重要,第一个索引列是最重要的)
a. B-Tree索引适用于全值匹配(和所有索引列匹配)、匹配最左前缀(只使用第一项索引列)、匹配列前缀(匹配某个索引列的开头部分,如查找以J开头的姓的人)、匹配范围值(索引列的范围,从xx到xx)、精确匹配某一列并范围匹配某一列
b. 一些限制:必须要从最左的索引列开始查找,否则无法使用索引,比如如果不使用第一列索引列而只使用第二第三索引列,将无效;如果查询中有某个索引列使用范围查找而不是精准匹配,则其右边的索引列都无法使用索引
2. 哈希索引hash index
a. 索引自身只需存储对应的哈希值,所以索引的结构十分紧凑,所以哈希查找的速度非常快
b. 限制:哈希索引只包含哈希值和指向哪一行的指针,并不储存字段值,所以不能直接取值而是根据指针去找值,不过访问内存中的行的速度很快,所以无大碍;哈希索引数据并不是按照索引值顺序排列储存的,所以无法用于排序;哈希索引不支持部分索引列匹配查找,比如在A, B两列上建立哈希索引,如果只用A列,则无法使用索引;哈希索引只支持等值比较查询,不支持任何范围查询;哈希冲突很多的话,删除一行就需要遍历对应哈希值得链表的每一行来找到并删除该行,维护操作的代价搞
c. InnoDB引擎有一个特殊功能“自适应哈希索引”,当引擎注意到某些索引值被使用得非常频繁时,会在内存中基于B-Tree索引之上再创建一个哈希索引。这是一个全自动的、内部的行为,用户无法控制或者配置,但有需要可以关闭
3. 索引的好处
a. 大大减少服务器需要扫描的数据量
b. 索引可以帮助服务器避免排序和临时表
c. 索引可以将随机I/O变为顺序I/O
4. 一些索引策略
a. 前缀索引(如果索引列数据很长的情况下可考虑),只对一个列数据的前几个字符建立索引,如对城市名的前七个字符建立索引:ALTER TABLE sakila.city_demo ADD KEY (city(7)); 缺点是无法使用前缀索引做ORDER BY和GROUP BY,也无法做覆盖扫描
b. 如果要对多个列建立索引,索引列顺序是很重要的。一个经验法则:将选择性最高的列放到索引最前列(选择性的计算请看P155)
c. InnoDB的主键索引是聚簇的,每一个都保存了主键列、事务ID、回滚指针和其他的剩余列,可以说InnoDB的主键索引本身就是一张“表”。而InnoDB的二级索引(也就是非主键的索引)是非聚簇的,里面只包含主键值和二级索引列的值,所以查找的时候要先得到主键值,然后根据主键值到主键索引里面查找目标行的数据,进行了两次B-Tree查找
d. MyISAM的主键索引和二级索引都是非聚簇的,MyISAM本身自带一个行号,主键索引和二级索引储存的都是一个行号加上一个主键列值/二级索引列的列值
5. 查询
a. 切分查询:比如如果要定期清除大量数据,可以在应用代码层把它切分查询,一次删除一万行,这样就能将服务器上原本一次性的压力分散到一个较长的时间段中,大大降低对服务器的影响和减少删除时锁的持有时间:
1 rowsAffected = 0; 2 do { 3 rowsAffected = doQuery( 4 "DELETE FROM messages WHERE created < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 10000") 5 } while rowsAffected > 0;
b. 分解关联查询,把x JOIN x ON xx = xx的那些查询分解为几个单个查询,能使缓存效率更高(单表查询对应的结果更方便缓存;对应MySQL的查询缓存,如果关联中的某个表发生了变化,就无法使用缓存了)、执行单个查询可以减少锁的竞争、查询本身也可能会有所提升(以下的例子就是用IN()来代替关联查询,可以让MySQL按照ID顺序进行查询,可能比随机的关联更高效):
1 SELECT * FROM tag 2 JOIN tag_post ON tag_post.tag_id = tag.id 3 JOIN post ON tag_post.post_id = post.id 4 WHERE tag.tag = 'mysql'; 5 6 //改为 7 SELECT * FROM tag WHERE tag = 'mysql'; 8 SELECT * FROM tag_post WHERE tag_id = 1234; 9 SELECT * FROM post WHERE post.id in (123, 456, 567, 9090);
c. MySQL客户端和服务器之间的通信协议是“半双工的”,意味着在任何一个时刻,要么是由服务器向客户端发送数据,要么是由客户端向服务器发送数据,这两个动作不能同时发生。这种协议让MySQL通信简单快速,但是也从许多地方限制了MySQL,所以参数max_allowed_packet就特别重要了,如果查询太大,服务端会拒绝接收更多的数据并抛出相应错误。相反的,一般MySQL服务器反映给用户的数据通常很多,由多个数据包组成,当服务器开始响应客户端请求时,客户端必须完整地接受整个返回结果,而不能只取一些结果然后断开,这就是为什么必要时一定要在查询中加上LIMIT限制的原因
d. MySQL执行查询的过程: 客户端发送一条查询给服务器 ——》服务器先检查查询缓存,如果命中了缓存,就立刻返回结果,否则继续 ——》服务器端进行SQL解析、预处理,再由优化器生成对应的执行计划 ——》MySQL根据优化器生成的执行计划,调用储存引擎的API来执行查询 ——》将结果返回给客户端
e. 用关联查询的时候,关联查询优化器会自动评估并选择一个代价最小的关联顺序,比如A, B, C三张表关联,可能先从C表查询而不是A表查询,这样代价最小,能筛选出更少的行数,然后再筛选
d. 排序是成本很高的操作,应尽可能避免排序或者尽可能避免对大量数据排序
e. 如果使用IN(), 里面又是一个子查询的话,查询的性能会很差