前言

日常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排查-改表时读数据不一致的更多相关文章

  1. 日常Bug排查-抛异常不回滚

    日常Bug排查-抛异常不回滚 前言 日常Bug排查系列都是一些简单Bug排查,笔者将在这里介绍一些排查Bug的简单技巧,同时顺便积累素材_. Bug现场 最近有人反映java应用操作数据库的时候,抛异 ...

  2. 日常Bug排查-系统失去响应-Redis使用不当

    日常Bug排查-系统失去响应-Redis使用不当 前言 日常Bug排查系列都是一些简单Bug排查,笔者将在这里介绍一些排查Bug的简单技巧,同时顺便积累素材_. Bug现场 开发反应线上系统出现失去响 ...

  3. 日常Bug排查-消息不消费

    日常Bug排查-消息不消费 前言 日常Bug排查系列都是一些简单Bug排查,笔者将在这里介绍一些排查Bug的简单技巧,同时顺便积累素材_. Bug现场 某天下午,在笔者研究某个问题正high的时候.开 ...

  4. 日常Bug排查-Nginx重复请求?

    日常Bug排查-Nginx重复请求? 前言 日常Bug排查系列都是一些简单Bug排查,笔者将在这里介绍一些排查Bug的简单技巧,其中不乏一些看起来很低级但很容易犯的问题. 问题现场 有一天运维突然找到 ...

  5. wordpress插件bug排查后记(记一次由于开启memecached引起的插件bug)

    这篇文章是写给自己的. 周三的时候我在维护公司的一个wordpress项目页面时发现了一个非常奇怪的情况:当我尝试更新网站上的一个页面后,在wordpress后台的编辑器中发现其内容并没有按我预期的将 ...

  6. pt-osc改表导致数据不一致案例分析

    2016-06-10 李丹 dba流浪猫 我们平时除了解决自己问题外,有时候也会协助圈内人士,进行一些故障排查,此案例就是帮某公司DBA进行的故障分析,因为比较典型,特分享一下,但仅仅是分享发生的过程 ...

  7. MySQL的数据类型,MySQL增删改--添加主外键、添加属性、删除主外键、改表名、获取系统当前时间等

    ls /etc/rc.d/init.d/mysql56service mysql56 start ps aux |grep "mysql"|grep "socket=&q ...

  8. EF 外键不显示、如何让外键显示!增、删、改 操作时,外键不显示,只显示导航属性!

    一.问题描述:EF 外键不显示.如何让外键显示!增.删.改 操作时,外键不显示,只显示导航属性! EF 添加.增加.插入数据时,外键不显示! 二.解决方案:在根据数据库生成模型的时候,选中“在模型中” ...

  9. mysql创建表时,设置timestamp DEFAULT NULL报错1067 - Invalid default value for 'updated_at'

    问题背景: 线上的linux服务器上的mysql服务器中导出数据库的结构.想要在本地创建一个测试版本 导出后再本地mysql上运行却报错   1067 - Invalid default value ...

  10. 解决MySQL联表时出现字符集不一样

    mysql 建表时都会设置表的字符集和排序规则,通常是 utf8,不过我这边习惯建表的字符集是 utf8mb4,排序规则是 utf8mb4_unicode_ci.有些 utf8mb4 的表默认排序规则 ...

随机推荐

  1. dotnet Microsoft.Recognizers.Text 超强大的自然语言关键词提取库

    本文和大家介绍一个使用超级简单,但是功能特别强大的自然语言关键词提取库,可以根据输入的自然语言提取出里面的信息.例如我在一句话里面说了哪些数值变量或者说了手机号码等 先看看下图的一个效果,下图是尝试识 ...

  2. 015_元器件BOM表的输出与打印

    015_元器件BOM表的输出与打印 BOM表,选中dsn/tools/Bill of materials/Header后添加封装参数\tPCB Footprint,Combined property ...

  3. Dinky实时计算平台

    前言:Apache Flink 作为新一代的实时计算框架已经被应用到各个行业与领域,其岂存在着应用的痛点比如 FlinkSQL 在线IDE.作业提交不友好.作业无监控报警等.很大程度上说,FlinkS ...

  4. LVS负载均衡(4)-- LVS FWM防火墙标记

    防火墙标记的作用是:借助于防火墙标记来分类报文,然后基于标记定义集群服务:可将多个不同的应用使用同一个集群服务进行调度. 实现方法: 在Director主机打标记,作用在mangle表的PREROUT ...

  5. Sublime Text 3 初试牛刀

    每次我在其他视频网站上看学习视频的时候,看着老师用的编辑器高大上档次,而我一般用Notepad,和Dreamweaver去编辑网页,需要每一行代码,打进去,效率低.最近看到sublime编辑器,在网上 ...

  6. ajax递归发送请求

    简介 大家都知道浏览器在处理http网络请求的时候,不同的浏览器会有不一样的并发限制,下表是一些主流浏览器对 HTTP 1.1 和 HTTP 1.0 的最大并发连接数目: Browser HTTP/1 ...

  7. synchronized原理-字节码分析、对象内存结构、锁升级过程、Monitor

    本文分析的问题: synchronized 字节码文件分析之 monitorenter.monitorexit 指令 为什么任何一个Java对象都可以成为一把锁? 对象的内存结构 锁升级过程 Moni ...

  8. C语言:输出大写的三角形字母表(进阶)

    //该程序只支持输入大写字母,输入G,就输出A~G的三角形字母表 /*       从A到Z的输出数量应该是1 3 5 7 9.....       */ 利用这个特性,对空格和字母进行输出 A字母在 ...

  9. VRRP 虚拟路由器冗余协议

    目录 文章目录 目录 为什么要使用 VRRP 技术? VRRP VRRP 的概念 VRRP 的工作原理 VRRP 的状态机 VRRP 的工作过程 VRRP 的选举机制 VRRP 的报文格式 VRRP ...

  10. RESTful风格openapi接口设计+openapi远程服务调用

    我们平常开发一般只使用GET.POST方法.而对于HTTP给出的PUT.DELETE等其他方法都没使用.以RESTful风格设计接口就能全部用上这些方法. 按照RESTful理查德森成熟度模型改造接口 ...