SQL

你真的会使用数据库的索引吗?

2022-02-09  本文已影响0人  越前君
配图源自 Freepik

转载自:你真的会使用数据库的索引吗?

使用索引也很简单,然而, 会使用索引是一回事, 而深入理解索引原理又能恰到好处使用索引又是另一回事。

一、前言

无论是面试、还是日常工作中,或多或少都会使用或者听到别人谈论索引这个技术。

然而很大一部份程序员对索引的了解仅限于到“加索引能使查询变快”这个概念为止。

使用索引也很简单,然而, 会使用索引是一回事, 而深入理解索引原理又能恰到好处使用索引又是另一回事。

这已经是两个相差甚远的技术层级了。

二、千万级数据表索引和无索引查询效率对比

现在有一个学生表 student,有 1000 万条数据

未加索引,查询 class_id=2 的学生信息的耗时:SELECT \* FROM student WHERE class_id=2 花费了 3.357 秒

加上索引,查询 class_id=2 的学生信息的耗时:SELECT \* FROM student WHERE class_id=2 花费了 0.017 秒

1000 万条数据下,两个查询的性能差了近 200 倍!!

这个差距是特别大的! 难怪需要加索引!!!

三、什么是索引

网上很多讲解索引的文章对索引的描述是这样的:

索引就像书的目录, 通过书的目录就可以准确的定位到书籍的具体的内容。

这句话概述的非常正确!

但说了跟没说一样,懂的人自然懂!不懂的人感觉懂了,但还是一脸蒙的状态!

其实想要理解索引原理,必须清楚一种数据结构:

「平衡树」(非二叉),也就是 B Tree 或者 B+Tree

当然, 有的数据库也使用哈希桶作用索引的数据结构 , 然而, 主流的 RDBMS 都是把平衡树当做数据表默认的索引数据结构的。

我们平时建表的时候都会为表加上主键, 在某些关系数据库中, 如果建表时不指定主键,数据库会拒绝建表的语句执行。

事实上, 一个加了主键的表,并不能被称之为“表”。一个没加主键的表,它的数据无序的放置在磁盘存储器上,一行一行的排列的很整齐。

如果给表上了主键,那么表在磁盘上的存储结构就由整齐排列的结构转变成了树状结构,也就是上面说的“平衡树”结构,换句话说,就是整个表就变成了一个索引。

没错, 再说一遍, 整个表变成了一个索引!

也就是所谓的“聚集索引”。 这就是为什么一个表只能有一个主键, 一个表只能有一个“聚集索引”,因为主键的作用就是把“表”的数据格式转换成“树(索引)”的格式。

未加索引时,之前执行的查询 SQL 会让数据库系统逐行的遍历整张表,对于每一行都要检查其 class_id 字段是否等于 2。因为我们要查找所有 class_id2 的员工,所以当我们发现了一条 class_id2 的记录后,并不能停止继续查找,因为可能还有 class_id 等于 2 的其他记录。

这就意味着,对于表中的千万条记录,数据库每一条都要检查。这就是所谓的“全表扫描”(full table scan)

而加上索引的最大作用就是加快查询速度,它能从根本上减少需要扫表的记录/行的数量。

四、Mysql 中的索引

在 MySQL 中, 索引有两种分类方式:逻辑分类物理分类

按照逻辑分类,索引可分为:

按照物理分类,索引可分为:

在目前用的最多的 mysql 的 InnoDB 存储引擎中,是使用 B+Tree 索引方法来进行索引建立的。

B+ 树索引是 B+ 树在数据库中的一种实现,是最常见也是数据库中使用最为频繁的一种索引。

B+ 树中的 B 代表平衡(balance),而不是二叉(binary),因为 B+ 树是从最早的平衡二叉树演化而来的。先了解二叉查找树、平衡二叉树(AVLTree)和平衡多路查找树(B-Tree),B+ 树即由这些树逐步优化而来。

具体的讲解可参考文章:MySQL 索引机制(B+Tree)

五、索引的优缺点

优点:

缺点:

六、索引何时应该使用

需创建索引的情况:

避免创建索引的情况:

七、哪些 sql 能命中索引

  1. 前导模糊查询不能使用索引,如 name like '%涛'

  2. unioninor 可以命中索引,建议使用 in

  3. 负条件查询不能使用索引,可以优化为 in 查询,其中负条件有 !=<>not innot existsnot like

  4. 联合索引最左前缀原则,又叫最左侧查询,如果在 (a, b, c) 三个字段上建立联合索引,那么它能够加快 a | (a, b) | (a, b, c) 三组的查询速度。

  5. 建立联合查询时,区分度最高的字段在最左边

  6. 如果建立了(a,b)联合索引,就不必再单独建立 a 索引。同理,如果建立了(a,b,c)索引就不必再建立 a,(a,b) 索引

  7. 存在非等号和等号混合判断条件时,在建索引时,要把等号条件的列前置

  8. 范围列可以用到索引,但是范围列后面的列无法用到索引。

索引最多用于一个范围列,如果查询条件中有两个范围列则无法全用到索引。范围条件有:<<=>>=between 等。

  1. 把计算放到业务层而不是数据库层。在字段上计算不能命中索引,

  2. 强制类型转换会全表扫描,如果 phone 字段是 varchar 类型,则下面的 SQL 不能命中索引。Select \* fromuser where phone=13800001234

  3. 更新十分频繁、数据区分度不高的字段上不宜建立索引。

更新会变更 B+ 树,更新频繁的字段建立索引会大大降低数据库性能。

“性别”这种区分度不太大的属性,建立索引是没有什么意义的,不能有效过滤数据,性能与全表扫描类似。

一般区分度在 80%以上就可以建立索引。区分度可以使用 count(distinct(列名))/count(\*)来计算。

  1. 利用覆盖索引来进行查询操作,避免回表。

被查询的列,数据能从索引中取得,而不是通过定位符 row-locator 再到 row 上获取,即“被查询列要被所建的索引覆盖”,这能够加速度查询。

  1. 建立索引的列不能为 null,使用 not null 约束及默认值

  2. 利用延迟关联或者子查询优化超多分页场景,

MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后放弃前 offset 行,返回 N 行,那当 offset 特别大的时候,效率非常低下,要么控制返回的总数,要么对超过特定阈值的页进行 SQL 改写。

  1. 业务上唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。

  2. 超过三个表最好不要用 join,需要 join 的字段,数据类型必须一致,多表关联查询时,保证被关联的字段需要有索引。

  3. 如果明确知道查询结果只要一条,limit 1 能够提高效率,比如验证登录的时候。

  4. Select 语句务必指明字段名称

  5. 如果排序字段没有用到索引,就尽量少排序

  6. 尽量用 union all 代替 unionunion 需要将集合合并后在进行唯一性过滤操作,这会涉及到排序,大量的 CPU 运算,加大资源消耗及延迟,当然,使用 union all 的前提条件是两个结果集没有重复数据。

八、总结

索引是非常重要的技术!

但每建立一个索引,实际上都需要在硬盘上开辟一块空间用于存储这个索引所需要的数据结构(虽然表述不太准确但是是这个意思),因此不建议对太长的字段建立索引。

而且建立的索引并不是越多越好,因为索引虽然能够提高查询效率,但是会大大得影响插入、删除和修改的效率,因为每一次数据的更新都会牵涉到对索引的修改。

综上所述,往往在对于大量数据的插入的情况的时候,我们需要先删除掉数据表的索引,等插入完毕后重新建立索引,这样才能最大限度地保证数据库的效率!

上一篇 下一篇

猜你喜欢

热点阅读