第五章 创建高性能的索引 此处的知识点需要借助《数据结构与算法》这更有助于理解
  1. 索引基础
  • 索引可以包含一个或多个列的值。如果索引包含多个列,那么列的顺序也十分重要,因为MySQL只能高效地使用索引的最左前缀列。创建一个包含两个列的索引,和创建两个只包含一列的索引是大不相同的。
  • B-Tree索引能够加快访问数据的速度,因为存储引擎不再需要进行全表扫描来获取需要的数据,取而代之的是从索引的根节点开始进行搜索。根节点的槽中存放了指向子节点的指针,存储引擎根据这些指针向下层查找。通过比较节点页的值和要查找的值可以找到合适的指针进入下层子节点,这些指针实际上定义了子节点页中值的上限和下限。最终存储引擎要么是找到对应的值,要么该记录不存在。
  • 索引对多个值进行排序的依据是create table语句中定义索引时列的顺序。
  • 在MySQL中索引实在存储引擎层而不是服务器层实现的。所以,并没有统一的索引标准:不同存储引擎的索引的工作方式并不一样,也不是所有的存储引擎都支持所有类型的索引。即使多个存储引擎支持同一种类型的索引,其底层的实现也可能不同。
  • B-Tree索引的限制(实际上很多存储引擎使用的是B+Tree,即每一个叶子节点都包含指向下一个叶子节点的指针,从而方便叶子节点的范围遍历)。
create table people (last_name varchar(50) not null,first_name varchar(50) not null,dob date not null,gender enum('m','f') not null,key (last_name,first_name,dob)
);
  • B-tree 索引的限制

    • 如果不是按照索引的最左列开始查找,则无法使用索引。例如上面例子中的索引无法用于查找bill的人,也无法查找某个特定生日的人,因为这两列不都是最左数据列。类似地,也无法查找姓氏以某个字母结尾的人。
    • 不能跳过索引中的列,也就是说,前面所述的索引无法用于查找姓为Smith并且在某个特定日期出生的人。如果不指定名(first_name),则MySQL只能使用索引的第一列。
    • 如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化 查找。例如:有查询where last_name='Smith' and first_name like 'J%' and dob = '1976-12-23'这个查询只能使用索引的前两列,因为这里like是一个范围条件(但是服务器可以把其余列用于其他目的)。如果范围查询列值的数量有限,那么可以通过使用多个等于条件来替代范围条件。
  • myisam使用前缀压缩技术使得索引更小,但innodb则按照原数据进行存储。再如myisam索引通过数据的物理位置引用被索引的行,而innodb则根据主键索引引用被索引的行。

  • 哈希索引的限制:

    • 哈希索引只包含哈希值和行指针,而不存储字段值,所以不能使用索引中的值来避免读取行。不过,访问内存中的行的速度很快,所以大部分情况下这一点对性能的影响并不明显。
    • 哈希索引数据并不是按照索引值顺序存储的,所以也就无法用于排序。
    • 哈希索引也不支持部分索引列匹配查找,因为哈希索引始终是使用索引列的全部内容来计算哈希值的。例如,在数据列(A/B)上建立哈希索引,如果查询只有数据列A,则无法使用该索引。
    • 哈希索引只支持等值比较查询,包括=、IN()、<=>(安全相等),也不支持任何范围查询,例如:where price > 100.
      访问哈希索引的数据非常快,除非有很多哈希冲突(不同的索引列值却有相同的哈希值)。当出现哈希冲突的时候,存储引擎必须遍历链表中所有的行指针,逐行进行比较,直到找到所有符合条件的行。
    • 如果哈希冲突很多的话,一些索引维护操作的代价也会很高。例如:如果在某个选择性很低(哈希冲突很多)的列上建立哈希索引,那么当从表中删除一行时,存储引擎需要编列对应哈希值的链表中的每一行,找到并删除对应行的引用,冲突越多,代价越大。
  • innodb引擎有一个特殊的功能叫做:‘自适应哈希索引’。当innodb注意到某些索引值被使用的非常频繁时,它会在内存中基于B-Tree索引之上在创建一个哈希索引,这样就让B-Tree也具有哈希索引的一些优点,比如快速的哈希查找。这是一个完全自动的,内部的行为,用户无法控制或者配置,不过如果有必要,完全可以关闭该功能。

  • 如果采用哈希索引,记住不要使用SHA1()和MD5()作为哈希函数。因为这两个函数计算出来的哈希值是非常长的字符串,会浪费大量空间,比较时也会更慢。SHA1()和MD5()是强加密函数,设计目标是最大限度消除冲突,但这里并不需要这样高的要求。简单哈希函数的冲突在一个可以接受的范围,同时又能够提供更好的性能。

  • 处理哈希冲突,当使用哈希索引进行查询的时候,必须在where子句中包含常量值。如果不是想查询具体值,例如只是统计记录数(不精确的),则可以不带入列值,直接使用CEC32()的哈希值查询即可。

# URL列值比较适合哈希索引,需要存储大量的URL,并需要根据URL进行搜索查找。如果使用B-Tree来存储URL,存储的内容就会很大,因为URL本身都很长。
SELECT word,crc FROM words WHERE crc = CRC32('gnu') AND word = 'gnu';
  • 空间数据索引(R-Tree)MySQL的GIS支持并不完善,所以大部分人都不会使用特性。开源关系数据库系统中对GIS的解决方案做的比较好的事PostgreSQL的PostGIS.
  • 全文索引:是一种特殊类型的索引,它查找的是文本中的关键词,而不是直接比较索引中的值。
  1. 索引的优点
  • 索引大大减少了服务器需要扫描的数据量。
  • 索引可以帮助服务器避免排序和临时表。
  • 索引可以将随机I/O变为顺序I/O。
  1. 高效性能的索引策略

3.1 独立的列

  • 查询中的列不是独立的,则MySQL就不会使用索引。‘独立的列’是指索引列不能是表达式的一部分,也不能是函数的参数。例如:SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;

3.2 前缀索引和索引选择性

  • 针对索引很长的字符列,这会让索引变得大且慢。一个策略是前面提到过的模拟哈希索引。再则就是索引部分字符
  • 索引的选择性是指:不重复的索引值和数据表的记录总数(#T)的比值,范围从1/#T到1之间,索引的选择性越高则查询效率越高,因为选择性高的索引可以让MySQL在查找时过滤掉更多的行。唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的
  • 对于blob、text或者很长的varchar类型的列,必须使用前缀索引,因为MySQL不允许索引这些列的完整长度。
  • 选择足够长的前缀以保证较高的选择性,同时又不能太长(以便节省空间)前缀应该足够长,以使的前缀索引的选择性接近于索引整个列。换句话说,前缀的‘基数’应该接近于完整列的‘基数’。
  • 前缀索引是一种能使索引更小,更快的有效办法,但第一方面也有其缺点:MySQL无法使用前缀索引做order by 和 group by,也无法使用前缀索引做覆盖扫描。
  • 创建前缀索引(前7个字符) ALTER TABLE sakila.city_demo ADD KEY(city(7));
  • 有时候后缀索引也有用途(例如找到某个域名的所有电子邮件地址)。MySQL原生并不支持反向索引,但是可以把字符串反转后存储,并基于此索引建立前缀索引。可以通过触发器来维护这种索引。

索引评判标准——三星系统:索引将相关的记录放到一起则获得一星;如果索引中的数据顺序和查找中的排列顺序一致则获得二星;如果索引中的列包含了查询中需要的全部列则获得三星。

3.3 多列索引

  • 索引合并策略有时候是一种优化的结果,但实际上更多时候说明了表上的索引建得很糟糕:

    • 当出现服务器对多个索引做相交操作时(通常用多个AND条件),通常意味着需要一个包含所有相关列的多列索引,而不是多个独立的单列索引。
    • 当服务器需要对多个索引做联合操作时(通常有多个OR条件),通常需要耗费大量CPU和内存资源在算法的缓存、排序和合并操作上。特别是当其中有些索引的选择性不高,需要合并扫描返回的大量数据的时候。
    • 更重要的是,优化器不会把这些计算到“查询成本”中,优化器只关心随机页面读取。这会使得查询的成本被“低谷”,导致该执行计划还不如直接走全表扫描,这样做不但会消耗更多的CPU和内存资源,还可能会影响查询的并发性,但如果是单独运行这样的查询则往往会忽略对并发性的影响。通常来说,还不如像在MySQL1.1或者更早的时代一样,将查询改写成union的方式往往更好。
    • 如果explain中看到有索引合并,应该好好检查一下查询和表的结构,看是不是已经是最优的。也可以通过参数optimizer_switch来关闭索引合并功能。也可以使用ignore index来提示让优化器忽略掉某些索引。

3.4 选择合适的索引列顺序

  • 对于如何选择索引的列顺序有一个经验法则:将选择性最高的列放到索引的最前列。
  • 当不需要考虑排序和分组时,将选择性最高的列放在前面通常是很好的。这时候索引的作用只是用于优化WHERE条件的查找。在这种情况下,这样设计的索引确实能够最快地过滤出需要的行,对于在WHERE子句中只使用了索引部分前缀列的查询来说选择性也更高。然而,性能不只是依赖于所有索引列的选择性(整体基数),也和查询条件的具体值有关,也就是和值的分布有关。
  • 真实案列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dU5W3781-1611310387054)(http://images1.poetchao.com//mysql/01.png)]

3.5 聚簇索引

  • 聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。

  • 术语“聚簇”表示数据行和相邻的健值紧凑地存储在一起。

  • 因为无法同时把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引(不过,覆盖索引可以模拟多个聚簇索引的情况)。

  • 存储引擎负责实现索引,因此不是所有的存储引擎都支持聚簇索引的。

  • 一些数据库服务器允许选择哪个索引作为聚簇索引,目前还没有任何一个MySQL内键的存储引擎支持这一点。innodb将通过主键聚集数据。如果没有定义主键,innodb会选择一个唯一的非空索引替代(InnoDB将会自动增加一个6字节(48位)的整数列,被叫做行ID)。如果没有这样的索引,innodb会隐式定义一个主键来作为聚簇索引。innodb只聚集在同一个页面中的记录,包含相邻健值的页面可能会相距甚远。

  • 聚集的数据的优点:

    • 可以把相关数据保存在一起。例如实现电子邮箱时,可以根据用户ID来聚集数据,这样只需要从磁盘读取少数的数据页就能获取某个用户的全部邮件,如果没有使用聚簇索引,则每封邮件可能导致一次磁盘I/O。
    • 数据访问更快。聚簇索引将索引和数据保存在同一个B-Tree中,因此从聚簇索引中获取数据通常比在非聚簇索引中查找要快。
    • 使用覆盖索引扫描的查询可以直接使用页节点的主键值。
  • 聚集的数据的缺点:

    • 聚簇数据最大限度地提高了I/O密集型应用的性能,但如果数据全部都放在内存中,则访问的顺序就没那么重要了,聚簇索引也就没有什么优势了。
    • 插入速度严重依赖于插入顺序。按照主键的顺序插入是加载数据到innodb表中速度最快的方式。但如果不是按照主键顺序加载数据,那么在加载完成后最好使用 OPTIMIZE TABLE 阅读命令重新组织一下表。
    • 更新聚簇索引列的代价很高,因为会强制innodb将每个被更新的行移动到新的位置。
    • 基于聚簇索引的表在插入新行,或者主键被更新导致需要移动行的时候,可能面临‘页分裂’的问题。当行的主键值要求必须将这一行插入到某个已满的页中时,存储引擎会将该页分裂成两个页面来容纳该行,这就是一次页分裂操作,页分裂会导致表占用更多的磁盘空间。
    • 聚簇索引可能导致全表扫描变慢,尤其是行比较稀疏,或者由于页分裂导致数据存储不连续的时候。
    • 二级索引(非聚簇索引)可能比想象的要更大,因为在二级索引的叶子节点包含了引用行的主键列。
    • 二级索引访问需要两次索引查找,而不是一次。
  • innodb的二级索引和聚簇索引很不相同。innodb二级索引的叶子节点中存储的不是“行指针”,而是主键值,并以此作为指向行的“指针”。这样的策略减少了当出现行移动或者数据页分裂时二级索引的维护工作。使用主键值当作指针会让二级索引占用更多的空间,换来的好处是,innodb在移动行时无须更新二级索引中的这个“指针”。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hzf8O5mt-1611310387057)(http://images1.poetchao.com//mysql/001.png)]

  • 最好避免随机的(不连续且值的分布范围非常大)聚簇索引,特别是对于I/O密集型的应用。例如:从性能的角度考虑,使用UUID来作为聚簇索引则会很糟糕:它使得聚簇索引的插入变得完全随机,这是最坏的情况,使得数据没有任何聚集特性。
  • innodb默认的最大值填充因子是页大小的15/16,留出部分空间用于以后修改。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-etO2jSZS-1611310387058)(http://images1.poetchao.com//mysql/002.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-890cw24O-1611310387060)(http://images1.poetchao.com//mysql/003.png)]

  • 顺序的主键什么时候会造成更坏的结果?

    • 对于高并发工作负载,在innodb中按主键顺序插入可能会造成明显的争用。主键的上界会成为‘热点’。因为所有的插入都发生在这里,所以并发插入可能导致间隙锁竞争。另一个热点可能是auto_increment锁机制;如果遇到这个问题,则可能需要考虑重新设计表或者应用,或者更改innodb_autoinc_lock_mode配置。

3.6 覆盖索引

  • 如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。
  • 不是所有类型的索引都可以成为覆盖索引。覆盖索引必须要存储索引列的值,而哈希索引、空间索引和全文索引等都不存储索引列的值,所以MySQL只能使用B-Tree索引做覆盖索引,不同的存储引擎实现覆盖索引的方式也不同。
  • mysql不能在索引中执行LIKE操作。这是底层存储引擎API限制,MySQL5.5和更早的版本中只允许在索引中做简单比较操作(例如:等于、不等于以及大于)。MySQL能在索引中做最左前缀匹配的LIKE比较,因为该操作可以转换为简单的比较操作,但是如果是通配符开头的LIKE查询,存储引擎就无法做比较匹配。这种情况下,MySQL服务器只能提取数据行的值而不是索引值来做比较。

3.7 使用索引扫描来做排序

  • mysql有两种方式可以生成有序的结果:通过排序操作;或者按索引顺序扫描;如果explain出来的type列的值为‘index’,则说明mysql使用了索引扫描来做排序。
  • 只有当索引的列顺序和order by子句的顺序完全一致,并且所有列的排序方向(倒序或正序)都一样时,mysql才能够使用索引来对结果做排序。如果查询需要关联多张表,则只有当order by子句引用的字段全部为第一个表时,才能使用索引做排序,order by 子句和查找型查询的限制是一样的:需要满足索引的最左前缀的要求;否则,mysql都需要执行排序操作,而无法利用索引排序。

书籍 -- 《高性能MySQL》持续更新中(四)相关推荐

  1. 在线书籍推荐(持续更新中)

    BASH Advanced Bash-Scripting Guide Bash Reference Manual The Linux kernel user-space API guide

  2. mysql查询更新优化_mysql查询优化(持续更新中)

    1.索引不会包含有NULL值的列 (1)   应尽量避免在where子句中对字段进行null值判断,否则将导致引擎放弃使用索引而进行全表扫描 (2)   数据库设计时不要让字段的默认值为null,可以 ...

  3. 【2019年6月全新大学英语四六级】商志英语4级 6级 CET4 CET6 持续更新中 资料网盘

    [2019年6月全新大学英语四六级]商志英语4级 6级 CET4 CET6 持续更新中 资料网盘 [2019年大学英语四六级]持续更新中!!! 链接:https://pan.baidu.com/s/1 ...

  4. 面试JAVA常被问到的问题(持续更新中)

    引言 有的面试会被问到有没有写博客,这时候我尴尬,不知道怎么回答,所以这篇文章仅仅是把我面试JAVA的遇到的问题记录下来而已,也算是我写博客迈出的第一步,起码,以后被问到:有没有写博客?我可以回答,我 ...

  5. 个人知识库(持续更新中)

    打造一个属于自己的知识库 为什么会有这个知识库 四大件 数据结构与算法 计算机网络 操作系统 计算机组成原理 基础知识 JavaSE 数据库 Java Web SSM框架 微服务开发 Linux运维 ...

  6. 面试1:Java、微服务、架构常见面试题(持续更新中)

    Java.微服务.架构常见面试题(持续更新中) 文章目录 Java.微服务.架构常见面试题(持续更新中) ==**Java**== 1.Java概述 (1)JVM.JRE和JDK (2)Java特点 ...

  7. 我学习 Java 的历程和体会(写给新手看,欢迎老司机批评和建议,持续更新中)

    我学习 Java 的历程和体会(写给新手看,欢迎老司机批评和建议,持续更新中) 最初写这篇文章的时候,是在今年的 9 月中旬.今天,我想再写写这将近两个多月以来的感受. 在今年的 10 月我来到北京求 ...

  8. JAVA面试大全(持续更新中...)

    本文旨在收集Java面试过程中出现的问题,力求全面,仅作学习交流,欢迎补充,持续更新中-,部分段落选取自网上,部分引用文章已标注,部分已记不清了,如侵权,联系本人 Java基础 1.面向对象的概述 面 ...

  9. 2020年拼多多校招面试题及答案-最全最新-持续更新中

    大家好我是好好学习天天编程的天天 一个整天在互联网上种菜和砍柴的程序员 2020年拼多多校招面试题及答案-最全最新-持续更新中 2020年拼多多校招面试题一面-牛客网 2020年拼多多校招面试题二面- ...

  10. 若依微服务框架ruoyi-cloud使用手册(持续更新中)

    若依微服务框架ruoyi-cloud使用手册(持续更新中) 一.项目启动事项 二.新建功能模块案例 三.不同微服务系统间接口调用案例 四.服务器部署 五.一些坑~ 一.项目启动事项 1.首先进行项目相 ...

最新文章

  1. Microsoft Academic Search vs Google Scholar
  2. CF#212 Two Semiknights Meet
  3. 在Ubuntu 8.04上安装Domino R8.02
  4. Linux学习之CentOS(八)--Linux系统的分区概念
  5. KUKA profesafe
  6. android 增加触摸范围,android seekBar 增加点击和滑动范围
  7. Leetcode--105. 从前序与中序遍历序列构造二叉树(Java)
  8. 从键盘中读取字符流 自定义异常
  9. Java中判断一个字符串全为数字和字母
  10. 数据库与表的操作之创建表(CREATE TABLE)
  11. 一元、二元、三元逻辑运算符
  12. IntelliJ IDEA使用技巧(七)——常用快捷键Mac篇
  13. 绵阳市:充分利用区块链等技术 为农民工证照办理提供线上便捷服务
  14. C# Udp测试工具开发
  15. 迅雷mac版精简教程
  16. 鼠标滑过图片文字遮罩效果
  17. python股票回测_Python量化投资框架:回测+模拟+实盘
  18. 回忆我的过去一年2020年考研以及研究生规划
  19. 用约束规划+概率图模型(信念传播)+神经网络端到端求解组合优化问题
  20. Android 11.0 MTK去掉开机通过长按电源键+音量加进入recovery 工厂模式

热门文章

  1. 最大后验估计(Maximum-a-Posteriori (MAP) Estimation) 【转】
  2. 计算机磁盘为uefi引导,科普一分钟|UEFI引导+GPT硬盘格式装系统方法
  3. 递归与迭代,台阶问题,斐波那契,分治算法
  4. java gdal_gdal java环境配置
  5. inventor 波纹阵列_Inventor技巧之草图驱动的阵列
  6. 数据库字段类型CHAR和INT
  7. python参数估计_最小二乘与最大似然参数估计及Python实现
  8. 如何解决Gridea部分主题不渲染Katex的问题
  9. Android UI - 粒子爆炸特效
  10. Mybatis配置文件http://mybatis.org/dtd/mybatis-3-config.dtd报错