[msyql]实战:关于回表的一次查询优化实战
- 起因与前置环境
- 思考与解决方案
- 第一个理解与方法——分块分页
- 第二个理解与方法——拆分子查询
- 第三个理解与方法——拆分子查询+分块分页
- 原理浅析与总结
- 回表和索引覆盖的浅解
- 原理简单说明
- MYSQL中回表的实现
- 总结与收获
- 回表和索引覆盖的浅解
起因与前置环境
目前在职的公司是已经运转挺久的电商类型公司,这个过程中其实因为版本不断迭代和很多历史问题。会出现一些慢sql的情况。而且很多时候其实本来会感觉是不应该出现慢sql的地方莫名其妙就出现慢sql了。这些地方其实最适合我们学习数据库的知识。这一次的优化也证明了底层原理知识真的很重要。
前置环境
各位老板放心,公司真实的数据我都不会放出来的,只是利用自己的服务器数据库模拟数据,代码部分直接语言解析一下就好了。
先放sql脚本:
CREATE TABLE `xm_order_items` (`order_item_no` char(50) CHARACTER SET utf8mb3 COLLATE utf8_general_ci NOT NULL COMMENT '订单项号',`order_no` char(50) CHARACTER SET utf8mb3 COLLATE utf8_general_ci NOT NULL COMMENT '订单号',`product_no` char(50) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL COMMENT '产品ID',`created_at` datetime NOT NULL COMMENT '创建时间',`updated_at` datetime DEFAULT NULL COMMENT '更新时间',PRIMARY KEY (`order_item_no`) USING BTREE,KEY `xm_items_order_no` (`order_no`) USING BTREE,KEY `xm_items_updated_at` (`updated_at`) USING BTREE,KEY `xm_items_created_at` (`created_at`) USING BTREE,KEY `xm_items_product_no_created_at` (`product_no`,`created_at`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 MAX_ROWS=1000000000 AVG_ROW_LENGTH=15000 ROW_FORMAT=COMPACT COMMENT='订单明细';
在这说一下对应的一个数据量是六百万左右,我是利用navicat直接生成的数据,所以各位也可以用不同的数据类型去直接生成对应数据量的测试数据。而且我们使用crerated_at这个创建时间去去排查,数据量大概是1000~2000 条\天这样吧。其中,对应的慢sql是:
SELECTxoi.order_no AS orderNo, xoi.order_item_no AS orderItemNoFROMxm_order_items xoiWHERExoi.product_no = 'HG-HGRS-0029'AND xoi.created_at >= '2022-07-01 00:00:00'AND xoi.created_at <= '2022-09-30 23:59:59';
对应的耗时解释:
- 起因与前置环境
- 思考与解决方案
- 第一个理解与方法——分块分页
- 第二个理解与方法——拆分子查询
- 第三个理解与方法——拆分子查询+分块分页
- 原理浅析与总结
- 回表和索引覆盖的浅解
- 原理简单说明
- MYSQL中回表的实现
- 总结与收获
- 回表和索引覆盖的浅解
起因与前置环境
目前在职的公司是已经运转挺久的电商类型公司,这个过程中其实因为版本不断迭代和很多历史问题。会出现一些慢sql的情况。而且很多时候其实本来会感觉是不应该出现慢sql的地方莫名其妙就出现慢sql了。这些地方其实最适合我们学习数据库的知识。这一次的优化也证明了底层原理知识真的很重要。
前置环境
各位老板放心,公司真实的数据我都不会放出来的,只是利用自己的服务器数据库模拟数据,代码部分直接语言解析一下就好了。
先放sql脚本:
CREATE TABLE `xm_order_items` (`order_item_no` char(50) CHARACTER SET utf8mb3 COLLATE utf8_general_ci NOT NULL COMMENT '订单项号',`order_no` char(50) CHARACTER SET utf8mb3 COLLATE utf8_general_ci NOT NULL COMMENT '订单号',`product_no` char(50) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL COMMENT '产品ID',`created_at` datetime NOT NULL COMMENT '创建时间',`updated_at` datetime DEFAULT NULL COMMENT '更新时间',PRIMARY KEY (`order_item_no`) USING BTREE,KEY `xm_items_order_no` (`order_no`) USING BTREE,KEY `xm_items_updated_at` (`updated_at`) USING BTREE,KEY `xm_items_created_at` (`created_at`) USING BTREE,KEY `xm_items_product_no_created_at` (`product_no`,`created_at`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 MAX_ROWS=1000000000 AVG_ROW_LENGTH=15000 ROW_FORMAT=COMPACT COMMENT='订单明细';
在这说一下对应的一个数据量是六百万左右,我是利用navicat直接生成的数据,所以各位也可以用不同的数据类型去直接生成对应数据量的测试数据。而且我们使用crerated_at这个创建时间去去排查,数据量大概是1000~2000 条\天这样吧。其中,对应的慢sql是:
SELECTxoi.order_no AS orderNo, xoi.order_item_no AS orderItemNoFROMxm_order_items xoiWHERExoi.product_no = 'HG-HGRS-0029'AND xoi.created_at >= '2022-07-01 00:00:00'AND xoi.created_at <= '2022-09-30 23:59:59';
对应的耗时解释:
思考与解决方案
第一个理解与方法——分块分页
直接从explain可以看出,其实我们已经使用了索引进行查询。作为一个初级程序员的菜鸟。我立刻想到这个在索引上已经没有优化空间。所以我认为这是因为每次获取的数据量太大了,因为是一次性获取三万的数据出来这样。第二个是觉得自己应该是时间跨度太大了。所以考虑使用java对时间进行分块,比如一周的数据分成一块。然后limit成1000条。这样也能避免一次获取的数据太多的问题。真实数据库的接口能快4倍这样子吧。
SELECTxoi.order_no AS orderNo, xoi.order_item_no AS orderItemNoFROMxm_order_items xoiWHERExoi.product_no = 'HG-HGRS-0029'AND xoi.created_at >= '2022-07-01 00:00:00'AND xoi.created_at <= '2022-07-06 23:59:59';
第二个理解与方法——拆分子查询
第一个方法虽然能优化个几倍的性能。但是事实上的问题还是很明显,而且数据量一直在增长。所以我还是回家偷偷内卷了一段时间,研究了一下性能优化部分的内容。这里主要发现影响到查询效率的是索引覆盖和回表这两个操作。所以考虑了利用java分成两次查询。这样就可以避免回表问题了。
第一次:list = SELECTxoi.order_item_no AS orderItemNoFROMxm_order_items xoiWHERExoi.product_no = 'HG-HGRS-0029'AND xoi.created_at >= '2022-07-01 00:00:00'AND xoi.created_at <= '2022-09-30 23:59:59';第二次:SELECTxoi.order_no AS orderNo
FROMxm_order_items AS xoi
WHERExoi.order_item_no IN ( list );
第三个理解与方法——拆分子查询+分块分页
最后的方法说是考虑到了后面数据增长的问题,增加了分页,如果时间跨度继续扩大,就进行时间分块的方式。
第一次:list = SELECTxoi.order_item_no AS orderItemNoFROMxm_order_items xoiWHERExoi.product_no = 'HG-HGRS-0029'AND xoi.created_at >= '2022-07-01 00:00:00'AND xoi.created_at <= '2022-09-30 23:59:59';第二次:SELECTxoi.order_no AS orderNo
FROMxm_order_items AS xoi
WHERExoi.order_item_no IN ( list ) limit 5000;
原理浅析与总结
- 回表和索引覆盖的浅解
原理简单说明
什么是回表和索引覆盖呢?
这里和我们使用的mysql中Innodb 引擎中的索引储存方式,可以理解为你构建了一个索引树(非主键)以后。Innodb会生成一个包含了索引的key + 主键 的节点。每次查找数据的时候如果你直接在索引树上可以命中你需要的所有数据,就会直接返回数据。
但是如果你像本次分享的sql一样product_no的索引没有order_no中的数据。所以下一步需要回表。
回表其实就是直接使用索引中的主键去再一次查询数据。
- 总结与收获
- 总结一下,其实这次算是本菜鸟的一次成长吧,真正的研究了一下索引覆盖,回表等mysql中查询相关的知识点。当然不得不承认的是sql的性能优化不能只是单纯的看索引覆盖和回表还有缓冲区之类的挺多东西后面遇到了再分享出来吧。大家有什么想讨论的可以在下面只有留言。
思考与解决方案
第一个理解与方法——分块分页
直接从explain可以看出,其实我们已经使用了索引进行查询。作为一个初级程序员的菜鸟。我立刻想到这个在索引上已经没有优化空间。所以我认为这是因为每次获取的数据量太大了,因为是一次性获取三万的数据出来这样。第二个是觉得自己应该是时间跨度太大了。所以考虑使用java对时间进行分块,比如一周的数据分成一块。然后limit成1000条。这样也能避免一次获取的数据太多的问题。真实数据库的接口能快4倍这样子吧。
SELECTxoi.order_no AS orderNo, xoi.order_item_no AS orderItemNoFROMxm_order_items xoiWHERExoi.product_no = 'HG-HGRS-0029'AND xoi.created_at >= '2022-07-01 00:00:00'AND xoi.created_at <= '2022-07-06 23:59:59';
第二个理解与方法——拆分子查询
第一个方法虽然能优化个几倍的性能。但是事实上的问题还是很明显,而且数据量一直在增长。所以我还是回家偷偷内卷了一段时间,研究了一下性能优化部分的内容。这里主要发现影响到查询效率的是索引覆盖和回表这两个操作。所以考虑了利用java分成两次查询。这样就可以避免回表问题了。
第一次:list = SELECTxoi.order_item_no AS orderItemNoFROMxm_order_items xoiWHERExoi.product_no = 'HG-HGRS-0029'AND xoi.created_at >= '2022-07-01 00:00:00'AND xoi.created_at <= '2022-09-30 23:59:59';第二次:SELECTxoi.order_no AS orderNo
FROMxm_order_items AS xoi
WHERExoi.order_item_no IN ( list );
第三个理解与方法——拆分子查询+分块分页
最后的方法说是考虑到了后面数据增长的问题,增加了分页,如果时间跨度继续扩大,就进行时间分块的方式。
第一次:list = SELECTxoi.order_item_no AS orderItemNoFROMxm_order_items xoiWHERExoi.product_no = 'HG-HGRS-0029'AND xoi.created_at >= '2022-07-01 00:00:00'AND xoi.created_at <= '2022-09-30 23:59:59';第二次:SELECTxoi.order_no AS orderNo
FROMxm_order_items AS xoi
WHERExoi.order_item_no IN ( list ) limit 5000;
原理浅析与总结
- 回表和索引覆盖的浅解
原理简单说明
什么是回表和索引覆盖呢?
这里和我们使用的mysql中Innodb 引擎中的索引储存方式,可以理解为你构建了一个索引树(非主键)以后。Innodb会生成一个包含了索引的key + 主键 的节点。每次查找数据的时候如果你直接在索引树上可以命中你需要的所有数据,就会直接返回数据。
但是如果你像本次分享的sql一样product_no的索引没有order_no中的数据。所以下一步需要回表。
回表其实就是直接使用索引中的主键去再一次查询数据。
- 总结与收获
- 总结一下,其实这次算是本菜鸟的一次成长吧,真正的研究了一下索引覆盖,回表等mysql中查询相关的知识点。当然不得不承认的是sql的性能优化不能只是单纯的看索引覆盖和回表还有缓冲区之类的挺多东西后面遇到了再分享出来吧。大家有什么想讨论的可以在下面只有留言。
[msyql]实战:关于回表的一次查询优化实战相关推荐
- 闪回的用途与实战(闪回表,闪回删除,闪回重名删除,闪回版本查询)
闪回可以做的操作有如下几种类型: 1.当数据错误删除,并且提交时(flashback table) 2.当错误删除了一张表drop table(flashback drop) 3.通过闪回获取表的历史 ...
- mysql主键创建非聚集索引_什么是聚集索引,非聚集索引,索引覆盖,回表,索引下推...
聚集索引 我们先建如下的一张表 CREATE TABLE `student` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '学号',`name` var ...
- 什么是聚集索引,非聚集索引,索引覆盖,回表,索引下推
聚集索引 我们先建如下的一张表 CREATE TABLE `student` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '学号',`name` var ...
- mysql回表_到底什么情况下mysql innodb会发生回表操作?
谢邀 MySQL innodb的主键索引是簇集索引,也就是索引的叶子节点存的是整个单条记录的所有字段值,不是主键索引的就是非簇集索引,非簇集索引的叶子节点存的是主键字段的值.回表是什么意思?就是你执行 ...
- LeetCode实战:回文数
题目英文 Determine whether an integer is a palindrome. An integer is a palindrome when it reads the same ...
- R语言将多个dataframe导出到excel的多个表单(sheet)实战
R语言将多个dataframe导出到excel的多个表单(sheet)实战 目录 R语言将多个dataframe导出到excel的多个表单(sheet)实战
- R语言生存分析寿命表(life table)实战案例:比较两种药物治疗感染患者的生存时间
R语言生存分析寿命表(life table)实战案例:比较两种药物治疗感染患者的生存时间 目录
- R语言tidyr包pivot_longer函数、pivot_wider函数数据表变换实战(长表到宽表、宽表到长表)
R语言tidyr包pivot_longer函数.pivot_wider函数数据表变换实战(长表到宽表.宽表到长表) 目录
- 生动的解释下什么是 MySQL 的“回表”?
1. 索引结构 要搞明白这个问题,需要大家首先明白 MySQL 中索引存储的数据结构.这个其实很多小伙伴可能也都听说过,B+Tree 嘛! B+Tree 是什么?那你得先明白什么是 B-Tree,来看 ...
最新文章
- java 快排_八大排序-快速排序(搞定面试之手写快排)
- 整理Silverlight资源列表(四)——Silverlight案例补充
- 【Android 逆向】selinux 进程保护 ( selinux 进程保护 | 宽容模式 Permissive | 强制模式 Enforcing )
- Javascript 严格模式详解
- LVS NAT/DR
- 计算 1+2!+3!+4!+...20!=?
- 洛谷 P2040 打开所有的灯-dfs
- Mysql截取中英数混合的字符串
- java seqlist_java_实现链表以及链表的测试类
- 智能家居火了这么久 何时到我家?
- 中考在即,杂乱的书房
- easyexcel excel自定义列导出 格式导出
- CDA备考学习笔记——基础知识篇(三)
- 海量上传文件服务器端,bat批量上传ftp文件到服务器
- 高等代数:4 矩阵的运算
- 高效能力人士必知必会的搜索引擎高级使用技巧
- Leetcode 592. 分数加减运算 C++
- 一些方法:JQ: append() 、appendTo() || JS:appendChild():
- thisisunsafe神奇操作
- 拆卸U2000服务器注意事项