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
有问题
为什么两个不同的事务将相同的数据锁定在我使用具有不同product_id的不同删除查询时?
谢谢
答案 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_date
和product_id
而又不与其他查询重叠(但不包含日期范围)。
您的原始表可能已经有更多的行,并且您可能不会调查是否有更多的行可以解决您的问题,因此您可能正在使用其他查询。最有可能是不同的日期范围(嗯,是实际范围而不是单个日期)。如果您这样做,查询将与w.r.t.重叠。日期,因为最有可能使用的索引(unique_row
)将从日期开始锁定整个日期范围(如果跨越一天以上)。 MySQL 可以使用product_id
上的索引,但可能不会(否则您将不会遇到该问题)。
因此,在您的情况下,添加索引(product_id,a_date)
(或以这些列开头的主键)应使查询仅锁定给定的product_id
。
稍微简化一下(可以找到有关索引工作方式的更多详细信息,例如in the documentation),在您的情况下,MySQL将锁定product_id
/ start_date
和product_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毫秒)或实际上只有少量的行,您可能需要进行其他调整。