我们的文章会在微信公众号IT民工的龙马人生和博客网站( www.htz.pw )同步更新 ,欢迎关注收藏,也欢迎大家转载,但是请在文章开始地方标注文章出处,谢谢!
由于博客中有大量代码,通过页面浏览效果更佳。
本文为个人学习《Expert Oracle Database Architecture Techniques and Solutions for High Performance and Productivity(第四版本》一书过程中的笔记与理解分享,仅用于学习与交流,部分内容参考原书观点并结合>实际经验进行整理。若涉及版权问题,请联系删除或沟通处理。也请大家支持购买原版书籍。
数据库事务:要么全做,要么全不做
什么是原子性?
想象你在网上购物,点击"提交订单"后,系统需要同时完成三件事:
- 从你的账户扣款
- 减少商品库存
- 生成订单记录
这三步必须"要么全部成功,要么全部取消"——这就是数据库的原子性。如果只扣款没生成订单,或者生成订单却没扣款,都会造成严重问题。
三个层次的原子性
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;
需要特别注意的情况
-
DDL语句(创建/修改表):执行前会自动提交当前事务
BEGIN INSERT INTO temp_table VALUES(...); -- 临时数据 CREATE TABLE new_table (...); -- 这里会先自动提交 -- 如果后面出错,前面的INSERT已经提交了! END;
-
异步提交(谨慎使用):
COMMIT WRITE NOWAIT; -- 不等待数据真正写入磁盘
适用场景:
- 股票行情系统(旧数据很快会被覆盖)
- 批量数据处理程序(可以重新运行)
-
PL/SQL中的COMMIT:
CREATE PROCEDURE fast_insert AS BEGIN FOR i IN 1..10000 LOOP INSERT INTO big_table VALUES(...); COMMIT; -- 实际是异步提交 END LOOP; -- 返回前会确保所有数据写入 END;
最佳实践建议
- 让应用程序控制事务,避免在存储过程中直接COMMIT/ROLLBACK
- 不要随意使用WHEN OTHERS捕获所有异常
- 异步提交只适合特定场景,普通应用不要使用
- 注意DDL语句会自动提交的特性
记住这个原则:数据库事务就像量子物理——要么完全成功"坍缩"到新状态,要么完全保持原状,没有中间态!
------------------作者介绍-----------------------
姓名:黄廷忠
现就职:Oracle中国高级服务团队
曾就职:OceanBase、云和恩墨、东方龙马等
电话、微信、QQ:18081072613
个人博客: (http://www.htz.pw)
CSDN地址: (https://blog.csdn.net/wwwhtzpw)
博客园地址: (https://www.cnblogs.com/www-htz-pw)