本文总结了一些MySQL索引的基本概念和原理,如果可以快速清晰回答这些问题可以出门左转提提宝贵建议。

什么是索引?索引为什么查询快,索引的数据结构是什么?

聚簇索引/非聚簇索引区别?

什么是覆盖索引?

唯一索引/普通索引?

单列索引/联合索引区别?

Full-index全文索引?

什么是下推索引?

什么是最左匹配,查询回表?

哪些字段适合建索引?

为什么一般主键索引最好是自增长的, 尽量短的数值类型?

为什么有些SQL不走索引?

索引的最佳实践?

索引为什么快

索引的本质是空间换时间。

  • +bonus: 加快检索速度,加快多表连接
  • -price: 额外空间开销,维护索引的额外时间开销

所以我们通过索引这个缓存来提高数据查询的效率。

假如我们自己设计数据库索引的话,我们会选取什么样的数据结构呢?下面我们来分析下各种查询常见的数据结构的性格,看看选谁是最合适的人选。

数据结构比较

  • 有序数组:等值查询和范围查询场景中的性能就都非常优秀。特定值查询用二分法就可以快速得到,这个时间复杂度是 O(log(N))。类似between[x, y]的 范围查询 也比较快,先用值查询二分法找到x, 然后向后遍历,知道找到y。 但是他最大的问题是插入或者删除一个新数据,这个新数据后面的整个数组都需要挪动,复杂度是O(N)。

  • HashMap:虽然可以快速定位,值查询的时间复杂度是O(1), 但是Hashmap没有顺序,进行范围查询的话复杂度高是O(N)。

  • 二叉树查找树BST:二叉树的高度不均匀,不能自平衡,查找效率跟数据量有关(树的高度),在极端情况下(插入数据本身就是有序的)这课树就退化成链表了,查询实际复杂度是O(N)

  • 红黑树:是平衡的BST,性能稳定在O(logN), 但因为是二叉树,树的高度随着数据量增加而增加,并且需要再平衡。适合数据都在内存的情况,比如Java里的HashMap。但是在硬盘寻址的场景下IO成本会比较高。

  • B-Tree:相比二叉树来说是一种多路平衡查询树,但是B树不管叶子节点还是非叶子节点,都会保存数据,这样导致在非叶子节点中能保存的指针数量变少(有些资料也称为扇出),指针少的情况下要保存大量数据,只能增加树的高度,导致IO操作变多,查询性能变低;

  • B+Tree: 从物理存储结构上说是N叉树,B-Tree和B+Tree都以页(4K)来划分节点的大小,但是由于B+Tree的中间节点(非叶子节点)不存储数据,存的是索引信息,索引包含Key和Point指针。 因此B+Tree能够在同样大小的节点中,存储更多的key,提高查找效率。

每一个索引在 InnoDB 里面对应一棵 B+ 树。以 InnoDB 的一个整数字段索引为例,这个 N 差不多是 1200。这棵树高是 4 的时候,就可以存 1200 ^(4-1) 个值,这已经 17 亿了。考虑到树根的数据块总是在内存中的,一个 10 亿行的表上一个整数字段的索引,查找一个值最多只需要访问 3 次磁盘。

聚簇索引/非聚簇索引

区别主要看叶子节点存了什么数据:

在 InnoDB 里,索引B+ Tree的叶子节点存储了整行数据的是主键索引,也被称之为聚簇索引。

而索引B+ Tree的叶子节点存储了主键的值的是非主键索引,也被称之为非聚簇索引。

聚簇索引查询相对会更快一些,因为主键索引树的叶子节点直接就是我们要查询的整行数据了。而非主键索引的叶子节点是主键的值,查到主键的值以后,还需要再通过主键的值再进行一次查询(这个过程叫做回表, 也就是查了2个索引树)。

覆盖索引

覆盖索引(covering index)指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。覆盖索引不是索引树,是一个结果。当一条查询语句符合覆盖索引条件时,MySQL只需要通过索引就可以返回查询所需要的数据,这样避免了查到索引后再返回表操作,减少I/O提高效率。

例如表T中有一个普通索引 idx_key(key),那么:

-- 索引覆盖了
select id from T where key = 'test'; -- 索引没覆盖,需要回表
select * from T where key = 'test';

问题,为什么第一个SQL索引覆盖了? 非聚簇索引的叶子节点存的是id。

唯一索引/普通索引

唯一索引和普通索引在查询和更新的时候区别:

  • 唯一索引找到满足的第一条记录会立马返回,通知检索(因为唯一性的保证)。但是这个区别并没有很大的性能区别,因为Innodb是按照页(默认16KB)读写的,读数据的时候是从B+树的根节点开始搜索,搜索的时候将整个页从硬盘加载到内存。

  • 唯一索引在插入的时候会多做些判断,想要做这个判断就必须先把数据页读入内存。但是普通索引不需要做这个判断,就可以把需要更新的数据做判断:如果数据在内存则直接更新;如果不在也不加载内存,而是先写入change buffer,等下次查询的时候再执行change buffer。这样普通索引会相对性能好一些。但是注意:如果业务场景是写入后立马有查询,其实还是会立马需要把数据页加载到内存,这样的情况下其实并不能带来优化IO的操作。

Full-index全文索引

Mysql 5.6 引入了全文索引Full text index,但是只能适用于分词的情况,如果是匹配字符串的一部分就不适用了。

MySQL支持三种模式的全文检索模式:自然语言模式(IN NATURAL LANGUAGE MODE),即通过MATCH AGAINST 传递某个特定的字符串来进行检索。

布尔模式(IN BOOLEAN MODE),可以为检索的字符串增加操作符,例如“+”表示必须包含,“-”表示不包含,“*”表示通配符(这种情况, 即使传递的字符串较小或出现在停词中,也不会被过滤掉),其他还有很多特殊的布尔操作符,可以通过如下参数控制:

查询扩展模式(WITH QUERY EXPANSION), 这种模式是自然语言模式下的一个变种,会执行两次检索,第一次使用给定的短语进行检索,第二次是结合第一次相关性比较高的行进行检索。

单列索引/联合索引

对于一个表里的多个列,比如是有些列高频查询,有些列低频查询。如果为每一个低频的列单独建立索引感觉有些浪费,如果不建立索引又只能走全表扫描。所以我们经常用联合索引来解决这个问题,联合索引如idx_key1_key2_key3(key1,key2,key3),相当于创建了(key1)(key1,key2)(key1,key2,key3)三个索引,那么在建立联合索引的时候,如何安排索引内的字段顺序?

  • 如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用
  • 按照字段在查询条件中出现的频度建立索引

我们考虑key1 是最常用的列放最前面,key2和key3不常用。

上面这种建立一个联合索引就实际上包含了3个索引的特性就是最左匹配原则。这个最左匹配可以是联合索引的最左 N 个字段,也可以是字符串索引的最左 M 个字符。

总结起来

  1. 索引的匹配规则是左匹配
  2. 只有复合索引的第一个字段出现在查询条件中,该索引才可能被使用
  3. 有了(A,B,C),就等于同时拥有了(A),(A,B)和 (A,B,C) 三个索引
  4. 只要索引内,开始用范围查询,后面的索引就失效了。

    这里注意:IN 在 where 中,也属于准确查询,不会使后面索引失效。

什么是下推索引?

在MySQL 5.6中,引入了Index Condition Pushdown Optimization 优化。本质是针对那些需要回表查找的部分如果索引里已经包含了该列,那么先在索引里做过滤判断。

以用户表的联合索引(name, age)为例。如果现在有一个需求:检索出表中“名字第一个字是张,而且年龄是 10 岁的所有男孩”。那么,SQL 语句是这么写的:

mysql> select * from tuser where name like '张 %' and age=10 and ismale=1;

我们已经知道了前缀索引规则,所以这个语句在搜索索引树的时候,只能用 “张”,找到第一个满足条件的记录 ID3。当然,这还不错,总比全表扫描要好。

然后呢?

当然是判断其他条件是否满足。

在 MySQL 5.6 之前,只能从 ID3 开始一个个回表。到主键索引上找出数据行,再对比字段值。

而 MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。

哪些字段适合建索引?

  1. 出现在 SELECT、UPDATE、DELETE 语句的 WHERE 从句中的列

  2. 包含在 ORDER BY、GROUP BY、DISTINCT 中的字段

  3. 并不要将符合 1 和 2 中的字段的列都建立一个索引, 通常将 1、2 中的字段建立联合索引效果更好

  4. 多表 join 的关联列

为什么有些SQL不走索引?

  1. 使用了通配符开头,NOT IN 语句或者
  2. 联合索引的第一个字段查询条件中
  3. 数据引擎的优化器选错了索引(可以适当使用 force index 语句来优化)

为什么一般主键索引最好是自增长的, 尽量短的数值类型?

  • 自增

结合B+Tree的特点,自增主键是连续的,在插入过程中尽量减少页分裂,即使要进行页分裂,也只会分裂很少一部分。并且能减少数据的移动,每次插入都是插入到最后。总之就是减少分裂和移动的频率。

由于InnoDB索引的特性,因此如果主索引不是自增的(id作主键),那么每次插入新的数据,都很可能对B+Tree的主索引进行重整,影响性能。因此,尽量以自增id作为InnoDB的主索引。

这就是为什么我们在《分布式ID总结》里提到主键的Id需求一般是整体趋势递增的原因。

  • 短数

每个非主键索引的叶子节点上都是主键的值。如果用UUID,比如 b8a52179-7d54-46de-b1de-d88911a42790 做主键,那么每个二级索引的叶子节点占用约 36字节,而如果用整型做主键,则只要 4字节,如果是长整型(bigint)则是 8字节。所以,主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。

《Snowflake分布式ID服务》 里利用了twitter的雪花算法来尽量做到生成短数字趋势自增的的ID。

索引的最佳实践?

要建索引

  1. 定义主键的数据列一定要建立索引。
  2. 定义有外键的数据列一定要建立索引。
  3. 对于经常查询的数据列最好建立索引。
  4. 对于需要在指定范围内的快速或频繁查询的数据列;
  5. 经常用在WHERE子句中的数据列。
  6. 经常出现在关键字order by、group by、distinct后面的字段,建立索引。如果建立的是复合索引,索引的字段顺序要和这些关键字后面的字段顺序一致,否则索引不会被使用。

不要建索引

  1. 对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。
  2. 对于定义为text、image和bit的数据类型的列不要建立索引。
  3. 对于经常存取的列避免建立索引

索引的坑

  1. 限制表上的索引数目。对一个存在大量更新操作的表,所建索引的数目一般不要超过3个,最多不要超过5个。

    索引虽说提高了访问速度,但太多索引会影响数据的更新操作。

  2. 对复合索引,按照字段在查询条件中出现的频度建立索引。在复合索引中,记录首先按照第一个字段排序。

    对于在第一个字段上取值相同的记录,系统再按照第二个字段的取值排序,以此类推。

    因此只有复合索引的第一个字段出现在查询条件中,该索引才可能被使用,因此将应用频度高的字段,放置在复合索引的前面,会使系统最大可能地使用此索引,发挥索引的作用。

索引不会包含有NULL值的列

  1. 只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。

    所以我们在数据库设计时不要让字段的默认值为NULL

使用短索引(列内容越短越好)

  1. 对列进行索引,如果可能应该指定一个前缀长度。

    例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引。

    短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。

索引列排序

  1. MySQL查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。

    因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。

like语句操作

  1. 一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引,而like “aaa%”可以使用索引。即:左匹配规则。可以使用reverse函数来支持逆序匹配,从而增强like走索引的可能。
ALTER TABLE `T` ADD `reverse_identifier` VARCHAR(255)  CHARACTER SET utf8  COLLATE utf8_general_ci;

select * from T where reverse_identifier like reverse('%SDTE');

不要在列上进行运算

  1. select * from users where YEAR(adddate)<2007;

    将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成

    select * from users where adddate<‘2007-01-01’;

不使用NOT IN和<>操作

  1. 因为MySQL只对<,<=,=,>,>=,BETWEEN,IN,以及某些时候的LIKE才会使用索引。

    因为在以通配符 % 和 _ 开头作查询时,MySQL不会使用索引。

推荐阅读:《阿里巴巴Java开发手册》索引规约章节 和 极客时间《MySQL实战45讲》。

欢迎关注我的公众号:好奇心森林

Mysql索引扫盲总结的更多相关文章

  1. mysql 索引相关

    引言: MYSQL由于其免费和开源的性质,在项目中用处广泛.大家都知道,一个MySQL数据库能够储存大量的数据,如果要在大量的数据中查找某一个数据,如果使用全表检索的话,即费时间又费力气,这时,就需要 ...

  2. MySQL数据库扫盲篇

    MySQL数据库扫盲篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   一.MySQL概述 1>.什么是MySQL MySQL是瑞典的MySQL AB公司开发的一个可用于各 ...

  3. 深入MySQL索引

    MySQL索引作为数据库优化的常用手段之一在项目优化中经常会被用到, 但是如何建立高效索引,有效的使用索引以及索引优化的背后到底是什么原理?这次我们深入数据库索引,从索引的数据结构开始说起. 索引原理 ...

  4. MySQL 索引

    MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度. 打个比方,如果合理的设计且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是 ...

  5. MYSQL索引结构原理、性能分析与优化

    [转]MYSQL索引结构原理.性能分析与优化 第一部分:基础知识 索引 官方介绍索引是帮助MySQL高效获取数据的数据结构.笔者理解索引相当于一本书的目录,通过目录就知道要的资料在哪里, 不用一页一页 ...

  6. MySQL索引原理及慢查询优化

    原文:http://tech.meituan.com/mysql-index.html 一个慢查询引发的思考 select count(*) from task where status=2 and ...

  7. 【转】MySQL索引背后的数据结构及算法原理

    摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BT ...

  8. [转]MySQL索引背后的数据结构及算法原理

    摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BT ...

  9. MySQL索引类型总结和使用技巧以及注意事项

    索引是快速搜索的关键.MySQL索引的建立对于MySQL的高效运行是很重要的.下面介绍几种常见的MySQL索引类型 在数据库表中,对字段建立索引可以大大提高查询速度.假如我们创建了一个 mytable ...

随机推荐

  1. 手机APP自动化环境搭建

    1 摘要 近年来,随着移动应用从数量上和逻辑复杂程度上的增长,以及产品发布周期的紧缩,使得回归测试迫在眉睫,鉴于此APP自动化测试变得越来流行,当前主流的APP自动化工具有:Appium.Roboti ...

  2. MySQL索引及查询优化

    mysql 索引 1.索引介绍 索引按数据结构分可分为哈希表,有序数组,搜索树,跳表: 哈希表适用于只有等值查询的场景 有序数组适用于有等值查询和范围查询的场景,但有序数组索引的更新代价很大,所以最好 ...

  3. Kubeedge-mapper 实现

    应用场景: 利用GitHub上的温度传感器的例子作为讲解,实现从云端获取设备终端状态及使用Java模拟设备数据.其实和官网给的视频一样,只需要将终端设备的数据转换为支持MQTT协议传输的数据,云端就可 ...

  4. xampp apache 安全性问题

    要禁止 Apache 显示目录结构列表,只需将 Option 中的 Indexes 去掉即可.<Directory "D:/Apa/blabla"> Options I ...

  5. 王艳 201771010127《面向对象程序设计(java)》第一周学习总结

    王艳 201771010127<面向对象程序设计(java)>第一周学习总结 第一部分:课程准备部分 填写课程学习 平台注册账号, 平台名称 注册账号 博客园:www.cnblogs.co ...

  6. BZOJ1010单调性DP优化

    1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 10707  Solved: 4445[Submit][S ...

  7. POJ3255

    题目链接:http://poj.org/problem?id=3255 解题思路: 昨晚两点多睡不着翻起来刷<挑战>的题,结果遇到这道求次短路的题,一脸懵逼.想了半小时没什么思路就看他的解 ...

  8. 如何利用BeautifulSoup选择器抓取京东网商品信息

    昨天小编利用Python正则表达式爬取了京东网商品信息,看过代码的小伙伴们基本上都坐不住了,辣么多的规则和辣么长的代码,悲伤辣么大,实在是受不鸟了.不过小伙伴们不用担心,今天小编利用美丽的汤来为大家演 ...

  9. 客服端负载均衡:Spring Cloud Ribbon

    Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具.服务间的调用,API网关的请求转发都是通过Ribbon实现的. 在微服务架构中使用客户端负载均衡需要两步: (1) ...

  10. 基本sql语法

    SQL 语句主要可以划分为以下 3 个类别. DDL(Data Definition Languages)语句:数据定义语言,这些语句定义了不同的数据段.数据库.表.列.索引等数据库对象的定义.常用 ...