Пожалуйста, помогите мне с оптимизацией таблиц / вложенных JOIN: Слишком медленно! - PullRequest
3 голосов
/ 06 мая 2011

В моей базе данных около 250 таблиц, каждая из которых содержит ровно 439340 строк.

mysql> SHOW CREATE TABLE data.b50d1 ;
+-------+--------------------------------------------------------------------------------------------
CREATE TABLE `b50d1` (
      `pTime` int(10) unsigned NOT NULL,
      `Slope` double NOT NULL,
      `STD` double NOT NULL,
      PRIMARY KEY (`pTime`),
      KEY `Slope` (`Slope`) USING BTREE
    ) ENGINE=MyISAM DEFAULT CHARSET=latin1 MIN_ROWS=43940 MAX_ROWS=43940 PACK_KEYS=1 ROW_FORMAT=FIXED |
+-------+--------------------------------------------------------------------------------------------

Как видите, в каждой таблице три столбца:

  • pTime : отметка времени POSIX.Этот столбец (и все его значения) одинаков в каждой таблице.Это мой PRIMARY KEY
  • Наклон
  • STD

Столбцы Slope и STD имеют двойную подпись'значения, которые отличаются от строки к строке и от таблицы к таблице.

Вот небольшой пример из одной из таблиц:

mysql> SELECT * FROM data.b50d1 limit 10;
+------------+------------+-------------+
| pTime      | Slope      | STD         |
+------------+------------+-------------+
| 1104537600 | 6.38733032 | -1.13387667 |
| 1104537900 | 5.58733032 | -0.93810617 |
| 1104538200 | 5.30135747 | -0.51912757 |
| 1104538500 |  5.4678733 | -0.54460575 |
| 1104538800 | 5.58190045 | -0.46369055 |
| 1104539100 | 5.50226244 | -0.46712018 |
| 1104714000 | 5.31221719 | -0.25210485 |
| 1104714300 | 4.72941176 |  0.00321249 |
| 1104714600 | 5.19638009 |  0.64116376 |
| 1104714900 | 5.12941176 |  0.39599099 |
+------------+------------+-------------+

Используя эти таблицы, я запускаюхранимая процедура.Эта процедура состоит из следующих шагов:

ШАГ 1) CREATE TEMPORARY TABLE MainList ...

ШАГ 2) INSERT результаты оператора SELECT в таблице.Полученный набор данных представляет собой отфильтрованный состав исходных таблиц.

STEP 3) SELECT оператор с вложенным JOIN s выполняет итерацию по каждому значению MainList.STD таблицы TEMPORARY (MainList) и возвращает первую строку из одной из исходных таблиц, что соответствует определенным указанным условиям (пример ниже).

ШАГ 4) JOIN результаты в MainList и вывод их пользователю.

Ниже приводится сама процедура:

DELIMITER $$

CREATE DEFINER=`root`@`localhost` PROCEDURE `GetTimeList`(t1 varchar(7),t2 varchar(7),t3 varchar(7),inp1 float,inp2 float,inp3 float,inp4 float,inp5 float,inp6 float,inp7 float,inp8 float,inp9 float,inp10 float)
    READS SQL DATA
BEGIN
DROP TABLE IF EXISTS MainList;

CREATE TEMPORARY TABLE MainList(
  `pTime` int unsigned NOT NULL,
  `STD` double NOT NULL,
  PRIMARY KEY (`pTime`),
    KEY (`STD`) USING BTREE
    ) ENGINE = MEMORY;  

SET @s = CONCAT('INSERT INTO MainList(pTime,STD)  SELECT DISTINCT t1.pTime, t1.STD FROM ',t1,' AS t1 JOIN (',t2,' as t2 ,',t3,' as t3 )',
' ON (( t1.Slope >= ', inp1,
' AND t1.Slope <= ', inp2,
' AND t1.STD  >= ', inp3,
' AND t1.STD  <= ', inp4,
' AND t2.Slope  >= ', inp5,
' AND t2.Slope  <= ', inp6,
' AND t3.Slope  >= ', inp7,
' AND t3.Slope  <= ', inp8,
' ) OR ( t1.Slope <= ', 0-inp1,
' AND t1.Slope >= ', 0-inp2,
' AND t1.STD  <= ', 0-inp3,
' AND t1.STD  >= ', 0-inp4,
' AND t2.Slope  <= ', 0-inp5,
' AND t2.Slope  >= ', 0-inp6,
' AND t3.Slope  <= ', 0-inp7,
' AND t3.Slope  >= ', 0-inp8,
' ) ) AND ((t1.Slope < 0 XOR t1.STD < 0) AND t1.pTime = t2.pTime AND t2.pTime = t3.pTime AND t1.pTime >= ', inp9,
' AND t1.pTime <= ', inp10,' ) ORDER BY t1.pTime'
);

PREPARE stmt FROM @s;
EXECUTE stmt;

SET @q= CONCAT('SELECT m.pTime as OpenTime, CASE WHEN m.STD < 0 THEN 1 ELSE -1 END As Type, mu.pTime As CloseTime from MainList m LEFT JOIN ',t1,' mu ON mu.pTime = ( SELECT DISTINCT md.pTime FROM ',t1,' md WHERE md.pTime>m.pTime',' AND md.pTime <= ', inp10,
                            ' AND SIGN (md.STD)!= SIGN (m.STD) AND ABS(md.STD) >= ABS(m.STD) ORDER BY md.pTime LIMIT 1 )');


PREPARE stmt FROM @q;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
DROP TABLE MainList;
END

Для простоты тестирования Iразбили описанную выше процедуру на два отдельных запроса.Вот эти запросы, сопровождаемые операторами EXPLAIN EXTENDED (временная таблица была создана заранее):

FIRST QUERY


INSERT INTO MainList(pTime,STD)
SELECT
  t1.pTime,
  t1.STD
 FROM
  b50d1 AS t1
 JOIN(b75d1 AS t2, b100d1 AS t3)ON(
  (
          t1.Slope >= 2.3169
          AND t1.Slope <= 7.0031
          AND t1.STD >= - 2.068
          AND t1.STD <= - 0.972
          AND t2.Slope >= 0.3179
          AND t2.Slope <= 5.7221
          AND t3.Slope >= 2.6466
          AND t3.Slope <= 35.7534
  )
  OR(
          t1.Slope <= - 2.3169
          AND t1.Slope >= - 7.0031
          AND t1.STD <= 2.068
          AND t1.STD >= 0.972
          AND t2.Slope <= - 0.3179
          AND t2.Slope >= - 5.7221
          AND t3.Slope <= - 2.6466
          AND t3.Slope >= - 35.7534
  )
 )
 AND(
  (t1.Slope < 0 XOR t1.STD < 0)
  AND t1.pTime = t2.pTime
  AND t2.pTime = t3.pTime
  AND t1.pTime >= 1104710000
  AND t1.pTime <= 1367700000
 )
 ORDER BY
  t1.pTime;

ОБЪЯСНИТЕ, РАСШИРЕНО:

+----+-------------+-------+--------+---------------+---------+---------+---------------+--------+----------+-----------------------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref           | rows   | filtered | Extra                       |
+----+-------------+-------+--------+---------------+---------+---------+---------------+--------+----------+-----------------------------+
|  1 | SIMPLE      | t1    | ALL    | PRIMARY,Slope | NULL    | NULL    | NULL          | 439340 |    25.79 | Using where; Using filesort |
|  1 | SIMPLE      | t2    | eq_ref | PRIMARY,Slope | PRIMARY | 4       | data.t1.pTime |      1 |   100.00 | Using where                 |
|  1 | SIMPLE      | t3    | eq_ref | PRIMARY       | PRIMARY | 4       | data.t1.pTime |      1 |   100.00 | Using where                 |
+----+-------------+-------+--------+---------------+---------+---------+---------------+--------+----------+-----------------------------+

ВТОРОЙ ЗАПРОС


SELECT
    m.pTime AS OpenTime,
    CASE WHEN m.STD < 0 THEN 1 ELSE - 1 END AS Type,
  mu.pTime AS CloseTime;
FROM
    MainList m
LEFT JOIN b50d1 mu ON mu.pTime =(
    SELECT DISTINCT
        md.pTime
    FROM
        b50d1 md
    WHERE
        md.pTime > m.pTime
    AND md.pTime <= 1367700000
    AND SIGN(md.STD)!= SIGN(m.STD)
    AND ABS(md.STD)>= ABS(m.STD)
    ORDER BY
        md.pTime
    LIMIT 1
);

ОБЪЯСНИТЕ, РАСШИРЕНО:

+----+--------------------+-------+--------+---------------+---------+---------+------+--------+----------+-------------+
| id | select_type        | table | type   | possible_keys | key     | key_len | ref  | rows   | filtered | Extra       |
+----+--------------------+-------+--------+---------------+---------+---------+------+--------+----------+-------------+
|  1 | PRIMARY            | m     | ALL    | NULL          | NULL    | NULL    | NULL |     16 |   100.00 |             |
|  1 | PRIMARY            | mu    | eq_ref | PRIMARY       | PRIMARY | 4       | func |      1 |   100.00 | Using index |
|  2 | DEPENDENT SUBQUERY | md    | range  | PRIMARY       | PRIMARY | 4       | NULL | 439338 |   100.00 | Using where |
+----+--------------------+-------+--------+---------------+---------+---------+------+--------+----------+-------------+

Запрос работает и возвращает правильные результаты, но он на порядка медленнее, чем мне нужно.Я признаю, что оператор type: ALL, присутствующий в обоих операторах EXPLAIN, указывает на то, что мои индексы, вероятно, неоптимальны.

Я использовал MYSQL только на прошлой неделе, и я начинаю чувствовать, что у меня над головой.Я был бы очень признателен за помощь.

Я создал файл SQL с операторами CREATE TABLE и INSERT, чтобы любой, кто захотел помочь мне, мог создавать меньшие версии моих таблиц в базе данных «Тест».: slowtables.SQL

Для полноты вот файл настроек my.ini - возможно, это узкое место?

[client]
pipe
socket=mysql
[mysql]
default-character-set=latin1
[mysqld]
skip-networking
enable-named-pipe
socket=mysql
basedir="C:/Program Files/MySQL/MySQL Server 5.5/"
datadir="C:/ProgramData/MySQL/MySQL Server 5.5/Data/"
character-set-server=latin1
default-storage-engine=MYISAM
sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
max_connections=100
query_cache_size=189M
table_cache=256
tmp_table_size=192M
key_buffer_size=594M
read_buffer_size=64K
read_rnd_buffer_size=256K
sort_buffer_size=256K

Ответы [ 2 ]

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

Я вижу два возможных улучшения, которые больше связаны с оптимизатором MySQL (или его слабостью).Во втором запросе DISTINCT в подзапросе является избыточным с учетом ПРЕДЕЛА 1. Запрос ORDER BY LIMIT 1 должен выполняться путем поиска в индексе до тех пор, пока не будет найдена запись, соответствующая другим критериям.(Вам действительно нужен LEFT JOIN ??)

В первом запросе MySQL, по-видимому, не может оптимизировать OR в UNION.Однако, если вы сделаете это вручную, он может выбрать гораздо лучшие планы для двух половин запроса UNION.

HTH.Я могу посмотреть позже.

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

Попытка работать с вашим первым примером запроса, и все таблицы имеют ... как вы упомянули ... одно и то же значение "pTime", я мог бы изменить запрос следующим образом ... Я не знаю дляуверен, что математическое XOR быстрее, чем прямое умножение наклона * STD.Если только ОДИН из них может быть отрицательным, единственный результат будет отрицательным, так как два отрицательных значения = положительные (как и два положительных) ...

Тем не менее, я переместил предложение WHERE вперед вявно определите временной интервал запроса, прежде чем он попытается завершить объединение с таблицами 2 и 3 ...

Я не был уверен насчет умножения по сравнению с вызовом XOR, но держу пари, что он имелфактор в более длительное время.Кроме того, пытаясь превентивно использовать проверку ABS () на склоне тоже.Тем не менее, я бы сделал это как UNION, так как наклон таблицы также является ключом к таблице, использую его как точную часть ключа, а НЕ через вычисляемую функцию в ABS ().Я могу сделать UNION ALL, поскольку один критерий проверяет отрицательный наклон, а другой - положительный, каждый уникальный SQL никогда не будет включать в себя набор результатов другого.Кроме того, мы можем исключить проверку XOR, так как остальные ваши предложения AND явно определяют наклон как противоположный знак стандарта.

THEN, так же придерживайтесь других ваших соображений о наклоне и std

INSERT INTO MainList(pTime,STD)
SELECT STRAIGHT_JOIN
      t1.pTime,
      t1.STD
   FROM
      b50d1 AS t1
         JOIN b75d1 AS t2
            ON t1.pTime = t2.pTime
         JOIN b100d1 AS t3
            ON t1.pTime = t3.pTime
   where 
          t1.pTime >= 1104710000
      AND t1.pTime <= 1367700000
      AND t1.Slope >= 2.3169
      AND t1.Slope <= 7.0031
      AND t1.STD >= - 2.068
      AND t1.STD <= - 0.972
      AND t2.Slope >= 0.3179
      AND t2.Slope <= 5.7221
      AND t3.Slope >= 2.6466
      AND t3.Slope <= 35.7534
   ORDER BY
      t1.pTime
UNION ALL
SELECT STRAIGHT_JOIN
      t1.pTime,
      t1.STD
   FROM
      b50d1 AS t1
         JOIN b75d1 AS t2
            ON t1.pTime = t2.pTime
         JOIN b100d1 AS t3
            ON t1.pTime = t3.pTime
   where 
          t1.pTime >= 1104710000
      AND t1.pTime <= 1367700000
      AND t1.Slope >= - 7.0031
      AND t1.Slope <= - 2.3169
      AND t1.STD >=  0.972
      AND t1.STD <=  2.068
      AND t2.Slope >= - 5.7221
      AND t2.Slope <= - 0.3179
      AND t3.Slope >= - 35.7534
      AND t3.Slope <= - 2.6466;

ТРЕТЬЕЙ версией будет предварительный запрос на соответствующие записи, а затем продолжение остальной части объединения ... (внутренний, построение набора результатов "PQ" PreQuery)

INSERT INTO MainList(pTime,STD)
SELECT STRAIGHT_JOIN
      pq.pTime,
      pq.STD
   FROM
      ( select
              t1.pTime,
              t1.slope,
              t1.std
           from 
              b50d1 t1
           where
                  t1.pTime >= 1104710000
              AND t1.pTime <= 1367700000
              AND (( t1.slope between 2.3169 and 7.0031
                    AND t1.std between -2.068 and -.972 )
                  OR 
                   ( t1.slope between -7.0031 and -2.3169  
                    AND t1.std between .972 and 2.068 )) ) PQ
         JOIN b75d1 AS t2
            ON p1.pTime = t2.pTime
         JOIN b100d1 AS t3
            ON p1.pTime = t3.pTime
   where 
          (     pq.slope > 0
            AND t2.Slope >= 0.3179
            AND t2.Slope <= 5.7221
            AND t3.Slope >= 2.6466
            AND t3.Slope <= 35.7534 
          )
       OR
          (     pq.slope > 0
            AND t2.Slope >= -5.7221
            AND t2.Slope <= -0.3179
            AND t3.Slope >= -35.7534 
            AND t3.Slope <= -2.6466
          )
   ORDER BY
      t1.pTime
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...