SQLinfo.ru - Все о MySQL

Форум пользователей MySQL

Задавайте вопросы, мы ответим

Вы не зашли.

#1 01.03.2011 15:38:58

deadka
Администратор
Зарегистрирован: 14.11.2007
Сообщений: 2421

трудность с Using sort_union()

Приветствую, Коллеги!

Поможите в войне с индексами пожалуйста, кто чем может :-).

Имеется таблица


CREATE TABLE `2011_02_24` (
  `srcaddr` int(4) unsigned NOT NULL default '0',
  `dstaddr` int(4) unsigned NOT NULL default '0',
  `nexthop` int(4) unsigned NOT NULL default '0',
  `input` tinyint(1) unsigned NOT NULL default '0',
  `output` tinyint(1) unsigned NOT NULL default '0',
  `dpkts` int(4) unsigned NOT NULL default '0',
  `doctets` int(4) unsigned NOT NULL default '0',
  `first` datetime NOT NULL default '0000-00-00 00:00:00',
  `last` datetime NOT NULL default '0000-00-00 00:00:00',
  `srcport` smallint(2) unsigned NOT NULL default '0',
  `dstport` smallint(2) unsigned NOT NULL default '0',
  `pad1` tinyint(1) unsigned NOT NULL default '0',
  `tcp_flags` tinyint(1) unsigned NOT NULL default '0',
  `prot` smallint(2) unsigned NOT NULL default '0',
  `tos` tinyint(1) unsigned NOT NULL default '0',
  `src_as` smallint(2) unsigned NOT NULL default '0',
  `dst_as` smallint(2) unsigned NOT NULL default '0',
  `src_mask` tinyint(1) unsigned NOT NULL default '0',
  `dst_mask` tinyint(1) unsigned NOT NULL default '0',
  `pad2` smallint(2) unsigned NOT NULL default '0',
  KEY `srcaddr` (`srcaddr`),
  KEY `dstaddr` (`dstaddr`),
  KEY `first` (`first`),
  KEY `last` (`last`),
  KEY `input` (`input`),
  KEY `output` (`output`),
  KEY `input_2` (`input`,`first`),
  KEY `output_2` (`output`,`first`)
) ENGINE=MyISAM;
 


В таблице ~ 257 миллионов записей.

Мне нужно выбрать данные за определенный временной период (пятиминутный), те, у которых input и/или output равны определенному числу.

В случае, если нужно ориентироваться только на нужный input или output, то все достаточно прозрачно:
SELECT count(*) FROM 2011_02_24 where (input=10  and (first>='2011-02-24 00:00:00' and first<='2011-02-24 00:04:59'));


mysql> explain SELECT count(*) FROM 2011_02_24 where (input=10  and (first>='2011-02-24 00:00:00' and first<='2011-02-24 00:04:59'));
+----+-------------+------------+-------+---------------------+---------+---------+------+-------+--------------------------+
| id | select_type | table      | type  | possible_keys       | key     | key_len | ref  | rows  | Extra                    |
+----+-------------+------------+-------+---------------------+---------+---------+------+-------+--------------------------+
|  1 | SIMPLE      | 2011_02_24 | range | first,input,input_2 | input_2 | 9       | NULL | 41588 | Using where; Using index |
+----+-------------+------------+-------+---------------------+---------+---------+------+-------+--------------------------+
1 row in set (0.00 sec)

Используется ключик (input,first), все в порядке.

А вот если выбирать данные, ориентируясь и на input и на output, то получается вот так.

SELECT count(*) FROM 2011_02_24 where (input=10  and (first>='2011-02-24 00:00:00' and first<='2011-02-24 00:04:59')) or (output=10 and (first>='2011-02-24 00:00:00' and first<='2011-02-24 00:04:59'));


mysql>explain SELECT count(*) FROM 2011_02_24 where (input=10  and (first>='2011-02-24 00:00:00' and first<='2011-02-24 00:04:59')) or (output=10 and (first>='2011-02-24 00:00:00' and first<='2011-02-24 00:04:59'));
+----+-------------+------------+-------+-------------------------------------+-------+---------+------+--------+-------------+
| id | select_type | table      | type  | possible_keys                       | key   | key_len | ref  | rows   | Extra       |
+----+-------------+------------+-------+-------------------------------------+-------+---------+------+--------+-------------+
|  1 | SIMPLE      | 2011_02_24 | range | first,input,output,input_2,output_2 | first | 8       | NULL | 482223 | Using where |
+----+-------------+------------+-------+-------------------------------------+-------+---------+------+--------+-------------+
1 row in set (0.00 sec)

Несмотря на то, что имеются индексы и на (input,first) и на (output,first), оптимизатор предпочитает использовать индекс first.

А если попросить его этого не делать
(
SELECT count(*) FROM 2011_02_24 ignore index(first) where (input=10  and (first>='2011-02-24 00:00:00' and first<='2011-02-24 00:04:59')) or (output=10 and (first>='2011-02-24 00:00:00' and first<='2011-02-24 00:04:59'));

), то:

mysql> explain SELECT count(*) FROM 2011_02_24 ignore index(first) where (input=10  and (first>='2011-02-24 00:00:00' and first<='2011-02-24 00:04:59')) or (output=10 and (first>='2011-02-24 00:00:00' and first<='2011-02-24 00:04:59'));
+----+-------------+------------+-------------+-------------------------------+------------------+---------+------+--------+-------------------------------------------------+
| id | select_type | table      | type        | possible_keys                 | key              | key_len | ref  | rows   | Extra                                           |
+----+-------------+------------+-------------+-------------------------------+------------------+---------+------+--------+-------------------------------------------------+
|  1 | SIMPLE      | 2011_02_24 | index_merge | input,output,input_2,output_2 | input_2,output_2 | 9,9     | NULL | 113720 | Using sort_union(input_2,output_2); Using where |
+----+-------------+------------+-------------+-------------------------------+------------------+---------+------+--------+-------------------------------------------------+
то он использует оба индекса, что (как я понимаю) логичнее.

Подскажите пожалуйста, кто может в данном затруднении - почему оптимизатор выбирает данные по индексу first, хотя по нему данных выбирается в несколько раз больше, чем если по Using sort_union(input_2,output_2);

И стоит ли насильно ему указывать, какие индексы использовать?

Заранее спасибо!

P. S. Скорость выполнения запросов подтверждает мои подозрения, если попросить не использовать index first

mysql>
SELECT SQL_NO_CACHE count(*) FROM 2011_02_24 where (input=10  and (first>='2011-02-24 00:00:00' and first<='2011-02-24 00:04:59')) or (output=10 and (first>='2011-02-24 00:00:00' and first<='2011-02-24 01:04:59'));
                 +----------+
| count(*) |
+----------+
|  1037403 |
+----------+
1 row in set (9.78 sec)

mysql>
SELECT SQL_NO_CACHE count(*) FROM 2011_02_24 ignore index(first) where (input=10  and (first>='2011-02-24 00:00:00' and first<='2011-02-24 00:04:59')) or (output=10 and (first>='2011-02-24 00:00:00' and first<='2011-02-24 01:04:59'));

+----------+
| count(*) |
+----------+
|  1037403 |
+----------+
1 row in set (3.95 sec)

Отредактированно deadka (01.03.2011 16:14:48)


Зеленый свет для слабаков, долги отдают только трусы, тру гики работают только в консоли...

Неактивен

 

#2 01.03.2011 20:05:28

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: трудность с Using sort_union()

Мне очень нравится начало вопроса — «в табличке ~257млн записей» smile

Вопрос — почему оптимизатор делает то или иное — достаточно сложный.
Можешь посмотреть EXPLAIN EXTENDED от этой штуки и SHOW WARNINGS?
Есть ощущение, что оптимизатор делает сначала логическое упрощение
в (first BETWEEN ... ) AND (input = 10 OR output=10), а потом к нему ищет
индексы.

Запросы на OR-логику вообще очень тяжело рассматривать оптимизатору.
Например, если переписать запрос в виде UNION, он уже не ошибется в
выборе индексов. С другой стороны, кажется, план по сортированному объе-
динению индексов будет работать лучше, чем объединять сразу данные и
искать DISTINCT. Тут еще влияет то, что иногда прочитать больше строк с
диска последовательно оказывается более быстрым, чем вытащить строки
по индексу (+fileseek) и потом еще и посортировать в памяти.

Насильно указывать индексы, разумеется, стоит: именно для этого и служат
эти директивы. Человек всегда знает данные лучше, чем машина (точнее —
должен знать wink ), а потому должен корректировать машину, если она ошиба-
ется.

Да, индексы input и output — лишние при наличии input_2 и output_2.

Неактивен

 

#3 10.03.2011 12:08:39

deadka
Администратор
Зарегистрирован: 14.11.2007
Сообщений: 2421

Re: трудность с Using sort_union()

Приветствую, paulus!, спасибо за ответ.

paulus написал:

Мне очень нравится начало вопроса — «в табличке ~257млн записей»

Ок, убедил, если очень приблизительно, то в табличке примерно 257413139 записей smile.

EXPLAIN EXTENDED совпало с explain, SHOW WARNINGS вернул пустую выборку.

С union все получилось, конечно.

Касательно насильственного указания индексов прошу уточнить - а как ему насильно указать, чтобы он использовал Using sort_union? Просто запретить использование индекса first ("ignore index(first)") и пусть сам разбирается, как ему использовать множественные индексы?

Или лучше прямо прописать use index(input_2,output_2)?

paulus написал:

Да, индексы input и output — лишние при наличии input_2 и output_2.

Да, конечно, просто в этой таблице изначально были одиночные индексы в том числе на input и output, а после я стал пытаться работать с множественными, дабы вышеуказанный запрос быстро работал smile.

Отредактированно deadka (10.03.2011 12:12:57)


Зеленый свет для слабаков, долги отдают только трусы, тру гики работают только в консоли...

Неактивен

 

#4 11.03.2011 14:04:44

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: трудность с Using sort_union()

Ээ... SHOW WARNINGS не может выдать пустую выборку после EXPLAIN EXTENDED.
Может быть, клиент накидал еще запросов между этими?

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

Неактивен

 

#5 14.03.2011 11:29:00

deadka
Администратор
Зарегистрирован: 14.11.2007
Сообщений: 2421

Re: трудность с Using sort_union()

Да, это какие-то фокусы MySQL Workbench (насчёт пустой выборки от show warnings) smile, в консольном клиенте все показало.

Без насильственного указания индексов

mysql> explain extended SELECT sql_no_cache count(*) FROM `2011_03_04` where (input=10  and (first>='2011-03-04 00:00:00' and first<='2011-03-04 00:04:59')) or (output=10 and (first>='2011-03-04 00:00:00' and first<='2011-03-04 00:04:59'));
+----+-------------+------------+-------+---------------------------------------------+-------+---------+------+--------+-------------+
| id | select_type | table      | type  | possible_keys                               | key   | key_len | ref  | rows   | Extra       |
+----+-------------+------------+-------+---------------------------------------------+-------+---------+------+--------+-------------+
|  1 | SIMPLE      | 2011_03_04 | range | first,input,output,input_first,output_first | first | 8       | NULL | 618561 | Using where |
+----+-------------+------------+-------+---------------------------------------------+-------+---------+------+--------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> show warnings\G
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: select sql_no_cache count(0) AS `count(*)` from `2011_03_04` where (((`2011_03_04`.`input` = 10) and (.`2011_03_04`.`first` >= _latin1'2011-03-04 00:00:00') and (.`2011_03_04`.`first` <= _latin1'2011-03-04 00:04:59')) or ((`2011_03_04`.`output` = 10) and (`2011_03_04`.`first` >= _latin1'2011-03-04 00:00:00') and (.`2011_03_04`.`first` <= _latin1'2011-03-04 00:04:59')))
1 row in set (0.00 sec)

С насильственным указанием

mysql> explain extended SELECT sql_no_cache count(*) FROM `2011_03_04` ignore index(first) where (input=10  and (first>='2011-03-04 00:00:00' and first<='2011-03-04 00:04:59')) or (output=10 and (first>='2011-03-04 00:00:00' and first<='2011-03-04 00:04:59'));
+----+-------------+------------+-------------+---------------------------------------+--------------------------+---------+------+-------+---------------------------------------------------------+
| id | select_type | table      | type        | possible_keys                         | key                      | key_len | ref  | rows  | Extra                                                   |
+----+-------------+------------+-------------+---------------------------------------+--------------------------+---------+------+-------+---------------------------------------------------------+
|  1 | SIMPLE      | 2011_03_04 | index_merge | input,output,input_first,output_first | input_first,output_first | 9,9     | NULL | 57521 | Using sort_union(input_first,output_first); Using where |
+----+-------------+------------+-------------+---------------------------------------+--------------------------+---------+------+-------+---------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> show warnings\G
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: select sql_no_cache count(0) AS `count(*)` from `2011_03_04` IGNORE INDEX (`first`) where (((`2011_03_04`.`input` = 10) and (`2011_03_04`.`first` >= _latin1'2011-03-04 00:00:00') and (`2011_03_04`.`first` <= _latin1'2011-03-04 00:04:59')) or ((``2011_03_04`.`output` = 10) and (`2011_03_04`.`first` >= _latin1'2011-03-04 00:00:00') and (`2011_03_04`.`first` <= _latin1'2011-03-04 00:04:59')))
1 row in set (0.00 sec)

То же самое, но в профиль sad.


Зеленый свет для слабаков, долги отдают только трусы, тру гики работают только в консоли...

Неактивен

 

#6 16.03.2011 21:47:06

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: трудность с Using sort_union()

Да, оптимизатор ничего не выкинул. Ну, значит, тогда только указанием индексов sad

Неактивен

 

Board footer

Работает на PunBB
© Copyright 2002–2008 Rickard Andersson