Исправление производительности процедуры стека MySQL, SELECT - PullRequest
0 голосов
/ 05 июля 2018

Мне нужно запустить процедуру MYSQL, которая выберет записи временных рядов для одной точки из таблицы pointValues. Конечно, количество записей может быть огромным - поэтому мне нужно выбрать только 200 (лимит) из них, чтобы нарисовать диаграмму. Я решил разделить все записи в соответствии со следующей логикой:

a) records / (limit / 2) -> количество строк в каждой группе
б) получить минимальное и максимальное значение из каждой группы, определенной в а).

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

    CREATE TABLE secChart 
    (
         id int(11) NOT NULL,
         dataPointId int(11) NOT NULL,
         dataType int(11),
         pointValue DOUBLE NOT NULL,
         ts bigint(20) NOT NULL 
    ) ENGINE=InnoDB;
    
    DROP PROCEDURE dataChart;
    DROP PROCEDURE IF EXISTS dataChart;
    
    DELIMITER //
    
    CREATE PROCEDURE dataChart(iter int, step int, pointId int, setStart int, 
    

    setStop int) НАЧАТЬ TRUNCATE TABLE secChart;

        SET @i = 0;
        SET @iter = iter;
        SET @pointId = pointId; myLoop: 
    
        WHILE (@i < @iter) 
        DO 
             IF @i = 0 THEN
                SET setStart = 0;
                SET setStop = step-1; 
             END IF; 
    
             IF @i > 0 THEN
                SET setStart = @i * step;
                SET setStop = setStart + (step-1);
                SET @start = setStart;
                SET @stop = setStop; 
             END IF; **
    
             INSERT INTO secChart
                 (SELECT *
                  FROM pointvalues
                  WHERE dataPointId = @pointId
                    AND (pointValue = (SELECT MIN(pointValue)
                                       FROM 
                                           (SELECT *
                                            FROM flex2.pointvalues
                                            WHERE dataPointId = @pointId
                                            ORDER BY id ASC
                                            LIMIT setStart, setStop) AS b)
                         OR pointValue = (SELECT MAX(pointValue)
                                          FROM
                                              (SELECT *
                                               FROM flex2.pointvalues
                                               WHERE dataPointId = @pointId
                                               ORDER BY id ASC
                                               LIMIT setStart, setStop) AS b2))
                 ORDER BY id
                 LIMIT 0, 2);**
    
         SET @i = @i + 1; 
    
         IF @i > @iter 
         THEN 
             LEAVE myLoop; 
         END IF; 
    END WHILE; 
    END//
    DELIMITER ;
    
    CALL dataChart(100, 80, 1, 0, 0);
    

    Для почти 15 000 записей это занимает 158 с ...

Другой выбранный мной тест:

INSERT INTO idx
VALUES(@start, @stop , @i, step);

INSERT INTO stt
    (SELECT * 
     FROM
         ((SELECT * 
           FROM
               (SELECT id, pointValue, ts
                FROM flex2.pointvalues AS pv
                WHERE pv.dataPointId = 1
                ORDER BY id
                LIMIT setStart, setStop) AS minval
           ORDER BY pointValue DESC
           LIMIT 0, 1)
          UNION
           (SELECT *  
            FROM
                (SELECT id, pointValue, ts
                 FROM flex2.pointvalues AS pv
                 WHERE pv.dataPointId = 1
                 ORDER BY id
                 LIMIT setStart, setStop) AS maxval
            ORDER BY pointValue ASC
            LIMIT 0, 1)) AS selectScore);

Для почти 15 000 записей это занимает 58 секунд - быстрее, но недостаточно быстро.

Третья идея - выбрать n строк (например, 200 строк из 12 000)

SELECT COUNT(*) 
FROM flex2.pointvalues 
WHERE dataPointId = 1 
  AND id IN (SELECT id 
             FROM flex2.pointvalues 
             WHERE dataPointId = 1 
               AND id BETWEEN 
                           (SELECT MIN(id) FROM flex2.pointvalues 
                            WHERE dataPointId = 1) AND 
                           (SELECT MAX(id) FROM flex2.pointvalues 
                            WHERE dataPointId = 1)) 
              AND id % 10 = 0;

Лучше всего будет исправить представление идеи 2. Пожалуйста, помогите!

Ответы [ 2 ]

0 голосов
/ 09 июля 2018

Я оптимизировал свои идеи для решения ниже:

`DROP procedure chartSelection;      
 DROP table chartSelectionTable;       
 Delimiter //        
 CREATE PROCEDURE chartSelection(iter int, step int, pointId int, setStart int, setStop int)
 BEGIN
 CREATE temporary TABLE if not exists chartSelectionTable(id int(11) NOT NULL, 
 pointValue double NOT NULL,ts bigint(20) NOT NULL) engine=InnoDB;

TRUNCATE TABLE chartSelectionTable;

SET @i=0;
SET @iter = iter; 
SET @pointId = pointId;
chart: WHILE (@i < @iter)  DO 
IF @i = 0 THEN
    SET setStart = 0; 
    SET setStop = step-1;
END IF;
IF @i >0 THEN
    SET setStart = @i*step;
    SET setStop = setStart + (step-1);
END IF;
     insert into chartSelectionTable(   

     select id, pointValue,ts from (

     select * from pointvalues where (
            id = (select id from( select * from flex2.pointvalues as pv where pv.dataPointId=pointId order by ts limit setStart,setStop) as minval  order by pointValue asc limit 0,1) 
            or
            id = (select id from( select * from flex2.pointvalues as pv where pv.dataPointId=pointId order by ts limit setStart,setStop) as maxval  order by pointValue desc limit 0,1)
      )) as b       
                );

SET @i = @i+1;
IF @i > @iter THEN
    LEAVE chart;
END IF;
END WHILE;
select * from     chartSelectionTable; 
drop table chartSelectionTable;
END//
delimiter ;

`

Я звоню по: CALL chartSelection(100,90,1,0,0);

Но с Java (на уровне сервера) он вызывается:

`import java.sql.Statement;
  (...)
 Statement createProcedureStmt = conn.createStatement();`

`createProcedureStmt.execute( "CREATE PROCEDURE `chartSelection` "+
                "(iter int, step int, pointId int, setStart int, setStop int )"+
                " BEGIN "+
                " TRUNCATE TABLE chartSelectionTable;"+
                    " SET @i=0;"+
                    " SET @iter = iter; "+
                    " SET @pointId = pointId;"+
                    " chart: WHILE (@i < @iter)  DO "+
                    " IF @i = 0 THEN"+
                                "   SET setStart = 0;"+ 
                    " SET setStop = step-1;"+
                            " END IF;"+
                        " IF @i >0 THEN"+
                        " SET setStart = @i*step;"+
                        " SET setStop = setStart + (step-1);"+
                            " END IF;"+
                        " insert into chartSelectionTable("+                                 
                                     " select id, pointValue,ts from ("+                                 
                                     " select * from pointvalues where ("+
                                     " id = (select id from( select * from flex2.pointvalues as pv where pv.dataPointId=pointId order by ts limit setStart,setStop) as minval  order by pointValue asc limit 0,1)"+ 
                                            " or"+
                                    " id = (select id from( select * from flex2.pointvalues as pv where pv.dataPointId=pointId order by ts limit setStart,setStop) as maxval  order by pointValue desc limit 0,1)"+
                                    " )) as b       "+
                              "         );"+

                             " SET @i = @i+1;"+
                        " IF @i > @iter THEN"+
                        " LEAVE chart;"+
                        "   END IF;"+
                        " END WHILE;"+
                        " select * from     chartSelectionTable; "+
                    " END ");

        /* Call stored procedure */
        java.sql.CallableStatement stmt = conn
                .prepareCall("{call chartSelection(?,?,?,0,0)}");
        stmt.setInt(1, iteracje);
        stmt.setInt(2, step);
        stmt.setInt(3, dataPointId);`

Время оценки сокращается примерно на 50%, но все равно это слишком долго. Что изменилось: - временная таблица вместо реальной, - выбор только точки значения и отметки времени из всех столбцов, способ выбора.

Как преобразовать его в решение без петель? Как выйти из лимитов и порядка?

---- ДЛЯ ПОДКЛЮЧЕНИЯ ---- 1) у меня 1 000 000 записей для id = x, 2) у меня лимит = 200 записей для графика 3) Так что в Java я вычисляю - прежде чем я вызову SQL, 1 000 000 / (limit / 2) = 10 000 записей в 100 группах. 4) ОСНОВНАЯ ПРОБЛЕМА: шаг за шагом выбирайте каждую группу (на основе отметок времени) и набирайте и max (pointValue и отметка времени) в группе.

0 голосов
/ 06 июля 2018

Сначала несколько вопросов и комментариев.

  • Вы хотите выбрать равномерно распределенные точки из набора данных временного ряда?
  • Вы делаете "подсвечники" (отсюда мин и макс)?
  • Не используйте петли любого типа; это будет медленно.
  • Стремитесь к одному SELECT (без петли), чтобы захватить все нужные предметы. SQL оптимизирован для этого.
  • Почему в таблице 5 столбцов, а не просто 2 (для x и y, aka ts и value)?
  • Хотите, чтобы ваш график основывался на времени или на индексе в таблице? будет случаев пропущенных данных, поэтому вы захотите основать график на времени .
  • Избегайте OFFSET (т. Е. LIMIT m,n), он должен сканировать все предыдущие строки; следовательно, медленно.

Давайте сделаем шаг назад. Вместо того, чтобы получать свечи, давайте сначала поработаем над способом использования AVG вместо MIN и MAX. Как только вы освоите это, тогда, возможно, можно сделать подсвечники.

SELECT FLOOR(ts / 300000) AS '5-minute-intervals',  -- see below
       AVG(value)
    FROM tbl
    WHERE ts ...   -- limit the time span
    GROUP BY 1     -- shorthand, referring to the FLOOR(..)

300000 предполагает миллисекунду ts (на языке Java). Вы предварительно вычислили это число на основе временного интервала, основываясь на обсуждении «200 ...».

Это все, что нужно.

Теперь для подсвечников:

SELECT FLOOR(ts / 300000) AS '5-minute-intervals',
       MIN(value),
       MAX(value)
    FROM tbl
    WHERE ts ...   -- limit the time span
    GROUP BY 1     -- shorthand, referring to the FLOOR(..)

И тогда вашей программе графиков нужно взять минимальное и максимальное значения и как-то превратить их в вертикальную линию. Если вам действительно нужны определенные процентили вместо минимума и максимума, то получается действительно грязно.

Получение интервалов ...

Пожалуйста, используйте удобочитаемое время. Мне действительно не нравятся оси X и Y, которые были получены из данных, но не смогли использовать «круглые» числа. (Например, вместо 100, 200, ..., 1500 они используют 143, 286, ..., 1432, когда целью было получить около 10 тиков, но они решили, что это означает точно 10 тиков .)

Чтобы сделать это «правильно», нужно найти общий максимум и минимум, выполнить некоторую арифметику с использованием floor () и / или ceil (). И подбрасывая некоторые эвристики, чтобы получить "круглые числа" Это может быть другое обсуждение. Это чисто алгоритм - он может быть одинаково хорошо реализован на вашем языке программирования или на SQL.

...