![]() |
Задавайте вопросы, мы ответим
Вы не зашли.
Страниц: 1
Доброго времени суток, Коллеги!
Есть таблица, содержащая, допустим, товары, у товара есть цена, имя, категория.
mysql> explain SELECT * FROM `t` WHERE cat = 10 order by price limit 1\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t
partitions: NULL
type: ref
possible_keys: cat_price
key: cat_price
key_len: 5
ref: const
rows: 1000
filtered: 100.00
Extra: Using index condition
1 row in set, 1 warning (0.00 sec)key_len используется не полностью (вроде как), опять же рядов собирается перебрать 1000, а не 1.
mysql> explain SELECT * FROM `t` WHERE `cat` = 10 AND `price` = 20\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t
partitions: NULL
type: ref
possible_keys: cat_price
key: cat_price
key_len: 13
ref: const,const
rows: 1
filtered: 100.00
Extra: Using index condition
1 row in set, 1 warning (0.00 sec)тут-то 13 длина, и rows = 1.
Подскажите, кто в теме по след. пунктам плиз:
1) Почему keylen=5? paulus писал, что это может быть обманом, что на самом деле индекс используется полностью, и нужно
исходить из того, что нету надписи filesort, как-то так?
2) Нужно ли стараться избежать using where? или именно filesort плох? Если шире - каков критерий, что запрос можно еще соптимизировать?
3) Значение rows - на него нужно или не нужно обращать внимание? Analyze table делал.
4) В чем принципиальная разница между Using index и Using index condition? Не очень понял из документации.
5) Может ли служить критерием Handler_read_rnd_next? Если зафлашить статус перед запросом, дальше выполнить запрос, и посмотреть чему Handler_read_rnd_next? и Если видим, что 1, а не 1000 - то типа все в порядке? )
Неактивен

1. keylen иногда врет, видимо в 5.7 это осталось
2,4. using where - после того как хранилище данных прочитало строку и вернуло её серверу идет проверка дополнительных условий (например, индекс только на с1, а условие where c1=.. and c2=..). Бывает, что using where пишет не по делу.
Using index condition - значит доп условия проверяются на уровне хранилища.
https://mariadb.com/kb/en/mariadb/index … -pushdown/ -- хорошие иллюстрации
аналогично using where иногда пишется не по делу
Using index - из другой серии. это значит для выполнения запроса достаточно только данных индекса.
3. имхо нет
5. да
Неактивен
Спасибо, добрый человек :-).
Немного изменив индексы таблицы, наткнулся на следующий непонятный момент:
CREATE TABLE `t` (
`price` bigint(20) unsigned NOT NULL,
`name` varchar(255) NOT NULL,
`cat` int(11) DEFAULT NULL,
KEY `cat_idx` (`cat`),
KEY `price_idx` (`price`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Смотрю explain на тот же запрос с сортировкой:
mysql> explain select * from t where cat = 1 order by price limit 1\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t
partitions: NULL
type: index
possible_keys: cat_idx
key: price_idx
key_len: 8
ref: NULL
rows: 997
filtered: 0.10
Extra: Using where
1 row in set, 1 warning (0.00 sec)Почему выбранный key (price_idx) не указывается в possible keys (cat_idx)? Такое возможно вообще или скорее бага?
Неактивен

Такое бывает. Можно назвать багой, но в MySQL как-то на подобные особенности EXPLAIN смотрят спокойно. Попробуй EXPLAIN EXTENDED или EXPLAIN FORMAT=JSON.
Неактивен
Ну дела...
Explain extended показывает прямо скажем другой результат, нежели explain:
mysql> explain extended select * from t where cat = 1 order by price\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t
partitions: NULL
type: ref
possible_keys: cat_idx
key: cat_idx
key_len: 5
ref: const
rows: 1000
filtered: 100.00
Extra: Using index condition; Using filesort
1 row in set, 2 warnings (0.00 sec)В то же время летит warning
| Warning | 1681 | 'EXTENDED' is deprecated and will be removed in a future rele
ase.
Так кому же верить, explain'у или explain extended'у? Разные ведь планы запросов получаются.
Неактивен

Попробуй
Неактивен
mysql> explain extended select * from t where cat = 1 order by price\G show warnings;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t
partitions: NULL
type: ref
possible_keys: cat_idx
key: cat_idx
key_len: 5
ref: const
rows: 1000
filtered: 100.00
Extra: Using index condition; Using filesort
1 row in set, 2 warnings (0.00 sec)
+---------+------+--------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------+
| Level | Code | Message
|
+---------+------+--------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------+
| Warning | 1681 | 'EXTENDED' is deprecated and will be removed in a future rele
ase.
|
| Note | 1003 | /* select#1 */ select `olegabr`.`t`.`price` AS `price`,`olega
br`.`t`.`name` AS `name`,`olegabr`.`t`.`cat` AS `cat` from `olegabr`.`t` where (
`olegabr`.`t`.`cat` = 1) order by `olegabr`.`t`.`price` |
+---------+------+--------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------+
2 rows in set (0.00 sec)mysql> explain format=json select * from t where cat = 1 order by price\G
*************************** 1. row ***************************
EXPLAIN: {
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "1200.00"
},
"ordering_operation": {
"using_filesort": true,
"table": {
"table_name": "t",
"access_type": "ref",
"possible_keys": [
"cat_idx"
],
"key": "cat_idx",
"used_key_parts": [
"cat"
],
"key_length": "5",
"ref": [
"const"
],
"rows_examined_per_scan": 1000,
"rows_produced_per_join": 1000,
"filtered": "100.00",
"index_condition": "(`olegabr`.`t`.`cat` <=> 1)",
"cost_info": {
"read_cost": "1000.00",
"eval_cost": "200.00",
"prefix_cost": "1200.00",
"data_read_per_join": "765K"
},
"used_columns": [
"price",
"name",
"cat"
]
}
}
}
}Так, что, видимо, используется cat_idx.
Неактивен
Так что же получается, что индекс все же используется не полностью? Судя по выводу explain'a.
Если же полностью используется, то как это понять/определить?
Смущает
"rows_examined_per_scan": 1000,
"rows_produced_per_join": 1000,
ведь по идее должен был моментально найти нужную запись (.
mysql> show create table t;
+-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------+
| Table | Create Table
|
+-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------+
| t | CREATE TABLE `t` (
`price` bigint(20) unsigned NOT NULL,
`name` varchar(255) NOT NULL,
`cat` int(11) DEFAULT NULL,
KEY `cat_idx` (`cat`),
KEY `price_idx` (`price`),
KEY `cat_price` (`cat`,`price`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> explain format=json select * from t use index(cat_price) where cat = 1 order by price limit 1\Gshow warnings;
*************************** 1. row ***************************
EXPLAIN: {
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "1200.00"
},
"ordering_operation": {
"using_filesort": false,
"table": {
"table_name": "t",
"access_type": "ref",
"possible_keys": [
"cat_price"
],
"key": "cat_price",
"used_key_parts": [
"cat"
],
"key_length": "5",
"ref": [
"const"
],
"rows_examined_per_scan": 1000,
"rows_produced_per_join": 1000,
"filtered": "100.00",
"index_condition": "(`olegabr`.`t`.`cat` <=> 1)",
"cost_info": {
"read_cost": "1000.00",
"eval_cost": "200.00",
"prefix_cost": "1200.00",
"data_read_per_join": "765K"
},
"used_columns": [
"price",
"name",
"cat"
]
}
}
}
}
1 row in set, 1 warning (0.00 sec)
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------+
| Level | Code | Message
|
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------+
| Note | 1003 | /* select#1 */ select `olegabr`.`t`.`price` AS `price`,`olegabr`.`t`.`name` AS `name`,`olegabr`.`t`.`cat` AS `cat` from `olegabr`.`t` USE INDEX (`cat_price`) where
(`olegabr`.`t`.`cat` = 1) order by `olegabr`.`t`.`price` limit 1 |
+-------+------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------+
1 row in set (0.00 sec)Неактивен

Используется только часть `cat`, но оптимизатор считает, что условию cat=1 удовлетворяет 1000 строк. EXPLAIN FORMAT=JSON также может быть неточен.
Неактивен
Оптимизатор правильно считает, cat=1 - там правда 1000 таких строк.
А что из этого следует?
Но как-то странно получается, то есть судя по explain субд вроде собирается выполнять запрос одним способом, то есть используя только первую часть индекса (cat) а выполняет другим, судя по Handler_read_next.
Собственно:
1) Используем осознанно только индекс по категории, то есть после спуска по индексу к нужной категории сканируем 1000 строчек с товарами.
mysql> explain format=json select * from t force index(cat_idx) where cat = 1 order by price limit 1;flush status;reset query cache;select * from t force index(cat_idx) where cat =
1 order by price limit 5;show status like 'handler%';
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN
|
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| {
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "1200.00"
},
"ordering_operation": {
"using_filesort": true,
"table": {
"table_name": "t",
"access_type": "ref",
"possible_keys": [
"cat_idx"
],
"key": "cat_idx",
"used_key_parts": [
"cat"
],
"key_length": "5",
"ref": [
"const"
],
"rows_examined_per_scan": 1000,
"rows_produced_per_join": 1000,
"filtered": "100.00",
"index_condition": "(`olegabr`.`t`.`cat` <=> 1)",
"cost_info": {
"read_cost": "1000.00",
"eval_cost": "200.00",
"prefix_cost": "1200.00",
"data_read_per_join": "765K"
},
"used_columns": [
"price",
"name",
"cat"
]
}
}
}
} |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
+-------+------+------+
| price | name | cat |
+-------+------+------+
| 1000 | 2002 | 1 |
| 1001 | 2004 | 1 |
| 1002 | 2006 | 1 |
| 1003 | 2008 | 1 |
| 1004 | 2010 | 1 |
+-------+------+------+
5 rows in set (0.01 sec)
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Handler_commit | 1 |
| Handler_delete | 0 |
| Handler_discover | 0 |
| Handler_external_lock | 2 |
| Handler_mrr_init | 0 |
| Handler_prepare | 0 |
| Handler_read_first | 0 |
| Handler_read_key | 1 |
| Handler_read_last | 0 |
| Handler_read_next | 1000 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 0 |
| Handler_rollback | 0 |
| Handler_savepoint | 0 |
| Handler_savepoint_rollback | 0 |
| Handler_update | 0 |
| Handler_write | 0 |
+----------------------------+-------+
18 rows in set (0.00 sec)Handler_read_next = 1000
то есть как бе все по логике, все объяснимо.
В то же время если заставить его использовать правильный индекс, то вот что выходит
mysql> explain format=json select * from t force index(cat_price) where cat = 1 order by price limit 1;flush status;reset query cache;select * from t force index(cat_price) where cat = 1 order by price limit 5;show status like 'handler%';
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-+
| EXPLAIN
|
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-+
| {
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "1200.00"
},
"ordering_operation": {
"using_filesort": false,
"table": {
"table_name": "t",
"access_type": "ref",
"possible_keys": [
"cat_price"
],
"key": "cat_price",
"used_key_parts": [
"cat"
],
"key_length": "5",
"ref": [
"const"
],
"rows_examined_per_scan": 1000,
"rows_produced_per_join": 1000,
"filtered": "100.00",
"index_condition": "(`olegabr`.`t`.`cat` <=> 1)",
"cost_info": {
"read_cost": "1000.00",
"eval_cost": "200.00",
"prefix_cost": "1200.00",
"data_read_per_join": "765K"
},
"used_columns": [
"price",
"name",
"cat"
]
}
}
}
} |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-+
1 row in set, 1 warning (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
+-------+------+------+
| price | name | cat |
+-------+------+------+
| 1000 | 2002 | 1 |
| 1001 | 2004 | 1 |
| 1002 | 2006 | 1 |
| 1003 | 2008 | 1 |
| 1004 | 2010 | 1 |
+-------+------+------+
5 rows in set (0.00 sec)
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Handler_commit | 1 |
| Handler_delete | 0 |
| Handler_discover | 0 |
| Handler_external_lock | 2 |
| Handler_mrr_init | 0 |
| Handler_prepare | 0 |
| Handler_read_first | 0 |
| Handler_read_key | 1 |
| Handler_read_last | 0 |
| Handler_read_next | 4 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 0 |
| Handler_read_rnd_next | 0 |
| Handler_rollback | 0 |
| Handler_savepoint | 0 |
| Handler_savepoint_rollback | 0 |
| Handler_update | 0 |
| Handler_write | 0 |
+----------------------------+-------+
18 rows in set (0.00 sec)Судя по explain использует только первую часть индекса, с категорией.
В то же время, если исходить из Handler_read_next = 4
явно выбрал нужные товары исходя из индекса по price, иначе как бы он выбрал нужные товары (отсортированные то есть), не перебрав всю тысячу? Плюс filesort во втором случае = false, а в первом-то true.
Итого, получается explain обманывает во всех случаях? ((
А СУБД поступает по своему, и лишь по handler'у мы можем судить о том, что сделано на самом деле?
Вот бяда...
Неактивен

Красивая иллюстрация получилась. Это точно бага эксплейна и ее нужно на bugs.mysql.com
Неактивен
Ясно
, спасибо большое! Отпишу.
Неактивен
Страниц: 1