日常Bug排查-改表时读数据不一致
前言
日常Bug排查系列都是一些简单Bug的排查。笔者将在这里介绍一些排查Bug的简单技巧,同时顺便积累素材。
Bug现场
线上连续两天出现NP异常,而且都是凌晨低峰期才出现,在凌晨的流量远没有白天高峰期大。而出问题的接口又是通常的业务请求。于是,很自然的,我们就想凌晨有什么特殊的运维动作,翻了下时间线。发现,每天凌晨都会进行改表,而修改的这张表恰好就是出现NP异常的表。如下图所示:

在此解释下业务的相关场景。A表是主表,B表是子表,两者都是严格保证在一个事务内一块插入和更新的,在该表时刻确出现了在一个事务内查询,能查到A确查不到B的现象。

思路
数据库的一个核心特性就是原子性,看上去这个场景破坏了原子性。但是由于是和改表强相关,其它时间没有类似错误。那么很明显的,思路就会指向该表这个动作会短暂的破坏原子性。由于线上使用的ghost进行改表,于是笔者就看了下ghost改表的原理:
ghost会创建一个影子表,在影子表上完成alter改表,然后分批将全量数据应用到新表。
同时在处理增量数据的时候,通过解析binLog事件,将任务期间的新增数据应用到新表。
最后一步,通过Rename语句使新表替换老表
从这个原理中可以推断,最后一步Rename的时候才会对当前的SQL产生影响,是不是刚好这个这个Rename操作短暂的使读数据不一致了呢?看了下DBA那边的改表日志,发现Rename那个时刻和NP异常出现时刻完全吻合。看来它就是罪魁祸首了。
为什么Rename会导致读数据不一致?
笔者稍加思索就明白了原因。首先,线上库的隔离级别是RR的,也就是可重复读。而Alter表的时候势必会有一张旧表B和新表BNew。业务的事务保证是操作在A和B上的,而读数据不一致应该是A和BNew上,所以无法保证A和BNew的一致。只能通过binLog的重放保证最终一致。 那么最终导致问题的原因就很明显了,如下所示:

BNew新表通过ghost的binlog重放将原B表中相关的binLog重放到BNew表中。但是在事务T2开始的时候BNew这张表中新纪录B还没有被重放。在事务T2开始的时候首先查询了A表建立了MVCC视图,这时候的数据库实际快照就是A表有A,BNew表没有B。尽管在Rename表的时候MySQL会对B和BNew都进行锁表,这时候所有对于这两张表的访问都会等锁表的结束。但是由于RR的原因,这个事务内后续读BNew表的时候始终就是A表有A,BNew表没有B这样的现象。在后续的查询中select B查询的实际上是BNew表,进而产生了数据不一致,进而导致了NullPointerException。
测试复现实验的一个小问题
还有一个小问题,就是笔者在线下设计相关实验复现问题的时候。这个复现的实验看上去是比较容易的,模拟一下事务顺序,新建一张BNew表然后Rename下,看看现象是否一致就可以了,如下图所示:

但笔者发现,在Rename的时候,模拟的请求2在做select 新B表的时候始终会出现
Table definition has changed,please retry transaction
这个报错。于是笔者看了下MySQL的源代码,要想让Rename不报错,必须在模拟的请求2事务开始之前就创建这个BNew表,否则请求2在查询BNew表的时候就会由于找不到UndoHistory导致报错。MySQL源代码如下所示:
row_vers_old_has_index_entry(......){
......
/* If purge can't see the record then we can't rely on
the UNDO log record. */
bool purge_sees = trx_undo_prev_version_build(
rec, mtr, version, index, *offsets, heap,
&prev_version, NULL, vrow, 0);
// 在这边,如果找不到这张表在t1前的undo history的话,则会报"Table definition has changed, please retry transaction"
err = (purge_sees) ? DB_SUCCESS : DB_MISSING_HISTORY;
if (prev_heap != NULL) {
mem_heap_free(prev_heap);
}
......
}
总结
线上环境是错综复杂的,改表等运维操作也会导致出现意料之外的结果,很多组件的特性在一些特殊的情况下会被打破,所以防御式编程就显得尤其重要了。

日常Bug排查-改表时读数据不一致的更多相关文章
- 日常Bug排查-抛异常不回滚
日常Bug排查-抛异常不回滚 前言 日常Bug排查系列都是一些简单Bug排查,笔者将在这里介绍一些排查Bug的简单技巧,同时顺便积累素材_. Bug现场 最近有人反映java应用操作数据库的时候,抛异 ...
- 日常Bug排查-系统失去响应-Redis使用不当
日常Bug排查-系统失去响应-Redis使用不当 前言 日常Bug排查系列都是一些简单Bug排查,笔者将在这里介绍一些排查Bug的简单技巧,同时顺便积累素材_. Bug现场 开发反应线上系统出现失去响 ...
- 日常Bug排查-消息不消费
日常Bug排查-消息不消费 前言 日常Bug排查系列都是一些简单Bug排查,笔者将在这里介绍一些排查Bug的简单技巧,同时顺便积累素材_. Bug现场 某天下午,在笔者研究某个问题正high的时候.开 ...
- 日常Bug排查-Nginx重复请求?
日常Bug排查-Nginx重复请求? 前言 日常Bug排查系列都是一些简单Bug排查,笔者将在这里介绍一些排查Bug的简单技巧,其中不乏一些看起来很低级但很容易犯的问题. 问题现场 有一天运维突然找到 ...
- wordpress插件bug排查后记(记一次由于开启memecached引起的插件bug)
这篇文章是写给自己的. 周三的时候我在维护公司的一个wordpress项目页面时发现了一个非常奇怪的情况:当我尝试更新网站上的一个页面后,在wordpress后台的编辑器中发现其内容并没有按我预期的将 ...
- pt-osc改表导致数据不一致案例分析
2016-06-10 李丹 dba流浪猫 我们平时除了解决自己问题外,有时候也会协助圈内人士,进行一些故障排查,此案例就是帮某公司DBA进行的故障分析,因为比较典型,特分享一下,但仅仅是分享发生的过程 ...
- MySQL的数据类型,MySQL增删改--添加主外键、添加属性、删除主外键、改表名、获取系统当前时间等
ls /etc/rc.d/init.d/mysql56service mysql56 start ps aux |grep "mysql"|grep "socket=&q ...
- EF 外键不显示、如何让外键显示!增、删、改 操作时,外键不显示,只显示导航属性!
一.问题描述:EF 外键不显示.如何让外键显示!增.删.改 操作时,外键不显示,只显示导航属性! EF 添加.增加.插入数据时,外键不显示! 二.解决方案:在根据数据库生成模型的时候,选中“在模型中” ...
- mysql创建表时,设置timestamp DEFAULT NULL报错1067 - Invalid default value for 'updated_at'
问题背景: 线上的linux服务器上的mysql服务器中导出数据库的结构.想要在本地创建一个测试版本 导出后再本地mysql上运行却报错 1067 - Invalid default value ...
- 解决MySQL联表时出现字符集不一样
mysql 建表时都会设置表的字符集和排序规则,通常是 utf8,不过我这边习惯建表的字符集是 utf8mb4,排序规则是 utf8mb4_unicode_ci.有些 utf8mb4 的表默认排序规则 ...
随机推荐
- k8s网页访问实战
流程: 客户端访问--->ingress七层代理---->service四层代理---->deployment 详细情况:https://www.cnblogs.com/yangme ...
- pod QoS等级(A)
一.了解Pod Qos等级 一个节点不一定能提供所有pod所指定的资源limits之和那么多的资源量. 假设有两个pod,pod A使用了节点内存的 90%,pod B突然需要比之前更多的内存,这时节 ...
- mosquitto的安装与使用
一款实现了消息推送协议 MQTT v3.1 的开源消息代理软件,提供轻量级的,支持可发布/可订阅的的消息推送模式,使设备对设备之间的短消息通信变得简单,比如现在应用广泛的低功耗传感器,手机.嵌入式 ...
- 微软开源 MS-DOS「GitHub 热点速览」
上周又是被「大模型」霸榜的一周,各种 AI.LLM.ChatGPT.Sora.RAG 的开源项目在 GitHub 上"争相斗艳".这不 Meta 刚开源 Llama 3 没几天,苹 ...
- ITIL4 服务价值系统(SVS):一场服务管理的革新之旅
在这个数字化时代,每一家企业都在追求高效的服务管理和卓越的客户体验.今天,我们就来聊一聊ITIL4中的服务价值系统(Service Value System, SVS)--一个让服务管理变得更加直观和 ...
- idea推送代码忽略指定文件,文件夹配置
idea推送代码忽略指定文件,文件夹配置 今天碰到一个问题,配置了.gitignore文件后没有生效,整了半天,最后发现一种直接配置的方法,可以指定文件夹,或者指定文件类型 打开设置
- get pull报错 Please commit your changes or stash them before you merge
当本地分支和远程修改了同一个文件代码,pull远程分支的代码的时候会出现文件冲突 出现这个错误 Please commit your changes or stash them before you ...
- 助力抗疫 Splashtop 远程控制软件限时免费
近期国内疫情又有抬头趋势,给我们的工作.生活带来诸多不便.面对疫情,居家办公是一个兼顾安全健康和保持生产力的好办法.据了解,很多广州的企业现在已经在关注或开始部署远程办公方案. 为了帮助疫情中高风险地 ...
- Linux上执行内存中的脚本和程序
在Linux中可以不需要有脚本或者二进制程序的文件在文件系统上实际存在,只需要有对应的数据在内存中,就有办法执行这些脚本和程序. 原理其实很简单,Linux里有办法把某块内存映射成文件描述符,对于每一 ...
- Django RESTful API设计与实践指南
title: Django RESTful API设计与实践指南 date: 2024/5/14 15:37:45 updated: 2024/5/14 15:37:45 categories: 后端 ...