Postgresql select 语句 share VS update 用过吗?

2020-09-10 16:13:04 浏览数 (2)

for

Postgresql select 语句不够用吗, select for share ,select for udpate 这样的语句我从来没有用过,是不是多余。

回答是:NO

曾经还听到一个声音,select = select for share 没差的,到底是不是这样

首先要确认一点的是,无论是 share 还是update 都是因为在业务中的逻辑造成你要访问的数据需要保护,所以如果你还没有用过,可能你的业务比较简单,或者你的业务并不简单,只是你没有考虑某些问题,而从未使用过他们。

假设我们下面的图中的一个业务,下面是一个拍卖二手车的场景,在各地的拍卖都在买同一个二手车,人们在竞价,而这时有时间限制的,所以每个人读到的最近一次的购买人出的价格,并在这个价格上,提价。而这里面就会牵扯一个问题,如果我要在价格上更改,那这时的价格被我独占,否则我无法在得到一个价格后,更改的时候保证这个价格不变,然后在这个价格上我在加钱。

上面的业务场景,就设计到下面的这段截图的操作

1 首先他必须是一个事务

2 在我查询的时候我必须不能让人在更改价格

3 我要迅速的更新这个价格

4 然后释放,让别人继续出价

想想如果没有for update 这个语句,那在程序上要花费多大的力气来满足这个“事务”。

for update 将这一行数据上了锁,一个别人都不能对这行数据更改的锁,事情就到此为止了,NO NO NO

如果这时候多个人都要加 for update 锁会怎么样,那一定只能有一个人,加上这个锁。

session 1

session 2

从上图我们可以很清晰的看出,session2 无法将select for update 语句执行,一直在等待,他等待session 1 释放那个锁。session 2 只能等到 session 1 commit 后才能获得那个锁,进行更改,而那时数据一个改变,他读取的数据必然是session 1 已经更改的数据,周而复始。

我们可以把上面的过程叫做,安全更新。

那这for update 产生了什么锁我们的看一下

可以很清晰的看到 for update 加了 exclusivelock 排他锁

那问题来了,如果很多人都在竞争这行数据,那应用程序那边怎么办,都在等待,如果这边设计有问题,并没有超时设计,那其他的应用不就都卡死了。

那我们就的拿出for update NOWAIT 这条语句

session 1

session 2

从上图可以很清楚的看到,使用了for update nowait 则马上因为已经对那行数据上锁了,所以其他session 在去锁,就会报错,并提示,无法获得lock。

巧妙的使用这两句话,可以减少很多在应用程序中需要考虑的事情,简简单单就将事情解决了。

那么问题还没有完,如果两个事物锁定的记录部分重合,部分不重合,那我应该怎么办,业务逻辑中,例如抢注商标,在你确认的一刻,你有两个选择,你选择的这一批商标里面,有一部分已经被抢注了,一部分有,你此时是要不都注册,要不就都不注册,实际上此时你希望还有第三种选择,就是那个商标没有注册,你就马上注册上,其他就随它去。

那这时事情怎么办?shiyong skip locked 就可以满足上面的需求。

所以一个 for update 的用法和对应的业务逻辑都不同,适当的选择不同的方法可以事半功倍。

那说了这么半天,for share 到底又有几个意思。

相对于 for update for share 用法比较单纯,一句话,有我你别改,但都可以读。

最后一定会有一个问题,for share 和 for update 之间的最大的区别在哪里,下边这张图给出的答案。

附:上面使用的查询锁的view 代码

CREATE VIEW lockview AS

SELECT pid, virtualtransaction AS vxid, locktype AS lock_type,

mode AS lock_mode, granted,

CASE

WHEN virtualxid IS NOT NULL AND transactionid IS NOT NULL

THEN virtualxid || ' ' || transactionid

WHEN virtualxid::text IS NOT NULL

THEN virtualxid

ELSE transactionid::text END AS xid_lock, relname,

page, tuple, classid, objid, objsubid

FROM pg_locks LEFT OUTER JOIN pg_class ON (pg_locks.relation = pg_class.oid)

WHERE -- do not show our view’s locks

pid != pg_backend_pid();

0 人点赞