三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Python能不能写一个验证事务效果的样例代码

Python能不能写一个验证事务效果的样例代码

很多开发者学习MySQL事务的时候只停留在理论层面,不知道事务真正生效是什么样子。本文提供两套对照可运行代码:无事务产生脏数据、开启事务异常自动回滚。直接运行代码,观察数据库数据变化,直观验证事务效果。

环境准备

安装依赖

pipinstallpymysql

测试表SQL

必须使用InnoDB引擎,MyISAM不支持事务。

CREATETABLE`account`(`id`INTPRIMARYKEYAUTO_INCREMENT,`username`VARCHAR(32)NOTNULL,`balance`DECIMAL(12,2)NOTNULLDEFAULT0)ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;-- 初始化测试数据:张三、李四各1000元INSERTINTOaccount(username,balance)VALUES('张三',1000.00),('李四',1000.00);

业务场景:张三转账200元给李四。
流程:

  1. 张三余额扣减200
  2. 模拟程序抛出异常
  3. 李四余额增加200

案例一:不使用事务(自动提交,产生脏数据)

pymysql默认autocommit=True,每执行一条SQL就直接写入数据库。
当中间代码抛出异常,后面SQL无法执行,就会出现一部分成功、一部分失败的数据错乱。

importpymysqldefdemo_without_transaction():# 默认 autocommit=Trueconn=pymysql.connect(host="127.0.0.1",port=3306,user="root",password="你的密码",database="test_db",charset="utf8mb4")cursor=conn.cursor()# 1.张三扣200,这条SQL立刻生效落库cursor.execute("UPDATE account SET balance = balance - %s WHERE username=%s",(200,"张三"))# 模拟业务异常、接口报错、程序崩溃print("模拟程序发生异常")1/0# 2.李四加钱,这行代码永远不会执行cursor.execute("UPDATE account SET balance = balance + %s WHERE username=%s",(200,"李四"))cursor.close()conn.close()if__name__=="__main__":demo_without_transaction()

运行现象

  1. 程序抛出ZeroDivisionError异常终止
  2. 查询数据库:张三余额变成800,李四仍然1000
  3. 出现脏数据,钱凭空消失,业务逻辑被破坏

⚠️复现完成后记得重置表数据,方便测试下一个案例。

UPDATEaccountSETbalance=1000.00;

案例二:开启事务,异常自动回滚

关键点:

  1. 设置conn.autocommit = False关闭自动提交
  2. 所有修改操作先在事务缓冲区,不会真正写入数据库
  3. 正常执行完毕调用conn.commit()持久化数据
  4. 捕获异常调用conn.rollback()撤销全部修改
importpymysqldefdemo_with_transaction():conn=pymysql.connect(host="127.0.0.1",port=3306,user="root",password="你的密码",database="test_db",charset="utf8mb4")# 关闭自动提交,开启事务模式conn.autocommit=Falsecursor=conn.cursor()try:# 第一步:张三扣款cursor.execute("UPDATE account SET balance = balance - %s WHERE username=%s",(200,"张三"))# 模拟异常,触发回滚逻辑print("模拟程序发生异常")1/0# 第二步:李四收款cursor.execute("UPDATE account SET balance = balance + %s WHERE username=%s",(200,"李四"))# 全部执行成功,提交事务,数据真正写入数据库conn.commit()print("✅事务提交成功,转账完成")exceptExceptionase:# 出现任何异常,回滚本次事务所有变更conn.rollback()print(f"❌捕获异常,事务已全部回滚,异常信息:{e}")finally:cursor.close()conn.close()if__name__=="__main__":demo_with_transaction()

运行现象(异常分支)

  1. 程序抛出除零异常,进入except代码块执行rollback()
  2. 查询数据库,张三、李四余额依旧都是1000,数据完全没有变化
  3. 所有update操作全部撤销,不会产生脏数据

测试正常提交场景

把代码中的1 / 0注释掉,再次运行。

  • 不会触发异常,执行commit()
  • 数据库:张三800,李四1200,转账业务正常完成。

案例三:演示部分回滚(SAVEPOINT保存点)

MySQL支持保存点,可以实现事务内部局部回滚,不需要全部回滚。

importpymysqldefdemo_savepoint():conn=pymysql.connect(host="127.0.0.1",port=3306,user="root",password="你的密码",database="test_db",autocommit=False,charset="utf8mb4")cur=conn.cursor()try:cur.execute("UPDATE account SET balance=balance-100 WHERE username='张三'")# 设置保存点sp1cur.execute("SAVEPOINT sp1")cur.execute("UPDATE account SET balance=balance+100 WHERE username='李四'")# 回滚到保存点,撤销上面李四加钱操作,张三扣钱保留cur.execute("ROLLBACK TO SAVEPOINT sp1")conn.commit()print("保存点演示完成")exceptExceptionase:conn.rollback()print(f"异常回滚{e}")finally:cur.close()conn.close()

对比总结表

场景无事务 autocommit=True开启事务 autocommit=False
程序中途报错部分SQL生效,生成脏数据全部操作回滚,数据不变
程序正常结束逐条SQL立即生效commit之后统一生效

开发避坑要点

  1. autocommit=False是开启事务的前提,忘记关闭自动提交,commit/rollback完全无效。
  2. 同一个事务内所有SQL必须使用同一个connection对象,更换连接代表全新事务。
  3. 异常分支必须手动执行rollback(),否则未提交事务会挂起,占用数据库锁资源。
  4. 表引擎必须是InnoDB,MyISAM不支持事务。
  5. 尽量缩小事务粒度,避免长事务,长事务会造成锁等待、数据库性能下降。
  6. SELECT查询语句不会修改数据,不会产生事务变更;SELECT ... FOR UPDATE会增加行锁。

业务模板

订单、库存、转账等多写业务直接套用这套模板:

conn.autocommit=Falsetry:# 多条写sqlconn.commit()exceptException:conn.rollback()finally:conn.close()

通过以上样例代码,就可以直观验证MySQL事务原子性效果:一组操作要么全部成功,要么全部撤销。

← 返回列表