
后端開發中轉賬、訂單創建、庫存扣減這類業務必須保證多條數據庫操作全部成功或者全部回滾這就是事務的價值。本文基于 pymysql 講解 Python 下 MySQL 事務完整用法包含原理、代碼示例、異常處理、常見坑與最佳實踐。環境準備安裝 pymysqlpipinstallpymysql注意MySQL 只有 InnoDB 引擎支持事務MyISAM 不支持事務。測試表準備CREATETABLEaccount(idINTPRIMARYKEYAUTO_INCREMENT,usernameVARCHAR(32)NOTNULL,balanceDECIMAL(12,2)NOTNULLDEFAULT0)ENGINEInnoDBDEFAULTCHARSETutf8mb4;INSERTINTOaccount(username,balance)VALUES(zhangsan,1000.00),(lisi,1000.00);什么是事務事務是一組SQL操作的邏輯單元全部執行成功執行commit()提交修改永久生效任意步驟失敗執行rollback()回滾撤銷這一組所有修改。ACID四大特性原子性(Atomicity)事務內操作不可分割全部成功或全部失敗。一致性(Consistency)事務執行前后業務數據保持合法狀態。隔離性(Isolation)多個事務之間互相隔離受事務隔離級別控制。持久性(Durability)commit提交之后修改永久保存數據庫宕機也不會丟失。pymysql事務核心要點pymysql 默認autocommitTrue每條SQL執行后立即提交此時沒有事務效果。使用事務必須關閉自動提交conn.autocommit(False)。兩個關鍵方法conn.commit()提交事務conn.rollback()回滾事務同一個事務必須使用同一個connection連接對象不能換連接。示例1轉賬業務try?except標準寫法張三轉賬200元給李四模擬業務異常回滾。importpymysqldeftransfer():connpymysql.connect(host127.0.0.1,port3306,userroot,passwordxxx,databasetest_db,charsetutf8mb4)# 關閉自動提交開啟事務模式conn.autocommit(False)cursorconn.cursor(pymysql.cursors.DictCursor)try:# 1. 張三扣200cursor.execute(UPDATE account SET balance balance - %s WHERE username %s,(200,zhangsan))# 2. 模擬異常觸發回滾# 1 / 0# 3. 李四加200cursor.execute(UPDATE account SET balance balance %s WHERE username %s,(200,lisi))# 全部成功提交事務conn.commit()print(事務提交成功)exceptExceptionase:# 出現任何異常回滾所有變更conn.rollback()print(f事務回滾異常{e})finally:cursor.close()conn.close()if__name____main__:transfer()打開代碼中1/0模擬報錯你會發現兩條update全部失效不會出現張三扣錢、李四沒加錢的數據錯亂。示例2with上下文管理器用法pymysql 的 connection 支持 with退出上下文如果沒有commit會自動回滾。importpymysqldeftransfer_with():connpymysql.connect(host127.0.0.1,userroot,passwordxxx,databasetest_db,autocommitFalse,charsetutf8mb4)try:withconn.cursor(pymysql.cursors.DictCursor)ascur:cur.execute(UPDATE account SET balancebalance-%s WHERE username%s,(100,zhangsan))cur.execute(UPDATE account SET balancebalance%s WHERE username%s,(100,lisi))# with游標結束不會自動commit需要手動提交conn.commit()print(提交成功)exceptExceptionase:conn.rollback()print(f回滾{e})finally:conn.close()??重要提醒with cursor()只是管理游標不會自動commit/rollback事務的提交回滾仍然由connection控制。示例3嵌套事務MySQL沒有真正嵌套事務MySQL InnoDB 不支持真正嵌套事務可以使用保存點 savepoint實現局部回滾。defsavepoint_demo():connpymysql.connect(host127.0.0.1,userroot,passwordxxx,databasetest_db,autocommitFalse)curconn.cursor()try:cur.execute(UPDATE account SET balancebalance-50 WHERE usernamezhangsan)# 設置保存點cur.execute(SAVEPOINT sp1)cur.execute(UPDATE account SET balancebalance50 WHERE usernamelisi)# 回滾到保存點sp1只撤銷后面的操作前面的保留cur.execute(ROLLBACK TO SAVEPOINT sp1)conn.commit()exceptExceptionase:conn.rollback()print(e)finally:cur.close()conn.close()常見踩坑清單autocommit忘記關閉autocommitTrue時每一條SQL直接生效commit/rollback完全無效事務失效。事務中途更換connection對象同一個事務的多條SQL必須在同一個連接換連接等于新開另一個事務。查詢不會加鎖update/delete才會產生事務變更普通select不會修改數據SELECT ... FOR UPDATE會開啟行鎖用于并發扣庫存場景。異常捕獲后忘記rollback如果發生異常不執行rollback未提交的事務會一直掛起占用數據庫鎖資源產生鎖等待。MyISAM引擎使用事務MyISAM不支持事務commit/rollback調用無效果建表必須指定ENGINEInnoDB。長事務事務不要長時間不commit/rollback長事務會大量占用回滾段、鎖資源嚴重影響數據庫性能。結合SELECT FOR UPDATE并發扣庫存場景悲觀鎖示例防止并發超賣defdeduct_stock():connpymysql.connect(host127.0.0.1,userroot,passwordxxx,databasetest_db,autocommitFalse)curconn.cursor()try:# for update 行鎖其他事務會阻塞此處cur.execute(SELECT balance FROM account WHERE usernamezhangsan FOR UPDATE)rowcur.fetchone()ifrow[0]100:cur.execute(UPDATE account SET balancebalance-100 WHERE usernamezhangsan)conn.commit()exceptExceptionase:conn.rollback()print(e)finally:cur.close()conn.close()最佳實踐總結業務涉及多寫操作必須使用事務設置autocommitFalse。使用try?except?finally異常分支必須執行rollback()最后關閉連接。事務粒度盡量小避免長事務執行完盡快commit或rollback釋放鎖。并發場景需要鎖時合理使用SELECT ... FOR UPDATE悲觀鎖或業務層樂觀鎖。確認表引擎為InnoDB。一個事務全程復用同一個數據庫連接對象。如果你使用 SQLAlchemy ORM框架會封裝事務邏輯但底層依然是MySQL事務機制。