I am trying to understand in which cases will a select query make a gap locking on an indexed column select.
What I read so far:
- Select queries use the repeatable read mode in InnoDB and will not lock any row in any isolation level except serialized.
- If you use select.. for update, or LOCK IN SHARE MODE, than a lock will be performed.
Now I have received a deadlock between these 2 transactions:
UPDATE indicators.filesi fi
JOIN
(
SELECT fo.sha256
FROM indicators.filesoi fo
WHERE fo.lastSeen > '2016-03-16 11:10:22'
) x ON x.sha256=fi.sha256
SET hasAutorunOccurances=1
And:
INSERT IGNORE INTO indicators.fileso (hostId, ...)
VALUES
('8662', ...),
('9143', ...),
on duplicate key update ....
Where the fileso.lastSeen column has a non-clustered index.
In this case, I got a deadlock on the lastseen index. But from what I have read that shouldn't happen when using select without specifying 'for update' or 'lock in shared mode'. So why am I getting a deadlock here?
My question is: Does a subquery select with an update in the outer query act like a 'select from update'?
InnoDB Status
------------------------
LATEST DETECTED DEADLOCK
------------------------
2016-03-16 11:15:24 2580
*** (1) TRANSACTION:
TRANSACTION 2264758566, ACTIVE 1 sec fetching rows
mysql tables in use 3, locked 3
LOCK WAIT 13566 lock struct(s), heap size 1603112, 43921 row lock(s)
MySQL thread id 1319, OS thread handle 0x1c64, query id 260771445 localhost 127.0.0.1 root Sending data
UPDATE indicators.filesi fi
JOIN
(
SELECT fo.sha256
FROM indicators.filesoi foi
JOIN indicators.fileso fo ON foi.uniqueness = fo.uniqueness
WHERE fo.lastSeen > '2016-03-16 11:10:22' AND foi.fileType IN (3, 4, 50, 52, 53, 54, 56, 57, 58, 59, 60,
61,62,63,64,65,66,67)
) x ON x.sha256=fi.sha256
SET hasAutorunOccurances=1
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 859892 page no 1508578 n bits 888 index `lastSeen` of table `indicators`.`fileso` trx id 2264758566 lock mode S waiting
*** (2) TRANSACTION:
TRANSACTION 2264758569, ACTIVE 1 sec updating or deleting
mysql tables in use 1, locked 1
42 lock struct(s), heap size 6544, 72 row lock(s), undo log entries 39
MySQL thread id 1310, OS thread handle 0x2580, query id 260771450 localhost 127.0.0.1 root update
INSERT IGNORE INTO indicators.fileso (hostId, sha256, p_runningUser, NTFSOwner, fullPath,
fileName, s_serviceName, d_driverName, p_commandLineParams,
p_ParentPath ,ar_regPath, ar_regKey, ar_regValue, hostName, extension, p_parentUser,
uniqueness, riskLevel) VALUES ('8662',0xB2816FFDF5D612811E9A6CBDCD16C4271A7C16179E410E7C9F8CDE40BB6CC38B,'NT AUTHORITY\\SYSTEM','NT SERVICE\\TrustedInstaller','c:\\windows\\system32\\conhost.exe','conhost.exe','','','"-6004143636298202223525083761531271068-1940101893-8600483621900200321-29408863','c:\\windows\\system32\\csrss.exe','',
'','','DAVIDMI','exe','NT AUTHORITY\\SYSTEM',0x1fdecd56f60185cf77581b1841d6ffe7ebdd100de05471c55aa01b238d40ac34,'-45'),('9143',0xB2816FFDF5D612811E9A6CBDCD16C4271A7C16179E410E7C9F8CDE40BB6CC38B,'NT AUTHOR
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 859892 page no 1508578 n bits 888 index `lastSeen` of table `indicators`.`fileso` trx id 2264758569 lock_mode X locks rec but not gap
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 859892 page no 1508578 n bits 904 index `lastSeen` of table `indicators`.`fileso` trx id 2264758569 lock_mode X locks gap before rec insert intention waiting
*** WE ROLL BACK TRANSACTION (2)
Table definition
CREATE TABLE `fileso` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`hostId` INT(11) UNSIGNED NOT NULL COMMENT 'code',
`sha256` BINARY(32) NOT NULL COMMENT 'meir',
`fileName` VARCHAR(150) NULL DEFAULT NULL COMMENT 'meir',
`fullPath` VARCHAR(350) NULL DEFAULT NULL COMMENT 'meir',
`datein` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`lastSeen` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`lastRA` TIMESTAMP NULL DEFAULT NULL COMMENT '?',
`riskLevel` SMALLINT(6) NOT NULL DEFAULT '-1' COMMENT '?',
`policiesViolated` VARCHAR(1000) NULL DEFAULT NULL,
`NTFSOwner` VARCHAR(255) NULL DEFAULT NULL COMMENT 'meir',
`NTFSOwnerId` INT(11) NOT NULL DEFAULT '-1' COMMENT 'onIdle -done',
`p_runningUser` VARCHAR(255) NULL DEFAULT NULL COMMENT 'meir',
`p_parentUser` VARCHAR(255) NULL DEFAULT NULL,
`p_commandLineParams` VARCHAR(1000) NULL DEFAULT NULL COMMENT 'meir',
`p_runningUserId` INT(11) NOT NULL DEFAULT '-1' COMMENT 'onIdle -done',
`p_ParentPath` VARCHAR(350) NULL DEFAULT NULL COMMENT 'meir',
`ar_regPath` VARCHAR(350) NULL DEFAULT NULL COMMENT 'meir',
`ar_regKey` VARCHAR(100) NULL DEFAULT NULL COMMENT 'meir',
`ar_regValue` VARCHAR(350) NULL DEFAULT NULL COMMENT 'meir',
`s_serviceName` VARCHAR(100) NULL DEFAULT NULL COMMENT 'meir',
`d_driverName` VARCHAR(100) NULL DEFAULT NULL COMMENT 'meir',
`hostName` CHAR(20) NULL DEFAULT NULL,
`extension` CHAR(5) NOT NULL DEFAULT '',
`uniqueness` BINARY(32) NOT NULL DEFAULT '0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0',
PRIMARY KEY (`id`),
UNIQUE INDEX `uniqueness` (`uniqueness`),
INDEX `p_runningUserId` (`p_runningUserId`),
INDEX `riskLevel` (`riskLevel`),
INDEX `NTFSOwnerId` (`NTFSOwnerId`),
INDEX `sha256` (`sha256`, `extension`),
INDEX `lastSeen` (`lastSeen`),
INDEX `datein` (`datein`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=2328978;
Best Answer
It took me some time to find a plausible reason because your statement
update .... select
acquires a shared lock. I have thought of the following reasons.Then I read the manual:
14.3.4 Consistent Nonlocking Reads
Select...update
happens to take a shared lock on selected the table. I have done the following experiment:Setting the following variables has the effect to print on error log the innodb monitor with extra informations about locks. (I use MySQL 5.7, but you can activate the innodb monitor for previous version in different way).
I created the following tables:
And finally I issued a query similar to yours:
Without commit. If you look at the MySQL error log, you will find something like this:
So
insert ... select
has acquired three locks in particular one shared onfo
table and another shared onfoi
table. Previous table are involved into the select subquery.