MySQL之基于代价的慢查询优化建议

1.背景

慢查询是指数据库中查询时间超过指定阈值(美团设置为 100ms)的 SQL,它是数据库的性能杀手,也是业务优化数据库访问的重要抓手。

如何优化慢查询呢?最直接有效的方法就是选用一个查询效率高的索引。关于高效率的索引推荐,主要在日常工作中,基于经验规则的推荐随处可见,对于简单的、SQL,如

select * from sync_test1 where name like 'Bobby%'

直接添加索引 IX(name) 就可以取得不错的效果;但对于稍微复杂点的 SQL,如

select from sync_test1 where name like 'Bobby%' and dt > '2021-07-06'

到底选择 IX(name)、IX(dt)、IX(dt,name) 还是 IX(name,dt),该方法也无法给出准确的回答。更别说像多表 Join、子查询这样复杂的场景了。所以采用基于代价的推荐来解决该问题会更加普适,因为基于代价的方法使用了和数据库优化器相同的方式,去量化评估所有的可能性,选出的是执行 SQL 耗费代价最小的索引

2.基于代价的优化器介绍

2.1 SQL 执行与优化器

一条 SQL 在 MySQL 服务器中执行流程主要包含:SQL 解析、基于语法树的准备工作、优化器的逻辑变化、优化器的代价准备工作、基于代价模型的优化、进行额外的优化和运行执行计划等部分。具体如下图所示:
在这里插入图片描述

2.2 代价模型介绍

而对于优化器来说,执行一条 SQL 有各种各样的方案可供选择,如表是否用索引、选择哪个索引、是否使用范围扫描、多表 Join 的连接顺序和子查询的执行方式等。如何从这些可选方案中选出耗时最短的方案呢?这就需要定义一个量化数值指标,这个指标就是代价 (Cost),我们分别计算出可选方案的操作耗时,从中选出最小值。

代价模型将操作分为 Server 层和 Engine(存储引擎)层两类,Server 层主要是CPU 代价,Engine 层主要是 IO 代价,比如 MySQL 从磁盘读取一个数据页的代价io_block_read_cost 为 1,计算符合条件的行代价为 row_evaluate_cost 为 0.2。除此之外还有:

  1. memory_temptable_create_cost (default 2.0) 内存临时表的创建代价。

  2. memory_temptable_row_cost (default 0.2) 内存临时表的行代价。

  3. key_compare_cost (default 0.1) 键比较的代价,例如排序。

  4. disk_temptable_create_cost (default 40.0) 内部 myisam 或 innodb 临时

    表的创建代价。

  5. disk_temptable_row_cost (default 1.0) 内部 myisam 或 innodb 临时表的行

代价。

在 MySQL 5.7 中,这些操作代价的默认值都可以进行配置。为了计算出方案的总代价,还需要参考一些统计数据,如表数据量大小、元数据和索引信息等。MySQL 的代价优化器模型整体如下图所示:

在这里插入图片描述

2.3 基于代价的索引选择

还是继续拿上述的 SQL

 select * from sync_test1 where name like  'Bobby%' and dt > '2021-07-06' 

为例,我们看看 MySQL 优化器是如何根据代价模型选择索引的。

首先,我们直接在建表时加入四个候选索引

Create Table: CREATE TABLE `sync_test1` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `cid` int(11) NOT NULL,
 `phone` int(11) NOT NULL,
 `name` varchar(10) NOT NULL,
 `address` varchar(255) DEFAULT NULL,
 `dt` datetime DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `IX_name` (`name`),
 KEY `IX_dt` (`dt`),
 KEY `IX_dt_name` (`dt`,`name`),
 KEY `IX_name_dt` (`name`,`dt`)
 ) ENGINE=InnoDB

通过执行 explain 看出 MySQL 最终选择了 IX_name 索引。

mysql> explain select * from sync_test1 where name like ‘Bobby%and dt >2021-07-06;
+----+-------------+------------+------------+-------+-------------------------------------+----
-----+---------+------+------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key 
| key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+-------+-----------------------------------
-----+---------+------+------+----------+------------------------------------+
| 1 | SIMPLE | sync_test1 | NULL | range | IX_name,IX_dt,IX_dt_name,IX_name_dt | IX_
name | 12 | NULL | 572 | 36.83 | Using index condition; Using where |
+----+-------------+------------+------------+-------+-----------------------------------
-----+---------+------+------+----------+------------------------------------+

然后再打开 MySQL 追踪优化器 Trace 功能。可以看出,没有选择其他三个索引的原因均是因为在其他三个索引上使用 range scan 的代价均 >= IX_name

mysql> select * from INFORMATION_SCHEMA.OPTIMIZER_TRACE\G;
*************************** 1. row ***************************
TRACE: {
...
“rows_estimation”: [
{
“table”:“`sync_test1`,
“range_analysis”: {
“table_scan”: {
 “rows”: 105084,
 “cost”: 21628
},
...
“analyzing_range_alternatives”: {
 “range_scan_alternatives”: [
 {“index”:“IX_name”,
 “ranges”: [
 “Bobby\u0000\u0000\u0000\u0000\u0000 <= name <= Bobbyÿÿÿÿÿ”
 ],
 “index_dives_for_eq_ranges”: true,
 “rowid_ordered”: false,
 “using_mrr”: false,
 “index_only”: false,rows”: 572,
 “cost”: 687.41,
 “chosen”: true
 },
 {
 “index”:“IX_dt”,
 “ranges”: [0x99aa0c0000 < dt”
 ],
 “index_dives_for_eq_ranges”: true,
 “rowid_ordered”: false,
 “using_mrr”: false,
 “index_only”: false,rows”: 38698,
 “cost”: 46439,
 “chosen”: false,
 “cause”:“cost”
 },
 {
 “index”:“IX_dt_name”,
 “ranges”: [0x99aa0c0000 < dt”
 ],
 “index_dives_for_eq_ranges”: true,
 “rowid_ordered”: false,
 “using_mrr”: false,
 “index_only”: false,rows”: 38292,
 “cost”: 45951,
 “chosen”: false,
 “cause”:“cost”
 },
 {
 “index”:“IX_name_dt”,
 “ranges”: [
 “Bobby\u0000\u0000\u0000\u0000\u0000 <= name <= Bobbyÿÿÿÿÿ”
 ],
 “index_dives_for_eq_ranges”: true,
 “rowid_ordered”: false,
 “using_mrr”: false,
 “index_only”: false,rows”: 572,
 “cost”: 687.41,
 “chosen”: false,
 “cause”:“cost”
 }
 ],
 “analyzing_roworder_intersect”: {
 “usable”: false,
 “cause”:“too_few_roworder_scans”
 }
},
“chosen_range_access_summary”: {
 “range_access_plan”: {
 “type”:“range_scan”,index”:“IX_name”,rows”: 572,
 “ranges”: [
 “Bobby\u0000\u0000\u0000\u0000\u0000 <= name <= Bobbyÿÿÿÿÿ”
 ]
 },
 “rows_for_plan”: 572,
 “cost_for_plan”: 687.41,
 “chosen”: true
}
...
}

1.走全表扫描的代价:io_cost + cpu_cost = (数据页个数 * io_block_read_cost)+ ( 数 据 行 数 * row_evaluate_cost + 1.1) = (data_length / block_size + 1)+ (rows * 0.2 + 1.1) = (9977856 / 16384 + 1) + (105084 * 0.2 + 1.1) = 21627.9。
2.走二级索引 IX_name 的代价:io_cost + cpu_cost = ( 预估范围行数 * io_block_read_cost + 1) + ( 数据行数 * row_evaluate_cost + 0.01) = (572 * 1 + 1) + (5720.2 + 0.01) = 687.41。
3.走二级索引 IX_dt 的代价:io_cost + cpu_cost = ( 预估范围行数 * io_block_read_cost + 1) + ( 数据行数 * row_evaluate_cost + 0.01) = (38698 * 1 + 1) + (38698
0.2 + 0.01) = 46438.61。

4.走二级索引 IX_dt_name 的代价 : io_cost + cpu_cost = ( 预估范围行数 * io_block_read_cost + 1) + ( 数 据 行 数 * row_evaluate_cost + 0.01) = (38292 * 1 + 1) + (38292 * 0.2 + 0.01) = 45951.41。

5.走二级索引 IX_name_dt 的代价:io_cost + cpu_cost = ( 预估范围行数 * io_block_read_cost + 1) + ( 数 据 行 数 * row_evaluate_cost + 0.01) = (572 * 1 + 1) + (572*0.2 + 0.01) = 687.41。

2.4 基于代价的索引推荐思路

如果想借助 MySQL 优化器给慢查询计算出最佳索引,那么需要真实地在业务表上添加所有候选索引。对于线上业务来说,直接添加索引的时间空间成本太高,是不可接受的。MySQL 优化器选最佳索引用到的数据是索引元数据和统计数据,所以我们想否可以通过给它提供候选索引的这些数据,而非真实添加索引的这种方式来实现。

通过深入调研 MySQL 的代码结构和优化器流程,我们发现是可行的:一部分存在于Server 层的 frm 文件中,比如索引定义;另一部分存在于 Engine 层中,或者通过调用 Engine 层的接口函数来获取,比如索引中某个列的不同值个数、索引占据的页面大小等。索引相关的信息,如下图所示:

在这里插入图片描述

因为 MySQL 本身就支持自定义存储引擎,所以索引推荐思路是构建一个支持虚假索引的存储引擎,在它上面建立包含候选索引的空表,再采集样本数据,计算出统计数据提供给优化器,让优化器选出最优索引,整个调用关系如下图所示:

在这里插入图片描述

3.索引推荐实现

因为存储引擎本身并不具备对外提供服务的能力,直接在 MySQL Server 层修改也难以维护,所以我们将整个索引推荐系统拆分成支持虚假索引的 Fakeindex 存储引擎和对外提供服务的 Go-Server 两部分,整体架构图如下:

在这里插入图片描述

首先简要介绍一下 Fakeindex 存储引擎,这是一个轻量级的存储引擎,负责将索引的相关接口透传到 Go-Server 部分。因为它必须采用 C++ 实现,与 Go-Server 间存在跨语言调用的问题,我们使用了 Go 原生的轻量级 RPC 技术 +cgo 来避免引入重量级的 RPC 框架,也不必引入第三方依赖包。函数调用链路如下所示,MySQL优化器调用 Fakeindex 的 C++ 函数,参数转换成 C 语言,然后通过 cgo 调用到Go 语言的方法,再通过 Go 自带的 RPC 客户端向服务端发起调用。

在这里插入图片描述

4.慢查询治理运营

我们主要从时间维度的三个方向将慢查询接入索引推荐,推广治理:

在这里插入图片描述

4.1 过去 - 历史慢查询

这类慢查询属于过去产生的,并且一直存在,数量较多,治理推动力不足,可通过收集历史慢查询日志发现,分成两类接入:

● 核心数据库:该类慢查询通常会被周期性地关注,如慢查询周报、月报,可直

接将优化建议提前生成出来,接入它们,一并运营治理。

● 普通数据库:可将优化建议直接接入数据库平台的慢查询模块,让研发自助地

选择治理哪些慢查询。

4.2 现在 - 新增慢查询

这类慢查询属于当前产生的,数量较少,属于治理的重点,也可通过实时收集慢查询日志发现,分成两类接入:

● 影响程度一般的慢查询:可通过实时分析慢查询日志,对比历史慢查询,识别出新增慢查询,并生成优化建议,为用户创建数据库风险项,跟进治理。

● 影响程度较大的慢查询:该类通常会引发数据库告警,如慢查询导致数据库Load 过高,可通过故障诊断根因系统,识别出具体的慢查询 SQL,并生成优化建议,及时推送到故障处理群,降低故障处理时长。

4.3 未来 - 潜在慢查询

这类查询属于当前还没被定义成慢查询,随着时间推进可能变成演变成慢查询,对于

一些核心业务来说,往往会引发故障,属于他们治理的重点,分成两类接入:

● 未上线的准慢查询:项目准备上线而引入的新的准慢查询,可接入发布前的集成测试流水线,Java 项目可通过 agentmain 的代理方式拦截被测试用例覆盖到的 SQL,再通过经验 +explain 识别出慢查询,并生成优化建议,给用户在需求管理系统上创建缺陷任务,解决后才能发布上线。

● 已上线的准慢查询:该类属于当前执行时间较快的 SQL,随着表数据量的增 加,会演变成慢查询,最常见的就是全表扫描,这类可通过增加慢查询配置参 数 log_queries_not_using_indexes 记录到慢日志,并生成优化建议,为用户创建数据库风险项,跟进治理。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++之条件编译

在C中&#xff0c;条件编译是一种特殊的编译方式&#xff0c;允许在编译时根据特定条件决定是否编译某段代码。条件编译通常用于在编译时根据不同的平台、编译器或配置选项选择性地包含或排除代码。 C中的条件编译可以通过预处理器指令来实现。预处理器是C编译器的一部分&…

Windows中磁盘未知没有初始化怎么办?

当我们尝试在Windows11/10/8/7上使用外部硬盘驱动器时&#xff0c;在小概率情况下可能会遇到磁盘未知没有初始化情况&#xff0c;此时如果您进入磁盘管理工具中查看&#xff0c;将会发现您的外部硬盘驱动器显示为未知、未初始化、没有磁盘空间&#xff0c;或者在某些情况下它还…

VS Code 如何调试Python文件

VS Code中有1,2,3处跟Run and Debug相关的按钮&#xff0c; 1 处&#xff1a;调试和运行就不多说了&#xff0c;Open Configurations就是打开workspace/.vscode下的lauch.json文件&#xff0c;而Add Configuration就是在lauch.json文件中添加当前运行Python文件的Configuratio…

十二.视图

视图 1.常见数据库对象2.视图概述2.1为什么使用视图2.2视图的理解 3.创建视图3.1创建单表视图3.2创建多表联合试图3.3基于试图创建视图 4.查看视图5.更新视图的数据5.1一般情况5.2不可更新的视图 6.修改、删除视图6.1修改视图6.2删除视图 7.总结7.1视图优点7.2视图不足 1.常见数…

能翻页的电子图册怎么做

​随着科技的进步&#xff0c;电子图册已经成为了越来越多企业宣传和展示产品的重要工具。相比于传统的纸质图册&#xff0c;电子图册具有更多的优点&#xff0c;如方便携带、易于分享、可交互性强等。那么&#xff0c;如何制作一款能翻页的电子图册呢&#xff1f; 一、确定主题…

为什么我不建议大学生接公司单?

大家好&#xff0c;我是鱼皮。前两天&#xff0c;我 编程导航 的鱼友提了个问&#xff1a;大学生怎么接公司的单赚点零花钱&#xff1f; 然后我很认真地评论了一句&#xff1a;我不建议大学生接公司单。 这位小伙伴很认真&#xff0c;又通过微信单独问我&#xff1a; 想了想&am…

右键添加 idea 打开功能

1.开始运行regedit 2.找到: HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Directory\shell _3.开始设置 一、右键shell目录新建项Idea二、右键Idea新建command三、选择Idea 右侧空白出新建字符串 名字为Icon 值填入idea的运行程序地址 四、选择command 默认项填入idea的运行程序地址…

真机调试HarmonyOS应用报错

问题表现&#xff1a; 01/04 19:00:01: Launching com.example.simplevideo $ hdc shell am force-stop com.example.simplevideo $ hdc shell bm uninstall com.example.simplevideo $ hdc file send E:\harmony\SimpleVideo\entry\build\default\outputs\default\entry-defau…

nifi详细介绍--一款开箱即用、功能强大可靠,可用于处理和分发数据的大数据组件

目录 目录 一、引言 二、NiFi 的历史背景介绍 三、NiFi 是什么&#xff1f; 核心特性 应用领域 四、NIFI 入门 五 、NiFi 工作流程 六、实际应用场景 七、优势总结 一、引言 NiFi&#xff08;Apache NiFi&#xff09;&#xff0c;全名为“Niagara Files”&#xff0…

洗地机什么牌子最好?家用洗地机推荐指南

随着人们对健康和卫生的关注日益增长&#xff0c;洗地机成为了现代家庭清洁的必备工具。然而&#xff0c;在市场上琳琅满目的洗地机品牌中&#xff0c;洗地机哪个品牌最好最实用呢?这是消费者最为关心的问题。现本文将为您介绍几个备受推崇的洗地机品牌&#xff0c;帮助您在众…

【期末复习向】数据可视化技术

一、重点复习 题型&#xff1a;填空题&#xff08;15道&#xff0c;2分一个&#xff09;与简答题&#xff08;3道题目&#xff0c;10分一个&#xff09;与绘图题&#xff08;选画2个类型的图&#xff09; 1.什么是数据可视化 在计算机视觉领域&#xff0c;数据可视化是对数据的…

re:Invent 2023 技术上新|Amazon Q 使用生成式 AI 助理,更快更轻松地获得数据洞察...

Amazon Q in QuickSight 现已提供预览版。现在&#xff0c;您可以体验 Amazon QuickSight 于7月26日发布的生成式 BI 功能&#xff0c;以及为业务用户提供的两项附加功能。 利用 Amazon Q in QuickSight 更快地将见解转化为影响力 随着这项功能的发布&#xff0c;业务用户现在…

Linkage Mapper 各工具参数详解——Barrier Mapper

【小白一学就会无需其他教程】此文档用于解析使用Linkage Mapper 各输入输出参数详情以及可能的影响&#xff0c;并介绍了如何解释模型输出结果和输出参数&#xff0c;适合刚入手的人。篇幅很长很啰嗦&#xff0c;是因为每个参数都解释的万分细致。 从以下链接中获取内容&#…

express+mongoDB开发入门教程之mongoose使用讲解

系列文章 node.js express框架开发入门教程 expressmongoDB开发入门教程之mongoDB安装expressmongoDB开发入门教程之mongoose使用讲解 文章目录 系列文章前言一、Mongoose是什么&#xff1f;二、Mongoose安装三、Mongoose在express项目中使用步骤一、连接mongoDB数据库步骤二、…

游戏缺少emp.dll详细修复教程,快速解决游戏无法启动问题

在现代游戏中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“emp.dll丢失”。emp.dll是一个动态链接库文件&#xff0c;它包含了许多程序运行所需的函数和数据。当一个程序需要调用这些函数时&#xff0c;系统会从emp.dll文件中加载相应的内容。因此&#x…

Maven简介及环境搭建和基本使用(Java开发中的实用工具)

一、概述 Maven 是 Apache 软件基金会的一个开源项目,是一个优秀的项目构建工具,它 用来帮助开发者管理项目中的 jar,以及 jar 之间的依赖关系、完成项目的编译、 测试、打包和发布等工作。 Maven的相关概念 pom.xml文件&#xff1a;里面可以配置相关信息&#xff0c;指导ma…

Mac内心os:在下只是个工具,指望我干人事?

呜呜呜&#xff0c;今天去医院看病了&#xff0c;乌央央的好多人。最近在研究苹果开发者工具中的HealthKit&#xff0c;等我研究透给大家安利。今天还是继续闲聊吧&#x1f602;&#x1f602;提前感谢大家体谅我这个病号&#xff0c;发射小红心&#xff0c;biubiubiu~ 据说&am…

uniapp 无限级树形结构面包屑、单选-多选、搜索、移除功能插件,基于【虚拟列表】高性能渲染海量数据,加入动态高度、缓冲区

hyq-tree-vtw 无限级树形结构面包屑、单选-多选、搜索、移除功能 示例项目 单选-user 单选-任意一项 多选-关联下级 多选-任意一项 已选择数据弹框 说明 本插件需要使用uni-popup、uni-transition用于已选择数据弹框&#xff0c;因此需要有这些依赖,请自行导入本插件基于【虚…

vue-mixins混入处理

定义 mixins&#xff08;混入&#xff09;&#xff1a;一种分发 Vue 组件中可复用功能的非常灵活的方式&#xff0c;mixins 是一个 js 对象&#xff0c;它可以包含我们组件script中的任意功能选项&#xff0c;如&#xff1a;data、components、methods、created、computed 等等…

202365读书笔记|《温暖和百感交集的旅程》——时间帮助着一个人的一生在几千字的篇幅里栩栩如生

202365读书笔记|《温暖和百感交集的旅程》——时间帮助着一个人的一生在几千字的篇幅里栩栩如生 我能否相信自己温暖和百感交集的旅程前言和后记 《温暖和百感交集的旅程》作者余华&#xff0c;之前读完的一本书&#xff0c;已经不太记得内容了&#xff0c;看着笔记整理的这篇书…