MySQL锁定具有不同条件的多个删除查询的相同数据

时间:2018-07-31 06:13:20

标签: mysql

Mysql版本: 5.7.14-log

我有16行的下表。

CREATE TABLE aggr (
 a_date DATE,
 product_id INT(11),
 data_point VARCHAR(16),
 los INT(11),
 hour_0 DOUBLE(4,2),
 UNIQUE KEY `unique_row` (a_date,product_id,data_point,los),
 INDEX product_id(product_id)
);


INSERT INTO aggr(a_date,product_id,data_point,los,hour_0) 
VALUES
('2018-07-29',1,'arrivals',1,10),('2018-07-29',1,'departure',1,9),
('2018-07-29',1,'solds',1,12),('2018-07-29',1,'revenue',1,45.20),
('2018-07-30',1,'arrivals',2,10),('2018-07-30',1,'departure',2,9),
('2018-07-30',1,'solds',2,12),('2018-07-30',1,'revenue',2,45.20),

('2018-07-29',2,'arrivals',1,10),('2018-07-29',2,'departure',1,9),
('2018-07-29',2,'solds',1,12),('2018-07-29',2,'revenue',1,45.20),
('2018-07-30',2,'arrivals',2,10),('2018-07-30',2,'departure',2,9),
('2018-07-30',2,'solds',2,12),('2018-07-30',2,'revenue',2,45.20);

在我的应用程序中,两个线程尝试执行删除查询,但是卡住了。因此,我尝试如下在Mysql中重现相同的内容。

如何复制

启动2个不同的mysql会话(我正在使用SQLYoug)

尝试在第一个会话中进行以下查询

START TRANSACTION;
DELETE FROM aggr
WHERE a_date BETWEEN '2018-07-29' AND '2018-07-29' 
AND product_id = 1 ;

尝试在第二次会话中进行以下查询。

START TRANSACTION;
DELETE FROM aggr
WHERE a_date BETWEEN '2018-07-29' AND '2018-07-29' 
AND product_id = 2 ;

现在执行以下查询

SELECT * FROM `information_schema`.`INNODB_LOCKS`;

所以上面的查询表明两个不同的事务正在运行并且使用相同的事务 lock_mode,lock_space,lock_page和lock_data

检查以下屏幕截图。 enter image description here

有问题

为什么两个不同的事务将相同的数据锁定在我使用具有不同product_id的不同删除查询时?

谢谢

1 个答案:

答案 0 :(得分:1)

MySQL尝试查找要删除的行时会锁定它查看的行。如果可以,MySQL将为此使用索引。

很遗憾,您的样本有些误导。 MySQL本身就太聪明了,因此当它意识到必须无论如何都要读取大部分表时,它将执行此操作并读取整个表,而无需使用二级索引,因此可以使用主键进行锁定(由于您没有,所以在您的情况下,它是GEN_CLUST_INDEX中列出的内部lock_index)。实际上,这只会锁定所有行。

使用explain delete FROM aggr WHERE a_date BETWEEN ...会告诉您将使用哪个索引(在key列中)。为其他日期添加一些行(直到explain不再显示键的null为止),并且您的示例应与该 exact 查询一起使用,因为MySQL将开始使用使用您的unique_row索引查找(单个)a_dateproduct_id而又不与其他查询重叠(但不包含日期范围)。

您的原始表可能已经有更多的行,并且您可能不会调查是否有更多的行可以解决您的问题,因此您可能正在使用其他查询。最有可能是不同的日期范围(嗯,是实际范围而不是单个日期)。如果您这样做,查询将与w.r.t.重叠。日期,因为最有可能使用的索引(unique_row)将从日期开始锁定整个日期范围(如果跨越一天以上)。 MySQL 可以使用product_id上的索引,但可能不会(否则您将不会遇到该问题)。

因此,在您的情况下,添加索引(product_id,a_date)(或以这些列开头的主键)应使查询仅锁定给定的product_id

稍微简化一下(可以找到有关索引工作方式的更多详细信息,例如in the documentation),在您的情况下,MySQL将锁定product_id / start_dateproduct_id / end_date。要知道“之间”的含义,索引中的列顺序是相关的。在由product_id首先排列的列表中,然后由a_date排列,该范围内没有其他product_id(因为product_id = 2将在每个{{1}之后排列) }以及每个可能的日期)。在product_id = 1然后a_date排在第一位的列表中,product_id / date = 2018-07-30将位于product_id = 2 / date = 2018-07-30和{{ 1}} / product_id = 1。因此,在第一种情况(索引date = 2018-07-31)中,产品之间没有重叠,而在第二种情况(例如,product_id = 1索引中),如果以下情况,则多个产品将处于锁定范围内您的日期范围跨度超过一个日期。

这是非常特定于您的(假设的)查询(固定的(product_id, a_date)和日期范围),如果您更改条件(例如,使用产品范围)或将其与其他查询结合使用(否则,您可能不会需要事务,否则您可能不在乎其他查询是否需要等待10毫秒)或实际上只有少量的行,您可能需要进行其他调整。