mysql优化参考(二)-索引
参考:https://dev.mysql.com/doc/refman/5.7/en/mysql-indexes.html
索引(Indexs)
一、作用(轻定义):索引用于快速查找具有特定列值的行。没有索引,MySQL必须从第一行开始,然后通读整个表以找到相关的行。表越大,花费时间越长。如果表中有相关列的索引,MySQL可以快速确定要在数据文件中间查找的位置,而不必查看所有数据。这比顺序读取每一行要快得多。
- 减少服务器(Mysql-Server)需要扫描的数据量
 - 减少文件排序和临时表(子查询可能产生临时表)(Order By 全表排序)
 - 随机读取->顺序读取(速度更快)
 
二、数据结构:B+树(官方写的是B树)-与二叉树不同:多个分叉
- 文档
 - 与存储引擎的关系:InnoDB、MyIsAM,Memory也可以使用(但一般使用哈希表会更合适)。B树更适合范围查询,哈希表更适合值匹配
 
- 
WHERE中作为查询条件快速查找与子句匹配的行。 - 
从考虑中消除行(possible_keys中有多个可考虑的索引的时候,mysql通常会选择最少行数的索引)。最具 选择性的索引)。
 - 
如果表具有多列索引,那么优化器可以使用索引的任何最左前缀来查找行。举例来说,如果你有一个三列的索引
(col1, col2, col3),你有索引的搜索功能(col1),(col1, col2)以及(col1, col2, col3)。有关更多信息,请参见 第8.3.5节“多列索引”(https://dev.mysql.com/doc/refman/5.7/en/multiple-column-indexes.html)。【这个指的应该是组合索引,会使用最左匹配,优先用组合索引最左的进行匹配】 - 
执行联接时从其他表中检索行。如果声明相同的类型和大小,MySQL可以更有效地在列上使用索引。在这种情况下,
VARCHAR与CHAR被认为是相同的,如果它们被声明为相同的大小。例如,VARCHAR(10)和CHAR(10)是相同的大小,但是VARCHAR(10)和CHAR(15)不是。【应该是指用于连接两个表的字段大小一致】对于非二进制字符串列之间的比较,两个列应使用相同的字符集。例如,将一
utf8列与一latin1列进行比较会排除使用索引。如果不能不通过转换直接比较值,则比较不同的列(例如,将字符串列与时间或数字列进行比较)可能会阻止使用索引。对于给定的值,如
1在数值列,它可能比较等于在字符串列,例如任何数量的值'1',' 1','00001',或'01.e1'。这排除了对字符串列使用任何索引的可能性。 - 
查找特定索引列的
MIN()或MAX()值key_col。这由预处理器优化,该预处理器检查您是否正在 索引中之前出现的所有关键部分上使用。在这种情况下,MySQL为每个表达式或 表达式执行一次键查找,并将其替换为常量。如果所有表达式都用常量替换,查询将立即返回。例如:WHEREkey_part_N=constantkey_colMIN()MAX()SELECT MIN(key_part2),MAX(key_part2) FROM tbl_name WHERE key_part1=10; - 
如果排序或分组是在可用索引的最左前缀(例如)上完成的,则对表进行排序或分组 。如果所有关键部分后面都有,则按相反顺序读取密钥。请参见 第8.2.1.14节“按优化排序”和 第8.2.1.15节“按优化分组”。
ORDER BYkey_part1,key_part2DESC - 
在某些情况下,可以优化查询以检索值而无需查询数据行。(为查询提供所有必要结果的索引称为 覆盖索引。)如果查询仅从表中使用某些索引中包含的列,则可以从索引树中检索所选值,以提高速度:
SELECT key_part3 FROM tbl_name WHERE key_part1=1 
四、索引类型(为主键和外键默认建立索引)
- 主键索引:从非空约束中受益,非空在索引执行效率上有帮助(避免一些复杂的比较、范围判断等)
 - 唯一索引:
 - 普通索引:
 - 全文索引:
 - 组合索引:
 
五、索引的匹配方式
- 全值匹配:比如a,b,c组合索引, where a=1 and b=2 and c=3
 - 匹配最左前缀:比如a,b,c组合索引,where a=1 and b=2
 - 匹配列前缀:比如a索引,where a like 'ga%'(尝试是range,其他%ga%是index,倒数第二差的方式)
 - 匹配范围值:比如a索引,where a > 1
 - 精确匹配一列并范围匹配另一列:比如a,b组合索引,where a=1 and b>2
 - 只访问索引的查询(覆盖索引):查询的列都建了索引
 
参考材料: sakila database
六、一些名词概念
- 哈希索引:一般用于memory内存型引擎
- 按照哈希值排序,所以相当于无序
 - 因为存储的事哈希值,所以结构比较紧凑
 - 一般仅适用于精确查找,不适合范围查找
 - 缺点:
- 哈希索引只包含索引值和行指针,而不存储字段值,不能用于覆盖索引
 - 无序
 - 不支持部分匹配和范围匹配
 - 哈希冲突的时候需要逐行访问比较,效率受到影响;哈希冲突严重的话,影响插入和查询性能【哈希冲突如何解决:除非很牛逼,不然只能用别人写好的哈希算法:crc32(循环冗余校验)】
 
 
 - 聚簇索引:InnoDB(数据和索引一起)
- 优点:
- 相关数据存储一起
 - 数据访问更快(访问索引的时候就可以得到数据)
 - 可以使用覆盖索引
 
 - 缺点
- 不适用内存模型
 - 插入速度依赖插入顺序,按照主键的顺序是最快的
 - 更新聚簇索引的代价很大,结合数组长度扩展理解
 - 页分裂问题 - 硬盘空间分为多个格子,索引的大小会影响其存入的方式,如低于格子的50%,可能产生页分裂;而如果大于的话,可能出现页合并
 - 影响全表扫描的速度,和页分裂也有一定关系
 
 
 - 优点:
 - 非聚簇索引:MyisAM(数据和索引分开)
 - 覆盖索引(Extra:Using Index)
- 一般查询的列都创建了索引的情况下即是覆盖索引
 - 需要索引包含了完整的列值
 - 像memory引擎就不支持覆盖索引,其存储的是哈希值和数据指针
 - 优势:
- 索引行大小通常小于行数据,减少访问数据的IO
 - 索引的顺序访问会提高IO效率
 - InnoDB的聚簇索引特性特别适用覆盖索引
 
 
 
七、优化技巧
- 减少函数或计算(where中)
 - 尽量触发主键索引,避免回表操作
 - 字符长度长的类型使用前缀索引(减少索引的数据量,但需要注意索引的选择性-重复性索引的问题)
 - 使用索引扫描排序,避免文件排序(order by不用索引,或者包含一个asc,一个desc也会造成文件排序)
 - union、all、in、or都能使用索引,但建议使用in
 - 范围列可以用到索引
- 使用范围查询之后,下一个索引无法命中(组合索引)
 
 - 强制类型转换会全表扫描
- 所以关联字段类型要一致,否则出现类型转换会无法使用索引
 
 - 更新十分频繁,数据区分度不高的字段不宜建立索引
- 更新频繁会不断触发索引构建,所以有些批量插入操作会临时关闭索引,插入完成之后再开启
- 开启:ALTER TABLE `test` ENABLE KEYS;
 - 关闭:ALTER TABLE `test` DISABLE KEYS;
 
 - 数据区分度比如这个列只有男和女两种类型,则就算建立了索引,也和哈希取模一样,会有大量的哈希冲突,需要实际到链表之中查询一样,和没有建立索引差别不大
- 区分度可以通过:select count(distinct columnName)/count(1) from tableNam; (一般超过百分之80可以建立索引)
 - 同样可以对字符串类型的进行前缀的区分度分析select count(distinct left(columnName, size))/count(1) from tableNam;
 
 
 - 更新频繁会不断触发索引构建,所以有些批量插入操作会临时关闭索引,插入完成之后再开启
 - 表连接最好不超过三张,连接字段类型必须一致,否则触发类型转换会使得索引失效
- cross join 笛卡尔积
 - (inner) join 内连接,左右顺序无关
 - (outer) join : left join | right join,left把左表所有数据查出,right把右表所有数据查出。
 
 - 明确结果只有一条的时候,limit 1性能更高(匹配到一行就会停止扫描),如果触发文件排序就没有差别了。
 - 单表索引建议5个以内
 - 单索引字段数不超过5个
 - 避免过早优化
 
附录:
回表:比如过滤条件是name,查询的字段是age,扫描b+树之后获取到的主键id,再通过主键ID去扫描出数据 【存在于普通索引】
索引覆盖:查询的字段直接在索引中,不需要回表查询;以回表的例子来看,我们把查询的字段改为主键id,那就不需要再扫描数据【查询计划中的Extra中会含有Using index】
最左匹配:一般用于优化过滤,优先过滤数据较少的表
索引下推:一般是指组合索引中,两个过滤条件命中的情况下,会一次过滤,而不是多次回表过滤
                    
                
                
            
        
浙公网安备 33010602011771号