有一个业务是查询最新审核的5条数据

SELECT `id`, `title`
FROM `th_content`
WHERE `audit_time` < 1541984478
AND `status` = 'ONLINE'
ORDER BY `audit_time` DESC, `id` DESC
LIMIT 5;

查看当时的监控情况 cpu 使用率是超过了100%,show processlist看到很多类似的查询都是处于create sort index的状态。

查看该表的结构

CREATE TABLE `th_content` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(500) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT '内容标题',
`content` mediumtext CHARACTER SET utf8 NOT NULL COMMENT '正文内容',
`audit_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '审核时间',
`last_edit_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最近编辑时间',
`status` enum('CREATED','CHECKING','IGNORED','ONLINE','OFFLINE') CHARACTER SET utf8 NOT NULL DEFAULT 'CREATED' COMMENT '资讯状态',
PRIMARY KEY (`id`),
KEY `idx_at_let` (`audit_time`,`last_edit_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

索引有一个audit_time在左边的联合索引,没有关于status的索引。

分析上面的sql执行的逻辑:

  • 从联合索引里找到所有小于该审核时间的主键id(假如在该时间戳之前已经审核了100万条数据,则会在联合索引里取出对应的100万条数据的主键 id)
  • 对这100万个 id 进行排序(为的是在下面一步回表操作中优化 I/O 操作,因为很多挨得近的主键可能一次磁盘 I/O 就都取到了)
  • 回表,查出100万行记录,然后逐个扫描,筛选出status='ONLINE'的行记录
  • 最后对查询的结果进行排序(假如有50万行都是ONLINE,则继续对这50万行进行排序)

最后因为数据量很大,虽然只取5行,但是按照我们刚刚举的极端例子,实际查询了100万行数据,而且最后还在内存中进行了50万行数据库的内存排序。

所以是非常低效的。

画了一个示意图,说明第一步的查询过程,粉红色部分表示最后需要回表查询的数据行。
图中我按照索引存储规律来YY伪造填充了一些数据,如有不对请留言指出。希望通过这张图大家能够看到联合索引存储的方式和索引查询的方式

改进思路 1

范围查找向来不太好使用好索引的,如果我们增加一个audit_timestatus的联合索引,会有哪些改进呢?

ALTER TABLE `th_content` ADD INDEX `idx_audit_status` (`audit_time`, `status`);
mysql> explain select `id`, `title` from `th_content` where `audit_time` < 1541984478 and `status` = 'ONLINE' order by `audit_time` desc, `id` desc limit 5;
+----+-------------+------------+-------+------------------------------------------+------------------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+-------+------------------------------------------+------------------+---------+------+--------+-------------+
| 1 | SIMPLE | th_content | range | idx_at_ft_pt_let,idx_audit_status | idx_audit_status | 4 | NULL | 209754 | Using where |
+----+-------------+------------+-------+------------------------------------------+------------------+---------+------+--------+-------------+

细节:因为audit_time是一个范围查找,所以第二列的索引用不上了,只能用到audit_time,所以key_len是4。而下面思路2中,还是这两个字段key_len则是5。

还是分析下在添加了该索引之后的执行过程:

  • 从联合索引里找到小于该审核时间的audit_time最大的一行的联合索引
  • 然后依次往下找,因为< audit_time是一个范围查找,而第二列索引的值是分散的。所以需要依次往前查找,匹配出满足条件(status='ONLINE')的索引行,直到取到第5行为止。
  • 回表查询需要的具体数据

在上面的示意图中,粉红色标识满足第一列索引要求的行,依次向前查询,本个叶子节点上筛选到了3条记录,然后需要继续向左,到前一个叶子节点继续查询。直到找到5条满足记录的行,最后回表。

改进之处

因为在索引里面有status的值,所以在筛选满足status='ONLINE'行的时候,就不用回表查询了。在回表的时候只有5行数据的查询了,在iops上会大大减少。

该索引的弊端

如果idx_audit_status里扫描5行都是statusONLINE,那么只需扫描5行;
如果idx_audit_status里扫描前100万行中,只有4行statusONLINE,则需要扫描100万零1行,才能得到需要的5行记录。索引需要扫描的行数不确定。

改进思路 2

ALTER TABLE `th_content` DROP INDEX `idx_audit_status`;
ALTER TABLE `th_content` ADD INDEX `idx_status_audit` (`status`, `audit_time`);

这样不管是排序还是回表都毫无压力啦。

原文链接
本文为云栖社区原创内容,未经允许不得转载。

一个案例彻底弄懂如何正确使用 mysql inndb 联合索引的更多相关文章

  1. 通过一个案例彻底读懂10046 trace--字节级深入破解

    转载请注明出处:http://blog.csdn.net/guoyjoe/article/details/37840583 2014.7.23晚20:30 Oracle support组猫大师分享&l ...

  2. Java 多态 ——一个案例 彻底搞懂它

    最近,发现基础真的hin重要.比如,Java中多态的特性,在学习中就是很难懂,比较抽象的概念.学的时候就犯糊涂,但日后会发现,基础在日常工作的理解中占有重要的角色. 下面,我将用一个代码实例,回忆和巩 ...

  3. 一个简单例子弄懂什么是javascript函数劫持

    javascript函数劫持很简单,一般情况下,只要在目标函数触发之前,重写这个函数即可. 比如,劫持eval函数的代码如下: var _eval=eval; eval=function(x){ if ...

  4. 正确理解Mysql的列索引和多列索引

    MySQL数据库提供两种类型的索引,如果没正确设置,索引的利用效率会大打折扣却完全不知问题出在这. CREATE TABLE test ( id         INT NOT NULL, last_ ...

  5. 用一个性能提升了666倍的小案例说明在TiDB中正确使用索引的重要性

    背景 最近在给一个物流系统做TiDB POC测试,这个系统是基于MySQL开发的,本次投入测试的业务数据大概10个库约900张表,最大单表6千多万行. 这个规模不算大,测试数据以及库表结构是用Dump ...

  6. 必须弄懂的495个C语言问题

    1.1 我如何决定使用那种整数类型? 如果需要大数 值(大于32, 767 或小于¡32, 767), 使用long 型.否则, 如果空间很重要(如有大数组或很多结构), 使用short 型.除此之外 ...

  7. 彻底弄懂 JavaScript 执行机制

    本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我. 不论你是javascript新手还是老鸟,不论是面试求职,还是日常开发工作,我们经常会遇到这样的情况:给定 ...

  8. 彻底弄懂JS的事件冒泡和事件捕获(不推荐阅读)

    由于搬去敌台了,好久没来博客园,今天无意中翻到有“误认子弟”的评论,这里特意做个说明. 本文中关于事件冒泡和事件捕获的描述和例子都是OK的,错就错在后面用jquery去展示了利用事件冒泡的例子有误,其 ...

  9. js进阶 12-2 彻底弄懂JS的事件冒泡和事件捕获

    js进阶 12-2 彻底弄懂JS的事件冒泡和事件捕获 一.总结 一句话总结:他们是描述事件触发时序问题的术语.事件捕获指的是从document到触发事件的那个节点,即自上而下的去触发事件.相反的,事件 ...

随机推荐

  1. netcore程序部署及守护

    一.程序发布 1.在本机编译无误的情况下,选择发布成文件系统.注意如果使用了swagger 需要将生成的xml文档说明复制到发版包里面.否则会报错.(可以在项目的csproj 中加入 <Prop ...

  2. Spring-boot在windows上安装CLI(Command Line Interface)的步骤!

    首先去下载安装包,我这里整了一个zip包,一个tar包,下载地址:https://github.com/zhangyawei117/Spring-boot-CLI.git 下载完了之后,把zip包解压 ...

  3. 如何在Ubuntu上安装LAMP服务器系统?

    在Ubuntu上安装LAMP Ubuntu的开发人员让人们很容易安装和配置LAMP软件包,只要借助一个终端命令.所以,打开终端窗口,让我们开始入手吧. sudo apt-get install lam ...

  4. Kinfu配置指南

    Kinfu配置指南 欢迎加入Kinfu讨论群:563741937 写给准备配置pcl-kinfu的同学,这个是我的配置方法,已经能用的请无视. 我的配置:Windows7,VS2010(用这个的是不是 ...

  5. C# 获取外网IP地址

    很多情况下我们需要获取外网的IP地址,一般用自带的方法获取到的都是不准确,往往获取到的是内网的IP地址,所以需要采用外部网站接口来获取. 代码 通过访问第三方接口来获取真实的ip地址 public s ...

  6. 《你不知道的JavaScript》三卷读后感

    本系列的作者是Kyle Simpson,上卷译者赵望野.梁杰,中卷译者单业,下卷译者单业.姜南. 我个人觉得第一卷是本系列最好的(必读),而第二卷虽然也讲解了很多知识点,但是对于异步和性能的那部分提及 ...

  7. 出错with root cause

    [背景:] 我自己写了一个项目,主页可以看到一个数据库里的一个应用的users用户表的所有数据,包括用户的年龄,姓名,出生日期等信息.后来又想再增加一个注册功能,写好了之后进行单元测试,结果就出现了w ...

  8. 最近一个dish项目的建设思考

    系统通用能力的沉淀:a.核心模型的数据沉淀 b.通用服务能力的沉淀 ps1:以前重心主要放在了业务的抽象和通过设计模式来增加可复用的扩展性.局限在于,抽象的范围会被单个业务或者当前的业务所束缚,在更大 ...

  9. Base 底层库开源项目总结

    在Android开发中,我们经常使用一些开源的项目,一般情况下,这些开源项目都是基于开源的底层库进行的开发,以适配各自的用户场景.下面来列举一下本人收藏或Star的项目: 一.JavaCV 项目地址: ...

  10. 吴恩达机器学习笔记35-诊断偏差和方差(Diagnosing Bias vs. Variance)

    当你运行一个学习算法时,如果这个算法的表现不理想,那么多半是出现两种情况:要么是偏差比较大,要么是方差比较大.换句话说,出现的情况要么是欠拟合,要么是过拟合问题.那么这两种情况,哪个和偏差有关,哪个和 ...