TiDB SQL调优案例TiFlash

背景

早上收到某系统的告警tidb节点挂掉无法访问,情况十万火急。登录中控机查了一下display信息,4个TiDB、Prometheus、Grafana全挂了,某台机器hang死无法连接,经过快速重启后集群恢复,经排查后是昨天上线的某个SQL导致频繁OOM。

企业微信截图_20230316113735.png

于是开始亡羊补牢,来一波近期慢SQL巡检 #手动狗头#。。。

随便找了一个出现频率比较高的慢SQL,经过优化后竟然性能提升了1500倍以上,感觉有点东西,分享给大家。

分析过程

该慢SQL逻辑非常简单,就是一个单表聚合查询,但是耗时达到8s以上,必有蹊跷。

脱敏后的SQL如下:

SELECTcast( cast( CAST( SUM( num ) / COUNT( time ) AS CHAR ) AS DECIMAL ( 9, 2 )) AS signed ) speed,... -- 此处省略n个字段
FROM(SELECT DATE_FORMAT( receive_time, '%Y-%m-%d %H:%i:00' ) AS time,COUNT(*) AS num FROMdb1.table WHEREcreate_time > DATE_SUB( sysdate(), INTERVAL 20 MINUTE ) GROUP BYtime ORDER BYtime ) speed;

碰到慢SQL不用多想,第一步先上执行计划:

企业微信截图_20230316150702.png

很明显,这张900多万行的表因为创建了TiFlash副本,在碰到聚合运算的时候优化器选择了走列存查询,最终结果就是在TiFlash完成暴力全表扫描、排序、分组、计算等一系列操作,返回给TiDB Server时基本已经加工完成,总共耗时8.02s。

咋一看好像没啥优化空间,但仔细观察会发现一个不合理的地方。执行计划倒数第二排的Selection算子,也就是SQL里面子查询的where过滤,实际有效数据1855行,却扫描了整个表接近950W行,这是一个典型的适合索引加速的场景。但遗憾的是,在TiFlash里面并没有索引的概念,所以只能默默地走全表扫描。

那么优化的第一步,先看过滤字段是否有索引,通常来说create_time这种十有八九都建过索引,检查后发现确实有。

第二步,尝试让优化器走TiKV查询,这里直接使用hint的方式:

SELECT /*+ READ_FROM_STORAGE(TIKV[db1.table]) */cast( cast( CAST( SUM( num ) / COUNT( time ) AS CHAR ) AS DECIMAL ( 9, 2 )) AS signed ) speed,... -- 此处省略n个字段
FROM(SELECT DATE_FORMAT( receive_time, '%Y-%m-%d %H:%i:00' ) AS time,COUNT(*) AS num FROMdb1.table WHEREcreate_time > DATE_SUB( sysdate(), INTERVAL 20 MINUTE ) GROUP BYtime ORDER BYtime ) speed;

再次生成执行计划,发现还是走了TiFlash查询。这里就引申出一个重要知识点,关于hint作用域的问题,也就是说hint只能在指定的查询范围内生效。具体到上面这个例子,虽然指定了db1.table走TiKV查询,但是对于它所在的查询块来说,压根不知道db1.table是谁直接就忽略掉了。所以正确的写法是把hint写到子查询中:

SELECTcast( cast( CAST( SUM( num ) / COUNT( time ) AS CHAR ) AS DECIMAL ( 9, 2 )) AS signed ) speed,... -- 此处省略n个字段
FROM(SELECT  /*+ READ_FROM_STORAGE(TIKV[db1.table]) */DATE_FORMAT( receive_time, '%Y-%m-%d %H:%i:00' ) AS time,COUNT(*) AS num FROMdb1.table WHEREcreate_time > DATE_SUB( sysdate(), INTERVAL 20 MINUTE ) GROUP BYtime ORDER BYtime ) speed;

对应的执行计划为:

企业微信截图_20230316153949.png

小提示:

也可以通过set session tidb_isolation_read_engines = 'tidb,tikv';来让优化器走tikv查询。

发现这次虽然走了TiKV查询,但还是用的TableFullScan算子,整体时间不降反升,和我们预期的有差距。

没走索引那肯定是和查询字段有关系,分析上面SQL的逻辑,开发是想查询table表创建时间在最近20分钟的数据,用了一个sysdate()函数获取当前时间,问题就出在这。

获取当前时间常用的函数有now()sysdate(),但这两者是有明显区别的。引用自官网的解释:

  • now()得到的是语句开始执行的时间,是一个固定值
  • sysdate()得到的是该函数实际执行的时间,是一个动态值

听起来比较饶,来个栗子一看便知:

mysql> select now(),sysdate(),sleep(3),now(),sysdate();
+---------------------+---------------------+----------+---------------------+---------------------+
| now()               | sysdate()           | sleep(3) | now()               | sysdate()           |
+---------------------+---------------------+----------+---------------------+---------------------+
| 2023-03-16 15:55:18 | 2023-03-16 15:55:18 |        0 | 2023-03-16 15:55:18 | 2023-03-16 15:55:21 |
+---------------------+---------------------+----------+---------------------+---------------------+
1 row in set (3.06 sec)

这个动态时间就意味着TiDB优化器在估算的时候并不知道它是个什么值,走索引和不走索引哪个成本更高,最终导致索引失效。

从业务上来看,这个SQL用now()sysdate()都可以,那么就尝试改成now()看看效果:

SELECTcast( cast( CAST( SUM( num ) / COUNT( time ) AS CHAR ) AS DECIMAL ( 9, 2 )) AS signed ) speed,... -- 此处省略n个字段
FROM(SELECT  /*+ READ_FROM_STORAGE(TIKV[db1.table]) */DATE_FORMAT( receive_time, '%Y-%m-%d %H:%i:00' ) AS time,COUNT(*) AS num FROMdb1.table WHEREcreate_time > DATE_SUB( now(), INTERVAL 20 MINUTE ) GROUP BYtime ORDER BYtime ) speed;

企业微信截图_20230316160428.png

最终结果4.43ms搞定,从8.02s到4.43ms,1800倍的提升。

滥用函数,属于是开发给自己挖的坑了。

解决方案

经过以上分析,优化思路已经很清晰了,甚至都是常规优化不值得专门拿出来讲,但前后效果差异太大,很适合作为一个反面教材来提醒大家认真写SQL。

其实就两点:

  • 让优化器不要走TiFlash查询,改走TiKV,可通过hint或SQL binding解决
  • 非必须不要使用动态时间,避免带来索引失效的问题

深度思考

优化完成之后,我开始思考优化器走错执行计划的原因。

在最开始的执行计划当中,优化器对Selection算子的估算值estRows和实际值actRows相差非常大,再加上本身计算和聚合比较多,这可能是导致误走TiFlash的原因之一。不清楚TiFlash的estRows计算原理是什么,如果在估算准确的情况并且索引正常的情况下会不会走TiKV呢?

另外,我还怀疑过动态时间导致优化器判断失误(认为索引失效才选择走TiFlash),但是在尝试只修改sysdate()now()的情况下,发现依然走了TiFlash,说明这个可能性不大。

在索引字段没问题的时候,按正常逻辑来说,我觉得一个成熟的优化器应该要能够判断出这种场景走TiKV更好。

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

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

相关文章

OpenGL 绘制Mesh数据(Qt)

文章目录 一、简介二、实现代码三、实现效果一、简介 Mesh数据的结构主要就是点与三角面片,因此本质上仍然是对三角面片进行绘制。这里我们借助VCG这个库实现对Mesh数据的读取,这个库相对简单轻巧,很方便使用。 二、实现代码 由于修改的部分很多,我们逐一进行解释一下: --…

Seata 中封装了四种分布式事务模式,分别是: AT 模式, TCC 模式, Saga 模式, XA 模式,

文章目录 seata概述Seata 中封装了四种分布式事务模式,分别是:AT 模式,TCC 模式,Saga 模式,XA 模式, 今天我们来聊聊seata seata 概述 在微服务架构下,由于数据库和应用服务的拆分&#xff0c…

【SAM系列】Auto-Prompting SAM for Mobile Friendly 3D Medical Image Segmentation

论文链接:https://arxiv.org/pdf/2308.14936.pdf 核心: finetune SAM,为了不依赖外部prompt,通过将深层的特征经过一个编-解码器来得到prompt embedding;finetune完之后做蒸馏

PTA-感染人数

设某住宿区域是一个nn的方阵,方阵中的每个小方格为一个房间,房间里可能住一个人,也可能空着。第一天,某些房间中住着的人得了一种高传染性的流感,以后每一天,得流感的人会使其邻居(住在其上、下…

76 Python开发-内外网收集Socket子域名DNS

目录 Python开发相关知识点本篇文章涉及知识点演示案例:IP&Whois&系统指纹获取代码段-外网CDN&子域名&端口扫描&交互代码段-外网IP&计算机名&存活主机&端口扫描代码段-内网Py格式解析环境与可执行程序格式转换-Pyinstaller 涉及资源&#xff1…

git 学习 之一个规范的 commit 如何写

最好的话做一件完整的事情就提交一次

一种删除 KubeSphere 中一直卡在 Terminating 的 Namespace--KubeSphere Logging System的简单方法

文章目录 一、问题提出二、删除方法1,获取kubesphere-logging-syste的详细信息json文件2,编辑kubesphere-logging-system.json3,执行清理命令 三、检查结果 一、问题提出 在使用 KubeSphere 的时候发现有一个日志服务KubeSphere Logging Sys…

YoloV8改进策略:基于自研的图注意力机制改进| 独家改进方法|图卷积和注意力融合模块

摘要 SE注意力机制是一种通过显式建模卷积特征的信道之间相互依赖性的方法,旨在提高网络产生的表示的质量。SE注意力机制包括两个步骤:Squeeze和Excitation。在Squeeze步骤中,通过全局平均池化操作将输入特征图压缩成一个向量,然后通过一个全连接层将其映射到一个较小的向…

HTML的学习记录

<br /> 标签在 HTML 页面中创换行符。 <hr /> 标签在 HTML 页面中创建水平线。 段落是通过 <p> 标签定义的。 浏览器会自动地在段落的前后添加空行。&#xff08;<p> 是块级元素&#xff09; 文本格式 <b>This text is bold</b>字体加粗 …

2024年最新软件测试必问面试题,面试前一天刷效果更佳

1.你为什么选择软件测试行业 因为之前有了解软件测试这个行业&#xff0c;觉得他的发展前景很好。 2.根据你以前的工作经验描述一下软件开发、测试过程&#xff0c;由那些角色负责&#xff0c;你做什么 要有架构师、开发经理、测试经理、程序员、测试员。我在里面主要是负责所…

HOJ 项目部署-前端定制 默认勾选显示标签、 在线编辑器主题和字号大小修改、增加一言功能 题目AC后礼花绽放

# 项目拉取地址&#xff1a; https://gitee.com/himitzh0730/hoj.git # 切换到hoj-vue目录执行以下命令 #安装依赖 npm install #运行服务 npm run serve #修改代码后构建项目到dist文件夹&#xff0c;到服务器docker-compose.yml中修改hoj-frontend文件映射即可 npm run build…

dvwa问题篇 -- dvwa出现数据库无法访问的时候,Could not connect to the MySQL service. -- 小黑解决教程

各位小伙伴初次玩dvwa会出现各种问题&#xff0c;本来想把一些问题直接总结写一篇dvwa文章来着&#xff0c;但因为都是关键字搜索&#xff0c;所以将一些问题都拆分出来&#xff0c;以便大家方便查类似问题。&#xff08;大家有遇到不一样的问题欢迎投稿&#xff01;&#xff0…

ElasticSearch 搜索数据

精确查询 存在查询 Exists query 用于查询某个字段不为空的数据。如下所示&#xff0c;查询 age 不为空的 数据 POST user/_search {"query": {"exists": {"field": "age"}} }主键查询 通过 _id 字段查询数据 POST user/_search …

48道Linux面试题

本博客将汇总 Linux 面试中常见的题目&#xff0c;并提供详细的解答。 文章目录 1、绝对路径用什么[符号表](https://so.csdn.net/so/search?q符号表&spm1001.2101.3001.7020)示&#xff1f;当前目录、上层目录用什么表示&#xff1f;主目录用什么表示? 切换目录用什么命…

异步通知

文章目录 一、异步通知1、应用场景2、执行流程&#xff08;基于读取按键值的情景&#xff09;2.1、应用程序具体做什么&#xff1f;2.2、驱动程序具体做什么&#xff1f; 三、程序1、驱动程序2、测试应用程序 三、总结 一、异步通知 1、应用场景 当应用程序不想休眠时&#x…

【办公技巧】为什么有的pdf不能编辑

pdf文件大家应该都经常接触&#xff0c;但是不知道大家会遇到这种情况&#xff1a;有些PDF文件打开之后无法编辑&#xff1f;是什么原因呢&#xff1f;今天我们来分析一下都是那些原因导致的。 首先我们可以考虑一下&#xff0c;PDF文件中的内容是否是图片&#xff0c;如果确认…

「Verilog学习笔记」超前进位加法器

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 超前进位加法器的实质是&#xff1a;对于输出的每一位Si 其实都可以用Si Ai ^ Bi ^ Cin来表示 我们需要做的只是判断加法结果的最高位该取几 例如本题中 输入的两个数A和B…

windows无命令升级node版本

1. node最新版本下载链接 点击最新下载链接&#xff0c;找到对应版本下载并解压 2. 通过命令where node找到node.exe位置 3. 将该位置的node.exe替换为下载解压的最新node.exe 4. 重新执行node -v查看版本

docker应用部署

Docker 应用部署 一、部署MySQL 搜索mysql镜像 docker search mysql拉取mysql镜像 docker pull mysql:5.6创建容器&#xff0c;设置端口映射、目录映射 # 在/root目录下创建mysql目录用于存储mysql数据信息 mkdir ~/mysql cd ~/mysqldocker run -id \ -p 3307:3306 \ --na…

Secondo数据库下载安装

SECONDO - An Extensible Database System (secondo-database.github.io) 官网地址 1配置环境 ubuntu20.04.2&#xff08;这个版本不能错&#xff0c;我试了20.04.3就安装不超过&#xff09;&#xff0c;镜像下载地址如下&#xff1a;Index of /releases/20.04.2 2安装步骤 安…