欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

为何出现了trx_mysql_thread_id为0 的事务

程序员文章站 2022-04-12 11:44:54
今天巡检时突然发现有很多锁等待超时的情况,原以为是一个简单的小事,一查,结果令人深思。 1. 问题现象 发现日志中出现了大量的 ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction 错误 2. 排查过程 ......

今天巡检时突然发现有很多锁等待超时的情况,原以为是一个简单的小事,一查,结果令人深思。

 

1.  问题现象

发现日志中出现了大量的 error 1205 (hy000): lock wait timeout exceeded; try restarting transaction 错误

为何出现了trx_mysql_thread_id为0 的事务

 

2. 排查过程

发现此类情况后,挑了其中一个sql脚本手动运行了一下,发现同样报此错误

mysql> update tbname set column_name = 2 where col_id= '25945fa285904ea59cd92a73a3850ceb' and ayear = 2018 and amonth = 5;

error 1205 (hy000): lock wait timeout exceeded; try restarting transaction

出现此情况,第一反应是查看是否有未提交的事务或有其他的sql运行时也需要对该条记录进行写操作。

# 查看正在运行的sql

select  *  from information_schema.processlist where info is not null;

结果集中并无对该表的任何操作,因此,很大可能是有未提交的事务了。

 # 查看事务

select  *from  information_schema.innodb_trx;

  结果中确实存在大量事务,此时原本以为已经查到问题,直接将对应为提交的事务杀掉即可(已与相关人员确认可以杀)

于是把脚本准备好,准备大开杀戒

#  杀sql会话
select concat('kill ',trx_mysql_thread_id,";")t_sql from information_schema.innodb_trx;

  但是仔细一看,trx_mysql_thread_id全部都是0 

为何出现了trx_mysql_thread_id为0 的事务

经确认,trx_mysql_thread_id=0 的事务全部为xa事务。

3. 处理过程

因为trx_mysql_thread_id=0 的事务无法通过kill trx_mysql_thread_id 的方式处理,所以,需要回滚这些xa事务。

查看xa事务信息

mysql>  xa recover;

+------------+--------------+--------------+-------------------------------+

| formatid   | gtrid_length | bqual_length | data                          |

+------------+--------------+--------------+-------------------------------+

| 1096044365 |           20 |            9 | tm156393736565426841tm1333009 |

| 1096044365 |           20 |            9 | tm156393708714926372tm1332251 |

| 1096044365 |           20 |            9 | tm156393726166726646tm1332693 |

...

+------------+--------------+--------------+-------------------------------+
43 rows in set (0.00 sec)

 拼接生成xa事务回滚脚本

# xa事务回滚命令的格式:
xa rollback 'left(data,gtrid_length)','substr(data,gtrid_length+1,bqual_length)', formatid; 

# 以上查出来的信息拼接结果为(以下举其中一个为例)
xa rollback 'tm156393736565426841','tm1333009',1096044365;

执行回滚脚本

mysql> xa rollback 'tm156393736565426841','tm1333009', 1096044365;
query ok, 0 rows affected (0.00 sec)

为何出现了trx_mysql_thread_id为0 的事务

检查是否还存在未提交的xa事务

发现已经无正在执行事务

为何出现了trx_mysql_thread_id为0 的事务

xa信息

为何出现了trx_mysql_thread_id为0 的事务

 

测试能否正常更新记录

# 发现也已正常

为何出现了trx_mysql_thread_id为0 的事务

 

再检查各日志,此类锁等待问题也未出现。

 

 4.  xa事务(分布式事务)浅析

在本应用中,为了降低单点压力,根据业务情况进行了分表分库,将表分布在不同的库中(库分布在不同的机器上)。在这种场景下,事务的提交会变得相对复杂,因为多个节点(库)的存在,可能存在部分节点提交失败的情况,即事务的acid特性需要在各个不同的数据库实例中保证。比如更新db1库的a表时,必须同步更新db2库的b表,两个更新形成一个事务,要么都成功,要么都失败,起初,为了简化应用程序在事务处理的难度,因此直接使用mysql数据库的分布式事务。
 
 两阶段提交
分布式事务通常采用2pc协议,全称two phase commitment protocol。该协议主要为了解决在分布式数据库场景下,所有节点间数据一致性的问题。分布式事务通过2pc协议将提交分成两个阶段:

mysql> xa start 'xatest';
query ok, 0 rows affected (0.00 sec)

mysql> insert into mytable (i) values(10);
query ok, 1 row affected (0.04 sec)

mysql> xa end 'xatest';
query ok, 0 rows affected (0.00 sec)

mysql> xa prepare 'xatest';
query ok, 0 rows affected (0.00 sec)

mysql> xa commit 'xatest';
query ok, 0 rows affected (0.00 sec)
阶段一为准备(prepare)阶段。即所有的参与者准备执行事务并锁住需要的资源。参与者ready时,向transaction manager报告已准备就绪。
阶段二为提交阶段(commit)。当transaction manager确认所有参与者都ready后,向所有参与者发送commit命令。
如下图所示:

为何出现了trx_mysql_thread_id为0 的事务

因为xa 事务是基于两阶段提交协议的,所以需要有一个事务协调者(transaction manager)来保证所有的事务参与者都完成了准备工作(第一阶段)。如果事务协调者(transaction manager)收到所有参与者都准备好的消息,就会通知所有的事务都可以提交了(第二阶段)。mysql 在这个xa事务中扮演的是参与者的角色,而不是事务协调者(transaction manager)。

xa事务的性能问题

xa的性能很低。一个数据库的事务和多个数据库间的xa事务性能对比可发现,性能差10倍左右。因此要尽量避免xa事务,例如可以将数据写入本地,用高性能的消息系统分发数据。或使用数据库复制等技术。只有在这些都无法实现,且性能不是瓶颈时才应该使用xa。并发高的情况下不建议使用,可以借助redis或其他方法来改造。

关于xa事务的问题及优化的方案有什么建议可以留言沟通。

 

耿小厨已开通个人微信公众号,想进一步沟通或想了解其他文章的同学可以关注我

为何出现了trx_mysql_thread_id为0 的事务