我尝试同时运行多个 INSERT SELECT 语句并遇到死锁问题。我想同时插入多个传感器和值类型的计算中值最小值、最大值、平均值。
10.3.39-MariaDB - Ubuntu 20.04
------------------------
LATEST DETECTED DEADLOCK
------------------------
2024-05-06 20:08:00 0x7fa7a95c3700
*** (1) TRANSACTION:
TRANSACTION 1077705, ACTIVE 1 sec inserting
mysql tables in use 48, locked 48
LOCK WAIT 546 lock struct(s), heap size 90232, 104961 row lock(s), undo log entries 1
MySQL thread id 5195, OS thread handle 140358081038080, query id 452803 localhost 127.0.0.1 USERNAME Creating sort index
//INSERT STATEMENT
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 566 page no 849 n bits 1160 index fk_device of table `home_prod`.`tbl_value` trx id 1077705 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000003; asc ;;
1: len 4; hex 80000004; asc ;;
*** (2) TRANSACTION:
TRANSACTION 1077706, ACTIVE 0 sec setting auto-inc lock
mysql tables in use 48, locked 48
543 lock struct(s), heap size 90232, 104959 row lock(s)
MySQL thread id 5197, OS thread handle 140358077658880, query id 452804 localhost 127.0.0.1 USERNAME Creating sort index
//INSERT STATEMENT
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 566 page no 849 n bits 1160 index fk_device of table `home_prod`.`tbl_value` trx id 1077706 lock mode S locks gap before rec
Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000003; asc ;;
1: len 4; hex 80000004; asc ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
TABLE LOCK table `home_prod`.`tbl_value` trx id 1077706 lock mode AUTO-INC waiting
*** WE ROLL BACK TRANSACTION (2)
CREATE TABLE `tbl_value` (
`value_id` int(11) NOT NULL AUTO_INCREMENT,
`device_id` int(11) NOT NULL,
`unit_id` int(11) NOT NULL,
`value_type_id` int(11) NOT NULL,
`comment_id` int(11) NOT NULL,
`datetime_utc` datetime NOT NULL,
`datavalue` double NOT NULL,
PRIMARY KEY (`value_id`),
KEY `fk_unit` (`unit_id`),
KEY `fk_device` (`device_id`),
KEY `fk_value_type` (`value_type_id`),
KEY `fk_comment` (`comment_id`),
CONSTRAINT `fk_comment` FOREIGN KEY (`comment_id`) REFERENCES `tbl_comment` (`comment_id`),
CONSTRAINT `fk_device` FOREIGN KEY (`device_id`) REFERENCES `tbl_device` (`device_id`),
CONSTRAINT `fk_unit` FOREIGN KEY (`unit_id`) REFERENCES `tbl_unit` (`unit_id`),
CONSTRAINT `fk_value_type` FOREIGN KEY (`value_type_id`) REFERENCES `tbl_value_type` (`value_type_id`)
) ENGINE=InnoDB AUTO_INCREMENT=243101 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
CREATE TABLE `tbl_device` (
`device_id` int(11) NOT NULL AUTO_INCREMENT,
`device_type_id` int(11) NOT NULL,
`device_name_id` int(11) NOT NULL,
`location_id` int(11) NOT NULL,
PRIMARY KEY (`device_id`),
KEY `fk_device_type` (`device_type_id`),
KEY `fk_device_name` (`device_name_id`),
KEY `fk_location` (`location_id`),
CONSTRAINT `fk_device_name` FOREIGN KEY (`device_name_id`) REFERENCES `tbl_device_name` (`device_name_id`),
CONSTRAINT `fk_device_type` FOREIGN KEY (`device_type_id`) REFERENCES `tbl_device_type` (`device_type_id`),
CONSTRAINT `fk_location` FOREIGN KEY (`location_id`) REFERENCES `tbl_location` (`location_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
使用不同的unit_name查询1和2:
INSERT INTO tbl_value(
device_id,
unit_id,
value_type_id,
comment_id,
datetime_utc,
datavalue
)
SELECT
(
SELECT
device_id
FROM
tbl_device
INNER JOIN tbl_device_name ON tbl_device.device_name_id = tbl_device_name.device_name_id
INNER JOIN tbl_device_description ON tbl_device_name.device_description_id = tbl_device_description.device_description_id
INNER JOIN tbl_device_type ON tbl_device.device_type_id = tbl_device_type.device_type_id
INNER JOIN tbl_location ON tbl_device.location_id = tbl_location.location_id
INNER JOIN tbl_floor ON tbl_location.floor_id = tbl_floor.floor_id
INNER JOIN tbl_room ON tbl_location.room_id = tbl_room.room_id
INNER JOIN tbl_device_location ON tbl_location.device_location_id = tbl_device_location.device_location_id
INNER JOIN tbl_address ON tbl_location.address_id = tbl_address.address_id
INNER JOIN tbl_country ON tbl_address.country_id = tbl_country.country_id
INNER JOIN tbl_city ON tbl_address.city_id = tbl_city.city_id
INNER JOIN tbl_street ON tbl_address.street_id = tbl_street.street_id
WHERE
device_description = 'xy' AND device_name = 'xy' AND device_type = 'Sensor' AND FLOOR = 'xy' AND room = 'xy' AND device_location = 'xy' AND country_name = 'xy ' AND city_name = 'xy' AND street_name = 'xy'
),
(
SELECT
unit_id
FROM
tbl_unit
WHERE
unit_name = 'Strom'
),
(
SELECT
value_type_id
FROM
tbl_value_type
WHERE
value_type = 'Istwert Median'
),
(
SELECT
comment_id
FROM
tbl_comment
WHERE comment
= 'Istwert Median der letzten Minute'
),
(
SELECT
sub.*
FROM
(
SELECT
datetime_utc
FROM
tbl_value
INNER JOIN tbl_device ON tbl_value.device_id = tbl_device.device_id
INNER JOIN tbl_value_type ON tbl_value.value_type_id = tbl_value_type.value_type_id
INNER JOIN tbl_unit ON tbl_value.unit_id = tbl_unit.unit_id
INNER JOIN tbl_comment ON tbl_value.comment_id = tbl_comment.comment_id
INNER JOIN tbl_device_name ON tbl_device.device_name_id = tbl_device_name.device_name_id
INNER JOIN tbl_device_description ON tbl_device_name.device_description_id = tbl_device_description.device_description_id
INNER JOIN tbl_device_type ON tbl_device.device_type_id = tbl_device_type.device_type_id
INNER JOIN tbl_location ON tbl_device.location_id = tbl_location.location_id
INNER JOIN tbl_floor ON tbl_location.floor_id = tbl_floor.floor_id
INNER JOIN tbl_room ON tbl_location.room_id = tbl_room.room_id
INNER JOIN tbl_device_location ON tbl_location.device_location_id = tbl_device_location.device_location_id
INNER JOIN tbl_address ON tbl_location.address_id = tbl_address.address_id
INNER JOIN tbl_country ON tbl_address.country_id = tbl_country.country_id
INNER JOIN tbl_city ON tbl_address.city_id = tbl_city.city_id
INNER JOIN tbl_street ON tbl_address.street_id = tbl_street.street_id
WHERE
datetime_utc >= DATE_SUB(NOW(), INTERVAL 1 MINUTE) AND country_name = 'xy ' AND city_name = 'xy' AND street_name = 'xy' AND FLOOR = 'xy' AND room = 'xy' AND device_location = 'xy' AND device_name = 'xy' AND device_type = 'Sensor' AND unit_name = 'Strom' AND value_type = 'Istwert'
ORDER BY
datavalue ASC
LIMIT 5) sub
LIMIT 1 OFFSET 2
),
(
SELECT
sub.*
FROM
(
SELECT
datavalue
FROM
tbl_value
INNER JOIN tbl_device ON tbl_value.device_id = tbl_device.device_id
INNER JOIN tbl_value_type ON tbl_value.value_type_id = tbl_value_type.value_type_id
INNER JOIN tbl_unit ON tbl_value.unit_id = tbl_unit.unit_id
INNER JOIN tbl_comment ON tbl_value.comment_id = tbl_comment.comment_id
INNER JOIN tbl_device_name ON tbl_device.device_name_id = tbl_device_name.device_name_id
INNER JOIN tbl_device_description ON tbl_device_name.device_description_id = tbl_device_description.device_description_id
INNER JOIN tbl_device_type ON tbl_device.device_type_id = tbl_device_type.device_type_id
INNER JOIN tbl_location ON tbl_device.location_id = tbl_location.location_id
INNER JOIN tbl_floor ON tbl_location.floor_id = tbl_floor.floor_id
INNER JOIN tbl_room ON tbl_location.room_id = tbl_room.room_id
INNER JOIN tbl_device_location ON tbl_location.device_location_id = tbl_device_location.device_location_id
INNER JOIN tbl_address ON tbl_location.address_id = tbl_address.address_id
INNER JOIN tbl_country ON tbl_address.country_id = tbl_country.country_id
INNER JOIN tbl_city ON tbl_address.city_id = tbl_city.city_id
INNER JOIN tbl_street ON tbl_address.street_id = tbl_street.street_id
WHERE
datetime_utc >= DATE_SUB(NOW(), INTERVAL 1 MINUTE) AND country_name = 'xy ' AND city_name = 'xy' AND street_name = 'xy' AND FLOOR = 'xy' AND room = 'xy' AND device_location = 'xy' AND device_name = 'xy' AND device_type = 'Sensor' AND unit_name = 'Strom' AND value_type = 'Istwert'
ORDER BY
datavalue ASC
LIMIT 5) sub
LIMIT 1 OFFSET 2
);
我可以看到 trx1 中的独占锁和 trx2 中的共享锁。 我研究了读取提交的快照隔离,但这是我的问题的最佳解决方案/可能的解决方案还是我需要一些代码优化?
这可能是因为 ORDER BY 或像 MAX() 这样的聚合函数吗? 非常感谢有关如何解决该问题的建议:)
事务 2 由于子查询而持有
tbl_value
上的 S 间隙锁:
SELECT
datavalue
FROM
tbl_value
事务 1 获取了 auto_increment 锁并到达需要插入此范围的位置。
事务 2 到达插入点,需要事务 1 具有的自动增量锁,然后因此死锁。
为了避免事务获取间隙锁,可以选择 READ COMMITTED 事务隔离模式,而不是默认的(更高)REPEATABLE READ。