
情况是这样的,有些 api,第三方系统会推数据过来,有两个不同的 api,这两个 api 都会往一个表里面写同样的数据,两边业务逻辑上都有查重的,现在查历史记录,看见在有同时推数据到这两个接口的请求,导致写入重复的数据,创建时间都一样。除了加唯一索引有没有其他什么办法避免?
1 jugelizi 2019-06-29 10:29:58 +08:00 队列 加锁 |
2 haiyang416 2019-06-29 10:32:49 +08:00 写操作串行化,扔队列呗。 |
3 srx1982 2019-06-29 10:33:35 +08:00 "两边业务逻辑上都有查重的",都查重了,还能重,那就是查重写得不对 |
4 LeeSeoung 2019-06-29 10:34:00 +08:00 先查后写。。mysql 好像没 merge into |
5 laravel 2019-06-29 10:40:10 +08:00 为啥把 unique 除了? |
6 R18 2019-06-29 10:41:36 +08:00 via Android 查重没有加锁… |
7 qiyuey 2019-06-29 10:49:14 +08:00 你没有开事务吧 |
11 xuanbg 2019-06-29 11:03:06 +08:00 数据库唯一索引外,唯一解决办法,API 上面加分布式锁 |
12 Vegetable 2019-06-29 11:11:11 +08:00 队列 API 将数据塞到队列里,顺序消费掉避免并发写入. 这牺牲了实时性.如果要求实时返回插入结果的话不适用,只能使用事务 |
13 chrisliu1314 2019-06-29 11:15:18 +08:00 via iPhone 1 )加个字段,然后加唯一索引 2 )或者这两个 api 都调用底层的一个服务接口。服务接口里面采用分布式锁+先查后插 不知道是否可行 |
14 zisway 2019-06-29 11:28:20 +08:00 数据库解决,pg 是有语法可以插入时遇到重复直接忽略,但不知道 mysql 有没有,可以查文档看看。业务上解决可以用分布式锁,或者 api 不入库,而是扔到 redis 队列中,由 job 去取出入库。 |
16 zy445566 2019-06-29 11:47:25 +08:00 via Android 如果没插入,开了事务还重复,九成是因为你没有判断每条 sql 是否有影响行数,就一个 try catch 就回滚了。事实上每条 sql 都要判断影响行数,如果影响行数为 0,理论上也是要进行回滚的。 |
18 limuyan44 2019-06-29 12:00:18 +08:00 via Android 唯一索引建不了你怎么判断重复的,按你说如果重复的数据你都分不清哪一条有没有用,那你去重的意义在哪里? |
19 harvies 2019-06-29 12:16:40 +08:00 15 楼正解 |
20 luman 2019-06-29 12:17:56 +08:00 看着是有历史遗留问题,导致存量数据有重复数据。而且不好清洗。如果对数据及时性要求不高,我觉得可以 copy 一张一模一样的表,并添加唯一索引。使用 duplicate key update 插入数据。然后定时 insert select 同步表数据。相对来说插入效率和开发难度比自己加锁要快吧。 |
21 Navee 2019-06-29 12:22:00 +08:00 何必为难自己 |
22 yiplee 2019-06-29 13:06:07 +08:00 建一新张表它的主键当唯一索引用。插入的时候开启事务,先插入新表,如果插入成功了,再插入现有的业务表。 |
23 qf19910623 2019-06-29 13:16:32 +08:00 做一个短时间的缓存锁队列 |
24 agui2200 2019-06-29 14:21:59 +08:00 用 for update + 事务,查询共有的全局锁表,做悲观锁 |
27 lihongjie0209 2019-06-29 14:50:12 +08:00 只能用队列了 |
28 Kylinsun 2019-06-29 15:20:41 +08:00 via iPhone 建议增加一个字段,然后需要加唯一键的列加强这个辅助唯一键作为唯一键。 |
29 hosaos 2019-06-29 15:53:20 +08:00 针对你该条数据的业务唯一建做分布式锁,抢锁成功后先查后插入 |
30 karllynn 2019-06-29 15:57:44 +08:00 开分布式锁或者串行化 或者你新建一张表 /加个字段,然后把原来的数据处理一下迁移过来 |
31 Cbdy 2019-06-29 16:07:34 +08:00 把其中一个 API 的请求代理另一个 API |
32 linbiaye 2019-06-29 16:45:49 +08:00 笨方法:分布式锁,可以基于数据库做,不需要引入其它组件。 begin(read committed 级别即可) 1. 插入锁表 2. 根据待插入数据 count 是否已存在,存在则 rollback 4. 插入数据 5. 删除锁 commit 个人倾向的方法:表新加个唯一 column |
33 passerbytiny 2019-06-29 18:05:34 +08:00 悲观锁方式:查重的时候直接锁表(因为后面是要新增数据的,所以只能锁表),新增数据或超时后解锁。此方式基本没人用。 变相乐观锁方式:第三方直接推送,若收到“有重复数据”错误再做后续处理;你这边单事务内查重加插入。此方式没啥特殊性,就是注册用户判断重复的逻辑,但是若你这个业务是高并发并且冲突情况占比大,此方式也不是太合适。 如果是高并发场景,并且第三方确实会发送重复数据,建议还是允许重复数据的好。或者,给第三方分配 ID,表中加一列“来源”,这样就不会出现重复数据了。 你的描述少了一个关键场景:第三方查重后如果发现重复了,是怎么处理的。 |
34 Takamine 2019-06-29 18:54:36 +08:00 via Android 得看具体业务,乐观一点或者悲观一点。 |
35 jaskle 2019-06-30 07:03:51 +08:00 via Android 你无法保证外部数据是否有重复,就算你有队列和分布式锁,但是他就是发了两个一样的,所以加唯一索引是最佳选择,如果旧数据过多可以考虑双联合索引,手动差异化一下。 |
36 msg7086 2019-07-01 03:35:50 +08:00 要避免同时写入那就只能串行化。串行化要么加锁,要么队列。 |
37 qsbaq 2019-07-01 09:42:38 +08:00 唯一索引是最佳选择。 |
38 justRua 2019-07-01 11:48:15 +08:00 这是发生幻读了吧,把数据库隔离级别设置成串行化,也可以用队列在业务层串行化,唯一索引貌似是最方便的。 |
39 werty 2019-07-01 11:52:47 +08:00 写之前全表加锁, 然后再 select 一次, 看看有没有主键重复, 最后 insert; 只需要改改 DAO 层就够了 |
40 IamUNICODE 2019-07-01 13:04:57 +08:00 用 redis 的话,setnx 一下? |
41 dyllen OP @passerbytiny 其实这个也算不上什么高并发,就是第三方会完全不分青红皂白,同时请求了两个会写相同数据的不同接口。 |
42 dyllen OP @passerbytiny 重复了要把重复的删掉,反馈之后我查了一下,数据不多,有个 20 条左右相同的数据的吧,因为都还在部分测试吧。 |
43 javaWeber 2019-07-01 14:11:34 +08:00 select for update,排他锁。先查再写入。 |