1

mysql 8.0.x

我们坚持混淆 SELECT ... LIMIT 1 FOR UPDATE SKIP LOCKED 的不同行为取决于主索引字段类型。

让我们考虑 2 个类似表的情况。

情况1:

CREATE TABLE `test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

案例2:

CREATE TABLE `test` (
  `id` binary(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

它们仅在字段类型上有所不同 - INT 和 BINARY。

插入 6 个 id 为 1 到 6 的项目。

运行 2 个并发事务。

Transaction 1:

BEGIN;

SELECT id FROM test
LIMIT 1 
FOR UPDATE skip locked;

SELECT SLEEP(10);  #for test

COMMIT;

交易2:

BEGIN;

SELECT id
FROM test
WHERE id = 4
FOR UPDATE;

COMMIT;

如果 id 是一个 INT 字段,则 select ... for update from transaction 2 执行时无需等待第一个事务提交。

如果 id 是二进制文件 select ... for update from transaction 2 在第二个事务提交后执行

第二种情况的 SHOW ENGINE INNODB STATUS 输出的一部分:

---TRANSACTION 38229, ACTIVE 3 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 12, OS thread handle 6068, query id 1532 localhost 127.0.0.1 root Sending data
SELECT id
FROM test
where id = 4
FOR UPDATE
------- TRX HAS BEEN WAITING 3 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 19 page no 4 n bits 80 index PRIMARY of table `test`.`test` trx id 38229 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 11; hex 3100000000000000000000; asc 1          ;;
 1: len 6; hex 000000009530; asc      0;;
 2: len 7; hex 80000000000000; asc        ;;

------------------
---TRANSACTION 38228, ACTIVE 5 sec
2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 8, OS thread handle 12052, query id 1530 localhost 127.0.0.1 root User sleep
SELECT SLEEP(10)
-----------------------------

这是一个很大的惊喜...

谁能解释为什么mysql(innodb)在这些情况下表现如此不同。

4

1 回答 1

0

您正在经历的是(需要做的)自动转换的效果,请参阅表达式评估中的类型转换

对于二进制列,您要求 MySQL 将整数4与表中的二进制(字符串)值进行比较,当您这样做时,MySQL 会尽其所能并尝试将列中的这些字符串解释为数字。

MySQL 将评估为等于整数的值的示例例如是4字符串'4'、、、甚至。'04''000000004'' 004''04abc'

MySQL 不能对其使用索引查找,但必须检查(并锁定)所有行,因为它们可能是一个可以计算为整数 4 的字符串。其中一个行是您在第一个事务中锁定的行,并且因此它必须等到锁被释放。

另一方面,如果你的列是整数,MySQL可以使用索引直接跳转到带有 的行id = 4,除非它是在你的第一个事务中锁定的行,否则第二个事务可以继续。

因此,为了使您的二进制大小写与整数大小写相同,请将二进制列与二进制值进行比较,例如使用WHERE id = '4'. 然后不必进行铸造。

于 2021-03-21T00:15:46.503 回答