msyql 除了加唯一索引,又其他办法防止同一时刻写入重复数据么?

2019 年 6 月 29 日
 dyllen

情况是这样的,有些 api,第三方系统会推数据过来,有两个不同的 api,这两个 api 都会往一个表里面写同样的数据,两边业务逻辑上都有查重的,现在查历史记录,看见在有同时推数据到这两个接口的请求,导致写入重复的数据,创建时间都一样。除了加唯一索引有没有其他什么办法避免?

10180 次点击
所在节点    MySQL
43 条回复
Navee
2019 年 6 月 29 日
何必为难自己
yiplee
2019 年 6 月 29 日
建一新张表它的主键当唯一索引用。插入的时候开启事务,先插入新表,如果插入成功了,再插入现有的业务表。
qf19910623
2019 年 6 月 29 日
做一个短时间的缓存锁队列
agui2200
2019 年 6 月 29 日
用 for update + 事务,查询共有的全局锁表,做悲观锁
agui2200
2019 年 6 月 29 日
@agui2200 这个方案实施起来比较简单,但是性能差一些
Asice
2019 年 6 月 29 日
@agui2200 #24 已经是高并发才会出现重复了,还悲观锁,要搞死数据库吗
lihongjie0209
2019 年 6 月 29 日
只能用队列了
Kylinsun
2019 年 6 月 29 日
建议增加一个字段,然后需要加唯一键的列加强这个辅助唯一键作为唯一键。
hosaos
2019 年 6 月 29 日
针对你该条数据的业务唯一建做分布式锁,抢锁成功后先查后插入
karllynn
2019 年 6 月 29 日
开分布式锁或者串行化

或者你新建一张表 /加个字段,然后把原来的数据处理一下迁移过来
Cbdy
2019 年 6 月 29 日
把其中一个 API 的请求代理另一个 API
linbiaye
2019 年 6 月 29 日
笨方法:分布式锁,可以基于数据库做,不需要引入其它组件。
begin(read committed 级别即可)
1. 插入锁表
2. 根据待插入数据 count 是否已存在,存在则 rollback
4. 插入数据
5. 删除锁
commit
个人倾向的方法:表新加个唯一 column
passerbytiny
2019 年 6 月 29 日
悲观锁方式:查重的时候直接锁表(因为后面是要新增数据的,所以只能锁表),新增数据或超时后解锁。此方式基本没人用。
变相乐观锁方式:第三方直接推送,若收到“有重复数据”错误再做后续处理;你这边单事务内查重加插入。此方式没啥特殊性,就是注册用户判断重复的逻辑,但是若你这个业务是高并发并且冲突情况占比大,此方式也不是太合适。

如果是高并发场景,并且第三方确实会发送重复数据,建议还是允许重复数据的好。或者,给第三方分配 ID,表中加一列“来源”,这样就不会出现重复数据了。

你的描述少了一个关键场景:第三方查重后如果发现重复了,是怎么处理的。
Takamine
2019 年 6 月 29 日
得看具体业务,乐观一点或者悲观一点。
jaskle
2019 年 6 月 30 日
你无法保证外部数据是否有重复,就算你有队列和分布式锁,但是他就是发了两个一样的,所以加唯一索引是最佳选择,如果旧数据过多可以考虑双联合索引,手动差异化一下。
msg7086
2019 年 7 月 1 日
要避免同时写入那就只能串行化。串行化要么加锁,要么队列。
qsbaq
2019 年 7 月 1 日
唯一索引是最佳选择。
justRua
2019 年 7 月 1 日
这是发生幻读了吧,把数据库隔离级别设置成串行化,也可以用队列在业务层串行化,唯一索引貌似是最方便的。
werty
2019 年 7 月 1 日
写之前全表加锁, 然后再 select 一次, 看看有没有主键重复, 最后 insert;
只需要改改 DAO 层就够了
IamUNICODE
2019 年 7 月 1 日
用 redis 的话,setnx 一下?

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

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

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

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

© 2021 V2EX