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

248 天前
 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,导致用户版本号丢失一次更新记录。这个问题要怎么解决呢?

3241 次点击
所在节点    程序员
49 条回复
themostlazyman
248 天前
@themostlazyman 2.redis 设置业务键+:userid+:business_type 为键来上锁。先上锁再读版本。
avadakur
248 天前
@justfindu
@shinelamla
每个用户的不同 business_type 都是重新计算版本号的,不用业务有不同版本号,自增操作要通过触发器来更新,触发器现在公司尽量不能使用
markgor
248 天前
`
INSERT INTO test_tbl (userid , business_type , config_id , total_version) VALUES (1,1,1,(SELECT maxVersion FROM (select IFNULL(max(total_version),0)+1 as maxVersion from test_tbl where userid =1 and business_type =1) as b))
`

沒試過高並發下效果,可以自己試試
avadakur
248 天前
@themostlazyman 上锁可以解决所有并发问题,但是现在可能会对性能有影响,我一开始的方案也是上锁
markgor
248 天前
另外換一個角度,為什麼不使用纳秒呢?
比如業務場景下,根據數據產生時間生成纳秒級別記錄,此時沒插入,丟入隊列;
然後根據數據庫承受能力調控出列寫入速度;

查詢的場景根據排序自己加上版本號,通過數據產生時的納秒當作排序;

這樣會不會更好解決?
zhengwenk
248 天前
虚心求教 同一个 userid,business_type 在什么场景下有高并发
shinelamla
248 天前
@markgor 正是我前面说到的第一点,所见略同
markgor
248 天前
@shinelamla 我在用毫秒,就是數據處理不過來,丟隊列裡面,處理完后根據接收時候的毫秒入庫。
業務大概 20QPS 以內,毫秒級別暫時沒出現過重複。
edward1987
248 天前
userid & total_version 设置成组合 uniq key,插入失败就重新插入就行了吧
avadakur
248 天前
@shinelamla
@markgor 这是一个好方法 我会尝试从此方面入手,因为有需求是根据某个版本号,返回全部的 config_id ,在查询时我要先将 userid,business_type 排序,获取对应的入参版本号的记录,获取时间戳,返回该小于该时间戳的所有 config_id
avadakur
248 天前
@zhengwenk 同一个用户账号在多端同时操作这个业务的配置信息
themostlazyman
248 天前
@avadakur 插入时用 redis 上锁对数据库性能没影响。你这个插入时的版本号为啥不是 1 ,不太理解插入为啥最大版本号,不建议把业务表当日志表。
avadakur
248 天前
@themostlazyman 插入的版本号是用户的总版本号,比如用户插入了配置 A ,此时 version=1 ,用户再次插入配置 B ,此时 version=2 ,版本号记录了用户的所有操作记录
yuyuf
248 天前
你这是针对每个用户 id 的共享资源,再高并发,每个用户能同时有多少操作。更新用乐观锁,插入用悲观锁
yuanwenpu00
248 天前
数据库记录锁。应该能解决。代码里的锁不保险。
9fan
248 天前
insert into article_views(url, views)
values (#{url}, #{views})
on duplicate key update views = views + #{views} 类似这种吗
dog82
248 天前
最简单的就是不要先查,而是在改的语句里查一下,不过这依然会出问题……
可以用队列和分布式锁,不过比较麻烦。
-----------
其实楼主的问题根本不是问题,userid,business_type 做联合主键,天然唯一,根本不需要版本号
aminaSucci
248 天前
我昨天也遇到了这个问题,我是把这张表的自增 id 赋值给 total_version ,因为我对 total_version 没有别的要求,只要 userid | business_type 确定时,total_version 递增不重复就行。
AceGo
248 天前
如果不改变表结构,insert 貌似只能加锁
先查询有没有记录,有则使用 version=version+1 更新;否则加锁分布式锁 redis 等,再查询记录,为空则 insert ,最后释放锁。使用行锁间隙锁记录锁等都不能解决多次 insert 的问题。
MoYi123
248 天前
直接用 mysql 的事务 id 怎么样?

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

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

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

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

© 2021 V2EX