关于数据库高并发插入的版本号问题

199 天前
 avadakur

我的数据库设计的是如下格式 userid | business_type | config_id | total_version

(userid,business_type) 这两个的组合会控制 total_version 版本号字段,每次在对(userid,business_type)更新或者插入时,都会让 total_version 自增然后插入或者更新。 问题: 在高并发插入的情况下,我先获取(userid,business_type)的最大版本号,然后执行插入的这个过程,会导致多个插入记录同时都获取的 totoal_version= 1 然后两个都会执行 version + 1 = 2,导致用户版本号丢失一次更新记录。这个问题要怎么解决呢?

3177 次点击
所在节点    程序员
49 条回复
yjhatfdu2
199 天前
开事务
coderxy
199 天前
用一个自增操作不就完事了?
themostlazyman
199 天前
更新的时候:获取版本号,然后条件加版本号
yjhatfdu2
199 天前
如果不是必须先获取当前 version 做业务操作然后再更新,那么直接 update version set version=version+1 where xxxx 就可以了,如果必须先获取 version 再做一些业务操作再更新,就得开事务了,然后高并发的情况还得考虑事务失败概率高的问题,可以根据你的数据库实际情况考虑使用显式锁来减少事务冲突导致的失败
themostlazyman
199 天前
@themostlazyman 上锁的话,并发少行锁,高的话可以用 reids 上锁。
avadakur
199 天前
@coderxy 数据库里的每个用户和业务类型 都有一份自己的总版本号 并不是这个表的 total_version 是一个自增的
shinelamla
199 天前
想了一下开不开事务都没什么用,除非你开的隔离级别是已提交读。

应该用乐观锁,既然你是”先获取(userid,business_type)的最大版本号“,那你在更新或者插入之前就知道了最大版本号了,在更新操作的时候,update set total_version = total_version + 1 where userid =xxx and business_type=xxx and total_version = 你刚才获取的版本号
avadakur
199 天前
@themostlazyman 更新的时候是没问题的 可以用 CAS 来控制,但是我现在在 Insert 的时候不知道要怎么控制
shinelamla
199 天前
@yjhatfdu2 请问开事务有什么作用吗?多个事务之间,该覆盖还不是一样会覆盖吗?
avadakur
199 天前
@shinelamla 在高并发插入的时候怎么处理比较好呢
avadakur
199 天前
@shinelamla 在高并发下两条 insert 语句同时获得了上一个最新的版本号,这样会导致版本号丢失一次自增
yjhatfdu2
199 天前
@shinelamla 这个是个典型的不可重复读问题,在 RR 的隔离等级下,这种情况不被允许,一般来说,后一个提交的事务会失败,以避免数据不一致
vikaptain
199 天前
我怎么感觉像是个 XY 问题。要不你说说需求,看你这个数据库设计有点别扭
sunjiayao
199 天前
userid business_type total_version 设置唯一索引 用 insert on update
yjhatfdu2
199 天前
如果是 pg 的话,可以考虑用 advisory_lock,读之前针对 user_id 的值加锁,更新完解锁,这样不会对表或者行加高级的锁,避免影响其他业务,也可以避免引入 redis 带来的通讯开销,应该是性能非常高的方案了
securityCoding
199 天前
加分布式锁最安全
yjhatfdu2
199 天前
@shinelamla 你这样的问题也是高并发下,失败概率会很高
shinelamla
199 天前
@avadakur 其实有办法处理的,你这种场景。
1. 考虑你的版本号就不要使用需要自己处理自增的形式,换成毫秒甚至纳秒时间戳,请求必然有个先来后到的
2. 考虑使用事务进行两次插入,先插入一次获取自增 id ,再结合自增 id 更新版本号
3. 最简单,就还是前面几楼提到的:用一个自增操作不就完事了?
justfindu
199 天前
一定要加 1 吗, 每个用户增量唯一的话, 是否可以使用 自增的 id 来作为最后的版本 number
themostlazyman
199 天前
@themostlazyman 细化下,假设是 mysql 的 innodb 引擎。插入场景:1.userid,business_type 建立联合索引,数据库上锁 userid,business_type 锁范围

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

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

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

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

© 2021 V2EX