并发消费入库,保证数据一致性,一个mysql事务执行数据锁问题探讨

需求,

上游下单到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认为这样会好些,出现死锁概率会小.

猜你喜欢

转载自www.cnblogs.com/reachlins/p/9151278.html