乐趣区

关于mysql:故障分析-记一次-mysql-更新未成功的排查过程

作者:王向

爱可生 DBA 团队成员,负责公司 DMP 产品的运维和客户 MySQL 问题的解决。善于数据库故障解决。对数据库技术和 python 有着浓重的趣味。

本文起源:原创投稿

* 爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。

本文目录:

update 更新“未胜利”?

前言

问题场景

MySQL 呈现“写了 binlog 但并没有写 redo-log”

  • 简略看下两阶段提交的流程
  • 两阶段写日志的意义?

排查陷入僵局

排查 binlog

  • 排查这段时间内的所有和这个 id 无关的 binlog 记录

总结

update 更新“未胜利”?

前言

笔者最近解决了一个十分波折的问题,就是业务反映有一条数据进行 update 并且胜利后,查问仍然是旧数据。于是开始一路排查,最初才完满解释了所有的景象。

在这里将整个过程写成文章记录下来,心愿可能对读者有所帮忙。(篇幅可能会有点长,急躁看完,相对物有所值~)

问题场景

业务小明:有一笔订单更新,更新数据返回胜利,然而数据库里还是旧的数据。

看了这组数据后,百思不得其解:“岂但批改的数据没有失效,utime 也不一样了”。于是登录数据库进行查问,后果确实是小明同志形容的那样。怎么办呢?

翻了一下对于这条数据的 binlog 记录的语句的确就是进行了更新,那么问题来了。这不就意味着:

写了 binlog 但并没有进行 redo-log 的更新,这不就数据不统一了?

MySQL 呈现“写了 binlog 但并没有写 redo-log”

家喻户晓,MySQL 具备 WAL 机制(先写日志,再写磁盘)。咱们要搞清楚会不会呈现“写了 binlog 但并没有写 redo-log”的状况,就须要钻研这个 WAL 机制的二段提交个性。

在说两阶段提交事务之前,咱们先来说说事务。

简略看下两阶段提交的流程

两阶段提交的时序图:

粗略察看一下上图,MySQL 想要执行事务的时候会分成两个阶段

第一阶段(prepare 阶段):写 redo-log 并将其标记为 prepare 状态。

紧接着写 binlog

第二阶段(commit 阶段):写 binlog 并将其标记为 commit 状态。

两阶段写日志的意义?

你有没有想过这样一件事,binlog 默认都是不开启的!

也就是说,如果你基本不须要 binlog 带给你的个性,那你基本就用不着让 MySQL 写 binlog,也用不着什么两阶段提交。

只用一个 redolog 就够了。无论你的数据库如何 crash,redolog 中记录的内容总能让你 MySQL 内存中的数据恢复成 crash 之前的状态。

所以说,两阶段提交的次要用意是:为了保障 redolog 和 binlog 数据的平安一致性(划重点!!! 拐杖敲黑板 3 次)。只有在这两个日志文件逻辑上高度一致了。你能力释怀的应用 redolog 帮你将数据库中的状态复原成 crash 之前的状态,应用 binlog 实现数据备份、复原、以及主从复制。而两阶段提交的机制能够保障这两个日志文件的逻辑是高度一致的。没有谬误、没有抵触。

排查陷入僵局

看到这里咱们就发现两阶段提交保障了 redolog 和 binlog 数据的平安一致性,binlog 里进行 commit,redolog 里肯定是胜利的也就是说:

基本不可能呈现写了 binlog 但并没有写 redo-log 的状况,齐全不会呈现小明形容的那样的问题。

通过重复思考,真実 (しんじつ) は いつも ひとつ 平假名: しんじつはいつもひとつ(假相只有一个)。

那就是形容信息里有脱漏,在更新后和查问前必然有一个事务对这个记录进行了操作。

排查 binlog

1. 排查这段时间内的所有和这个 id 无关的 binlog,并提取出相干记录

2. 找出更新后和查问前的那个事务的 binlog

排查这段时间内的所有和这个 id 无关的 binlog 记录

如何出排查这段时间内的所有和这个 id 无关的 binlog 记录呢,这么多的 binlog。那只能写个脚本进行批处理了。

file_list=$(ls mysql-bin.00*)
for i in file_list
do
    count=`mysqlbinlog -vv -d t100w.t_250w $i |grep -c "{主键 id}"`
    [$count -gt 0] && (echo $i $count)
done

## 代码解释:# mysqlbinlog -d t100w.t_250w 只查看 t100w 库 t_250w 表的 binlog
# grep -c 统计文件中搜寻关键字的个数(等价于 select count(*) from table where id > ?)# 通过 ls 获取到所有 mysql-bin,通过 for 循环找到搜寻关键字的个数大于 0 的文件, 并打印文件名和统计个数

而后应用 less 命令,搜寻主键 id。

mysqlbinlog -vv -d t100w.t_250w mysql-bin.009820|less
# less 内
# / 主键 id

最终找到了那条记录,水落石出:

总结

有时候问题并没有那么简单,并不是 MySQL 底层除了问题,而是“小明”在提供信息的时候不肯定能把问题精确的形容分明。导致咱们的一些误判。在解决问题上应该先从间隔问题最近的角度登程。感激大家
看完一名菜鸡 DBA 的文章。咱们下个文章再见!

退出移动版