数据库索引推荐大PK,DBdoctor和资深DBA的终极较量

news2024/11/15 12:38:32

前言

在上一篇文章《端午特别篇:你真的了解数据库索引吗?》中,纪宽针对一个业务SQL推荐索引优化问题提出了疑问。他发现DBdoctor推荐的索引组合(status, purchase_date,device_name, device_id)似乎与他作为DBA凭借多年经验推荐的索引方案大相径庭。DBdoctor与资深DBA之间究竟孰对孰错?我们将在本文中详细解密DBdoctor和资深DBA的PK结果,我将用实际的验证和剖析来狠狠地抽打他!

SELECT *FROM     deviceWHERE     purchase_date >= '2023-05-31'    AND status = 'active' and device_id>=0  AND device_name like '%162b%'

SQL分析

为了验证上述推荐索引的准确性,我们将DBA经验推荐的索引和DBdoctor推荐的索引都加上去,最终交给MySQL自己,看它究竟会选择哪个索引。

DBA推荐的:

KEY `dbdoctor_idx_status_purchase_date` (`status`,`purchase_date`)

DBdoctor推荐的:

KEY `dbdoctor_idx_status_purchase_date_device_name_device_id` (`status`,`purchase_date`,`device_name`,`device_id)`

DBA的结论:认为DBdoctor推荐的不准,完全不符合MySQL的原理规则,比如最左原则和模糊匹配等,那真实情况如何呢?

手动验证谁才是最优的索引

1)给device表增加上面三个索引,表结构如下:

CREATE TABLE `device` (  `id` int NOT NULL AUTO_INCREMENT,  `device_id` int DEFAULT NULL,  `device_name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL,  `device_type` varchar(100) COLLATE utf8mb4_general_ci NOT NULL,  `purchase_date` date DEFAULT NULL,  `status` varchar(50) COLLATE utf8mb4_general_ci DEFAULT 'active',  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,  PRIMARY KEY (`id`),  KEY `dbdoctor_idx_status_purchase_date_device_name_device_id` (`status`,`purchase_date`,`device_name`,`device_id`),  KEY `dbdoctor_idx_purchase_date_status` (`purchase_date`,`status`),  KEY `dbdoctor_idx_status_purchase_date` (`status`,`purchase_date`)) ENGINE=InnoDB AUTO_INCREMENT=20446488 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
 

索引采样page和采样记录数:

图片

2)开启trace查看SQL实际三个索引的cost消耗,发现DBdoctor推荐最优。

set session optimizer_trace="enabled=on";

得到以下trace cost消耗信息,我们发现确实是DBdoctor推荐的`dbdoctor_idx_status_purchase_date_device_name_device_id这个索引是最优的,cost消耗最小。

图片

DBA们是不是开始怀疑人生了,为何会是这样?下面我们再进一步剖析。

进一步刨根问底

再回到这两个索引:

DBdoctor推荐:KEY `dbdoctor_idx_status_purchase_date_device_name_device_id` (`status`,`purchase_date`,`device_name`,`device_id`)
DBA推荐:KEY `dbdoctor_idx_status_purchase_date` (`status`,`purchase_date`)

细心的你可以发现,上面两个索引trace中有两个怀疑点:

  • DBA推荐的索引开启了MRR算法

  • DBdoctor推荐的索引rows和DBA推荐的索引rows竟然结果不一样

下面我们来进一步分析这两个疑点:

1)我们仔细看下上面trace日志,发现DBA推荐的索引里还走了MRR优化算法,这个相当于多做了一次排序,然后再回表查其他字段,可以将离散IO换成顺序IO,带来性能的提升。难道是这个MRR出了问题,推荐的算法不是最优的,导致整体cost开销变大了?

关闭MRR算法,验证MRR是否是它导致的COST消耗增加:

图片

我们发现dbdoctor_idx_status_purchase_date开启mrr反而cost开销减少很多,带来优化,cost减少 533216-509474=23742

结论:MRR算法是带来了优化,MySQL推荐的算法没有问题,那剩下的那个疑点Rows为何不一样呢?

2)从上面排查的结论来看,问题出在预估的扫描行数上面,我们通过强制索引分别查看执行计划,看下扫描行。

  • DBdoctor推荐的索引的扫描行为439628

    图片

  • DBA推荐的索引:强制走dbdoctor_idx_purchase_date_status索引,扫描行为484744

    图片

DBA推荐的索引的扫描行偏大,多扫描了484744-439628=45116行记录,从MySQL源码Cost计算公式我们知道,每一条row的cost为Server层row_evalute_cost(默认为0.1),那么该索引偏大的一部分原因来自于这个扫描行。那为什么rows差异这么大呢?

我们来看下mysql源码,rows到底怎么得来的?

通过GDB我们快速找到了预估rows的函数,递归函数btr_estimate_n_rows_in_range_on_level 用于估计在 B-tree 某一级别上两个槽位(slot)之间的索引行数,其大致原理如下:

/* 4867 */static int64_t btr_estimate_n_rows_in_range_on_level(/* ...  *//* 4876 */    bool *is_n_rows_exact)        /*!< out: true if the returned/* 4877 */                                   value is exact i.e. not an/* 4878 */                                   estimation *//* 4879 */{/* 4880 */  int64_t n_rows;/* 4881 */  ulint n_pages_read;/* 4882 */  ulint level;/* 4883 *//* 4884 */  n_rows = 0;/* 4885 */  n_pages_read = 0;/* 4886 *//* 4887 */  /* Assume by default that we will scan all pages between/* 4888 */  slot1->page_no and slot2->page_no. *//* 4889 */  *is_n_rows_exact = true;/* ...  *//* 4905 */  /* Count the records in the pages between slot1->page_no and/* 4906 */  slot2->page_no (non inclusive), if any. *//* 4907 *//* 4908 */  /* Do not read more than this number of pages in order not to hurt/* 4909 */  performance with this code which is just an estimation. If we read/* 4910 */  this many pages before reaching slot2->page_no then we estimate the/* 4911 */  average from the pages scanned so far. *//* 4912 *//* 4913 */  constexpr uint32_t N_PAGES_READ_LIMIT = 10;/* ...  *//* 4940 */    /* It is possible that the tree has been reorganized in the/* 4941 */    meantime and this is a different page. If this happens the/* 4942 */    calculated estimate will be bogus, which is not fatal as/* 4943 */    this is only an estimate. We are sure that a page with/* 4944 */    page_no exists because InnoDB never frees pages, only/* 4945 */    reuses them. *//* 4946 */    if (!fil_page_index_page_check(page) ||/* 4947 */        btr_page_get_index_id(page) != index->id ||/* 4948 */        btr_page_get_level(page) != level) {/* 4949 */      /* The page got reused for something else *//* 4950 */      mtr_commit(&mtr);/* 4951 */      goto inexact;/* 4952 */    }/* ...  *//* 4985 */inexact:/* 4986 *//* 4987 */  *is_n_rows_exact = false;/* 4988 *//* 4989 */  /* We did interrupt before reaching slot2->page *//* 4990 *//* 4991 */  if (n_pages_read > 0) {/* 4992 */    /* The number of pages on this level is/* 4993 */    n_rows_on_prev_level, multiply it by the/* 4994 */    average number of recs per page so far *//* 4995 */    n_rows = n_rows_on_prev_level * n_rows / n_pages_read;/* 4996 */  } else {/* 4997 */    /* The tree changed before we could even/* 4998 */    start with slot1->page_no *//* 4999 */    n_rows = 10;/* 5000 */  }/* 5001 *//* 5002 */  return (n_rows);/* 5003 */}
 

从起始槽位slot1 所在的页面开始,向右扫描几个页面。

如果在扫描过程中很快到达了目标槽位slot2所在的页面,则可以准确计算出 slot1 和 slot2 之间的记录数,并将 is_n_rows_exact 标志设置为 true。

返回估算的行数(不包括边界记录),并根据是否精确计算设置 is_n_rows_exact标志。

  • 从起始页开始

  • 统计行数

    读取每个页面,统计其包含的记录数。

  • 判断是否到达目标页面

  • 估算剩余页面的行数

    • 如果没有快速到达 slot2所在的页面,则计算已扫描页面中的平均记录数。

    • 根据这个平均值,估算未扫描页面中的记录数。假设这些页面中的记录数与已扫描页面的记录数相同。

    • 乘以 slot1 和 slot2之间的页面数,得出估算的总记录数。

  • 返回结果

该递归函数通过快速扫描初始几个页面来尝试精确计算行数,如无法精确计算,则基于平均值进行估算,以达到在性能和准确性之间的平衡。从trace中我们能看到,两个索引路径的sql拥有同样的range,但索引高度不一样,递归次数不同,页面平均记录数也不同,得出的预估rows不一样。

从上面的数据我们能看到,额外增加的Cost=DBA推荐的索引Cost-DBdoctor推荐的索引Cost=533216-483589=49627,DBA推荐的索引Cost增大主要有以下原因:

  • 预估偏差Rows的Sever CPU Cost=45116*0.1=4511.6

  • 预估偏差Rows涉及的Page IO Cost(单次io_read_block_cost默认为1)

图片

综上所述:精确估算行数和平均值预估得出的行数是存在数据偏差的,从而也会影响到索引的最终选择,这一块由内核的固化代码逻辑来决定,DBA在做索引推荐的时候是无法感知到这一层的,通过规则方式不能覆盖到该场景。而通过DBdoctor的eBPF技术是可以感知到该层面数据,从而可以做出和MySQL选择一致的最优路径,从上面的执行结果可以看到DBdoctor推荐的索引比DBA推荐的索引执行耗时减少一倍

总结

DBdoctor基于实际Cost的最优路径分析才是最准的,DBA的经验规则在一定场景下确实是可以带来优化,但在一些场景下是很难搞定的,比如上述场景,这里我初略的列了以下几点:

  • 多种算法路径会导致Cost不一样

  • 在不同版本中路径行为都不一样,导致cost不一样

  • 索引的预估rows不一样,Cost不一样

  • 额外的一些变量因子也会导致变化

综上所述,DBdoctor的旁路优化器才是终极杀器!不管何种场景,用SQL审核功能复制粘贴SQL,可一分钟快速得出最优索引结论。

  1. 端午特别篇:你真的了解数据库索引吗?
  2. 快薅羊毛,这款企业级数据库监控诊断平台终于免费啦
  3. 被锁住的大象(Postgres),如何跟MySQL赛跑

图片

DBdoctor推出长久免费版

DBdoctor是一款企业级数据库全方位性能监控与诊断平台,致力于解决一切数据库性能问题。可以对商业数据库、开源数据库、国产数据库进行统一性能诊断。具备:SQL审核巡检报表监控告警存储诊断审计日志权限管理等免费功能,不限实例个数,可基于长久免费版快速搭建企业级数据库监控诊断平台。同时拥有:性能洞察、锁分析、根因诊断、索引推荐、SQL发布前性能评估等高阶功能,官网可快速下载,零依赖,一分钟快速一键部署。如果您想要试用全部功能可添加公众号自助申请专业版license。成为企业用户可获得产品定制、OpenAPI集成、一对一专家等高阶服务。迎添加小助手微信了解详细信息!

1️⃣ 产品介绍:

内核级数据库性能诊断工具DBdoctor

2️⃣免费下载/在线试用:

https://dbdoctor.hisensecloud.com/col.jsp?id=126

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

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

相关文章

vue element-ui 下拉框 以及 input 限制输入,小数点后保留两位 界面设计案例 和 例子:支持mp4和m3u8视频播放

vue input 限制输入&#xff0c;小数点后保留两位 以及 图片垂直居中显示 和 分享 git 小技巧-CSDN博客文章浏览阅读430次&#xff0c;点赞5次&#xff0c;收藏4次。error:Your local changes to the following files would be overwritten by merge:_error: your local change…

传神社区|数据集合集第3期|中文NLP数据集合集

自从ChatGPT等大型语言模型&#xff08;Large Language Model, LLM&#xff09;出现以来&#xff0c;其类通用人工智能&#xff08;AGI&#xff09;能力引发了自然语言处理&#xff08;NLP&#xff09;领域的新一轮研究和应用浪潮。尤其是ChatGLM、LLaMA等普通开发者都能运行的…

计算机SCI期刊,中科院3区,IF=3.4,难度不大,无预警风险

一、期刊名称 Automated Software Engineering 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;计算机科学 影响因子&#xff1a;3.4 中科院分区&#xff1a;3区 三、期刊征稿范围 自动化软件工程是一份档案&#xff0c;同行评审的期刊&#xff0c;发表研…

网络故障排除:保持网络稳定与业务连续

目录 什么是网络故障&#xff1f; 网络故障排除的基本步骤 1. 问题识别 2. 确定故障范围 3. 检查物理连接 4. 检查设备配置 5. 测试与诊断 6. 实施解决方案 7. 验证与监控 了解更多 在现代企业中&#xff0c;网络的稳定性和性能直接影响业务的连续性和效率。作为一名…

算法day26

第一题 429. N 叉树的层序遍历 本题的要求我们可以通过队列来辅助完成层序遍历&#xff1b; 如下图的n叉树&#xff1a; 步骤一&#xff1a; 我们定义一个队列&#xff0c;先进行根节点入队列操作&#xff1b; 步骤二&#xff1a; 我们进行当前队列每一个元素的出队列操作&…

复旦大学首本大模型中文书太厉害啦!【大模型书籍推荐】

前言 在信息爆炸的时代&#xff0c;自然语言处理&#xff08;NLP&#xff09;技术如同璀璨的星辰&#xff0c;照亮了我们与机器沟通的道路。而今&#xff0c;复旦大学自然语言处理实验室的教授团队&#xff0c;如同航海家般&#xff0c;为我们带来了一本指引大语言模型领域前行…

[Java基础揉碎]网络相关概念

目录 网络通信 网络 ip地址 ​编辑 域名 ​编辑 网络协议 TCP和UDP 网络编程比较重要的的InetAddress类 Socket ​编辑 tcp字节流编程 案例一 案例二​编辑 案例三 网络上传文件 ​编辑​编辑 ​编辑 netstat tcp网络通信客户端也是通过端口和服务端进行通讯的…

python怎么保留小数

保留两位小数&#xff0c;并做四舍五入处理 方法一&#xff1a;使用字符串格式化 a 12.345 print("%.2f" % a)# 12.35 方法二&#xff1a;使用round内置函数 a 12.345 a1 round(a, 2) print(a1)# 12.35 方法三&#xff1a;使用decimal模块 from decimal import D…

每日一题39:甲板上的战舰

一、每日一题 题意 这题的标题应该是《棋盘上的战舰》&#xff0c;来源于 海战棋&#xff0c;把横着或竖着的连续 X 看成一艘战舰&#xff0c;统计棋盘上有多少艘战舰。 思路 战舰的个数&#xff0c;等于战舰「头部」的个数。如下图&#xff0c;我们只需要统计蓝色 X 的个数&…

LLM应用实战:当图谱问答(KBQA)集成大模型(三)

1. 背景 最近比较忙(也有点茫)&#xff0c;本qiang~想切入多模态大模型领域&#xff0c;所以一直在潜心研读中... 本次的更新内容主要是响应图谱问答集成LLM项目中反馈问题的优化总结&#xff0c;对KBQA集成LLM不熟悉的客官可以翻翻之前的文章《LLM应用实战&#xff1a;当KBQ…

Android 14 系统启动流程 之 启动init进程、启动Zygote进程

Android 14 系统启动流程 之 启动init进程、启动Zygote进程 废话不多说&#xff0c;先上图&#xff0c;不清楚的可以在评论区留言。

计算机体系结构学习(一)

计算机体系结构的发展 20 世纪五六十年代, 由于工艺技术的限制, 计算机都做得很简单, 计算机体系结构主要研究怎么做加减乘除, Computer Architecture 基本上等于 Computer Arithmetic。 20 世纪七八十年代的时候, 以精简指令集 (Reduced Instruction Set Computer, 简称 RISC…

AI大模型战场:通用大模型与垂直大模型的角逐

随着人工智能技术的迅猛发展&#xff0c;AI大模型已成为推动科技进步的重要力量。然而&#xff0c;在AI大模型的战场上&#xff0c;通用大模型与垂直大模型之间的分化日益明显。两者各有其独特的优势和潜力&#xff0c;在不同的应用场景中发挥着重要作用。那么&#xff0c;在这…

SSM 离散数学线上考试系统-计算机毕业设计源码83059

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;高校当然也不例外。离散数学线上考试系统是以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;采…

centos下创建raid6磁盘阵列

在CentOS系统中创建RAID 6阵列&#xff0c;可以使用mdadm工具。 以下是创建RAID 6阵列的基本步骤和示例代码&#xff1a; 安装mdadm工具&#xff08;如果尚未安装&#xff09;&#xff1a; sudo yum install mdadm 假设你有至少四个以上的磁盘设备&#xff08;例如 /dev/sdi…

信创4.0|暴雨出席空间智能软件技术交流会

6月19日&#xff0c;由自然资源部地理信息系统技术创新中心、中国测绘学会软件与信息技术服务工委会、内蒙古自治区测绘学会共同主办的“空间智能软件技术交流会暨自然资源数字化治理交流会”在呼和浩特举办&#xff0c;500余位嘉宾汇聚一堂&#xff0c;就空间智能赋能数字经济…

MyBatis的配置文件,即:src->main->resources的配置

目录 1、properties 标签 1.1 mybatis-config.xml 1.2 db.properties 1.3 在SqlMapConfig.xml 中 引入数据库配置信息 2、typeAliases 标签 2.1 定义别名 2.2 使用别名 3、Mappers标签 作用&#xff1a;用来在核心配置文件中引入映射文件 引入方式&#xff0c;有以下…

vue生成二维码中间自定义logo并截图分享

需求描述&#xff1a;在公众号中&#xff0c;生成二维码&#xff0c;并在二维码中央添加自定义logo&#xff0c;然后生成一张分享给好友的 二维码图片。 一、用到的依赖包 npm install --save html2canvas <script srchttps://cdn.staticfile.org/jquery/2.1.1/jquery.min…

【APP移动端自动化测试】第一节.环境配置和adb调试工具

文章目录 前言一、Java环境搭建二、AndroidSDK环境搭建三、Android模拟器安装四、adb调试工具基本介绍 4.1 adb构成和基本原理 4.2 adb获取包名&#xff0c;界面名 4.3 adb文件传输 4.4 adb获取app启动时间 4.5 adb获取手机日志 4.6 adb其他有关…

室内、户外安防监控超低功耗24g人体雷达感应模块,智能门铃、灯光控制新体验

在当今这个智能科技日新月异的时代&#xff0c;雷达感应模块正逐渐成为智能家居领域的一颗璀璨新星。其超低功耗、无误触、无漏触等特性&#xff0c;以及在户外环境下能够智能过滤环境干扰的独特优势&#xff0c;使得雷达感应模块在智能门铃、灯光照明等方面的应用越来越广泛。…