172024.05

项目纪实 | MySQL核心源码分析 & 定位解决 万里数据库凭实力征服客户

2024.05.17

 

某国有大型商业银行A业务系统使用MySQL 5.7.21版本数据库,多年来一直稳定运行,DBA表示很信赖(主要是运行稳定,能睡好觉、不掉发)。



然而某个深夜,仅仅是做了一个不痛不痒的变更,银行业务人员却说处理结果和数量不对???引得现场人员惊慌失措、茫然四顾。




01 源起:故障背景一览


银行业务人员通过查阅代码,表示业务逻辑非常简单,大致如下:

1.select count(1) from table where x>=a and x<=b 拿到要处理的结果集数量

2.把结果集分给几个线程进行分段处理, 每个线程再根据 select user_id from table where x>=c and x<=d 拿到一批 user_id ,然后逐条处理

重点是在第2步中本来匹配 [c,d] 的结果集是10条,实际只查到了1条!!!


DBA接手后,怀疑点很明确:

为什么这支业务A变更前没问题,变更后出现问题?

是否会同时有SQL在1和2中间把结果集做了update/delete等?


然而银行业务人员认为变更的业务只是跑批时间调整,看不出对系统有任何影响,不知道为何会突然出问题?




02 溯因:故障原因何在?


经过变更代码、分析binlog,确认确实没有其它会话影响本业务后,接着每处SQL都多次执行、输出 user_id 信息,发现获取到正确结果集后,再次查询只能拿到一条数据。


测例如下:

[prepare] 

create table t1(id int auto_increment primary key); 

insert into t1 values(1),(2),(3),(4),(5),(6),(10021); 


[session1] 

begin; 

insert into t1 values (7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23),(24),(25),(26),(27),(28),(29),(30),(31),(32),(33),(34),(35),(36),(37),(38),(39),(40),(41),(42),(43),(44),(45),(46),(47),(48),(49),(50),(51),(52),(53),(54),(55),(56),(57),(58),(59),(60),(61),(62),(63),(64),(65),(66),(67),(68),(69),(70),(71),(72),(73),(74),(75),(76),(77),(78),(79),(80),(81),(82),(83),(84),(85),(86),(87),(88),(89),(90),(91),(92),(93),(94),(95),(96),(97),(98),(99),(100),(101),(102),(103),(104),(105),(106),(107),(108),(109),(110);


[session2] 反复执行相同查询,结果集发生变化,且匹配行数始终在6行、1行之间变换:

select count(*) from t1 where id>0 and id<100;   -- 6条,正确

select count(*) from t1 where id>0 and id<100;   -- 1条,错误

select count(*) from t1 where id>0 and id<110;   -- 6条,正确


即使查询条件发生变化,只要还执行pk range,结果集就是错的

select count(*) from t1 where id>0 and id<115;   -- 1条,错误

select count(*) from t1 where id>0 and id<115;   -- 6条,正确

select count(*) from t1 where id>0 and id<115;   -- 1条,错误


经测试,输出错误的1条记录是第一条,而非其它记录

select id from t1 where id>0 and id<120;   -- 6条,正确,id为1-6

select id from t1 where id>0 and id<120;   -- 1条,错误,id为1

至此,问题很明显集中在MySQL数据库端返回结果集被上个SQL影响,造成结果集读取或返回时提前中断。


根据上述信息,DBA 翻阅了 MySQL  bug home发现bug#91032与测试现象高度吻合。


03 寻根:源码分析&定位处理


银行业务人员查看到bug#91032的复现步骤后,更迷茫了:因为业务SQL和处理流程都很常规。如果这样的常规步骤都有问题,那其它业务可能早就形成数据污染了……客户方业务人员越想越害怕。


到底SQL类型、执行计划、查询范围、返回数据量等要满足什么样的规律才会触发bug#91032呢?客户完全没有解决思路和头绪。


焦头烂额的状态下,为了尽快解决bug难题、对比验证,客户联系到多家合作的国内数据库厂商。有厂商很快确认了bug来源,并开始分析处理。

被客户第一时间找到的还有MySQL中国研发中心背景的万里数据库,客户急于知道该问题的准确答案。万里数据库紧急调度2名研发工程师前往现场解决难题。




基于在MySQL核心代码的多年技术积淀和源码掌控能力,万里数据库研发工程师先分析现场业务的网络抓包,再根据抓包中的SQL逻辑编写复现程序,果然准确地复现了问题场景,然后结合源码分析复现场景中m_prebuilt->m_end_range变量的变化,最后分析出造成查询结果集提前返回的原因和条件。


通过一套完整的方法论和专业缜密的源码分析、测试,最终梳理出触发条件需满足的几点关键规律:



1. SQL 使用x≥a and x≤b 或 x between a and b的范围查询,执行计划使用 range scan;


2.满足x≥a and x≤b 或 x between a and b查询条件的记录数≥4条;


3.假设 x>b 的记录数,其中已commit的记录数为m,未commit的记录数为n,m=[1,8),(m+n)≥100;


4.x 为索引字段。



04 验证:评估验证  确定结果


经各业务部门多次验证评估,只有本业务作业满足上述条件,才会触发bug#91032。客户经过分析对比发现:在所有数据库厂商给出的源码分析结果中,万里数据库给出的这几条关键触发规律是最精准且有效的。


基于此,得到准确结果后的客户,大大消除了心中疑虑,知晓了后续正确的应对方式,从而规避bug出现,挽回业务损失。



而万里工程师也凭借在这次问题分析中的杰(tong)出(xiao)表现,用专业过硬的技术能力征服了客户,获得客户由衷认可。



揭秘:万里数据库为何具备如此强大的MySQL源码掌控能力?


万里数据库于2006年成为MySQL中国研发中心和MySQL中国教育中心。MySQL中国研发中心作为MySQL全球研发队伍的重要组成部分,参与到当时最新的MySQL产品开发之中,是中国技术团队首次进入国际知名开源基础软件核心开发领域的典型代表,对MySQL核心源代码拥有强大的掌控力。


点击进入万里数据库替换MySQL技术专区5.7停服专区 (greatdb.com)



作为原MySQL中国研发中心,万里数据库不仅对MySQL源码和技术服务积淀深厚,还完全继承了MySQL技术路线最成熟的技术生态和语法、兼容性等,沉淀了MySQL源码服务、MySQL运维工具、MySQL支持服务三大维度七个模块的MySQL技术能力体系,可为MySQL用户在数据库使用、数据库迁移、数据库运维、数据库咨询等问题上提供高质量的定制化解决方案。


640 (3).png

▲ 万里数据库-MySQL技术能力体系


得益于公司在MySQL技术路线的多年技术研发与沉淀,万里数据库自主研发的商业数据库产品GreatDB,历经十多年打磨,不仅完美继承了MySQL技术生态,可帮助用户以最少代价、无缝平滑地将MySQL迁移到GreatDB安全数据库,业务改造工作量最少,改造成本接近于0


同时,GreatDB灵活的集中式及分布式架构部署切换模式、高安全、高性能等优势特性,能满足客户复杂业务系统中的数据库替换需求,且提供一系列配套的迁移和运维管理工具,即0成本就能完成数据库迁移工作,积累了多个重点行业的头部大型企业客户案例,助力各行业数字化进程加速发展。


此外,2021年,万里数据库基于自身MySQL技术路线的积累,主导成立了GreatSQL开源数据库社区,持续投入人力物力资源,推动GreatSQL这一MySQL技术路线的开源数据库持续更新迭代,至今已完成6个版本更新升级。与此同时,得益于GreatSQL的开源贡献度、社区活跃度等因素,GreatSQL社区于2023年成功捐赠给开放原子开源基金会,成为其旗下孵化开源项目,并获得中国信通院可信开源社区+可信开源项目双认证,延续MySQL技术路线贡献,持续回馈社会。




05 番外:后续操作指南


根据MySQL5.7.24版本的提交日志,可以查看bug#91032的修复:


commit 94208bcb882860f9f21e1a0d1e600cce268e1203
 

Author: Jimmy Yang <jimmy.yang@oracle.com>
 

Date:   Wed Jun 13 18:10:12 2018 -0700

           Fix Bug#28104394 - INNODB 5.7 PRIMARY KEY SCAN LACK DATA
           Reviewed-by: Sunny Bains<sunny.bains@oracle.com>

diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index ff82775ac7a..9b574c70f9a 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -3235,6 +3235,7 @@ ha_innobase::reset_template(void)
        m_prebuilt->keep_other_fields_on_keyread = 0;
        m_prebuilt->read_just_key = 0;
        m_prebuilt->in_fts_query = 0;
+     m_prebuilt->m_end_range = false;
 
        /* Reset index condition pushdown state. */
        if (m_prebuilt->idx_cond) {


实际上,每次SQL执行完成后,需要把相关的标志位正常重置。但对于生产环境而言,就需要合入该patch或升级到5.7.24及以上版本了,升级的处理办法可点击阅读原文查看具体方案。


参考链接

1.InnoDB 5.7 Primary key scan lack data:https://bugs.mysql.com/bug.php?id=91032


2.项目后续-合入修复patch并编译与官方一致的Linux_Generic包:

https://mp.weixin.qq.com/s/d2v_2KIhA0vEL8L4boASTQ


点击进入万里数据库替换MySQL技术专区5.7停服专区 (greatdb.com)