关于 PGSQL 的事物。

2016-01-02 02:34:41 +08:00
 cevincheung

PGSQL 的事物描述貌似是这样的:

Balance: 100

并发事物 A : update table set balance = balance -1 where id = 1 (Balance:99)
并发事物 B : select balance from table where id = 1 (Balance: 100)

这是不是不用事物更好…… - -#

2918 次点击
所在节点    PostgreSQL
14 条回复
lsylsy2
2016-01-02 05:04:16 +08:00
没看懂……首先是事务
然后“事务”的意义是
并发事物 A :
update table set balance = 101 where id = 1
select balance from table where id = 1

并发事物 B :
update table set balance = 99 where id = 1
select balance from table where id = 1

两个 select 的结果都等于自己那个事务 set 的那个值,不会出现“执行了 A 的 update ”-“执行了 B 的 update ”-“执行了 A 的 select ,得出 balance=99 的结果”这样的问题(这个例子好像不太体现事务的特征但是应该没有错误……大半夜的想不起来了)
从这样的角度来讲,你的那个 B 读出 100 并没有问题,因为 A 和 B 是同时执行的,先执行完了 B (读出 100 )后执行 A (减一)没有任何问题。
gamexg
2016-01-02 08:28:57 +08:00
手机不方便
pg 文档,有各种事务级别详细的说明。包括优势和缺陷。
tabris17
2016-01-02 11:17:08 +08:00
这就是事务隔离啊。大大的好处。怎么得出不用事务更好的结论的
cevincheung
2016-01-02 13:01:46 +08:00
@tabris17
假设用户同时在进行购物和提现的操作。分别是两个事物,购物的事物帐户余额扣除了 N 元,提现要要提 X 元,结果前面的事物还没提交,后端 select 到的账户余额是购物扣除前的余额?
lsylsy2
2016-01-02 16:45:59 +08:00
@cevincheung 就是这样,然后提现成功,购物扣款失败,很符合逻辑啊
cevincheung
2016-01-02 20:06:35 +08:00
@lsylsy2

假设。

事物 A 、 B 一个购买,一个提现。都是减钱的操作。

账户余额剩余 100 元。
A:消耗 1 元
B:消耗 100 元

A/B: BEGIN

A:select balance from user where id = 1
B:同 A

此时两个事物获取的 balance 都是 100 。代码逻辑验证通过
A:update user set balance = 99 where id = 1
A:insert into orders 完成支付并订单入库
B:update user set balance = 0 where id = 1

A/B : commit

期间也没有什么异常,是不是?
cevincheung
2016-01-02 20:13:14 +08:00
@lsylsy2
更关键的是, PGSQL 的事物是这样的:

A:begin
A:update user set balance = 0
B:begin
A:commit
B:select balance from user where id = 1 这个时候获取的 balance 不是 0 ?
lsylsy2
2016-01-02 20:22:00 +08:00
@cevincheung 我不太熟 PG ……用其它 SQL 比如 mysql 不会这样么?
首先是你第一个 at 我的内容:事务隔离如果设置成序列化或者可重复读的话,你的这两个 commit 都只会有一个成功,另一个会失败。
第二个的话,会是两种情况:
1 、读 0 , B commit 成功;
2 、读不是 0 ( Acommit 之前的值),然后 Bcommit 失败。
cevincheung
2016-01-02 20:56:52 +08:00
@lsylsy2
每次 begin 会分配一个事物 ID ,每个事物中的数据都是隔离的。

从该 SELECT 查询开始执行时,在此查询执行期间,任何其它并发事物针对该查询结果集的数据操作都将不会被本次查询读到,即本次查询获取的数据版本是与查询开始执行时的数据版本相一致。

可串行:
从该 SELECT 查询所在事物开始时,在此查询执行期间,任何其它并发事物针对该查询结果集的数据操作都将不会被本次查询读到,即本次查询获取的数据版本是与查询所在事物开始时的数据版本相一致。
ch3nz
2016-01-02 22:30:02 +08:00
A,B 如果是两个的事务,那么是不会出问题的,因为锁表了,只有一个事务在跑.
A,B 如果是两个加减钱的操作但是你放在一个事务里面并且不多加验证,跟拿刀捅自己并且问为什么流血了一样.

BEGIN
update user set balance = balance -1 where id = 1
insert into orders
SAVEPOINT save_order
update user set balance = balance - 100 where id = 1
//检查发现 balance 负数了
ROLLBACK to save_order
COMMIT
cevincheung
2016-01-02 23:34:16 +08:00
@ch3nz
可是 pgsql 的事物好像是 如果开启了事物 1 , select 一条数据,事物 2 中如果 update ,事物 1 中再 select ,获取到的仍旧是以前(或当前事物的最新版本)的数据。
gamexg
2016-01-02 23:40:48 +08:00
这就是正确的行为啊
如果不这样事务 2 回滚了怎么办?

看 pg 文档吧, 4 中事务级别很详细的介绍。
gamexg
2016-01-02 23:47:23 +08:00
tabris17
2016-01-04 09:55:34 +08:00
@cevincheung 当然了,有什么问题,事务没提交就是没操作成功

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/247740

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX