Python实现MySQL错误回滚机制的实战代码

Written by

in

文章目录
  • 本文演示了一个数据库事务处理的典型案例:转账业务,可以通过Python和PyMySQL实现一个包含异常回滚机制的转账场景,当系统在扣款后、加款前发生故障时,能够自动回滚到事务开始前的状态,确保资金不会丢失 代码重点展示了事务控制的关键步骤:关闭自动提交、执行SQL操作、异常处理中的回滚机制,以及使用InnoDB引擎的必要性,通过模拟系统崩溃的场景,验证了事务的ACID特性,最终确保Alice和Bob的账户余额保持正确一致, 这个例子模拟了一个经典的“转账”场景:A 给 B 转钱,如果在扣款后、收款前系统发生错误(比如断电、代码异常),必须让数据回到转账前的状态,保证钱不凭空消失。
  • 你需要安装 pymysql 库: pip install pymysql
  • import pymysql import sys # 数据库配置(请根据你的实际情况修改) DB_CONFIG = { ‘host’: ‘localhost’, ‘user’: ‘root’, ‘password’: ‘your_password’, ‘database’: ‘test_db’, ‘charset’: ‘utf8mb4’ } def setup_database(cursor): “””初始化测试表和数据””” try: cursor.execute(“DROP TABLE IF EXISTS accounts”) cursor.execute(“”” CREATE TABLE accounts ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50), balance DECIMAL(10, 2) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 “””) # 插入初始数据:Alice有1000元,Bob有500元 cursor.execute(“INSERT INTO accounts (name, balance) VALUES (‘Alice’, 1000.00)”) cursor.execute(“INSERT INTO accounts (name, balance) VALUES (‘Bob’, 500.00)”) print(“✅ 数据库初始化完成:Alice=1000, Bob=500″) except Exception as e: print(f”❌ 初始化失败: {e}”) def transfer_money_with_rollback(from_user, to_user, amount): “”” 模拟转账业务,并在发生错误时回滚 “”” connection = None try: # 1. 建立连接 connection = pymysql.connect(**DB_CONFIG) # 2. 关键步骤:关闭自动提交,开启事务 connection.autocommit(False) with connection.cursor() as cursor: # — 步骤一:扣款 — print(f”n💰 正在从 {from_user} 扣除 {amount} 元…”) sql_deduct = “UPDATE accounts SET balance = balance – %s WHERE name = %s” cursor.execute(sql_deduct, (amount, from_user)) # 模拟查询扣款后的余额(仅为了演示,实际业务中可能不需要) cursor.execute(“SELECT balance FROM accounts WHERE name = %s”, (from_user,)) result = cursor.fetchone() print(f” 👉 扣款后查询 {from_user} 余额: {result[0]} (此时数据在内存/Redo Log中,未永久落盘)”) # — 步骤二:模拟突发异常 — # 比如:此时服务器断电、网络中断、或者代码逻辑错误 print(“⚠️ 模拟系统崩溃:准备加款时发生除零错误!”) error_simulation = 1 / 0 # 故意制造一个异常 # — 步骤三:加款(正常情况下会执行,但上面报错了就不会走到这) — sql_add = “UPDATE accounts SET balance = balance + %s WHERE name = %s” cursor.execute(sql_add, (amount, to_user)) # 3. 如果一切顺利,提交事务 connection.commit() print(“✅ 转账成功,事务已提交!”) except Exception as e: print(f”n❌ 发生严重错误: {e}”) if connection: # 4. 核心:发生任何异常,回滚所有操作 print(“🔄 正在执行回滚操作 (ROLLBACK)…”) connection.rollback() print(“🛡️ 回滚成功!数据已恢复到事务开始前的状态。”) finally: if connection: # 5. 恢复自动提交模式并关闭连接 connection.autocommit(True) connection.close() def check_final_balance(): “””检查最终结果””” conn = pymysql.connect(**DB_CONFIG) with conn.cursor() as cursor: cursor.execute(“SELECT name, balance FROM accounts”) results = cursor.fetchall() print(“n—– 最终账户余额 —–“) for row in results: print(f”用户: {row[0]}, 余额: {row[1]}”) print(“————————“) # 验证结果 alice_balance = results[0][1] if results[0][0] == ‘Alice’ else results[1][1] bob_balance = results[0][1] if results[0][0] == ‘Bob’ else results[1][1] assert alice_balance == 1000, f”Alice余额错误!期望1000,实际{alice_balance}” assert bob_balance == 500, f”Bob余额错误!期望500,实际{bob_balance}” print(“🎉 验证通过:数据一致,回滚生效!”) conn.close() if __name__ == “__main__”: # 初始化 conn_init = pymysql.connect(**DB_CONFIG) with conn_init.cursor() as cur: setup_database(cur) conn_init.commit() conn_init.close() # 执行带回滚的转账 transfer_money_with_rollback(‘Alice’, ‘Bob’, 200) # 检查最终数据是否正确回滚 check_final_balance()
  • connection.autocommit(False):这是事务的开关。默认情况下 MySQL 是自动提交的(每句 SQL 都是一个事务),关掉它才能把多步操作打包成一个整体。 try…except… 结构:业务逻辑必须放在 try 里。 connection.rollback():这是“后悔药”。一旦进入 except 块,调用此方法会撤销从 autocommit(False) 之后的所有未提交更改。 connection.commit():这是“确认键”。只有执行了这个,数据才真正写入磁盘(配合 Redo Log 和 Binlog)。 Engine=InnoDB:注意建表时指定了引擎为 InnoDB。如果是 MyISAM 引擎,它不支持事务,rollback 会失效!这是面试常考点。 运行这个脚本,你会看到虽然执行了扣款 SQL,但因为中间报错触发了回滚,最后 Alice 的钱还是 1000,Bob 还是 500,完美保证了数据的一致性。 到此这篇关于Python实现MySQL错误回滚机制的实战代码的文章就介绍到这了,更多相关Python MySQL错误回滚内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: python3往mysql插入二进制图片出现1064错误 python使用pymysql操作MySQL错误代码1054和1064处理方式 Python django使用多进程连接mysql错误的解决方法 实现MySQL回滚的Python脚本的编写教程
  • 目录
    • 前言
    • 环境准备
    • 代码实现
    • 代码讲解重点

    本文演示了一个数据库事务处理的典型案例:转账业务,可以通过Python和PyMySQL实现一个包含异常回滚机制的转账场景,当系统在扣款后、加款前发生故障时,能够自动回滚到事务开始前的状态,确保资金不会丢失

    代码重点展示了事务控制的关键步骤:关闭自动提交、执行SQL操作、异常处理中的回滚机制,以及使用InnoDB引擎的必要性,通过模拟系统崩溃的场景,验证了事务的ACID特性,最终确保Alice和Bob的账户余额保持正确一致,

    这个例子模拟了一个经典的“转账”场景:A 给 B 转钱,如果在扣款后、收款前系统发生错误(比如断电、代码异常),必须让数据回到转账前的状态,保证钱不凭空消失。

    你需要安装 pymysql 库:

    pip install pymysql
    

    import pymysql
    import sys
    
    # 数据库配置(请根据你的实际情况修改)
    DB_CONFIG = {
        'host': 'localhost',
        'user': 'root',
        'password': 'your_password',
        'database': 'test_db',
        'charset': 'utf8mb4'
    }
    
    def setup_database(cursor):
        """初始化测试表和数据"""
        try:
            cursor.execute("DROP TABLE IF EXISTS accounts")
            cursor.execute("""
                CREATE TABLE accounts (
                    id INT AUTO_INCREMENT PRIMARY KEY,
                    name VARCHAR(50),
                    balance DECIMAL(10, 2)
                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
            """)
            # 插入初始数据:Alice有1000元,Bob有500元
            cursor.execute("INSERT INTO accounts (name, balance) VALUES ('Alice', 1000.00)")
            cursor.execute("INSERT INTO accounts (name, balance) VALUES ('Bob', 500.00)")
            print("✅ 数据库初始化完成:Alice=1000, Bob=500")
        except Exception as e:
            print(f"❌ 初始化失败: {e}")
    
    def transfer_money_with_rollback(from_user, to_user, amount):
        """
        模拟转账业务,并在发生错误时回滚
        """
        connection = None
        try:
            # 1. 建立连接
            connection = pymysql.connect(**DB_CONFIG)
            
            # 2. 关键步骤:关闭自动提交,开启事务
            connection.autocommit(False)
            
            with connection.cursor() as cursor:
                # --- 步骤一:扣款 ---
                print(f"n💰 正在从 {from_user} 扣除 {amount} 元...")
                sql_deduct = "UPDATE accounts SET balance = balance - %s WHERE name = %s"
                cursor.execute(sql_deduct, (amount, from_user))
                
                # 模拟查询扣款后的余额(仅为了演示,实际业务中可能不需要)
                cursor.execute("SELECT balance FROM accounts WHERE name = %s", (from_user,))
                result = cursor.fetchone()
                print(f"   👉 扣款后查询 {from_user} 余额: {result[0]} (此时数据在内存/Redo Log中,未永久落盘)")
    
                # --- 步骤二:模拟突发异常 ---
                # 比如:此时服务器断电、网络中断、或者代码逻辑错误
                print("⚠️  模拟系统崩溃:准备加款时发生除零错误!")
                error_simulation = 1 / 0  # 故意制造一个异常
                
                # --- 步骤三:加款(正常情况下会执行,但上面报错了就不会走到这) ---
                sql_add = "UPDATE accounts SET balance = balance + %s WHERE name = %s"
                cursor.execute(sql_add, (amount, to_user))
                
                # 3. 如果一切顺利,提交事务
                connection.commit()
                print("✅ 转账成功,事务已提交!")
    
        except Exception as e:
            print(f"n❌ 发生严重错误: {e}")
            if connection:
                # 4. 核心:发生任何异常,回滚所有操作
                print("🔄 正在执行回滚操作 (ROLLBACK)...")
                connection.rollback()
                print("🛡️ 回滚成功!数据已恢复到事务开始前的状态。")
                
        finally:
            if connection:
                # 5. 恢复自动提交模式并关闭连接
                connection.autocommit(True)
                connection.close()
    
    def check_final_balance():
        """检查最终结果"""
        conn = pymysql.connect(**DB_CONFIG)
        with conn.cursor() as cursor:
            cursor.execute("SELECT name, balance FROM accounts")
            results = cursor.fetchall()
            print("n----- 最终账户余额 -----")
            for row in results:
                print(f"用户: {row[0]}, 余额: {row[1]}")
            print("------------------------")
            # 验证结果
            alice_balance = results[0][1] if results[0][0] == 'Alice' else results[1][1]
            bob_balance = results[0][1] if results[0][0] == 'Bob' else results[1][1]
            assert alice_balance == 1000, f"Alice余额错误!期望1000,实际{alice_balance}"
            assert bob_balance == 500, f"Bob余额错误!期望500,实际{bob_balance}"
            print("🎉 验证通过:数据一致,回滚生效!")
        conn.close()
    
    if __name__ == "__main__":
        # 初始化
        conn_init = pymysql.connect(**DB_CONFIG)
        with conn_init.cursor() as cur:
            setup_database(cur)
            conn_init.commit()
        conn_init.close()
    
        # 执行带回滚的转账
        transfer_money_with_rollback('Alice', 'Bob', 200)
    
        # 检查最终数据是否正确回滚
        check_final_balance()
    

    • connection.autocommit(False):这是事务的开关。默认情况下 MySQL 是自动提交的(每句 SQL 都是一个事务),关掉它才能把多步操作打包成一个整体。
    • try...except... 结构:业务逻辑必须放在 try 里。
    • connection.rollback():这是“后悔药”。一旦进入 except 块,调用此方法会撤销从 autocommit(False) 之后的所有未提交更改。
    • connection.commit():这是“确认键”。只有执行了这个,数据才真正写入磁盘(配合 Redo Log 和 Binlog)。
    • Engine=InnoDB:注意建表时指定了引擎为 InnoDB。如果是 MyISAM 引擎,它不支持事务,rollback 会失效!这是面试常考点。

    运行这个脚本,你会看到虽然执行了扣款 SQL,但因为中间报错触发了回滚,最后 Alice 的钱还是 1000,Bob 还是 500,完美保证了数据的一致性。

    到此这篇关于Python实现MySQL错误回滚机制的实战代码的文章就介绍到这了,更多相关Python MySQL错误回滚内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

    您可能感兴趣的文章:

    • python3往mysql插入二进制图片出现1064错误
    • python使用pymysql操作MySQL错误代码1054和1064处理方式
    • Python django使用多进程连接mysql错误的解决方法
    • 实现MySQL回滚的Python脚本的编写教程

    站内搜索