Запрос JOIN слишком медленный.Не будете использовать INDEX? - PullRequest
1 голос
/ 08 мая 2011

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

CREATE TABLE SearchListA(
  `pTime`  int unsigned  NOT NULL ,
  `STD` double unsigned NOT NULL,
    `STD_Pos`   int unsigned NOT NULL,
  `SearchEnd`  int unsigned NOT NULL,
    UNIQUE INDEX (`pTime`,`STD` ASC) USING BTREE
    ) ENGINE = MEMORY;

Она выглядит так:

+------------+------------+---------+------------+
| pTime      | STD        | STD_Pos | SearchEnd  |
+------------+------------+---------+------------+
| 1105715400 | 1.58474499 |       0 | 1105723200 |
| 1106297700 |  2.5997839 |       0 | 1106544000 |
| 1107440400 | 2.04860375 |       0 | 1107440700 |
| 1107440700 | 1.58864998 |       0 | 1107467400 |
| 1107467400 | 1.55207218 |       0 | 1107790500 |
| 1107790500 | 2.04239417 |       0 | 1108022100 |
| 1108022100 | 1.61385678 |       0 | 1108128000 |
| 1108771500 | 1.58835083 |       0 | 1108771800 |
| 1108771800 | 1.65734727 |       0 | 1108772100 |
| 1108772100 | 2.09378189 |       0 | 1109027700 |
+------------+------------+---------+------------+

Только столбцы pTime и SearchEnd имеют отношение к моей проблеме.

Я намерен использовать эту таблицу для ускорения поиска в гораздо большей статической таблице.

Первый столбец pTime - это место, где поиск должен start
Четвертый столбец, SearchEnd , - это гдепоиск должен end

Таблица большего размера похожа;это выглядит так:

CREATE TABLE `b50d1_abs` (
  `pTime` int(10) unsigned NOT NULL,
  `Slope` double NOT NULL,
  `STD` double NOT NULL,
  `Slope_Pos` int(11) NOT NULL,
  `STD_Pos` int(11) NOT NULL,
  PRIMARY KEY (`pTime`),
  KEY `Slope` (`Slope`) USING BTREE,
  KEY `STD` (`STD`),
  KEY `ID1` (`pTime`,`STD`) USING BTREE
) ENGINE=MyISAM DEFAULT CHARSET=latin1 MIN_ROWS=339331 MAX_ROWS=539331 PACK_KEYS=1 ROW_FORMAT=FIXED;

+------------+-------------+------------+-----------+---------+
| pTime      | Slope       | STD        | Slope_Pos | STD_Pos |
+------------+-------------+------------+-----------+---------+
| 1107309300 |  1.63257919 | 1.39241698 |         0 |       1 |
| 1107314400 |   6.8959276 | 0.22425643 |         1 |       1 |
| 1107323100 | 18.19909502 | 1.46854808 |         1 |       0 |
| 1107335400 |  2.50135747 |  0.4736305 |         0 |       0 |
| 1107362100 |  4.28778281 | 0.85576985 |         0 |       1 |
| 1107363300 |  6.96289593 | 1.41299044 |         0 |       0 |
| 1107363900 |  8.10316742 |  0.2859726 |         0 |       0 |
| 1107367500 | 16.62443439 | 0.61587645 |         0 |       0 |
| 1107368400 | 19.37918552 | 1.18746968 |         0 |       0 |
| 1107369300 | 21.94570136 | 0.94261744 |         0 |       0 |
| 1107371400 | 25.85701357 |  0.2741292 |         0 |       1 |
| 1107375300 | 21.98914027 | 1.59521158 |         0 |       1 |
| 1107375600 | 20.80542986 | 1.59231289 |         0 |       1 |
| 1107375900 | 19.62714932 | 1.50661679 |         0 |       1 |
| 1107381900 |  8.23167421 | 0.98048205 |         1 |       1 |
| 1107383400 | 10.68778281 | 1.41607579 |         1 |       0 |
+------------+-------------+------------+-----------+---------+
...etc (439340 rows)

Здесь столбцы pTime , STD и STD_Pos имеют отношение к моей проблеме.

Для каждого элемента в меньшей таблице ( SearchListA ) мне нужно выполнить поиск в указанном диапазоне в большой таблице ( b50d1_abs () ) и вернуть строку ссамый низкий b50d1_abs.pTime , который выше текущего SearchListA.pTime и который также соответствует следующим условиям:

SearchListA.STD < b50d1_abs.STD AND SearchListA.STD_Pos <> b50d1_abs.STD_Pos

AND

b50d1_abs.pTime < SearchListA.SearchEnd

Последнее условие - просто сократить продолжительность поиска.

Мне кажется, это довольно простой запрос, который должен использовать индексы;тем более что все значения являются числами без знака - но я не могу заставить его работать достаточно быстро!Я думаю, что это потому, что он перестраивает всю таблицу каждый раз вместо того, чтобы просто пропустить значения из нее.

Я был бы чрезвычайно признателен, если бы кто-нибудь взглянул на мой код и нашел более эффективный способ сделать это:

SELECT   
    m.pTime as OpenTime,
    m.STD,
    m.STD_Pos,
    mu.pTime AS CloseTime
FROM
    SearchListA m
 JOIN b50d1_abs mu ON mu.pTime =(
    SELECT 
        md.pTime 
    FROM
        b50d1_abs as md
    WHERE
        md.pTime > m.pTime
    AND md.pTime <=m.SearchEnd
    AND m.STD < md.STD AND m.STD_Pos <> md.STD_Pos

     LIMIT 1
); 

Вот мое заявление EXPLAIN EXTENDED:

+----+--------------------+-------+--------+-----------------+---------+---------+------+--------+----------+--------------------------+
| id | select_type        | table | type   | possible_keys   | key     | key_len | ref  | rows   | filtered | Extra                    |
+----+--------------------+-------+--------+-----------------+---------+---------+------+--------+----------+--------------------------+
|  1 | PRIMARY            | m     | ALL    | NULL            | NULL    | NULL    | NULL |    365 |   100.00 |                          |
|  1 | PRIMARY            | mu    | eq_ref | PRIMARY,ID1     | PRIMARY | 4       | func |      1 |   100.00 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | md    | ALL    | PRIMARY,STD,ID1 | NULL    | NULL    | NULL | 439340 |   100.00 | Using where              |
+----+--------------------+-------+--------+-----------------+---------+---------+------+--------+----------+--------------------------+

Похоже, что в самом длинном запросе (# 2) индексы вообще не используются!

Если я попытаюсь FORCE INDEX, он отобразится в списке возможных_кнопок, но все равно будет перечислен NULL под ключом и все еще занимает очень много времени (более 80 секунд).

Мне нужно получить этот запрос менее чем за 10 секунд;и даже 10 слишком длинный.

Ответы [ 2 ]

1 голос
/ 08 мая 2011

Я начну новый ответ, потому что он начинает выглядеть как беспорядок;)

С вашими данными я получаю, используя MySQL 5.1.41

Query 1 : takes forever, Ctrl-C
Query 2 : 520 ms
Query 3 : takes forever, Ctrl-C

Объясните для 2 взглядовхорошо:

 +----+-------------+-------+------+---------------------+------+---------+------+--------+------------------------------------------------+
| id | select_type | table | type | possible_keys       | key  | key_len | ref  | rows   | Extra                                          |
+----+-------------+-------+------+---------------------+------+---------+------+--------+------------------------------------------------+
|  1 | SIMPLE      | m     | ALL  | PRIMARY,STD,ID1,ID2 | NULL | NULL    | NULL |    743 | Using temporary; Using filesort                |
|  1 | SIMPLE      | big   | ALL  | PRIMARY,ID1,ID2     | NULL | NULL    | NULL | 439340 | Range checked for each record (index map: 0x7) |
+----+-------------+-------+------+---------------------+------+---------+------+--------+------------------------------------------------+

Итак, я загрузил ваши данные в postgres ...

Query 1 :  14.8 ms
Query 2 : 100   ms
Query 3 :  14.8 ms (same plan as 1)

Фактически переписывание 2 в запросе 1 (или 3) исправляет небольшой недостаток оптимизатора и находитоптимальный план запроса для этого сценария.

Вы бы порекомендовали использовать Postgres поверх MySql для этого сценария?Скорость очень важна для меня.

Ну, я не знаю, почему mysql barfs так много в запросах 1 и 3 (которые довольно просты и легки), на самом деле это должно даже побить postgres (используя только сканирование индекса) но, видимо, нет, а.Вы должны спросить специалиста по MySQL!

Я больше привык к postgres ... давным-давно надоело работать с MySQL!Если вам нужны сложные запросы, postgres обычно выигрывает много времени (но вам нужно заново изучить, как оптимизировать и настроить новую базу данных) ...

1 голос
/ 08 мая 2011

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

Но если вы поместите этот подзапрос в состояние JOIN, он будет выполнен (строки в m) * (строки в mu) раз, несмотря ни на что.

Обратите внимание, что ваши результаты могут быть неверными, поскольку:

возвращает строку с самым низким b50d1_abs.pTime

но вы нигде не указываете это.

Попробуйте этот запрос:

SELECT   
m.pTime as OpenTime,
m.STD,
m.STD_Pos,
(
    SELECT min( big.pTime )
    FROM   b50d1_abs as big
    WHERE   big.pTime >  m.pTime
        AND big.pTime <= m.SearchEnd
        AND m.STD < big.STD AND m.STD_Pos <> big.STD_Pos
) AS CloseTime
FROM SearchListA m

или этот:

SELECT   
m.pTime as OpenTime,
m.STD,
m.STD_Pos,
min( big.pTime )
FROM   
    SearchListA m
    JOIN b50d1_abs as big ON (
        big.pTime >  m.pTime
        AND big.pTime <= m.SearchEnd
        AND m.STD < big.STD AND m.STD_Pos <> big.STD_Pos
    )
GROUP BY m.pTime

(если вы также хотите, чтобы строки, в которых поиск был неудачным, сделайте это ВЛЕВОМ СОЕДИНЕНИЕМ).

SELECT   
m.pTime as OpenTime,
m.STD,
m.STD_Pos,
(
    SELECT big.pTime
    FROM   b50d1_abs as big
    WHERE   big.pTime >  m.pTime
        AND big.pTime <= m.SearchEnd
        AND m.STD < big.STD AND m.STD_Pos <> big.STD_Pos
    ORDER BY big.pTime LIMIT 1
) AS CloseTime
FROM SearchListA m

(Попробуйте индексировать по b50d1_abs (pTime, STD, STD_Pos)

К вашему сведению, вот некоторые тесты, использующие Postgres для набора тестовых данных, который должен выглядеть как ваш (может быть, удаленно, lol)

CREATE TABLE small ( 
    pTime INT PRIMARY KEY, 
    STD FLOAT NOT NULL, 
    STD_POS BOOL NOT NULL, 
    SearchEnd INT NOT NULL 
);

CREATE TABLE big( 
    pTime INTEGER PRIMARY KEY, 
    Slope FLOAT NOT NULL, 
    STD FLOAT NOT NULL, 
    Slope_Pos BOOL NOT NULL, 
    STD_POS BOOL NOT NULL 
);

INSERT INTO small SELECT 
    n*100000, 
    random(), 
    random()<0.1,
    n*100000+random()*50000 
    FROM generate_series( 1, 365 ) n;

INSERT INTO big SELECT 
    n*100,
    random(),
    random(),
    random() > 0.5,
    random() > 0.5
    FROM generate_series( 1, 500000 ) n;

Query 1 :  6.90 ms (yes milliseconds)
Query 2 : 48.20 ms
Query 3 :  6.46 ms
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...