误用autocommit引起的业务hang住

本文分析了一种MySQL中常见的Select语句挂起情况,并详细解释了其背后的原因,涉及autocommit设置不当导致的MDL锁冲突。通过案例演示如何避免此类问题,并介绍了阿里云RDS提供的解决方案。

背景

有用户报告一个普通的select 语句被hang住了,执行超时。查明之后发现是autocommit使用不当导致。

这里将case简化,说明复现步骤及原因。

复现

session1 建表并插入数据:

create table if not exists t(id int primary key, c int);

set autocommit=0;

insert into t values(1,1);

insert into t values(2,2);

insert into t values(3,3);

commit;

select count(*) from t;

这个执行流程的目的很直观,建表、插入数据、查询结果。貌似没有问题。

维持session1不断,新建一个连接session2,执行 create table if not exists t(id int primary key, c int);

此时该语句处于等待状态.

再新建一个连接session3, 执行select count(*) from t; 该语句处于等待状态.

于是从业务上看就是一个select 语句被hang住。

原因分析

MySQL Tips:  如果服务中某些语句无法执行完成,追查问题时第一步要先保留现场,pstack <pid of mysqld> > tmplog之一个常用的方法。

​这两个等待线程的栈如:

#0  0x000000310ce0b7bb in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0

#1  0x000000000063ba46 in MDL_wait::timed_wait(THD*, timespec*, bool, char const*) ()

#2  0x000000000063e095 in MDL_context::acquire_lock(MDL_request*, unsigned long) ()

 可以看到,堵在MDL_wait.

 简单说明下什么是MDL。试想,如果一个语句在执行一个表上的查询过程中,表结构被改了,或者表被drop,这样会得到一个错误的结果。因此在一个事务持续期间,就需要对访问的表结构作保护。这个就是meta data lock (MDL).

很容易理解的,对表数据作增删改查,需要对MDL加读锁,修改表结构、删除表等操作则加写锁。

MySQL Tips: MDL是5.5才加入的机制,5.1版本下本文的case不会复现。

 MySQL Tips: 事务中MDL申请时机是在首次使用时,释放时机是在事务结束后。

也就是说文章开头的这个case,原因是session2等待在加写锁过程。而session3虽然只是加读锁,但与session2冲突,也需要等待。

session1的事务

也就是说session1还持有表t的MDL读锁。但我们的事务明明已经提交(commit)了。这里就涉及到一个常见的误解。以前有看过文章说,可以用set autocommit=0开启一个事务。其实这个描述不准确.

MySQL Tips: set autocommit=0是将本线程设置为非自动提交模式。在每个事务结束后,下个语句开始时自动新建一个事务。

这就意味着,session1最后的那个select count(*)操作,实际上之前隐含了一个begin操作。由于该事务没有提交,因此session1持有表t的MDL读锁。

 因此对于业务方的建议就是,及时提交这些读事务,或断开连接。

MySQL Tips: 连接断开时,MySQL会自动回滚当前未提交的事务。

由于本case里面session1的最后一个事务只是一个select语句,因此回滚不影响业务。

小结

1) 显式的启动事务的方法是begin或start transaction; 提交事务的方法是commit;

2) set autocommit=0的好处是在频繁开启事务的场景下,减少一次begin的交互。

3) 注意set autocommit=0修改了线程变量,会影响本线程存活期间的事务行为。

4) set autocommit=1可以提交事务并改变值,可以与set autocommit=0成对出现。

阿里云RDS feature

当出现如上的错误用法时,修改业务代码需要一段时间。

针对这种case,阿里云RDS版本提供了trx_idle_timeout参数,设置空闲事务停留时间。当超过设置的值(单位为秒)后,连接自动断开。

比如设置为10,在示例的case里面,sessino1在空闲10s后断开连接,session2继续执行,之后session3继续执行。

该值默认为0。



踩过类似的坑,就转了过来。

转自阿里云技术博客:http://blog.aliyun.com/434?spm=0.0.0.0.w3t9rm

 

<think>好的,用户遇到了“无法切换自动提交(autocommit)”的错误,需要解决这个问题。首先,我需要回忆数据库连接中autocommit的相关知识。可能的原因包括配置问题、权限不足、驱动程序问题或事务未提交等。 用户提供的引用中提到了MySQL配置文件里的autocommit=1,这可能意味着数据库默认启用了自动提交。如果用户尝试修改autocommit设置但失败,可能需要检查配置文件是否被正确加载,或者是否有其他覆盖设置的情况。 另外,引用[1]中的skip-grant-tables选项可能导致权限检查被绕过,但同时也可能影响某些功能的正常使用,比如修改autocommit。需要确认用户是否在使用这个选项,以及是否需要调整权限。 接下来,考虑驱动程序和连接字符串的问题。不同数据库驱动对autocommit的支持可能有差异,例如JDBC、ODBC或Python的驱动。用户可能在代码中尝试设置autocommit,但由于驱动不兼容或连接参数错误导致失败。 事务未提交也是一个可能的原因。如果之前有未提交的事务,新的autocommit设置可能无法生效,需要先提交或回滚现有事务。 权限方面,用户账户是否有足够的权限来更改autocommit设置?需要检查数据库用户的权限设置,尤其是SUPER或SESSION_VARIABLES_ADMIN权限。 最后,日志分析是关键。查看数据库的错误日志和通用查询日志,可以帮助定位问题根源。用户需要启用相关日志并复现问题,然后根据日志中的错误信息进一步排查。 总结可能的解决步骤:检查配置文件、验证权限、确保事务处理正确、更新驱动程序、检查连接字符串、查看日志。需要逐步引导用户进行这些检查,并提供具体的命令或配置示例,比如如何修改my.cnf,如何授予权限,如何检查事务状态等。</think>### 分步解决方案 #### 1. 检查数据库配置 - **确认`autocommit`参数设置** 在MySQL配置文件(如`/etc/my.cnf`或`my.ini`)中检查`autocommit`是否被强制锁定: ```ini [mysqld] autocommit=1 # 若设置为1,可能覆盖客户端请求[^1] ``` 若存在该配置,**注释或删除此行**后重启数据库: ```bash sudo systemctl restart mysql ``` #### 2. 验证用户权限 - **确保账户具备修改会话变量的权限** 执行以下SQL检查用户权限: ```sql SHOW GRANTS FOR 'your_user'@'host'; ``` 若无`SUPER`或`SESSION_VARIABLES_ADMIN`权限,需授权: ```sql GRANT SESSION_VARIABLES_ADMIN ON *.* TO 'your_user'@'host'; ``` #### 3. 检查事务状态 - **清理未提交的事务** 在尝试切换`autocommit`前,确保当前会话无活跃事务: ```sql SELECT @@in_transaction; -- 若返回1,需提交或回滚 COMMIT; -- 或 ROLLBACK; ``` #### 4. 更新数据库驱动 - **检查驱动兼容性** 例如Python的`mysql-connector`需≥2.1.6版本: ```bash pip show mysql-connector-python ``` 升级命令: ```bash pip install --upgrade mysql-connector-python ``` #### 5. 显式设置连接参数 - **在连接字符串中指定`autocommit`** Python示例(使用`pymysql`): ```python import pymysql connection = pymysql.connect( host='localhost', user='user', password='pass', database='db', autocommit=False # 显式关闭自动提交 ) ``` #### 6. 查看错误日志定位根源 - **启用并分析MySQL日志** 在配置文件中添加: ```ini [mysqld] general_log=1 general_log_file=/var/log/mysql/general.log ``` 复现错误后检查日志中的具体报错描述[^1]。 --- ### 相关问题 1. 如何验证MySQL配置文件的生效路径? 2. 数据库事务未提交会导致哪些连锁问题? 3. 为什么某些数据库驱动会限制`autocommit`的修改?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值