想象一下你在咖啡馆打工,一个服务员在厨房查蛋糕库存,另一个在帮新客户下蛋糕订单。在理想情况下,他们都应该看到一样的蛋糕数量,这样就不会出错,比如“重复预订”。但现实中并发操作可能会带来各种问题。
下面是三种常见的糟心情况:
Dirty Read(脏读): 一个查询能看到另一个还没提交的更改。如果这些更改被回滚了,第一个查询就像刚面试的大学生一样天真。
Non-Repeatable Read(不可重复读): 一个查询两次读取同一数据,但中间有人把它改了。这就像你去火车站看时刻表,过一分钟回来发现车次被取消了。或者你找钱的时候票被别人买走了 :)
Phantom Read(幻读): 一个查询看到一部分行,但两次执行之间有人插入了新行,结果就变了。这就像你公司投标输了,结果除了你和市长老婆的标书,其他全被取消了。
事务隔离级别
现在我们知道了这些问题,是时候看看PostgreSQL给我们解决方案了——事务隔离级别。这就像给并发事务之间定规则。隔离级别越高,事务之间互不干扰的保证就越强。但代价就是“服务速度”会变慢,也就是性能会下降。
PostgreSQL里的隔离级别
Read Uncommitted(读取未提交数据):
- 允许读取还没提交的更改(对,就是脏读的典型代表)。
- 在PostgreSQL里其实是按
Read Committed实现的,纯正的Read Uncommitted并不支持。PostgreSQL拒绝实现它,因为太不靠谱了。
Read Committed(读取已提交更改):
- 防止脏读。
- 事务只能看到执行命令时已经提交的数据。
- 但还是可能出现
Non-Repeatable Read和Phantom Read。
Repeatable Read(可重复读):
- 保证你读到的数据在事务期间不会变。
- 防止脏读和不可重复读。
- 但幻读还是有可能。
Serializable(可串行化):
- 保证所有事务像是一个接一个顺序执行的。
- 防止所有三种问题:脏读、不可重复读和幻读。
- 最严格——也是最慢——的隔离级别。
为什么事务隔离很重要?
想象一下一个电商数据库,上千用户同时下单。如果隔离级别没选好,可能会遇到一堆冲突:比如商品“消失”了,或者订单重复。
选对隔离级别能帮你搞定并发事务,平衡性能和数据完整性。比如:
- 分析系统一般选最低的隔离级别(比如Read Committed),因为数据精度不是那么关键。
- 金融系统更喜欢Serializable级别,避免计算出错或操作重复。
隔离级别的使用例子
- Read Committed
这个级别保证你不会读到可能被回滚的数据。
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
-- 读取账户数据。
SELECT balance FROM accounts WHERE account_id = 1;
-- 如果有别的事务更新了余额,这里的数据会立刻刷新。
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
- Repeatable Read
这个级别保证你读到的数据在整个事务期间都不会变。
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
-- 读取账户数据。
SELECT balance FROM accounts WHERE account_id = 1;
-- 就算有别的事务更新了余额,你也看不到变化。
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
- Serializable
在这个级别下,事务就像系统里只有你一个人一样。
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
-- 读取账户数据。
SELECT balance FROM accounts WHERE account_id = 1;
-- 任何别的事务想改这些数据,都得等你事务结束。
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
怎么选隔离级别?
选隔离级别要看你的需求:
- 如果速度比精度重要,而且你能接受幻读——选
Read Committed。 - 如果精度重要,但还想要高性能——用
Repeatable Read。 - 如果你要100%保证数据正确,哪怕慢点——那就
Serializable。
注意!高隔离级别可能导致锁和性能下降。合理的做法是性能和数据一致性之间找个平衡点。
GO TO FULL VERSION