insert 重复插入问题

2019-01-31 12:49:44 +08:00
 wmhack

先写点伪代码:

select * from user where email = 1000@qq.com and sex = 1;

if ( rs = null ){ insert ………… }

这种写法在单机单线程的情况不会出问题,可生产是两台机器。

有一次刚好碰到了两台机器同是 select,结果为空,就同时插入了,

导致后来出现了两条记录。

问一下各位 V 友,这种情况要怎样解决呢?

3385 次点击
所在节点    问与答
36 条回复
wmhack
2019-01-31 14:57:16 +08:00
@id4alex 我们是 oracle 数据库,类似这种 insert 时 where 的语句当时试过,mybatis 会报错,所以当时就放弃了
xomix
2019-01-31 16:05:18 +08:00
有很多解决方案,根据你实际项目资源和项目阶段选择:
1、上唯一索引,只要有 1 条完成插入后第二条再次插入就会报错。
2、利用 rides、zookeeper 等第三方分布式 k-v 存储制造分布式锁,利用分布式锁完成插入。
3、将所有写操作集中到同一进程,入列后等待完成。

最简单最便宜的是 1,但是这样你要拦截错误后自行处理,代码逻辑复杂度升高。
接下来是 3,但是如果采用方案 3 当你的写入队列进程挂掉或阻塞的时候会引起服务不可用。
2 是时下主流解决方案,但是投入资本最高,开发难度也较高。
ixiaozhi
2019-01-31 16:32:19 +08:00
想到个奇怪的思路,大佬们评估下可行性
1. 正常插入,插 2 条就 2 条吧
2. 查询的时候,控制以 id 小的为准 order by id limit 1
3. (可选)定期洗理重复且 id 大的数据
leon0903
2019-01-31 16:53:35 +08:00
设置好唯一索引,然后插入语句改为 insert into on duplicate update,这样即使数据重复插入也只是更新而已。
kkkkkrua
2019-01-31 17:08:50 +08:00
不想在数据库做处理就用分布式锁,或者吧这个 insert 动作丢给队列。一个一个处理
dilu
2019-01-31 17:52:27 +08:00
1. 插入前锁全表 开发成本最低,但是数据库开销最大并且有可能会影响业务 如果业务无所谓这种方法最合适
2. 设置一个唯一索引 开发成本低但是数据库开销大,如果本来就有一个字段需要加索引这种方法最合适
3. 用 redis 等缓存做分布式锁,拿到锁的进程 insert 别的进程返回。开发成本适中,反正分布式锁你后你也会用的。
4. 消息队列,直接把 insert 操作扔进去 消费者来做就行了。但是开发成本很高了。
5. 分布式事务 这种成本最高 了解一下就 OK
bk201
2019-01-31 18:26:55 +08:00
幻读,事务调到 SERIALIZABLE 级别
petelin
2019-01-31 20:08:26 +08:00
都跟你说了 select for update。不存在会锁住的
mmdsun
2019-01-31 20:16:08 +08:00
select lock in share mode 就行了吧。
wmhack
2019-01-31 20:42:57 +08:00
@xomix 这些方案感觉可行的,很好的一些建议
wmhack
2019-01-31 20:43:30 +08:00
@dilu 这些思路很好,感谢了🙏
wmhack
2019-01-31 20:45:28 +08:00
哪位大神如果有更好的方案,欢迎提出来额。大伙表示感谢啦
kaid97
2019-01-31 22:13:07 +08:00
在同个事务下,select for update 会用 next-key lock 锁住索引不让插入
zhenjiachen
2019-01-31 22:32:42 +08:00
重复提交问题,把请求的参数 md5 后存到 redis,然后设置一个超时时间,第二次来了从 redis 中先判断是否有现在的 md5 值有就判断为重复提交,没有就通过
Leigg
2019-01-31 23:49:11 +08:00
思路都差不多,在客户端和数据库中间加一层中间件做控制。锁的实现最好避免在数据库端实现,利用 redis/MQ 实现,实现高效的锁。
akira
2019-02-02 01:33:04 +08:00
INSERT IGNORE . 直接忽略后面的插入操作。 但是这种操作不是很严谨,个人还是比较倾向于使用队列

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

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

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

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

© 2021 V2EX