公司的一哥们在使用事务时未进行commit,链接断开后数据库执行了回滚操作,导致线上直播相关库的部分数据丢失,本文通过mysql命令行模拟该事故,并提供解决该问题的思路。希望对大家有帮助。
一、场景模拟
通过命令行模拟两个数据库链接,同时开启事务,操作同一行数据。
在数据库链接1开启事务之后,执行rollback之前,数据库链接2开启事务并执行更新操作。
- 原始数据
id | name | pinyin | description |
---|---|---|---|
22193 | 跽棺反侧 | ji’guan’fan’ce | !!!形容人多拥挤~~~111 |
- 数据库链接1
-
数据库链接2
实际执行中,数据库链接2在执行update操作时会卡住,排队等待数据库链接1释放操作完成。
在数据库链接1执行rollback操作后,数据库连接2执行commit操作,此时查看数据,会发现数据库中的数据并未更新成功,即数据库链接2的数据丢失。
二、紧急处理
如果线上碰到该问题,首先把出问题的线程kill掉,防止影响更多的正常功能,然后再根据mysql的binlog对受影响的数据进行恢复。
具体使用到的命令在第四部分。
三、原因分析
- 事务过程中执行其他非数据库操作,导致事务长期未被处理。
- 事务处理异常或实现逻辑有误,导致事务未被正常处理。
- 网络,数据库负荷过大等。
四、相关命令
1、查看正在执行的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
上图可以查看到正在进行的事务(未进行commit)操作的线程信息,线程id为122985;
2、查看正在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
3、查看等待锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
4、查看所有的线程列表
show full processlist;
结合第一步查到的线程id,可以确定该事务涉及到的表名和数据库链接信息。
确认出现问题的sql在该线程id时,及时使用下面语句kill掉线程,防止影响更多的数据。
kill 122985;
5、定位未提交的事务执行的sql语句
通过以上步骤并不能直观的定位到出问题的sql语句,此时我们可以通过开启通用日志的方式,定位具体的sql语句。
# 查看general log配置
show variables like '%general_log%'
# 开启general log
SET GLOBAL general_log = 1;
通用日志会记录所有sql信息,数据量很大,建议只在排查错误时开启,线上关闭。
此时根据找到
进程号
和
时间戳
找到对应的记录,定位具体的sql。
参考链接:
https://blog.csdn.net/flysnownet/article/details/114837550
https://www.cnblogs.com/kerrycode/p/11013479.html
https://blog.csdn.net/emoers/article/details/77971468
https://cloud.tencent.com/developer/article/1511089