需求,
上游下单到mq,bms消费mq,将订单数据存入数据库中
(上游有可能重复下单.要求最后一条单有效.)
会开启多个消费者同时消费mq,因此可能上游下发了两条重复的订单数据,两个消费者同时各拿到一条,然后入库.
这种情况要保证,最后只有一条数据有效.数据库中订单使用逻辑列is_delete = 0表示有效.
方案一:使用一个分布式锁,这种方式所有的消费者会顺讯执行所有入库操作.
方案二"每个订单使用一个分布式锁,"这种情况下,只有重复单才会阻塞,不同单不会阻塞.但是要创建大量的分布式锁,要求锁的实现能抗并发.
方案三,应用层无锁,使用数据库的事务原理,利用数据库自身的锁,保证.
方案三的语句如下:
set autocommit=0;
begin;
update financial_freight_bill b set b.is_delete = 1 where b.bill_code = '1'; //语句一
insert into financial_freight_bill (bill_code,is_delete,cust_code) VALUES('1',0,"tran2"); //语句二
COMMIT;
ROLLBACK;
在navicat中打开两个窗口测试;
连接1 执行语句一
连接2 执行语句一
连接1 执行语句二 会阻塞
连接2 执行语句二 Deadlock found when trying to get lock;
前两个,不锁的,第三个开始阻塞,第四个死锁,mysql自动杀死锁,这时连接1获得锁无阻塞执行.
b.bill_code = '1' 这个查询时没有数据的.
可见该事务会出现死锁现象.导致其中一个事务的执行失败
与dba讨论后,语句改为
set autocommit=0;
begin;
UPDATE financial_freight_bill b
INNER JOIN (
SELECT
c.id
FROM
financial_freight_bill c
WHERE
c.bill_code = '1'
) cc ON cc.id = b.id
SET b.is_delete = 1;
INSERT INTO financial_freight_bill (
bill_code,
is_delete,
cust_code
)
VALUES
('1', 0, "tran1");
COMMIT;
只改了第一条语句,这样还是会出现死锁,通原来一样的结果,但是dba认为这样会好些,出现死锁概率会小.