Mysql - InnoDB - возможный ключ NULL при использовании функции в запросе на обновление - PullRequest
0 голосов
/ 14 января 2020

ПРЕДПОСЫЛКИ

Я работаю с приложением с высоким трафиком c, которое кажется очень медленным при выполнении следующего.

Ниже приведено описание моей проблемы:

У меня определена следующая функция:

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 содержит более 30 миллионов записей.

ПРОБЛЕМА C ЗАПРОС

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 использует для выполнения поиска. Как вы можете видеть ниже, запрос, который использует функцию, имеет значение 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 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                        |
+-------------------------+------------------------------+

Я надеюсь, что мой вопрос был достаточно тщательным.

Ответы [ 2 ]

3 голосов
/ 14 января 2020

Я думаю, что

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

достаточно. И функция вообще не нужна.


Если вы хотите сохранить функцию и логи c, используйте

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

Но в качестве первого шага - попробуйте определить функцию как DETERMINISTIC.

2 голосов
/ 14 января 2020

Мне кажется, проблема в том, что движок MySQL не понимает, что getTableXMax() всегда возвращает одно и то же значение. Поэтому вместо того, чтобы вызывать функцию один раз, а затем находить эту строку в индексе для ее обновления, она сканирует всю таблицу, вызывая getTableXMax() для каждой строки, и сравнивает результат с ID, чтобы определить, следует ли обновить эту строку. .

Объявление функции DETERMINISTIC, вероятно, должно помочь этому. Это говорит оптимизатору, что функция всегда возвращает одно и то же значение, поэтому ей нужно вызывать его только один раз, а не для каждой строки в таблице.

Перезаписи в ответах Акины также будут работать, и вы также можете использовать переменная:

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