分布式任务调度内的 MySQL 分页查询优化

作者:vivo 互联网数据库团队- Qiu Xinbo

本文主要通过图示介绍了用主键进行分片查询的过程,介绍了主键分页查询存在SQL性能问题,如何去创建高效的索引去优化主键分页查询的SQL性能问题。

对于数据分布不均如何发现,提供了一些SQL查询案例来进行参考,对MySQL Index Condition Pushdown优化算法做了一些简单介绍。

一、背景介绍

最近在线上环境发现了一条执行较慢的分页查询,高并发执行,产生了大量的慢查询日志,CPU使用率逐步升高。

通过观察它的执行时间,发现该SQL查询时快时慢,执行时间并不稳定,以至于在高并发执行场景时,数据库来不及响应,数据库服务变慢。

图片

图片

二、分析定位

2.1 定位 SQL 执行变慢的原因

通过数据库管理平台查看SQL执行信息发现,SQL解析行数(扫描行数)和SQL执行时间都很不稳定,执行时长和解析行数(扫描行数)是成正比的。

这个也能解释的通为什么SQL执行时长变了,因为扫描行数变多了,SQL执行时间成比例增长。

-- SQL全文
selectid,uuid,name,user_type,is_deleted,modify_date
fromtest_user
whereis_deleted=0    and user_type=0    and id > 10000    and id % 10 = 9
order byid  limit 500;

图片

2.2 了解 SQL 的业务背景

通过与研发沟通发现,该SQL原来是串行执行,单个线程在跑,后来觉得比较慢,改为分布式任务并行执行,通过id取模0-9,调度10个线程,每个线程处理1个分区,这样就有10个并发相当于把数据做了切片,并发查询并发处理,由此带来数据库端的并发升高。从技术角度上看,提高数据处理速度,给数据做切片,改单线程为并发处理,并没有任何问题,反而是一种比较好的优化方案,但是高并发执行的SQL都是要有一个前提,SQL执行效率要特别高,否则会导致数据库端物理机资源耗尽,数据库服务来不及响应。

图片

2.3 定位 SQL 扫描行数变化的原因

2.3.1 慢 SQL 及表结构信息

-- 为了方便理解和说明,新建一个test_user表,造了一些模拟数据,将SQL做了一些简化,不影响整体的分析效果-- SQL全文
selectid,uuid,name,user_type,is_deleted,modify_date
fromtest_user 
whereis_deleted=0     and user_type=0     and id > 10000     and id % 10 = 9 
order byid  limit 500;-- 表信息CREATE TABLE `test_user` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`uuid` varchar(64) NOT NULL COMMENT '用户ID',`name` varchar(20) DEFAULT '' COMMENT '用户名',`user_type` tinyint(4) NOT NULL DEFAULT '0',`is_deleted` tinyint(4) NOT NULL DEFAULT '0',`modify_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',`create_date` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',PRIMARY KEY (`id`),UNIQUE KEY `uniq_uuid` (`uuid`),KEY `idx_modifydate` (`modify_date`)
) ENGINE=InnoDB AUTO_INCREMENT=7986024 DEFAULT CHARSET=utf8mb4

2.3.2 查看 SQL 执行计划

通过查看SQL执行计划,发现执行计划走主键索引扫描,以下是SQL执行计划的关键信息解读:

  • type=range     范围扫描

  • key = primary 使用主键索引

  • rows = 877w   预估的扫描行数

  • filter = 1.00     百分比,满足过滤条件返回的行数  = rows * filter 

mysql> explain  select->     id,->     uuid,->     name,->     user_type,->     is_deleted,->     modify_date-> from->     test_user -> where->     is_deleted=0     ->     and user_type=9     ->     and id > 10000     ->     and id % 10 = 9 -> order by->     id  limit 500; 
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+
| id | select_type | table     | partitions | type  | possible_keys | key     | key_len | ref  | rows    | filtered | Extra       |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+
|  1 | SIMPLE      | test_user | NULL       | range | PRIMARY       | PRIMARY | 8       | NULL | 8775507 |     1.00 | Using where |
+----+-------------+-----------+------------+-------+---------------+---------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

2.3.3 图示 SQL 执行过程

通过简单的图示,描述下SQL扫描过程,由于是通过主键索引遍历,避免了额外的排序行为,从最小id开始取到最大id。

mysql> select min(id),max(id) from test_user;
+---------+----------+
| min(id) | max(id)  |
+---------+----------+
|       3 | 17889149 |
+---------+----------+
1 row in set (0.00 sec)

图片

2.3.4 计算数据分布

从SQL过滤条件看只有is_deleted、user_type、id这三个,能预估到is_deleted和user_type区分度不高,通过SQL查看下数据的分布。

mysql> select is_deleted,user_type,count(*) from test_user group by is_deleted,user_type order by count(*) desc limit 1,10;
+------------+-----------+----------+
| is_deleted | user_type | count(*) |
+------------+-----------+----------+
|          1 |         1 |  4473019 |
|          1 |         0 |  4471648 |
|          0 |         0 |  4470140 |
|          0 |         2 |      999 |
+------------+-----------+----------+
4 rows in set (4.81 sec)
-- 从数据分布来看user_type等于2的数据较少,只有999条,其他相对比较均匀

数据分布验证测试

将上述4种结果(is_deleted和user_type)分别通过SQL查看最近1000条满足条件的数据的id区间,验证数据的分布。

  • is_deleted=1、user_type=1

  • is_deleted=1、user_type=0

  • is_deleted=0、user_type=0

-- 最近1000条is_deleted=1、user_type=1的数据记录分布在id 6-3876,大约扫描3871条数据,能返回500条满足条件的值,数据分布均匀.
mysql> select max(id),min(id) from( select id from test_user where  is_deleted=1 and user_type=1 order by id  limit 1000) a;
+---------+---------+
| max(id) | min(id) |
+---------+---------+
|    3876 |       6 |
+---------+---------+
1 row in set (0.00 sec)-- 最近1000条is_deleted=1、user_type=0的数据记录分布在id 3-4019,大约扫描4016条数据,能返回500条满足条件的值,数据分布均匀.
mysql> select max(id),min(id) from( select id from test_user where  is_deleted=1 and user_type=0 order by id  limit 1000) a;
+---------+---------+
| max(id) | min(id) |
+---------+---------+
|    4019 |       3 |
+---------+---------+
1 row in set (0.00 sec)-- 最近1000条is_deleted=0、user_type=0的数据记录分布在id 5-4020,大约扫描4015条数据,能返回500条满足条件的值,数据分布均匀.
mysql> select max(id),min(id) from( select id from test_user where  is_deleted=0 and user_type=0 order by id  limit 1000) a;
+---------+---------+
| max(id) | min(id) |
+---------+---------+
|    4025 |       5 |
+---------+---------+
1 row in set (0.00 sec)

图片

is_deleted=0、user_type=2

-- 最近1000条is_deleted=0、user_type=2的数据记录分布在id 17890648-17891147,是比较紧凑的,但是由于id比较大,整体排在较后的位置。
-- 如果按照主键遍历,需要遍历完前面的1700w条不符合条件数据,才能遍历到满足条件的数据。
mysql> select max(id),min(id) from( select id from test_user where  is_deleted=0 and user_type=2 order by id  limit 1000) a;
+----------+----------+
| max(id)  | min(id)  |
+----------+----------+
| 17891147 | 17890149 |
+----------+----------+
1 row in set (0.00 sec)

图片

2.3.5 实际执行测试

重要字段信息说明:

  • Query_time:SQL执行时间

  • Rows_examined:SQL扫描行数

  • Rows_sent:SQL返回行数

# Query_time: 0.012232  Lock_time: 0.000076 Rows_sent: 500  Rows_examined: 19507SET timestamp=1695711685;select id,uuid,name,user_type,is_deleted,modify_date from test_user where is_deleted=1 and user_type=1 and id > 0 and id % 10 = 9 order by id  limit 500;

# Query_time: 0.009549  Lock_time: 0.000074 Rows_sent: 500  Rows_examined: 20537SET timestamp=1695711745;select id,uuid,name,user_type,is_deleted,modify_date from test_user where is_deleted=1 and user_type=0 and id > 0 and id % 10 = 9 order by id limit 500;

# Query_time: 0.009835  Lock_time: 0.000081 Rows_sent: 500  Rows_examined: 21037SET timestamp=1695711779;select id,uuid,name,user_type,is_deleted,modify_date from test_user where is_deleted=0 and user_type=0 and id > 0 and id % 10 = 9 order by id limit 500;

(这边大家可能会有疑惑,为什么扫描行数要比预估的多一些,其实也正常,我们在做预估时并没有把取模的过滤条件加上,所以必然会多扫描)

# Query_time: 6.981938  Lock_time: 0.000076 Rows_sent: 100  Rows_examined: 17890145SET timestamp=1695711818;select id,uuid,name,user_type,is_deleted,modify_date from test_user where is_deleted=0 and user_type=2 and id > 0 and id % 10 = 9 order by id limit 500;

2.3.6 自此能得到结论

因为is_deleted和user_type数据分布不均匀并且数据区分度不高,执行计划走主键顺序扫描, 在查询is_deleted=0 and user_type=2 特定场景的时,因为走主键索引顺序遍历,满足user_type=2 的id比较靠后,需要先扫描完成前面1700w条数据后,才能找到满足user_type=2的数据,SQL扫描行数变多, SQL执行时间变长。

三、优化方案

3.1 优化方案确定

当前SQL执行计划以主键进行顺序遍历,是一个范围扫描,有点像在一片很大的居民区按照序号挨家挨户寻找一些特定的人一样,比较简单也比较低效。

既然查询是以is_deleteduser_type为主要的过滤条件,查询特定的人群信息,可以考虑直接在这两列上添加索引,记录特定人群信息的位置,根据位置直接去定向寻找。

虽然is_deleteduser_type字段区分度很低,但是成为有序结构,能避免这条SQL大量的读取不符合条件的数据的行为,添加索引的收益远大于索引带来负面影响。

最终的添加的索引:

alter table test_user add index idx_isdeleted_usertype_id(is_deleted,user_type,id);

添加该索引的考虑:遵循ESR原则(等值在前,排序在中间,范围在最后),既能高效扫描到对应的数据,还能避免id的排序,extra内显示使用了Using index condition。

mysql>  explain select id,uuid,name,user_type,is_deleted,modify_date from test_user where is_deleted=0 and user_type=2 and id > 0 and id % 10 = 9 order by id limit 500;
+----+-------------+-----------+------------+-------+-----------------------------------+---------------------------+---------+------+------+----------+-----------------------+
| id | select_type | table     | partitions | type  | possible_keys                     | key                       | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+-----------+------------+-------+-----------------------------------+---------------------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | test_user | NULL       | range | PRIMARY,idx_isdeleted_usertype_id | idx_isdeleted_usertype_id | 10      | NULL |  999 |   100.00 | Using index condition |
+----+-------------+-----------+------------+-------+-----------------------------------+---------------------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)  

3.2 优化效果对比

优化前

# Query_time: 6.981938  Lock_time: 0.000076 Rows_sent: 100  Rows_examined: 17890145
SET timestamp=1695711818;
select id,uuid,name,user_type,is_deleted,modify_date from test_user where is_deleted=0 and user_type=2 and id > 0 and id % 10 = 9 order by id limit 500;

优化后

# Query_time: 0.000884  Lock_time: 0.000091 Rows_sent: 100  Rows_examined: 100
SET timestamp=1695714485;
select id,uuid,name,user_type,is_deleted,modify_date from test_user where is_deleted=0 and user_type=2 and id > 0 and id % 10 = 9 order by id limit 500;

优化提升

扫描行数从1700w条降低为100条,查询时间从6.98s 降低为 0.8ms

3.3  图示的优化后的SQL执行过程

  1. 通过idx_isdeleted_usertype_id索引的有序性,进行二分查找,快速定位到满足is_deleted和user_type、id条件主键信息。

  2. 通过主键信息回表读取完整的数据。

  3. 返回数据给客户端服务。

图片

3.4 ICP特性(Index Condition Pushdown) 

补充下执行计划内extra列体现Using index condition优化。

  • 索引条件下推 (ICP) 是针对 MySQL 使用索引从表中检索行的情况的优化。

  • 如果没有 ICP,存储引擎会遍历索引以定位基表中的行,并将它们返回给 MySQL  server,由 MySQL  server评估行的 WHERE 条件。

  • 在启用 ICP 的情况下,如果 WHERE 条件的一部分可以通过仅使用索引中的列来评估,MySQL  server会将这部分 WHERE 条件下推到存储引擎。

  • 然后存储引擎通过使用索引条目来评估推送的索引条件,并且只有在满足这一条件时才从表中读取行。

  • ICP可以减少存储引擎必须访问基表的次数和MySQL server必须访问存储引擎的次数。

图片

ICP优化的使用和局限性路

ICP优化在数据库优化器内默认是开启的,ICP优化适用性取决于以下条件:

  • icp 对于使用rang、ref、eq_ref 和ref_or_null访问模式去检索全表数据行时候。

  • icp 只适用于innodb、myisam引擎的表,包括分区的InnoDB和MyISAM表。

  • icp只会使用二级索引,减少完整行记录的读取和减少I/O操作 对于聚集索引,完整行记录已经被读入innodb buffer中,using icp不能减少I/O操作。

  • icp不支持使用创建在虚拟列上的二级索引,innodb引擎支持在虚拟列上创建二级索引。

  • 引用子查询的条件无法下推。

  • 引用存储函数的条件无法下推。存储引擎无法调用存储的函数。

  • Triggered conditions cannot be pushed down。

-- 测试下相同的SQL执行在开启ICP优化和关闭ICP优化,执行时间和扫描行数的对比.-- 关闭ICP,SQL执行扫描行数是5043行,执行时间为8.03ms.
SET optimizer_switch='index_condition_pushdown=off';
# Query_time: 0.008031  Lock_time: 0.000085 Rows_sent: 500  Rows_examined: 5043
select id,uuid,name,user_type,is_deleted,modify_date from test_user where is_deleted=0 and user_type=0  and id > 10000 and id % 10 = 9  order by id limit 500;-- 开启ICP,SQL执行扫描行数仅为500行,执行时间为2.72ms.
SET optimizer_switch='index_condition_pushdown=on';
# Query_time: 0.002724  Lock_time: 0.000082 Rows_sent: 500  Rows_examined: 500
select id,uuid, name, user_type,is_deleted,modify_date from test_user where is_deleted=0 and user_type=0 and id > 10000 and id % 10 = 9 order by id limit 500;

结论:本次测试,开启ICP优化,SQL执行时扫描的行数仅为未开启时的1/10,执行时间提升约2-3倍。

四、总结

  1. 将SQL查询从串行改为高并发执行,需要评估下SQL查询效率是否足够高,评估的标准:SQL扫描行数/SQL返回行数  结果越大说明存在很多低效的数据扫描,执行效率不高。

  2. 分页查询通过主键遍历是顺序遍历,从最小id到最大id,当存在其它过滤条件时,需要再次判断数据是否满足这些过滤条件,扫描的行数会随着增长。

  3. 区分度较低的字段并非不适合创建索引,仔细评估查询的场景,建立特定的组合索引,触发MySQL icp优化,对查询性能会有很大提升。

参考文章

Index Condition Pushdown介绍:

  • Index Condition Pushdown

  • Index Condition Pushdown Optimization

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/13994.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言初阶——5操作符

一、算数操作符 除了% 操作符之外,其他的几个操作符可以作用于整数和浮点数。对于/ 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除 法。% 操作符的两个操作数必须为整数。返回的是整除之后的余数。 1、类型转换 C语言…

Simplicity Studui V5 新安装后无法Product Updates

之前(2021年)在SiliconLabs官网下载了SSV5,安装包我也保存在硬盘了,最近换了台电脑安装SSV5后安装 SDK之前必须Product Updates,但死活安装不上,老是提示发生了错误。来来回回卸载安装几十遍,后…

瓦解信息茧房,IPWO打破“墙”的限制

国外与国内的网络之间隔着一道无形的“墙”,这面“墙”让我们避免了海外不法分子的窥视,保护了我们的网络隐私。但是,“墙”的存在同样阻止了我们访问全球网络,获取海外资源,形成巨大的信息茧房。 越来越多的人渴望撕开…

Plant Simulation 双深位立库开发系列教程-出入口参数化

上一节讲到货架参数化,这一节我们继续讲出入口参数化,先看我们需要达到的效果,在Conveyor增加一个出入口设置功能,用户可以通过该功能设置多个出入口 步骤1:创建ConveyorList 在RackLane中新建一个表格ConveyorList,用于存储用户的设置参数 表格设置名字、类型、方向 、…

Docker配置国内镜像源

添加Docker国内镜像源 在/etc/docker/daemon.json文件中添加以下内容: {"registry-mirrors": ["http://hub-mirror.c.163.com","https://docker.mirrors.ustc.edu.cn","https://registry.docker-cn.com"] }重启docker s…

paligemma、Grounding-DINO-1.5简单无需标注无需训练直接可以使用的VLM图像到文本模型

1、paligemma 参考:https://github.com/google-research/big_vision/blob/main/big_vision/configs/proj/paligemma/README.md 模型架构: 文本与图像特征一起送入大模型 在线体验网址: https://huggingface.co/spaces/big-vision/paligemma 通过文字prompt既可与图片对话…

ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1+解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

QT creator centralwidget前面有个禁止符号

centralwidget前面有个禁止符号:表示分拆布局 在主窗口空白处,右键,选择布局即可选择不同的布局方式,表示对窗口内所有控件部件进行布局,不如垂直布局。

【机器学习】—机器学习和NLP预训练模型探索之旅

目录 一.预训练模型的基本概念 1.BERT模型 2 .GPT模型 二、预训练模型的应用 1.文本分类 使用BERT进行文本分类 2. 问答系统 使用BERT进行问答 三、预训练模型的优化 1.模型压缩 1.1 剪枝 权重剪枝 2.模型量化 2.1 定点量化 使用PyTorch进行定点量化 3. 知识蒸馏…

高光谱成像技术简介,怎么选择成像方案?

目录 一、什么是光谱?二、光谱和光谱分析方法的类型三、多光谱和高光谱的区别四、高光谱在水果品质检测中的应用1. 高光谱成像系统2. 高光谱图像的获取方式3. 高光谱图像处理与分析4. 在水果品质检测中的应用总结 五、针对自己的应用场景怎么使用高光谱技术六、参考…

海山数据库(He3DB)从方法到实践,构建以场景为中心的体验管理体系

编者按:体验优化的过程中设计师经常会遇到几个阶段,发现问题、定义问题、优化问题、查看反馈,但在产品快速迭代的过程中,体验的问题经常被归类到“不紧急”需求中,并逐步转为长尾问题,这些不被重视的问题聚…

【OpenGL实践10】关于几何着色器

目录 一、说明 二、几何着色器 2.1 设置 2.2 基本几何着色器 2.2.1 输入类型 2.2.2 输出类型 2.2.3 顶点输入 2.2.4 顶点输出 2.3 创建几何着色器 2.4 几何着色器和顶点属性 三、动态生成几何体 四、结论 练习 一、说明 几何着色器的应用比较高级,关于…

Epson推出的FC2012AN晶体专为小尺寸、低ESR应用设计

在可穿戴设备、loT产品、无线通信模块等领域,对于小型化、低功耗和高精度的时钟需求日益增长。Epson推出的FC2012AN系列晶体单元凭借其小尺寸、低ESR等特性使其成为这些应用的理想选择。 FC2012AN系列是一款32.768K频率的晶体单元,频率偏差为 20x10-6…

【云原生】kubernetes中的service原理、应用实战案例解析

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…

DSPy - prompt 优化

文章目录 一、关于 DSPy与神经网络的类比常见问题解答**DSPy 优化器可以调整什么?****我应该如何使用 DSPy 完成我的任务?****如果我对提示或合成数据生成有更好的想法怎么办?**DSPy 代表什么? 二、安装三、文档A) 教程B) 指南C) …

全球手游4月战报,《Monopoly GO!》荣耀再续!全球手游畅销榜冠军

易采游戏网5月22日消息,在刚刚过去的四月里,全球移动游戏市场的角逐愈发激烈。根据最新发布的数据,Scopely旗下的经典游戏《Monopoly GO!》再次蝉联全球手游畅销榜首冠军宝座,展现了无与伦比的市场魅力与玩家黏度。 4月Scopely《M…

中霖教育怎么样?二建继续教育几年一次?

中霖为大家介绍: 根据相关规定,二级建造师执业资格注册证书设定有效期限为三年。为确保持证人员的专业能力,在规定的期限内需要完成规定的继续教育课程并参加考核,以此来维护其职业资质的连续性。 在执业资格证书的有效期满前&a…

redis小知识

AOF与RDB的区别 AOF (Append Only File) 和 RDB (Redis Database) 都是Redis中的持久化机制,但有以下几点不同之处: 内容格式:AOF 以日志的形式记录所有写操作命令,而 RDB 则是在指定的时间间隔内对数据库进行快照,将数…

WDW-100G 高温拉力试验机 技术方案书

一、整机外观图: 二、项目简介: 微机控制高温拉力试验机是电子技术与机械传动相结合的新型材料试验机,它具有宽广准确的加载速度和测力范围,对载荷、变形、位移的测量和控制有较高的精度和灵敏度,还可以进行等速加载、…

FFmpeg开发笔记(三十)解析H.264码流中的SPS帧和PPS帧

《FFmpeg开发实战:从零基础到短视频上线》一书的“2.1.1 音视频编码的发展历程”介绍了H.26x系列的视频编码标准,其中H.264至今仍在广泛使用,无论视频文件还是网络直播,H.264标准都占据着可观的市场份额。 之所以H.264取得了巨大…