MariaDB / MySQL 死锁 - 并发插入

问题描述 投票:0回答:1

我尝试同时运行多个 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() 这样的聚合函数吗? 非常感谢有关如何解决该问题的建议:)

mysql mariadb deadlock
1个回答
0
投票

事务 2 由于子查询而持有

tbl_value
上的 S 间隙锁:

 SELECT
            datavalue
        FROM
            tbl_value

事务 1 获取了 auto_increment 锁并到达需要插入此范围的位置。

事务 2 到达插入点,需要事务 1 具有的自动增量锁,然后因此死锁。

为了避免事务获取间隙锁,可以选择 READ COMMITTED 事务隔离模式,而不是默认的(更高)REPEATABLE READ。

© www.soinside.com 2019 - 2024. All rights reserved.