欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

mysql索引详解

程序员文章站 2022-03-28 11:13:45
...

首先Mysql的基本存储结构是(记录都存在页里边):

mysql索引详解

 

mysql索引详解

 

  • 各个数据页可以组成一个双向链表
  • 每个数据页中的记录又可以组成一个单向链表
  • 每个数据页都会为存储在它里边儿的记录生成一个页目录,在通过主键查找某条记录的时候可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录
  • 其他列(非主键)作为搜索条件:只能从最小记录开始依次遍历单链表中的每条记录

所以说,如果我们写select * from user where username = 'Java3y'这样没有进行任何优化的sql语句,默认会这样做:

  • 定位到记录所在的页
  • 需要遍历双向链表,找到所在的页
  • 从所在的页内中查找相应的记录
  • 由于不是根据主键查询,只能遍历所在页的单链表了

很明显,在数据量很大的情况下这样查找会很慢

1.2索引提高检索速度

索引做了些什么可以让我们查询加快速度呢?

其实就是将无序的数据变成有序(相对)

mysql索引详解

 

要找到id为8的记录简要步骤:

mysql索引详解

 

很明显的是:没有用索引我们是需要遍历双向链表来定位对应的页,现在通过**“目录”**就可以很快地定位到对应的页上了!

其实底层结构就是B+树,B+树作为树的一种实现,能够让我们很快地查找出对应的记录。

参考资料:

  • Mysql索引

1.3索引降低增删改的速度

B+树平衡树的一种。

平衡树:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

如果一棵普通的树在极端的情况下,是能退化成链表的(树的优点就不复存在了)

mysql索引详解

 

B+树是平衡树的一种,是不会退化成链表的,树的高度都是相对比较低的(基本符合矮矮胖胖(均衡)的结构)【这样一来我们检索的时间复杂度就是O(logn)】!从上一节的图我们也可以看见,建立索引实际上就是建立一颗B+树。

  • B+树是一颗平衡树,如果我们对这颗树增删改的话,那肯定会破坏它的原有结构
  • 要维持平衡树,就必须做额外的工作。正因为这些额外的工作开销,导致索引会降低增删改的速度

B+树删除和修改具体可参考:

  • www.cnblogs.com/wade-luffy/…

1.4哈希索引

除了B+树之外,还有一种常见的是哈希索引。

哈希索引采用的结构和hashMap一样。数组+链表的形式。如果hash冲突大,性能也就越来越差。

  • 本质上就是把键值换算成新的哈希值,根据这个哈希值来定位

mysql索引详解

 

看起来哈希索引很牛逼啊,但其实哈希索引有好几个局限(根据他本质的原理可得):

  • 哈希索引是无序的所以不能用于排序order by,group by 后面
  • 由于存的不是索引值,而是映射而成的hash值,所以也不支持范围查找,前缀匹配和联合索引的最左匹配原则(要全匹配)。

1.5InnoDB支持哈希索引吗?

主流的还是使用B+树索引比较多,对于哈希索引,InnoDB引擎会根据索引值使用的频繁内部自动的在B+Tree索引上创建哈希索引,用户无法控制或者配置,不过可以关闭该优化特性。

1.6聚集和非聚集索引

简单概括:

  • 聚集索引就是以主键创建的索引
  • 非聚集索引就是以非主键创建的索引

区别:

  • 聚集索引在叶子节点存储的是表中的数据
  • 非聚集索引在叶子节点存储的是主键和索引列
  • 使用非聚集索引查询出数据时,拿到叶子上的主键再去查到想要查找的数据。(拿到主键再查找这个过程叫做回表)

非聚集索引也叫做二级索引

非聚集索引在建立的时候也未必是单列的,可以多个列来创建索引。

  • 此时就涉及到了哪个列会走索引,哪个列不走索引的问题了(最左匹配原则-->后面有说)
  • 创建多个单列(非聚集)索引的时候,会生成多个索引树(所以过多创建索引会占用磁盘空间)

mysql索引详解

 

在创建多列索引中也涉及到了一种特殊的索引-->覆盖索引

  • 我们前面知道了,如果不是聚集索引,叶子节点存储的是主键+列值
  • 最终还是要“回表”,也就是要通过主键查找一次。这样就会比较慢
  • 覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!

比如说:

  • 现在我创建了索引(username,age),在查询数据的时候:select username , age from user where username = 'Java3y' and age = 20。
  • 很明显地知道,我们上边的查询是走索引的,并且,要查询出的列在叶子节点都存在!所以,就不用回表了~
  • 所以,能使用覆盖索引就尽量使用吧~

1.7索引最左匹配原则

最左匹配原则

  • 索引可以简单如一个列(a),也可以复杂如多个列(a, b, c, d),即联合索引
  • 如果是联合索引,那么key也由多个列组成,同时,索引只能用于查找key是否存在(相等),遇到范围查询(>、<)等就不能进一步匹配了,后续退化为线性查找。
  • 因此,列的排列顺序决定了可命中索引的列数

例子:

  • 如有索引(a, b, c, d),查询条件a = 1 and b = 2 and c > 3 and d = 4,则会在每个节点依次命中a、b、c,无法命中d。(很简单:索引命中只能是相等的情况,不能是范围匹配)

1.8=、in自动优化顺序

不需要考虑=、in等的顺序,mysql会自动优化这些条件的顺序,以匹配尽可能多的索引列。

例子:

  • 如有索引(a, b, c, d),查询条件c > 3 and b = 2 and a = 1 and d < 4与a = 1 and c > 3 and b = 2 and d < 4等顺序都是可以的,MySQL会自动优化为a = 1 and b = 2 and c > 3 and d < 4,依次命中a、b、c。

1.9索引总结

索引在数据库中是一个非常重要的知识点!上面谈的其实就是索引最基本的东西,要创建出好的索引要顾及到很多的方面:

  • 1,最左前缀匹配原则。这是非常重要、非常重要、非常重要(重要的事情说三遍)的原则,MySQL会一直向右匹配直到遇到范围查询(>,<)就停止匹配,比如: a = 1 AND b = 2 AND c > 3 AND d = 4,如果建立 (a,b,c,d)顺序的索引,c,d是用不到索引的,如果建立(a,b,d,c)的索引,则都可以用到,a,b,d的顺序可以任意调整。
  • 3,尽量选择区分度高的列作为索引,区分度的公式是 COUNT(DISTINCT col) / COUNT(*)。表示字段不重复的比率,比率越大我们扫描的记录数就越少。
  • 4,索引列不能参与计算,尽量保持列“干净”。比如,FROM_UNIXTIME(create_time) = '2016-06-06' 就不能使用索引,原因很简单,B+树中存储的都是数据表中的字段值,但是进行检索时,需要把所有元素都应用函数才能比较,显然这样的代价太大。所以语句要写成 : create_time = UNIX_TIMESTAMP('2016-06-06')。
  • 5,尽可能的扩展索引,不要新建立索引。比如表中已经有了a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
  • 6,单个多列组合索引和多个单列索引的检索查询效果不同,因为在执行SQL时,MySQL只能使用一个索引,会从多个单列索引中选择一个限制最为严格的索引。

根据上面这些原则,我们来修改开篇的慢查询:


SELECT 
count(*) AS count 
FROM trade_bASe AS a
WHERE 
a.trade_status = 7 
AND a.create_time > '2015-09-01' 
AND a.booking_source = '2'

根据这条SQL,应该建立的索引是:trade_status, booking_source,create_time的联合索引;其中,trade_status、booking_source的顺序可以颠倒,而且 create_time 的区间查询放到后面。这就是利用了索引的最左匹配原则。

索引的优化方法

1,尽量使用组合索引,因为即使查询语句中有多个单列索引,但是优化器会只选择一个它认为最优的索引路径查询。

2,索引不会包含有NULL值的列:只要列中包含有NULL值,都将不会被包含在索引中,组合索引中只要有一列有NULL值,那么这一列对于此条组合索引就是无效的。所以我们在数据库设计时,不要让索引字段的默认值为NULL。

3,对于指比较长的列可以创建前缀索引,但是前缀索引无序无法用做orderby 和groupby和覆盖扫描。:假设,如果有一个数据类型为CHAR(255)的city列,在前10个字符内,绝大部分数据的值是唯一的,那么就不要对整个列进行索引,创建alter table t_demo add key(city(10));字段长度为10的city索引。

4,对于指比较长的列如url还可以使用伪哈希索引的方式。增加一列url对应的hash整型值。维护该列可以使用触发器实现。注意:因为会出现hash冲突,所以根据整型hash列查询的时候都要and 具体值。如:select id from urlTable where url_crc=CRC32(“www.baidu.com”) and url="www.baidu.com"

5,LIKE语句操作:LIKE "%aaaaa%"不会使用索引,但是LIKE "aaa%"可以使用索引。结合B+树查找规则理解

6,不要在索引列上进行运算:在建立索引的原则中,提到了索引列不能进行运算。

7,使用覆盖索引,索引包含需要查询的字段的值。

8,尽可能将需要做范围(>,<)查询的列放在索引后面,因为优化器会放弃使用索引。注意:in 和 between 是可以使用索引的,它们相当于等值查询。

最优的索引符合三星系统原则
1.索引将相关记录放在一起获得一星
2.索引中数据顺序和查询中的排序一致则获得二星
3.覆盖索引获得三星

Explain命令

explain select * from t_user;

列值的含义

possable_key:可能会用到的索引。

key:表示使用到的索引

相关标签: 索引