读书笔记:数据库事务:要么全做,要么全不做

我们的文章会在微信公众号IT民工的龙马人生博客网站( www.htz.pw )同步更新 ,欢迎关注收藏,也欢迎大家转载,但是请在文章开始地方标注文章出处,谢谢!
由于博客中有大量代码,通过页面浏览效果更佳。

本文为个人学习《Expert Oracle Database Architecture Techniques and Solutions for High Performance and Productivity(第四版本》一书过程中的笔记与理解分享,仅用于学习与交流,部分内容参考原书观点并结合>实际经验进行整理。若涉及版权问题,请联系删除或沟通处理。也请大家支持购买原版书籍。

数据库事务:要么全做,要么全不做

什么是原子性?

想象你在网上购物,点击"提交订单"后,系统需要同时完成三件事:

  1. 从你的账户扣款
  2. 减少商品库存
  3. 生成订单记录

这三步必须"要么全部成功,要么全部取消"——这就是数据库的原子性。如果只扣款没生成订单,或者生成订单却没扣款,都会造成严重问题。

三个层次的原子性

1. 单条SQL语句的原子性

即使一条简单INSERT语句也可能触发多个操作。例如:

-- 创建检查约束:只允许插入正数
CREATE TABLE products (
    id INT PRIMARY KEY,
    price INT CHECK (price > 0)  -- 价格必须大于0
);

-- 这个触发器会在每次插入时更新计数
CREATE TRIGGER count_trigger
AFTER INSERT ON products
FOR EACH ROW
BEGIN
    UPDATE stats SET product_count = product_count + 1;
END;

当你执行:

INSERT INTO products VALUES(1, -100);  -- 违反价格约束

虽然触发器被触发,但最终统计表不会被更新,因为整个INSERT语句被完整回滚了。

2. 存储过程的原子性

把多个SQL打包成存储过程时:

CREATE PROCEDURE place_order AS
BEGIN
    UPDATE accounts SET balance = balance - 100 WHERE user_id = 123; -- 扣款
    INSERT INTO orders VALUES(...); -- 生成订单
    -- 这里故意制造一个错误
    INSERT INTO log VALUES('order placed', SYSDATE, 1/0); -- 除零错误!
END;

调用这个存储过程时,要么全部成功,要么全部回滚,就像这些语句被括号包起来一样:

BEGIN
    place_order; -- 整个过程视为一个"语句"
END;

但如果加了异常处理:

BEGIN
    place_order;
EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('出错啦,但继续执行');
END;

这时前面的扣款和订单可能被保留,导致数据不一致!所以不要随意捕获所有异常。

3. 整个事务的原子性

多个SQL语句可以组成一个事务:

BEGIN
    -- 开始事务(Oracle中不需要显式开始)
    UPDATE accounts SET balance = balance - 100 WHERE user_id = 123;
    INSERT INTO orders VALUES(...);
    -- 只有执行到这里没出错才会真正生效
    COMMIT; 
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK; -- 有任何错误就全部撤销
        DBMS_OUTPUT.PUT_LINE('交易失败');
END;

需要特别注意的情况

  1. DDL语句(创建/修改表):执行前会自动提交当前事务

    BEGIN
        INSERT INTO temp_table VALUES(...); -- 临时数据
        CREATE TABLE new_table (...); -- 这里会先自动提交
        -- 如果后面出错,前面的INSERT已经提交了!
    END;
    
  2. 异步提交(谨慎使用)

    COMMIT WRITE NOWAIT; -- 不等待数据真正写入磁盘
    

    适用场景:

    • 股票行情系统(旧数据很快会被覆盖)
    • 批量数据处理程序(可以重新运行)
  3. PL/SQL中的COMMIT

    CREATE PROCEDURE fast_insert AS
    BEGIN
        FOR i IN 1..10000 LOOP
            INSERT INTO big_table VALUES(...);
            COMMIT; -- 实际是异步提交
        END LOOP;
        -- 返回前会确保所有数据写入
    END;
    

最佳实践建议

  1. 让应用程序控制事务,避免在存储过程中直接COMMIT/ROLLBACK
  2. 不要随意使用WHEN OTHERS捕获所有异常
  3. 异步提交只适合特定场景,普通应用不要使用
  4. 注意DDL语句会自动提交的特性

记住这个原则:数据库事务就像量子物理——要么完全成功"坍缩"到新状态,要么完全保持原状,没有中间态!

------------------作者介绍-----------------------
姓名:黄廷忠
现就职:Oracle中国高级服务团队
曾就职:OceanBase、云和恩墨、东方龙马等
电话、微信、QQ:18081072613
个人博客: (http://www.htz.pw)
CSDN地址: (https://blog.csdn.net/wwwhtzpw)
博客园地址: (https://www.cnblogs.com/www-htz-pw)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值