05 сентября, 2014

О кавычках в VARCHAR и о том, как не надо делать в целом

На днях звонят мне наши веб-разработчики и говорят, мол MySQL тормозит адово, сил нет никаких терпеть, а при нескольких одновременных запросах и вовсе транзакции валятся по таймауту.

Привычный осмотр processlist'a показал вполне ожидаемую картину. В таблице чуть более 11 миллионов строк, и уже где-то полминуты висит простенький запрос типа UPDATE some_table SET act_status = 1 WHERE grp_id = 10885882802223, останавливаться он при этом не собирается, а в State красуется "Queried about 5120000 rows".

«Опять индекс не поставили!», - подумал Штирлиц и отправил разработчиков расставлять индексы, попросив попутно не отвлекать более подобными пустяками.

Однако, индекс был. Но всё равно фуллскан. Как так? А очень просто, как оказалось в итоге. В вышеуказанном запросе поле grp_id, если положиться на здравый смысл, должно иметь какой-нибудь целочисленный тип. Но здравый смысл - это для кого-то другого. Тот, кто делал эту прекрасную таблицу, решил что лучше будет, если это поле сделать VARCHAR.

Ну а вишенкой на торте стал самописный недо-ORM, который был написан или тем человеком, который сделал ту прекрасную таблицу, или его сообщником (сообщниками?), ну или сразу группой лиц с особой жестокостью.

Этот самый недо-ORM строил запросы следующим образом: если запрашиваемое значение включало только цифры, то кавычек в запросе не было. Ну а если цифры и буквы, то кавычки заботливо подставлялись.

В результате мы имели то, что имели (проблему с фуллсканом и адовые тормоза).

Смотрим внимательно. Что получалось и как быть не должно, если уж мы имеем дело с VARCHAR:


EXPLAIN select * from some_table where grp_id = 10885882802223 LIMIT 1;
+------+-------------+------------+------+---------------+------+---------+------+---------+-------------+
| id   | select_type | table      | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+------+-------------+------------+------+---------------+------+---------+------+---------+-------------+
|    1 | SIMPLE      | some_table | ALL  | grp           | NULL | NULL    | NULL | 7747809 | Using where |
+------+-------------+------------+------+---------------+------+---------+------+---------+-------------+


А вот как надо было:

EXPLAIN select * from some_table where grp_id = '10885882802223' LIMIT 1;
+------+-------------+------------+------+---------------+-------+---------+-------+------+-------------+
| id   | select_type | table      | type | possible_keys | key   | key_len | ref   | rows | Extra       |
+------+-------------+------------+------+---------------+-------+---------+-------+------+-------------+
|    1 | SIMPLE      | some_table | ref  | grp           | grp   | 99      | const |    1 | Using where |
+------+-------------+------------+------+---------------+-------+---------+-------+------+-------------+


Ситуация на самом деле идиотская донельзя и руки бы отрывать за такие изыски. Какие-нибудь еще выводы оставляю право читателю сделать самостоятельно.