Mysql - InnoDB - 在更新查询中使用函数时可能的键为 NULL

Mysql - InnoDB - Possible Key NULL when using function in an Update Query

背景

我正在使用一个高流量应用程序,它在执行以下命令时似乎非常慢。

下面是我的问题的描述:

我定义了以下函数:

CREATE FUNCTION getTableXMax() RETURNS INT
BEGIN
DECLARE NUM INT DEFAULT 0;
SELECT COALESCE((SELECT MAX(ID) FROM TABLE_X),0) INTO NUM;
RETURN NUM;
END //

TABLE_X 有超过 3000 万个条目。

有问题的查询

mysql> UPDATE TABLE_X SET COST = 0 WHERE ID=49996728;
    -> //
Query OK, 1 rows affected (0.00 sec)
Rows matched: 1  Changed: 0  Warnings: 0
mysql> UPDATE TABLE_X SET COLUMN_X=0 WHERE ID=getTableXMax();
    -> //
Query OK, 1 rows affected (1 min 23.13 sec)
Rows matched: 1  Changed: 0  Warnings: 0

--------问题------------

正如您在上面看到的,问题在于使用 mysql 函数时,下面的查询需要一分多钟的时间来执行。我想了解为什么会发生这种情况(尽管整体实施可能很糟糕)。

--------调试-------------

我运行一些EXPLAIN查询来检查mysql为了执行搜索而使用的possible_keys。正如您在下面看到的,使用该函数的查询对 possible_keys 具有 NULL 值 - 因此我假设问题存在的原因可能已得到解答。剩下的问题就是怎么解决,是什么原因。

mysql> EXPLAIN UPDATE TRANSCRIPTIONS SET COST = 0 WHERE ID=12434;//
+----+-------------+----------------+-------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table          | type  | possible_keys | key     | key_len | ref   | rows | Extra       |
+----+-------------+----------------+-------+---------------+---------+---------+-------+------+-------------+
|  1 | SIMPLE      | TRANSCRIPTIONS | range | PRIMARY       | PRIMARY | 4       | const |    1 | Using where |
+----+-------------+----------------+-------+---------------+---------+---------+-------+------+-------------+
1 row in set (0.00 sec)
mysql> EXPLAIN UPDATE TRANSCRIPTIONS SET COST = 0 WHERE ID=getTableXMax();//
+----+-------------+----------------+-------+---------------+---------+---------+------+----------+-------------+
| id | select_type | table          | type  | possible_keys | key     | key_len | ref  | rows     | Extra       |
+----+-------------+----------------+-------+---------------+---------+---------+------+----------+-------------+
|  1 | SIMPLE      | TRANSCRIPTIONS | index | NULL          | PRIMARY | 4       | NULL | 38608423 | Using where |
+----+-------------+----------------+-------+---------------+---------+---------+------+----------+-------------+

MYSQL 版本

+-------------------------+------------------------------+
| Variable_name           | Value                        |
+-------------------------+------------------------------+
| innodb_version          | 5.6.34                       |
| protocol_version        | 10                           |
| slave_type_conversions  |                              |
| version                 | 5.6.34                       |
| version_comment         | MySQL Community Server (GPL) |
| version_compile_machine | x86_64                       |
| version_compile_os      | Linux                        |
+-------------------------+------------------------------+

我希望我的问题足够彻底。

我认为

UPDATE TABLE_X 
SET COLUMN_X=0
ORDER BY ID DESC 
LIMIT 1

够了。而且根本不需要这个功能。


如果要保存函数和逻辑,请使用

UPDATE TABLE_X, 
       ( SELECT getTableXMax() criteria ) fn
SET COLUMN_X=0 
WHERE ID=criteria;

但作为第一步 - 尝试将函数定义为 DETERMINISTIC

我认为问题在于 MySQL 引擎没有意识到 getTableXMax() 总是 returns 相同的值。因此,它不是调用函数一次,然后在索引中找到该行来更新它,而是扫描整个 table,为每一行调用 getTableXMax(),并将结果与​​ ID 进行比较以确定是否应该更新该行。

声明函数 DETERMINISTIC 应该可以帮助解决这个问题。这告诉优化器该函数总是 returns 相同的值,因此它只需要调用一次而不是对 table.

中的每一行调用它

Akina 的答案中的重写也可以,您也可以使用变量:

SET @maxID = getTableXMAx();
UPDATE TABLE_X
SET COLUMN_X = 0
WHERE ID = @maxID;