1
chaleaochexist OP v2markdown 不支持表格吗...
|
2
Maboroshii 2019-06-03 10:12:42 +08:00
redis ?
|
3
zhengxiaowai 2019-06-03 10:15:39 +08:00
行锁了解一下
|
4
chaleaochexist OP @zhengxiaowai +1 操作是在代码里执行. 行锁如何控制? 没想明白.
|
5
liuzhedash 2019-06-03 10:21:33 +08:00
2、唯一约束不能直接解决问题:当发现重复记录的时候,如何处理?
3、理论上可以,但是假设未来需要多台服务器进行负载均衡,这个方法就失效了。 3L 的行锁应该可以,但是我没有试过。 之前我遇到类似的问题是通过把请求先缓存到 redis 队列中,然后逐个插入数据库解决的。 |
6
NaVient 2019-06-03 10:22:18 +08:00
redis 实现分布式锁不行吗 以{table_name}_{id}的方式做键值
|
7
NewDraw 2019-06-03 10:24:23 +08:00 via Android
没看明白你说的啥意思。
是说,在现有表的基础上再插入一条 小明,吃饭,该记录 version 的值要是 3 嘛? 如果是上面的问题,那么就是问题就可以转化为如何解决幻读,四种事务隔离级别可以了解一下,这种情况只能用表锁了。 |
8
chaleaochexist OP @liuzhedash
谢谢. 2. 唯一约束可以解决问题, 代码里面捕获异常,然后+2,+3... 3. 您说的对.谢谢. 4. 行锁您能从理论上解释一下吗?我没反应过来,虽然我知道行锁是啥.也许对行锁了解不扎实.给点提示? 5. redis 可行. 我在想想. |
9
airfling 2019-06-03 10:25:28 +08:00
高并发应该是读的高并发吧,那就是加个写的锁,当写入完毕释放锁
|
10
guiling 2019-06-03 10:26:30 +08:00
这不是乐观锁么?
update b set version=version+1 where version=? 更新之前先查一下版本,需要其他限制加在 where 后面 |
11
chaleaochexist OP @NewDraw
对两个线程同时对数据库 小明 吃饭 +1 写了两条 version=3 的数据,这是不可以被接受的. |
12
NewDraw 2019-06-03 10:28:50 +08:00 via Android 1
额,是我说的意思啊,两个线程并发写入,就会有幻读的情况,可以让数据库隔离级别设为最高级。
|
13
chaleaochexist OP |
14
chaleaochexist OP @NewDraw 我刚才脑袋抽了.我在想想,好像你说的对.
|
15
mooncakejs 2019-06-03 10:30:36 +08:00 1
太追求数据库层面解决不是一个好的实践,锁缓存是个好主意,#6 楼说的分布式锁。
|
16
liukanshan 2019-06-03 10:35:12 +08:00
每一次更新操作之前 检查下版本号
def fun(){ def version = select version from xxx; //do something update xxx set field=value,version = version + 1 where version = $version; } |
17
night98 2019-06-03 10:38:57 +08:00
新增一个唯一主键,按唯一主键 + version 确定单条记录。
然后执行楼上说的: update b set version=version+1 where version=? 再根据返回的修改条数的数据判断是否修改成功。 或者就是你这种,联合主键 + version 确定单条记录,更新执行完毕后根据返回的更新条数判断是否更新成功。 |
18
fantastM 2019-06-03 10:52:22 +08:00
不知道你的程序逻辑里,具体需要执行哪些 SQL......粗略看下来,似乎是「幻读」的问题。你可以对着 MySQL 的文档看看,如果是「幻读」的话,那可以用 MySQL 的隔离级别来解决。当然也可以用分布式锁解决
参考资料: https://dev.mysql.com/doc/refman/5.7/en/innodb-next-key-locking.html https://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-isolation-levels.html https://www.cnblogs.com/zhoujinyi/p/3437475.html |
19
sleepiforest 2019-06-03 10:54:00 +08:00
需求是插入的时候,object_name 和 operation 相同的情况下,在原有最大的基础上+1,再插一条么……为啥要干这种事情。
mysql 的话直接用单机事务吧…… |
20
csys 2019-06-03 11:19:02 +08:00 via Android 2
低并发非分布式: 内存锁
低并发分布式: 数据库行锁 /乐观锁 /分布式锁 高并发非分布式: eventsourcing(queue+batch commit) 高并发分布式: actor+eventsourcing |
21
NullErro 2019-06-03 11:19:05 +08:00
这种情况一般不都是在代码端处理的吗?最直接暴力的就是在后端更新 version 值的时候加锁
|
22
gz911122 2019-06-03 11:21:25 +08:00 1
行锁就可以
select for update 即可 |
23
javlib 2019-06-03 12:05:05 +08:00
我也有同样的问题,我的要求低一些,不要求严格递增,只要能自增就行。
如果用 redis 做分布式锁,感觉太麻烦,而且降低了写入性能,不知道大佬门有没有简单的只用 sql 的方法。 我目前的做法是用乐观锁,给每个集合加了一个单独的行,比如(小明,吃饭,version ),这一行的 version 专门用来做小明吃饭的自增,每次自增用乐观锁,每次插入新的数据,先更新这一行的 version,如果更新成功,就用新的 version 作为插入的 version,如果失败,则退回重试。 |
24
w7938940 2019-06-03 12:21:05 +08:00
在这块业务的代码加个锁,保证多个进程不会同时执行这块代码,锁可以用 redis 实现
|
25
chmaple 2019-06-03 12:53:58 +08:00
@chaleaochexist
两个都读到 2,然后各自+1,执行 update set ...version = where id= and version=到数据库的时候,只有一条能执行成功,另一条 update 的返回 int 是 0,没有匹配的记录。 数据库 MySQL 默认级别是 repeatableRead。 就是要做好事务回滚的准备。 |
26
chmaple 2019-06-03 12:57:31 +08:00 1
@javlib 用 redis 还快一点,乐观锁重试失败不如竞争 redis 锁的性能吧
其实也要看业务的复杂程度,如果本身失败的可能性不高,冲突的几率很小的话,乐观锁也够了,实现起来还简单 但是如果并发的概率比较大,业务的要求还比较高的话,redis 分布式锁更好些。 |
27
leon0903 2019-06-03 14:37:07 +08:00
我有一个功能和你这个差不多, 我就是用的唯一索引, 每次更新前 先查出当前的最大 version,然后加 1,插入到数据库。 我们对并发的要求比较低,插入失败的时候直接返回错误了,可以重新执行一次就可以了。
|
28
conn4575 2019-06-03 21:55:11 +08:00 via Android
你这种情况要在 mysql 上处理要使用最高级别的串行化事务级别,很容易出现死锁,最好直接用 redis 锁来做
|
29
pisc 2019-06-03 22:49:36 +08:00
v2 上对数据库的理解都这么次么。。。一个 select for update 就能解决的事情,扯这么多有的没的真是误导人
|
30
wejaylyn 2019-06-06 16:14:27 +08:00
unique index + select for update
|