简体   繁体   中英

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

BACKGROUND

I am working with a high traffic application which seems is extremely slow when executing the following.

Below is a description of my problem:

I have the following function defined:

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 has more than 30 million entries.

PROBLEMATIC QUERY

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

------- QUESTION -----------

As you can see above, the problem is that the query below takes more than a minute to execute when using the mysql function. I want to understand why this happens (although overall implementation might be bad).

------- DEBUG --------------

I run some EXPLAIN queries to check the possible_keys that mysql uses in order to perform the search. As you can see below the query that uses the function has a NULL value for possible_keys - thus I assume the why the problem exists is probably answered. The questions remaining is how to fix it, and what is the reason.

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 VERSION

+-------------------------+------------------------------+
| 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                        |
+-------------------------+------------------------------+

I hope my question was thorough enough.

I think that

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

is enough. And the function is not needed at all.


If you want to save the function and the logic then use

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

But as the first step - try to define the function as DETERMINISTIC .

I think the problem is that the MySQL engine doesn't realize that getTableXMax() always returns the same value. So rather than calling the function once, and then finding that row in the index to update it, it scans the entire table, calling getTableXMax() for each row, and compares the result with ID to determine if it should update that row.

Declaring the function DETERMINISTIC should probably help this. This tells the optimizer that the function always returns the same value, so it only needs to call it once rather than for every row in the table.

The rewrites in Akina's answers will also work, and you could also use a variable:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM