在MySQL里,事务并不是独立于连接存在的抽象概念,而是绑定在每一个数据库会话之上的执行上下文。当我们通过程序去操作MySQL时,底层总会持有一个物理或逻辑连接,而事务的状态、锁的持有以及临时数据,都依附在这个连接上。如果脱离连接去谈事务管理,很容易出现数据不一致或者连接泄漏的问题。

事务与连接的关系本质
MySQL默认采用autocommit模式,也就是说,在没有显式开启事务的情况下,每执行一条独立的SQL语句,服务器都会自动为它开启一个事务,执行完后立刻提交。这种行为对单条语句是安全的,但在需要多条语句共同成功或共同失败的业务里,就必须由开发者手动控制事务边界。此时,事务的生命周期完全由当前连接决定:连接断开,未提交的事务会被回滚。
从实现角度看,每个客户端连接对应服务器端的会话对象,会话里记录了事务状态、隔离级别、临时表等信息。当我们调用start transaction或者set autocommit=0时,只是改变了当前会话的配置,并不影响其他连接。这也解释了为什么在多线程程序里,绝不能把同一个连接对象在线程间随意传递,否则一个线程提交或回滚,会直接影响另一个线程正在执行的逻辑。
用事务管理连接的基本写法
最基础的做法是在同一个连接内,显式开启事务,执行一系列操作,然后根据结果提交或回滚。以下以Python的pymysql为例,展示一段标准代码:
import pymysql
conn = pymysql.connect(host='127.0.0.1', user='root', password='test', db='demo')
try:
# 显式关闭自动提交,开启事务
conn.begin()
with conn.cursor() as cur:
cur.execute("update account set balance=balance-100 where id=1")
cur.execute("update account set balance=balance+100 where id=2")
# 两条语句都成功,提交事务
conn.commit()
except Exception as e:
# 出现异常,回滚当前连接上的事务
conn.rollback()
print("转账失败,已回滚")
finally:
conn.close()
上面的代码把事务控制在try块内,确保任何异常都会触发rollback。注意conn.close()放在finally中,无论成功失败都释放连接,避免连接泄漏。如果在commit之前程序崩溃,MySQL服务端检测到连接断开,也会自动回滚该会话的事务,这是连接管理的一道安全网。
在使用连接池时,这个模式要稍作调整。从池里借出的连接可能上一次被使用过且未正确清理,因此借出后应重置会话状态,例如执行rollback或者set autocommit=1,再开始新事务。很多连接池框架已经内置了借出重置逻辑,但开发者仍需确认,不能假设拿到的连接一定是干净状态。
常见误区与避坑要点
一个典型误区是认为事务可以跨连接生效。比如在一个函数里开启事务,然后把连接传给另一个函数继续操作,表面看没问题,但如果中间连接被还回池子又被别的请求借走,原事务就彻底失控。正确做法是把连接作为显式参数在调用链中传递,并且保证整个事务期间连接不被归还。
另一个误区是在长事务中执行无关查询。事务越长,持有的锁越多,其他连接等待时间越久,容易引发死锁或连接堆积。应当把事务压缩到最小必要范围,先查后算,最后集中开启事务做写操作。如下方示例,把只读查询放在事务外:
# 错误示范:在事务里做长时间外部调用
conn.begin()
cur.execute("select * from orders where id=10")
data = slow_remote_call() # 耗时网络请求
cur.execute("update orders set status='done' where id=10")
conn.commit()
# 正确示范:缩短事务
cur.execute("select * from orders where id=10")
data = slow_remote_call()
conn.begin()
cur.execute("update orders set status='done' where id=10")
conn.commit()
通过对比可以看出,把耗时代码移出事务,能显著降低锁占用时间。对于高并发系统,这种细节往往决定了数据库连接是否会被迅速耗尽。
隔离级别对连接行为的影响
MySQL提供READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE四种隔离级别,它们直接决定了一个连接中的事务能看到其他连接提交数据的哪些版本。默认REPEATABLE READ依靠MVCC实现一致性读,但在某些场景仍会出现幻读,需要配合间隙锁来防范。
设置隔离级别也是会话级的,只作用于当前连接。可以在开启事务前用set session transaction isolation level read committed来调整。如果应用程序不同模块对一致性要求不同,应为对应连接单独配置,而不是全局修改,避免影响其他业务。理解这点,才能用事务真正管好每一条MySQL连接。