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;
背景
我正在使用一个高流量应用程序,它在执行以下命令时似乎非常慢。
下面是我的问题的描述:
我定义了以下函数:
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;