要针对特定的条件进行加锁时,用什么方式是最佳实践?

2020-06-25 16:44:07 +08:00
 abcbuzhiming
大部分数据冲突,往往是用户之间数据冲突,比如秒杀业务,此时对共享资源上一般的锁,或者强迫用户排队,就可以得到解决。

但是有一类情况是这样的,用户和用户之间并没有数据冲突,但是要防止用户“自己抢自己”,比如每日签到奖励这样的业务,1 个用户只有 1 次机会每天,用户和用户之间并没有争抢,我希望根据用户 id 来区分,如果多个线程进入到签到业务(可能是误按,也可能是恶意)时发现用户 id 是同一个,则只会有一个线程进入签到业务处理代码块,其它线程阻塞;但是同时,访问到这块代码时,不是这个用户 id 的线程不受影响,能继续执行
3085 次点击
所在节点    Java
22 条回复
sujin190
2020-06-25 16:55:47 +08:00
对用户 ID 进行加锁就是了啊,如果复杂业务的还可以以一系列信息生成 hash id 来加锁,除了多线程常规的单进程锁外,也可以用 redis 、zookeeper 之类的外部服务来加锁也很方便的

https://github.com/snower/slock

用 go 实现过一个能用于此种场景的小服务,能实现锁的语义还挺多的,性能也不错
reus
2020-06-25 16:59:26 +08:00
有区别吗?
秒杀是锁商品,签到是锁用户,都是一样的逻辑,可以用一样的方法
abcbuzhiming
2020-06-25 17:47:39 +08:00
@sujin190 我不太清楚 Go 的做法,但是 java 好像没找到这种方式
abcbuzhiming
2020-06-25 17:51:03 +08:00
@reus 有区别的朋友,这个问题我也是思考过后才提的,秒杀场景里,所有用户争抢一把锁,用户和用户之间有数据冲突。签到场景里,你要防止的是用户抢自己的数据,用户和用户之间是不存在数据冲突的。

所以你说签到锁用户,在 Java 里,这到底是怎么个锁法?才能即避免用户自己和自己的数据冲突,但是锁不影响到其它用户
phx13ye
2020-06-25 18:36:29 +08:00
你对用户 id 和日期做幂等啊
EminemW
2020-06-25 19:11:19 +08:00
这跟语言没关系吧。
用 date_userId 作为 key,存到 redis 里当锁,不就能解决了。
skypyb
2020-06-25 19:18:26 +08:00
用中间件(zk 、redis) 就行了。业务线名字+用户 ID 拼出锁的 key 去获取就 OK
yukiloh
2020-06-25 19:26:32 +08:00
原来我遇到的点的快了给 2 次奖励是这么产生的…
我觉得让前段做个判断,点了就 didable 不就好了,锁后台多麻烦
chenqh
2020-06-25 20:08:06 +08:00
感觉直接 redis 锁就可以了吧,不管是锁所有人的,还是锁某个人的
abcbuzhiming
2020-06-25 21:07:58 +08:00
@phx13ye 朋友,幂等指的是结果,而不是实现这个结果的过程,我问的是“要如何实现这个幂等”?
abcbuzhiming
2020-06-25 21:09:00 +08:00
@EminemW
@skypyb
如果要借助第三方,不说 redis,zk,用 mysql 的多列唯一索引也能做。但是,我现在就是要在 JVM 特性层面上找解决方案
phx13ye
2020-06-25 21:12:46 +08:00
@abcbuzhiming 你不借助第三方,你的应用是单机跑的吗?
abcbuzhiming
2020-06-25 21:13:19 +08:00
@yukiloh 前端加重复提交令牌确实可以把用户重复请求挡在前端(正常操作情况下),但是并不代表说后端就不需要防御了,实际上当后端规模拉大一点,某些内部事件激发的处理代码确实可能因为种种原因,被同时调用了多次,此时幂等是必要的。问题在于如何实现这个幂等,我比较贪心,想在 JVM 层面上解决这个问题,同时隔离影响,不影响到其它用户
abcbuzhiming
2020-06-25 21:14:58 +08:00
@phx13ye 做研究嘛,有时候要看看一个语言到底能做到什么程度。否则 mysql 满地走的年代,为啥还有人用 java 写了个个叫 H2 的内存数据库呢?
phx13ye
2020-06-25 21:25:42 +08:00
@abcbuzhiming 。。。。看下 Semaphore 和 guava 的 Ratelimit 吧
siweipancc
2020-06-25 21:28:28 +08:00
:D 不用 redis? 那共享一个 map 实例行不行,setIfAbsent
cheng6563
2020-06-25 21:31:49 +08:00
单进程应用可以用个 Map 存 Lock 对象加锁
Ezez
2020-06-25 23:58:27 +08:00
是不是可以数据库里加个字段表示当天是否签到?
nuk
2020-06-26 00:32:20 +08:00
这个可以不用锁啊,既然只执行一次,弄个 counter,atomic fetchadd 一下,等于 1 就执行,不等于就退出。。
gaius
2020-06-26 00:37:44 +08:00
🐶一个用户一个锁呗

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

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

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

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

© 2021 V2EX