1
xuanbg 2020-04-29 10:16:22 +08:00
单机系统能不用锁就不要用锁。分布式系统用的也不是单机的锁,要用分布式锁才有用。
|
2
watzds OP @xuanbg #1 所谓乐观锁方式和下面这种不都会有行锁吗,索引正确情况
UPDATE stock SET amount = amount - $diff WHERE id=$id AND amount>$diff; |
3
watzds OP 所谓乐观锁是这种方式:UPDATE t_yue SET money=38, version=$version_new WHERE uid=$uid AND version=$version_old
|
5
xuanbg 2020-04-29 11:05:26 +08:00
@watzds UPDATE t_yue SET money=38, version=$version_new WHERE uid=$uid AND version=$version_old 这种方案的问题是并发的情况下只有一个线程能成功,其他线程都会失败。
数据库的行锁哪能没有呢,正是因为有行锁,对同一条记录进行更新时才会排队。导致后面相同的 sql 会因为 where 中的 version 值变了导致条件不符而失败。而 UPDATE stock SET amount = amount - $diff WHERE id=$id AND amount>$diff;这种方案就不会受影响。 |
6
optional 2020-04-29 11:10:24 +08:00 via Android
#3 才是乐观锁,你这个不是。
|
8
xmh51 2020-04-29 11:16:20 +08:00 1
第一个是不太友好的点是你拿不到当时的余额,第二个是你需要对两个字段做关联修改的时候就有问题了
|
9
watzds OP @xmh51 #8 假如需要拿到余额,做一下相关操作之后,再扣款,那乐观锁其实是替换了 for update 倒是有意义的
比如要求余额是素数才能扣款,那只能 for update 或者乐观锁了 不过一般余额足够就行,我没想到那样的业务场景 |
10
watzds OP @xuanbg #5 哦,那你是觉得一般应该用 UPDATE stock SET amount = amount - $diff WHERE id=$id AND amount>$diff; 而不是乐观锁是吗?其实我是这么觉得,只是看网上书上都说乐观锁方式比较多
|
11
lhx2008 2020-04-29 11:38:13 +08:00 via Android
MYSQL 配合事务可以基本保证幂等性的,超时没事,事务提交不了。语句执行成功就是成功,而且只能执行成功一次。
|
12
lhx2008 2020-04-29 11:40:36 +08:00 via Android
而且主要问题是多个事务并发的问题,而不是你自己重试的问题。比如说有个用户点了一下+10,马上又点一下+50,同时到数据库,那就可能有一个会执行失败,要不然就有可能最后只加了 50
|
13
watzds OP @lhx2008 #11 这个我有一点疑问,如果是 commit 发往 数据库,数据库收到了也提交了事务,但是应用没收到网络响应,连接断了,不知应用是怎么处理的,是会超时异常,还是重新建立连接后再次向数据库查询事务是否提交?
|
14
sioncheng 2020-04-29 11:43:55 +08:00
乐观锁和幂等性没什么相关性
|
15
watzds OP @lhx2008 #12 这个 sql 并没有并发问题,是能保证正确的,因为 UPDATE 是当前读,会加行数
UPDATE stock SET amount = amount - $diff WHERE id=$id AND amount>$diff; |
16
iffi 2020-04-29 11:51:29 +08:00
用乐观锁是为了提高并发性能,如果高并发场景下,你用悲观锁,系统吞吐量就会下降;当然在高并发场景下使用乐观锁,会有很多失败的请求,看你需求场景是否需要支持重试机制。
|
17
lhx2008 2020-04-29 11:54:22 +08:00
|
18
watzds OP @iffi #16 如果是 for update 这种悲观锁,性能影响应该是挺大的,不过这种扣减方式性能和乐观锁会有差别吗?
UPDATE stock SET amount = amount - $diff WHERE id=$id AND amount>$diff; |
19
Aresxue 2020-04-29 12:00:05 +08:00
乐观锁的本质上是消除锁定, 适用于高并发下 读多(读是无锁)写少 的情况, 用乐观锁就是写也不加锁,然后通过结果去重试, 如果写的请求很多极端点全是写的请求, 那么还不如悲观锁的效率高
|
20
noobsheldon 2020-04-29 13:34:17 +08:00
redis
|
21
bowie 2020-04-29 16:08:11 +08:00
你这种写法都不是什么锁,只是能够保证不会被扣负,所以也不能和 select for update 比较吧,这样写也解决不了并发问题呀
|
22
watzds OP @bowie #21 是的,只保证不会被扣负,不过什么余额、库存业务场景,不被扣负还不够呢?这个经验不多,能举些实际例子吗
只扣个余额, 要是用 select for update 或者乐观锁,是否 overkill |
23
watzds OP @lhx2008 #17
嗯,查了一下 commit 成功,但是返回给客户端失败的情况,应该没有标准处理,不过也极少发生,oracle 倒是有一些机制 Transaction Guard https://dba.stackexchange.com/questions/215579/what-happens-if-the-database-nodes-network-fails-just-after-commit-and-before-r?newreg=e21a89d0f6e3489b85a0a4e99ba08c6b |