不懂就问系列, web + 并发 + 锁 问题,具体情况在正文

2019-06-03 10:08:11 +08:00
 chaleaochexist
不知道这样的问题,如何归纳成主题.

数据库写操作.
其中一个字段`version`需要+1 操作.
在高并发情况下,如何确保 version 唯一.

table 大概是这样的

| version | object_name | operation |
| :-----: | :---------: | :-------: |
| 1 | 小明 | 吃饭 |
| 2 | 小明 | 吃饭 |
| 1 | 小红 | 吃饭 |
| 1 | 小红 | 睡觉 |

version 只在 object_name 和 operation 相同的情况下+1.
1. 自增不行.
2. 可以加一个唯一约束.
3. 有没有在后端的代码里面加锁控制? 求最佳实践和通用解决方案.
3258 次点击
所在节点    程序员
30 条回复
NullErro
2019-06-03 11:19:05 +08:00
这种情况一般不都是在代码端处理的吗?最直接暴力的就是在后端更新 version 值的时候加锁
gz911122
2019-06-03 11:21:25 +08:00
行锁就可以

select for update 即可
javlib
2019-06-03 12:05:05 +08:00
我也有同样的问题,我的要求低一些,不要求严格递增,只要能自增就行。

如果用 redis 做分布式锁,感觉太麻烦,而且降低了写入性能,不知道大佬门有没有简单的只用 sql 的方法。

我目前的做法是用乐观锁,给每个集合加了一个单独的行,比如(小明,吃饭,version ),这一行的 version 专门用来做小明吃饭的自增,每次自增用乐观锁,每次插入新的数据,先更新这一行的 version,如果更新成功,就用新的 version 作为插入的 version,如果失败,则退回重试。
w7938940
2019-06-03 12:21:05 +08:00
在这块业务的代码加个锁,保证多个进程不会同时执行这块代码,锁可以用 redis 实现
chmaple
2019-06-03 12:53:58 +08:00
@chaleaochexist
两个都读到 2,然后各自+1,执行 update set ...version = where id= and version=到数据库的时候,只有一条能执行成功,另一条 update 的返回 int 是 0,没有匹配的记录。
数据库 MySQL 默认级别是 repeatableRead。
就是要做好事务回滚的准备。
chmaple
2019-06-03 12:57:31 +08:00
@javlib 用 redis 还快一点,乐观锁重试失败不如竞争 redis 锁的性能吧
其实也要看业务的复杂程度,如果本身失败的可能性不高,冲突的几率很小的话,乐观锁也够了,实现起来还简单
但是如果并发的概率比较大,业务的要求还比较高的话,redis 分布式锁更好些。
leon0903
2019-06-03 14:37:07 +08:00
我有一个功能和你这个差不多, 我就是用的唯一索引, 每次更新前 先查出当前的最大 version,然后加 1,插入到数据库。 我们对并发的要求比较低,插入失败的时候直接返回错误了,可以重新执行一次就可以了。
conn4575
2019-06-03 21:55:11 +08:00
你这种情况要在 mysql 上处理要使用最高级别的串行化事务级别,很容易出现死锁,最好直接用 redis 锁来做
pisc
2019-06-03 22:49:36 +08:00
v2 上对数据库的理解都这么次么。。。一个 select for update 就能解决的事情,扯这么多有的没的真是误导人
wejaylyn
2019-06-06 16:14:27 +08:00
unique index + select for update

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

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

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

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

© 2021 V2EX