假设拿医院系统的药品部分举例, 药品表结构大概如下:
现在有多个地方可能会去操作药品的数量:
因为在系统里可能某些药的使用频率是非常高的,比如一些生理盐水、葡萄糖之类的。那么就经常会遇到,护士在生成请领单事务还没完成,药房正在发的药中也含有这些药,这样互相等待直接就被 mysql 的死锁检测机制检测到。
想请问大家遇到这种问题一般会采用什么样的方案来解决?
不知大家是否还有其他比较好的解决方案
1
henry19890701 2017-06-21 22:20:50 +08:00 1
保证加锁顺序就可以了,代码应该不怎么需要改
|
2
letitbesqzr OP @henry19890701 那其实也就只有让他同步,单线程的去减存库?
|
3
Mirana 2017-06-21 22:56:27 +08:00 1
加锁失败回滚 然后重试
|
4
wind3110991 2017-06-21 23:10:05 +08:00 1
感觉这个最主要的问题并不是事务或者是锁,而是你的流程问题:
为什么操作失败就要 回滚到 请领单状态? 建议如果并发量大容易锁,流程中可以穿插多个原子事务,失败回滚到一个中间状态,而不是后台一个接口大包大揽。 事务不是万能的,要符合你的当前使用场景才行 |
5
billlee 2017-06-21 23:12:37 +08:00 1
@letitbesqzr #2 这个肯定是要同步的。别说是数据库,就算是内存里的变量,多线程操作也要加锁进临界区啊。
|
6
cjyang1128 2017-06-21 23:18:21 +08:00 1
一般死锁问题都是通过保证加锁顺序实现的。除了楼主提的几个方案之外,可以把某些数据存储在 redis 中,因为 redis 是单线程的,所以不存在竞争问题。因为你本质上是 id=>数量的一个映射,所以也可以考虑一下。
|
7
letitbesqzr OP @wind3110991 就比如发药
1. 将请领单状态设置为已发药 2. 各种计费改状态 3. 减少存库 4. 写存库流水 5. 记录日志 那么某一步失败肯定需要回滚到第一个状态,实际情况一个事务里还会做更多的操作,业务非常复杂 |
8
letitbesqzr OP @wind3110991 意思就是,其实大事务拆成一个个短事务是比较常见的做法?
|
9
3dwelcome 2017-06-21 23:50:29 +08:00 1
如果是我的话,就用单线程队列。拆分事务只能让逻辑变复杂。代码应该多遵循 KISS 原则,能简单处理的问题,别复杂化。
|
10
ebony0319 2017-06-21 23:57:52 +08:00 via Android 1
如果是我我会采取队列方式。谁先就应该给谁,不应该抢资源。
|
11
letitbesqzr OP |
12
ryd994 2017-06-22 01:06:51 +08:00 via Android 1
一种药一个事务
A 药开不出和 B 药没有联系,不需要锁一起 |
13
reus 2017-06-22 08:21:30 +08:00 1
手工上锁 + 事务
|
14
msg7086 2017-06-22 10:01:20 +08:00 1
上锁超时跳过,回头来重试呗。
万一某个药没货了,病人别的药也不让吃了么…… |
15
jianzhiyao020 2017-06-22 15:29:57 +08:00 1
如果,
并发量不是特别大的话, 可以选择序列化事务隔离级别, 绝对不会死锁。 但是伴随来说, 速度会相应降低, 但是应该总比死锁好。 |
16
letitbesqzr OP @jianzhiyao020 试过,但是预算了一下,病人得排队到马路上。
|
17
jianzhiyao020 2017-06-22 15:40:32 +08:00
@letitbesqzr 那就要概念 hack 了,
例如葡萄糖放在一个地方, 大家都去拿,那是否是会增加堵塞的概率, 可以护士那里放一点, 药房那里放一点, 是不是就不会那么堵塞了, 好了, 我说那么多, 其实就是将葡萄糖分开几个记录存取。 |
18
wind3110991 2017-06-22 22:13:37 +08:00
@letitbesqzr 如你说的,回滚没有必要从滚到 1 之前啊
比如你在 5 出错了,滚到 4 不就好了,记录下当前状态,在队列等待就好了啊 |
19
ebony0319 2017-07-06 23:31:56 +08:00 via Android
最近又查了一些资料,好像都不满意,想问问你这个问题你们目前的思路么。
|
20
letitbesqzr OP @ebony0319
1. 减短事务 2. 使用了 mq 队列进行增删存库 目前采用上面两种方案已经能够满足目前的请求量了,下面一个预备的方案 3. 存库的数量都丢到 redis 中 程序只是操作 redis 里面的存库 定期同步到数据库 |