人人都看得懂的MySQL索引

设计原理(减少磁盘IO次数)

  • 1、由于读写磁盘的速度与读写内存的速度差好几个数量级,并且一般我们数据库存放数据都很多,那么在设计索引时就要考虑如果在存放更多的数据量的同时减少磁盘IO读写的次数。

  • 2、由于磁盘预读的原因,即使我们只需要读取一个字节的数据,它也会帮我们附近的数据一次过读取出来,加载到内存中

因此我们在设计索引时,一般选择的数据结构是树,因此树的查找速度至于树的高度有关,并且我们可以建造一个有顺序的树,这样更符合磁盘预读的原理。因此可以设计每读一个树节点就是一个磁盘IO,MySQL里面页存储大小是16KB

为什么不选择红黑树

红黑树的高度太高了,一般一个节点只能存两个节点,如果我们有几千万的数据, 那个红黑树会很深,那我们要可能就要进行很多次IO,而B+树,由于其一个树节点只存放键值,只有叶子节点才存放数据,一个节点可以保存更多的数据,并且由于它是多路查找树,一个节点下来可以接很多子节点,因此它树的高度也不会太深,一般只有3-4层高度,即我们查找数据,只需要经历3-4次IO就能找到我们想要的数据了。

为什么不用散列表做索引

  • 由于散列表都是无序的,并且会发生哈希冲突。当使用拉链法解决哈希冲突时,查找速度会退化(不然JDK1.8也不会将链表转成红黑树)

  • 由于其是无序的,在做范围查询和排序的时候,如果数据不在同一个页,则会发生多次IO读写。

为什么不用B树

  • B树的普通节点也是会存放数据的,因此会减少一个节点存放的数据。

  • B树不利于我们做范围查询以及排序,因此B树并不是所有节点都会在最后一层,因此很难做范围查询以及排序,而B+树的叶子节点会有一个链表把所有叶子节点连在一起,很方便做范围查询以及排序操作。

索引组成

MySQL里面是属于二级索引,一般MySQL会帮我们我们的主键建立聚集索引,然后其他索引都是辅助索引,聚集索引的意思是,它的叶子节点会存放完整的行数据,而辅助索引的叶子节点只会存放主键值,也就是说如果我们要找到一行完整的数据,是需要经历两颗索引树的查找(后面MySQL自身的索引优化会从这里进行优化)

如何建立一个索引

  • 选择值唯一性高的列做索引(就是值尽量不一样)

  • 尽量不选择经常修改的列作为索引(这样会增加mysql维护索引的成本)

  • 如果经常要同时利用到两三个索引同时进行查询,这个情况尽量建立多列索引,并且要主要建立多列索引的顺序(因为mysql使用多列索引会根据最左匹配原则来匹配),譬如说有name、age两个列,很多情况我们会更加需要对名字进行查询,因此我们可以把name放在age前面。

最左匹配原则

假如我们根据name、age、birth三列建立了多列索引,那我们在写Sql语句的时候就要注意:

  • 1、无法跳过name直接去使用age做索引查询

  • 2、如果对某个列做了范围查询,那么该列右边的索引是无法使用的

  • 3、使用like查询时尽量不要同时使用左通配符,这样会导致无法使用索引

Mysql索引优化思路

  • Mysql自身的优化思路

    • multi range read:由于机械硬盘的随机读写是十分慢的,而multi range read 就是在使用辅助索引做范围查询和索引 join操作时,会先把辅助索引找到的值先排好序,然后再回聚集索引查询,这样随机IO就变成顺序IO

    • 索引下推:索引下推是MySQL会默认帮我们开启的,它能够帮我们减少需要在主键索引树上的扫描数据量,假如有name、age多列索引,我们需要对他们同时进行范围查询,那么age就无法使用索引了,但是索引下推则会在辅助索引查找的时候把不适合age的范围的数据给去掉,那么回聚集索引查询时就能直接把数据量查询出来。如果没有索引下推则是把符合name的主键查找出来,如果循环对age做判断

    • change buffer(适合写多读少,并且非唯一索引时,因为唯一索引每次都需要进行读进内存进行比较是否唯一):它的思路是减少磁盘IO读次数,在我们要update时,会把update操作写进redo_log, 然后把update操作写进change buffer(也会定期刷回表空间),等下一次发生读的时候,才把页读入内存,并且取出change buffer里面的操作,对数据进行合并。如果没有change buffer,则每次修改都会先把页从硬盘调入到缓存池中。这样就进行了一次IO,而change buffer则是会merge这个操作,把这个IO等到要读的时候才进行。

      mysql 缓冲池

      mysql change buffer

  • 应用层面的优化:

    • 查看索引建立设计是否合适,是否把多个列的查询建立成了多个单列索引,或者说多列索引的顺序不对。

    • 尽量使用覆盖索引查询,即查询的列刚好是索引列,那么此时就不用回表读取数据。而我们写Sql一般不要使用*,而是使用自己想要的字段,减少返回的数据量。

    • 如果索引建立没问题,则查看是否sql语句编写有问题:

      • 0、查看是否使用索引进行操作

      • 1、索引不能运用函数操作以及用于表达式

      • 2、字符串要加单引号,不然数据库内部会进行cast而导致无法使用索引

      • 3、表的字符编码是否一致,如果不一致也是无法使用索引的

      • 4、查看多列索引的使用是否符合最左原则

      • 5、or关键字要审用,因为要or两侧的列都建立索引时才会使用索引查询

      • 6、不要使用!=,<>等范围操作,不然也会不使用索引查询

    • 如果是使用了大量的join操作导致的慢查询,则可以选择把选择把join操作放在程序中自己解决,这样更有利于mysql缓存数据。

    • 如果索引一直都匹配不上或者说mysql选择索引出错

      • 1、使用force index,强制让他使用正确的索引。

      • 2、删除索引匹配错误的索引。让他不在匹配使用索引

Mysql选择好索引一定是对的吗

不一定的,因为mysql选择索引的时候要考虑表扫描的数量(扫描的数量越少,访问磁盘数据的次数就越少),以及是否排序、是否使用临时表、回表查询的代价情况。因此mysql选择的索引未必是对的。

倒排索引

倒排索引

你可能感兴趣的:(人人都看得懂的MySQL索引)