Вот моя таблица:
CREATE TABLE `idx_weight` (
`ID` bigint(20) NOT NULL AUTO_INCREMENT,
`SECURITY_ID` bigint(20) NOT NULL COMMENT,
`CONS_ID` bigint(20) NOT NULL,
`EFF_DATE` date NOT NULL,
`WEIGHT` decimal(9,6) DEFAULT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `BPK_AK` (`SECURITY_ID`,`CONS_ID`,`EFF_DATE`),
KEY `idx_weight_ix` (`SECURITY_ID`,`EFF_DATE`)
) ENGINE=InnoDB AUTO_INCREMENT=75334536 DEFAULT CHARSET=utf8
Для запроса 1:
explain select SECURITY_ID, min(EFF_DATE) as startDate, max(EFF_DATE) as endDate from idx_weight where security_id = 1782
:
+----+-------------+------------+------+----------------------+---------------+---------+-------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+----------------------+---------------+---------+-------+--------+-------------+
| 1 | SIMPLE | idx_weight | ref | BPK_AK,idx_weight_ix | idx_weight_ix | 8 | const | 887856 | Using index |
+----+-------------+------------+------+----------------------+---------------+---------+-------+--------+-------------+
Этот запрос выполняется нормально.
Теперь запрос 2 (единственное, что изменилось, это параметр security_id):
explain select SECURITY_ID, min(EFF_DATE) as startDate, max(EFF_DATE) as endDate from idx_weight where security_id = 26622
:
+----+-------------+------------+------+----------------------+--------+---------+-------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+----------------------+--------+---------+-------+----------+-------------+
| 1 | SIMPLE | idx_weight | ref | BPK_AK,idx_weight_ix | BPK_AK | 8 | const | 10700002 | Using index |
+----+-------------+------------+------+----------------------+--------+---------+-------+----------+-------------+
Обратите внимание, что он получает индекс BPK_AK
и фактическийзапрос выполняется более 1 минуты.
Это неверно.Второй раз занял более 10 секунд.Я предполагаю, что в первый раз индекс не находится в пуле буферов.
Обойти это можно, добавив group by security_id
:
explain select SECURITY_ID, min(EFF_DATE) as startDate, max(EFF_DATE) as endDate from idx_weight where security_id = 26622 group by security_id
:
+----+-------------+------------+-------+----------------------+---------------+---------+------+-------+---------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+-------+----------------------+---------------+---------+------+-------+---------------------------------------+
| 1 | SIMPLE | idx_weight | range | BPK_AK,idx_weight_ix | idx_weight_ix | 8 | NULL | 10314 | Using where; Using index for group-by |
+----+-------------+------------+-------+----------------------+---------------+---------+------+-------+---------------------------------------+
Но я до сих пор не понимаю, почему mysql не выбирает idx_weight_ix
для некоторых security_id
, что является индексом покрытия для этого запроса (и намного дешевле).Есть идеи?
==============================================================================
Обновление: @oysteing Узнал новый трюк, круто!:)
Вот трассировка оптимизатора:
Запрос 1: https://gist.github.com/aping/c4388d49d666c43172a856d77001f4ce
Запрос 2: https://gist.github.com/aping/1af5504b428ca136a8b1c41c40d763e4
И некоторая дополнительная информация, которая может бытьполезно:
От INFORMATION_SCHEMA.STATISTICS
:
+------------+---------------+--------------+-------------+-------------+
| NON_UNIQUE | INDEX_NAME | SEQ_IN_INDEX | COLUMN_NAME | CARDINALITY |
+------------+---------------+--------------+-------------+-------------+
| 0 | BPK_AK | 1 | SECURITY_ID | 74134 |
| 0 | BPK_AK | 2 | CONS_ID | 638381 |
| 0 | BPK_AK | 3 | EFF_DATE | 68945218 |
| 1 | idx_weight_ix | 1 | SECURITY_ID | 61393 |
| 1 | idx_weight_ix | 2 | EFF_DATE | 238564 |
+------------+---------------+--------------+-------------+-------------+
CARDINALITY
для SECURITY_ID
различны, но технически они должны быть абсолютно одинаковыми, я прав?
Из этого: https://dba.stackexchange.com/questions/49656/find-the-size-of-each-index-in-a-mysql-table?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
+---------------+-------------------+
| index_name | indexentry_length |
+---------------+-------------------+
| BPK_AK | 1376940279 |
| idx_weight_ix | 797175951 |
+---------------+-------------------+
Размер индекса составляет около 800 МБ против 1,3 ГБ.
Работает select count(*) from idx_weight where security_id = 1782
возвращает 509994
и select count(*) from idx_weight where security_id = 26622
возвращает 5828054
Затем принудительное использование BPK_AK
для запроса 1:
select SQL_NO_CACHE SECURITY_ID, min(EFF_DATE) as startDate, max(EFF_DATE) as endDate from idx_weight use index (BPK_AK) where security_id = 1782
заняло 0,2 с.
Таким образом, в общем случае 26622
имеет в 10 раз больше строкчем 1782
, но с использованием того же индекса это заняло в 50 раз больше времени.
PS: размер пула буферов составляет 25 ГБ.