先写点伪代码:
select * from user where email = [email protected] and sex = 1;
if ( rs = null ){ insert ………… }
这种写法在单机单线程的情况不会出问题,可生产是两台机器。
有一次刚好碰到了两台机器同是 select,结果为空,就同时插入了,
导致后来出现了两条记录。
问一下各位 V 友,这种情况要怎样解决呢?
1
nybux 2019-01-31 13:01:49 +08:00
1.select for update
2.用唯一 3.为啥邮箱和性别要唯一? |
2
id4alex 2019-01-31 13:03:24 +08:00
上事务
|
3
milesnihao 2019-01-31 13:05:24 +08:00
数据库事务和锁?
|
4
GDC 2019-01-31 13:06:02 +08:00 via iPhone 1
加唯一键
|
5
itskingname 2019-01-31 13:06:43 +08:00
上分布式锁。
|
8
liprais 2019-01-31 13:25:14 +08:00
你的隔离级别设的是啥
|
12
ixiaozhi 2019-01-31 13:30:42 +08:00
1. 加个 requestid 从前端到后端做锁
2. 几个唯一条件联合做锁,如 redis |
14
id4alex 2019-01-31 13:41:45 +08:00
@wmhack
上事务 加上 insert tablename select 'col1' as col1, 'col2' as col2 from 伪表 where not exist select 1 from tablename where your condition |
15
GoLand 2019-01-31 13:44:16 +08:00
这加个 unique key 就能解决的吧。
|
16
id4alex 2019-01-31 13:44:59 +08:00
前面也不用判断 RS == null 了, 直接执行这个 SQL 就可以了
|
17
gejun123456 2019-01-31 13:46:57 +08:00 via iPhone
加了唯一索引 就不会两条都插入了
|
18
haoz1w0w 2019-01-31 14:16:53 +08:00
唯一索引
|
19
ThirdFlame 2019-01-31 14:30:32 +08:00
唯一所以 可以避免两次插入,第一次插入成功了,第二次就出错了。 也就无需 select 了,直接干
|
20
wmhack OP @ThirdFlame 是的,这是第二重限制。但报错的方式有时也不太好,如果没有更好的方案,那就只能靠唯一索引去控制了
|
21
wmhack OP @id4alex 我们是 oracle 数据库,类似这种 insert 时 where 的语句当时试过,mybatis 会报错,所以当时就放弃了
|
22
xomix 2019-01-31 16:05:18 +08:00 1
有很多解决方案,根据你实际项目资源和项目阶段选择:
1、上唯一索引,只要有 1 条完成插入后第二条再次插入就会报错。 2、利用 rides、zookeeper 等第三方分布式 k-v 存储制造分布式锁,利用分布式锁完成插入。 3、将所有写操作集中到同一进程,入列后等待完成。 最简单最便宜的是 1,但是这样你要拦截错误后自行处理,代码逻辑复杂度升高。 接下来是 3,但是如果采用方案 3 当你的写入队列进程挂掉或阻塞的时候会引起服务不可用。 2 是时下主流解决方案,但是投入资本最高,开发难度也较高。 |
23
ixiaozhi 2019-01-31 16:32:19 +08:00
想到个奇怪的思路,大佬们评估下可行性
1. 正常插入,插 2 条就 2 条吧 2. 查询的时候,控制以 id 小的为准 order by id limit 1 3. (可选)定期洗理重复且 id 大的数据 |
24
leon0903 2019-01-31 16:53:35 +08:00
设置好唯一索引,然后插入语句改为 insert into on duplicate update,这样即使数据重复插入也只是更新而已。
|
25
kkkkkrua 2019-01-31 17:08:50 +08:00
不想在数据库做处理就用分布式锁,或者吧这个 insert 动作丢给队列。一个一个处理
|
26
dilu 2019-01-31 17:52:27 +08:00 1
1. 插入前锁全表 开发成本最低,但是数据库开销最大并且有可能会影响业务 如果业务无所谓这种方法最合适
2. 设置一个唯一索引 开发成本低但是数据库开销大,如果本来就有一个字段需要加索引这种方法最合适 3. 用 redis 等缓存做分布式锁,拿到锁的进程 insert 别的进程返回。开发成本适中,反正分布式锁你后你也会用的。 4. 消息队列,直接把 insert 操作扔进去 消费者来做就行了。但是开发成本很高了。 5. 分布式事务 这种成本最高 了解一下就 OK |
27
bk201 2019-01-31 18:26:55 +08:00
幻读,事务调到 SERIALIZABLE 级别
|
28
petelin 2019-01-31 20:08:26 +08:00 via iPhone
都跟你说了 select for update。不存在会锁住的
|
29
mmdsun 2019-01-31 20:16:08 +08:00 via Android
select lock in share mode 就行了吧。
|
32
wmhack OP 哪位大神如果有更好的方案,欢迎提出来额。大伙表示感谢啦
|
33
kaid97 2019-01-31 22:13:07 +08:00
在同个事务下,select for update 会用 next-key lock 锁住索引不让插入
|
34
zhenjiachen 2019-01-31 22:32:42 +08:00 via iPhone
重复提交问题,把请求的参数 md5 后存到 redis,然后设置一个超时时间,第二次来了从 redis 中先判断是否有现在的 md5 值有就判断为重复提交,没有就通过
|
35
Leigg 2019-01-31 23:49:11 +08:00 via iPhone
思路都差不多,在客户端和数据库中间加一层中间件做控制。锁的实现最好避免在数据库端实现,利用 redis/MQ 实现,实现高效的锁。
|
36
akira 2019-02-02 01:33:04 +08:00
INSERT IGNORE . 直接忽略后面的插入操作。 但是这种操作不是很严谨,个人还是比较倾向于使用队列
|