有时候事务就像超级英雄电影里的角色。他们在崩溃、出错或者故障时拯救我们的数据库。如果你在做一件需要多个操作、而且这些操作不能分开的事,事务能保证它们一起完成。咱们来看看支付处理的例子,搞明白它是怎么工作的。
支付处理
想象一个经典场景:你有两个银行账户,我们想把钱从一个账户转到另一个。这可不是“点一下按钮”就完事的操作。得确保我们正确地从一个账户扣钱、再加到另一个账户。任何错误都可能是灾难:要么两个账户都没变,要么余额乱套(比如钱凭空消失或者凭空多出来)。
场景:账户间转账
来看下我们的代码。仔细读,就像是来自遥远星系的信息:
-- 开始事务
BEGIN;
-- 步骤 1. 从发起人账户扣钱
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;
-- 步骤 2. 给收款人账户加钱
UPDATE accounts
SET balance = balance + 100
WHERE account_id = 2;
-- 一切顺利?那就保存更改!
COMMIT;
这里什么最重要?
- 如果在
步骤 1或步骤 2出错(比如SQL写错、余额不够),事务可以用ROLLBACK回滚,数据会回到原样。 COMMIT保证只有所有步骤都成功才会应用更改。
加个余额检查
那如果发起人余额不够转账咋办?咱们加个余额检查,别让他“变成负数”。
-- 开始事务
BEGIN;
-- 获取发起人当前余额
DO $$
DECLARE
current_balance NUMERIC;
BEGIN
SELECT balance INTO current_balance FROM accounts WHERE account_id = 1;
-- 检查钱够不够
IF current_balance >= 100 THEN
-- 钱够,执行转账
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;
UPDATE accounts
SET balance = balance + 100
WHERE account_id = 2;
-- 提交更改
COMMIT;
ELSE
-- 钱不够,回滚
ROLLBACK;
RAISE NOTICE '余额不足,无法转账!';
END IF;
END $$;
这里更有意思了吧?
- 我们用PL/pgSQL块加了
IF条件判断。如果余额小于需要的金额,事务就会被拒绝,啥都不会变。 ROLLBACK会撤销更改(虽然这段代码其实还没改啥,但这样写是个好习惯)。
这节课讲的是事务在真实场景下的用法,所以我特地举了个现实生活的例子。它包含了存储过程,用PL-SQL写的。我觉得你们已经有足够经验能看懂这里是怎么回事。以后我们还会回到PL-SQL的话题,讲更复杂的例子。
在事务里批量更新数据
事务不仅仅用在转账这种事上。比如我们有个网店数据库,每天有几十个订单状态会变,比如从“配送中”变成“已完成”。怎么一次性更新很多记录,而且出错还能回滚?当然要用事务啦。
再来看一个场景:批量更新订单状态。
例子如下:
-- 开始事务
BEGIN;
-- 步骤 1. 更新已过配送日期的订单
UPDATE orders
SET status = '已完成'
WHERE delivery_date < CURRENT_DATE;
-- 步骤 2. 通知更新成功
RAISE NOTICE '所有订单状态已成功更新。';
-- 应用更改
COMMIT;
如果出错了咋办?
总有可能出错。比如你忘了写WHERE条件,结果所有订单状态都变成已完成。为了避免这种事,记得要么提交事务,要么明确回滚。
来看下回滚的场景:
-- 开始事务
BEGIN;
-- 步骤 1. 尝试不加条件更新订单(哎呀,出错了!)
UPDATE orders
SET status = '已完成';
-- 因为出错回滚事务
ROLLBACK;
-- 现在订单都没变
用SAVEPOINT加点“灵活性”
有时候你不想回滚整个事务。如果你的场景有好几个步骤,可能只想撤销其中一部分。这时候SAVEPOINT就派上用场了。
现在我们的场景是:处理多个步骤,可以只回滚其中一个。
比如你处理一个订单有好几步:从仓库扣货、更新订单状态、给客户发通知。如果通知没发成功,你只想撤销这一步,但保留数据库里的更改。
-- 开始事务
BEGIN;
-- 步骤 1. 从仓库扣货
UPDATE products
SET stock = stock - 1
WHERE product_id = 101;
-- 保存回滚点
SAVEPOINT step1;
-- 步骤 2. 更新订单状态
UPDATE orders
SET status = '已发货'
WHERE order_id = 202;
-- 尝试给客户发通知
SAVEPOINT step2;
-- 哎呀,通知出错了!
ROLLBACK TO SAVEPOINT step2;
-- 决定安全地提交事务
COMMIT;
总结
事务不仅仅是技术工具,更是你数据完整性的保障。它们能防止“多米诺骨牌效应”,一出错就全崩。每次你要做多个相关操作时,问问自己:“要是其中一个挂了会咋样?”如果答案是“灾难”,那就该用事务了。 记住:写事务多花几分钟,总比出事后花几个小时恢复数据强。你的用户(还有你的心态)都会感谢你的!
GO TO FULL VERSION