【MySQL 读书笔记】当我们在使用索引的时候我们在做什么
我记得之前博客我也写过关于索引使用的文章,但是并不全面,这次尽量针对重点铺全面一点。
因为索引是数据引擎层的结构我们只针对最常见使用的 Innodb 使用的 B+Tree 搜索树结构进行介绍。
每一个在 InnoDB 的中的索引都对应一颗 B+Tree。举个栗子:
创建这样一个表,并且在字段 k 上创建索引
mysql> create table T(
id int primary key,
k int not null,
name varchar(16),
index (k))engine=InnoDB;
这个时候我们插入值
(100,1)、(200,2)、(300,3)、(500,5) 和 (600,6) 这个时候主键的聚簇索引对应一颗树,然后 k 索引值对应一棵树。

基于 Innodb 引擎的特性,一个表只能有一个聚簇索引,聚簇索引上面会记录所有表字段的有序的信息。普通二级索引上面只有该索引字段和主键索引字段的对应信息。(如果可以看得比较明确)。当我们使用语句
select * from xxx where k = x
的时候,我们使用了二级索引。并且由于在二级索引上面没有索引覆盖到 name 字段,所以我们需要在二级索引里面拿到 id = x 的值然后再去聚簇索引树中搜索对应的 name 记录。这个过程被称作回表。
在频繁查询大表特别是字段非常多的表的时候,回表操作是非常消耗性能的。所以当我们设计索引的时候也尽量考虑能进行索引覆盖。也就是直接能在索引列就覆盖到我们需要经常取到的数据而不用回表。我实际中就遇到了类似的栗子,查询一个 5 e 左右的表,消耗高达多余的 50% cpu 都用在回表上面。还好我使用的 TokuDB ,TokuDB 支持多聚簇索引,才解决了这个问题。
索引维护
B+Tree 为了维护索引的有序性需要保持一定的规则和平衡。所以当我们在插入数据的时候就会需要考虑到这些问题。当我们插入一个新的 id 值为 700 ,我们需要在 r5 后面增加一个新的记录。如果插入 id 为 400 就麻烦一点,需要将r4后面的数据向后挪动空出位置。更糟糕的事情是如果 r5 所在的数据页正好满了,我们需要申请一个新的数据页并且挪动一些数据过去,这个过程性能会受到影响。这个过程被称作页分裂,而当我们进行多次分裂之后数据页利用率会变得很低,这是还会触发页的合并。
经常写业务的我们会注意到,目前很多公司的 DBA 都会建议我们使用自增 id 作为表的主键。
自增 id 作为主键有非常多的好处。比如数据拷贝,数据复制等带来的优越性。更重要的是主键递增插入,正好可以让我们避免之前说的挪动数据的情况,让我们的数据一直是以追加的形式增加进数据库的。如果以业务字段做主键,往往很难保证是一直追加。另外不要忘记,我们的二级索引上面存储的是索引列和主键列,如果主键列越小,当然性能就会越高。
使用二级索引查询执行过程
select * from T where k between 3 and 5
1. 在 k 索引树上找到 k=3 的记录,取得 ID = 300;
2. 再到 ID 索引树查到 ID=300 对应的 R3;
3. 在 k 索引树取下一个值 k=5,取得 ID=500;
4. 再回到 ID 索引树查到 ID=500 对应的 R4;
5. 在 k 索引树取下一个值 k=6,不满足条件,循环结束。
在这个过程中,回到主键索引那边去查询值的行为我们叫做回表。如果要不回表操作,就需要像我们上面说到的那样进行索引覆盖。如果二级索引上进行了索引覆盖,对应的查询就理所当然不需要回表了。
索引匹配列前缀
B+Tree 可以使用这一原则来进行索引搜寻

这是我们创建的一个二级索引
如果我们查找 like '张%' 的需求的时候,我们会搜到 id3 张六然后继续往后进行搜索直到条件不满足为止。
索引下推
而 MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
例如执行:
mysql> select * from tuser where name like '张 %' and age=10 and ismale=1;
我们有索引 (`name`,`age`) 的索引的情况下,会进行如下优化。

Reference:
本读书笔记皆来自发布在极客时间的 林晓斌(丁奇)的 MySQL 实战45讲:
极客时间版权所有: https://time.geekbang.org/ 版权所有:
https://time.geekbang.org/column/article/69236
https://time.geekbang.org/column/article/69636
https://segmentfault.com/a/1190000008545713 MySQL 中 InnoDB 引擎中页的概念
https://blog.51cto.com/thuhak/1261783 关于 Btree
http://wiki.jikexueyuan.com/project/python-actual-combat/tutorial-11.html 关于 B+tree (附 python 模拟代码)
【MySQL 读书笔记】当我们在使用索引的时候我们在做什么的更多相关文章
- 【MySQL 读书笔记】普通索引和唯一索引应该怎么选择
通常我们在做这个选择的时候,考虑得最多的应该是如果我们需要让 Database MySQL 来帮助我们从数据库层面过滤掉对应字段的重复数据我们会选择唯一索引,如果没有前者的需求,一般都会使用普通索引. ...
- 《高性能MySQL》读书笔记之创建高性能的索引
索引是存储引擎用于快速找到记录的一种数据结构.索引优化是对查询性能优化的最有效手段.索引能够轻易将查询性能提高几个数量级.创建一个最优的索引经常需要重写查询.5.1 索引基础 在MySQL中,存储引擎 ...
- 【MySQL 读书笔记】当我们在执行该查询语句的时候我们在干什么
看了非常多 MySQL 相关的书籍和文章,没有看到过如此优秀的专栏.所以未来一段时间我会梳理读完该专栏的所学所得. 当我们在执行该查询语句的时候我们在干什么 mysql> select * fr ...
- 【MySQL 读书笔记】“order by”是怎么工作的?
针对排序来说,order by 是我们使用非常频繁的关键字.结合之前我们对索引的了解再来看这篇文章会让我们深刻理解在排序的时候,是如何利用索引来达到少扫描表或者使用外部排序的. 先定义一个表辅助我们后 ...
- 【MySQL 读书笔记】SQL 刷脏页可能造成数据库抖动
开始今天读书笔记之前我觉得需要回顾一下当我们在更新一条数据的时候做了什么. 因为 WAL 技术的存在,所以当我们执行一条更新语句的时候是先写日志,后写磁盘的.当我们在内存中写入了 redolog 之后 ...
- 【MySQL 读书笔记】RR(REPEATABLE-READ)事务隔离详解
这篇我觉得有点难度,我会更慢的更详细的分析一些 case . MySQL 的默认事务隔离级别和其他几个主流数据库隔离级别不同,他的事务隔离级别是 RR(REPEATABLE-READ) 其他的主流数据 ...
- 【MySQL 读书笔记】全局锁 | 表锁 | 行锁
全局锁 全局锁是针对数据库实例的直接加锁,MySQL 提供了一个加全局锁的方法, Flush tables with read lock 可以使用锁将整个表的增删改操作都锁上其中包括 ddl 语句,只 ...
- MYSQL学习笔记——sql语句优化之索引
上一篇博客讲了可以使用慢查询日志定位耗时sql,使用explain命令查看mysql的执行计划,以及使用profiling工具查看语句执行真正耗时的地方,当定位了耗时之后怎样优化呢?这篇博客会介绍my ...
- 读书笔记 C# 接口中的索引器之浅析
在C#中,可以在类.结构或接口中用this关键字声明索引器,在索引器内部用get或set访问器访问类中集合的某项值.因此可以将索引器看作是类的属性一样去定义.索引器常用定义格式如下: public i ...
随机推荐
- mybatis在xml文件中处理转义字符
第一种方法: 用了转义字符把>和<替换掉,然后就没有问题了. AND start_date <= CURRENT_DATE AND end_date >= CURRENT_DA ...
- 阿里云Ubuntu下安装、配置权限和导入本地mongodb
---恢复内容开始--- 第一部分:首先先在Ubuntu下安装好mongodb,步骤如下: 首先我们需要借助远程管理工具链接到阿里云上的ubuntu系统,接着进行如下操作 一.导出软件源的公钥 sud ...
- 【JVM系列】一步步解析java执行内幕
对于任何一门语言,要想达到精通的水平,研究它的执行原理(或者叫底层机制)不失为一种良好的方式.在本篇文章中,将重点研究java源代码的执行原理,即从程 序员编写JAVA源代码,到最终形成产品,在整个过 ...
- webpack-插件机制杂记
系列文章 Webpack系列-第一篇基础杂记 webpack系列-插件机制杂记 前言 webpack本身并不难,他所完成的各种复杂炫酷的功能都依赖于他的插件机制.或许我们在日常的开发需求中并不需要自己 ...
- js获取url 中的值,并跳转相应页面
实现方法:一:获取URL带QUESTRING参数的JAVASCRIPT客户端解决方案,相当于asp的request.querystring,PHP的$_GET1.函数: <Script lang ...
- spring的理解
看过<fate系列>的博友知道,这是一个七位英灵的圣杯争夺战争.今天主要来谈谈圣杯的容器概念,以便对spring的理解. 圣杯: 圣杯本身是没有实体的,而是将具有魔术回路的存在(人)作为“ ...
- Java并发——synchronized关键字
前言: 只要涉及到Java并发那么我们就会考虑线程安全,实际上能够实现线程安全的方法很多,今天先介绍一下synchronized关键字,主要从使用,原理介绍 一.synchronized的使用方法 1 ...
- js中事件冒泡,事件捕获详解
一.事件流 事件是js与HTML交互的基础,事件流描述的是页面接受事件的顺序,而事件流又分为三个阶段:捕获阶段.目标阶段和冒泡阶段. 如果单纯的事件处理,事件捕获和事件冒泡二选一即可,导致两者并存的原 ...
- 【设计模式】原型模式 Pototype Pattern
前面讲了创建一个对象实例的方法单例模式Singleton Pattern, 创造多个产品的工厂模式(简单工厂模式 Simple Factory Pattern, 工厂方法模式 FactoryMothe ...
- jQueryMobile 網頁使用 ASP.NET Web API 服務
微軟的 ASP.NET Web API 框架,能以 RESTful 輕量級的架構風格,建立 HTTP 服務,讓多種不同的用戶端,如: 手機.平板.電腦(PC),透過 HTTP 的 GET.POST.P ...