оптимизировать запрос в MySQL 5.7 - PullRequest
0 голосов
/ 30 января 2020

Вот моя система:

Linux Ubuntu 18.04 LTS
MySQL 5.7

У меня есть запрос (ниже), выполнение которого занимает слишком много времени.

Это занимает столько времени, сколько 9 секунд. Это ненадежное количество времени для пользователя, ожидающего завершения загрузки веб-страницы.

Кроме того, набор данных большой и расширяется. Таблица событий имеет 250 000 строк. Я обычно добавляю от 1200 до 1800 строк в день.

Чтобы помочь в оптимизации, я хотел бы добавить индексы и тому подобное, но я не уверен, как (и если) я могу сделать это с помощью производного / объединенного выбора. запрос у меня есть.

Вот мой запрос. (Кто-нибудь знает, как ограничить ширину вывода, чтобы нам не приходилось прокручивать вправо, чтобы увидеть полную строку?)

mysql> explain select OUTSIDE.ownerUID as Owner, 
  OUTSIDE.propUID as Property, 
  OUTSIDE.camname as 'Camera Name', 
  OUTSIDE.direction as Direction, 
  OUTSIDE.camtimestamp as 'Event Time', 
  convert_tz(now(),'UTC','US/Central') as Now, 
  sec_to_time(convert_tz(now(),'UTC','US/Central') - OUTSIDE.CamTimeStamp) as 'Elapsed Time' 
from events OUTSIDE, 
  (select camname,max(camtimestamp) as maxtimestamp from events group by camname) as INSIDE 
where OUTSIDE.camname = INSIDE.camname 
  AND OUTSIDE.camtimestamp = INSIDE.maxtimestamp;
+----+-------------+------------+------------+-------+----------------------+--------------+---------+---------------------+--------+----------+-------------+
| id | select_type | table      | partitions | type  | possible_keys        | key          | key_len | ref                 | rows   | filtered | Extra       |
+----+-------------+------------+------------+-------+----------------------+--------------+---------+---------------------+--------+----------+-------------+
|  1 | PRIMARY     | <derived2> | NULL       | ALL   | NULL                 | NULL         | NULL    | NULL                | 263103 |   100.00 | Using where |
|  1 | PRIMARY     | OUTSIDE    | NULL       | ref   | camtimestamp,camname | camtimestamp | 6       | INSIDE.maxtimestamp |      1 |    99.73 | Using where |
|  2 | DERIVED     | events     | NULL       | index | camname              | camname      | 257     | NULL                | 263103 |   100.00 | NULL        |
+----+-------------+------------+------------+-------+----------------------+--------------+---------+---------------------+--------+----------+-------------+
3 rows in set, 1 warning (0.03 sec)

Вот результаты запроса:

mysql> select OUTSIDE.ownerUID as Owner, 
  OUTSIDE.propUID as Property, 
  OUTSIDE.camname as 'Camera Name', 
  OUTSIDE.direction as Direction, 
  OUTSIDE.camtimestamp as 'Event Time', 
  convert_tz(now(),'UTC','US/Central') as Now, 
  sec_to_time(convert_tz(now(),'UTC','US/Central') - OUTSIDE.CamTimeStamp) as 'Elapsed Time' 
from events OUTSIDE, 
  (select camname,max(camtimestamp) as maxtimestamp from events group by camname) as INSIDE 
where OUTSIDE.camname = INSIDE.camname 
  AND OUTSIDE.camtimestamp = INSIDE.maxtimestamp;
+-------+----------+-------------+-----------+---------------------+---------------------+--------------+
| Owner | Property | Camera Name | Direction | Event Time          | Now                 | Elapsed Time |
+-------+----------+-------------+-----------+---------------------+---------------------+--------------+
|     1 |        1 | wls1        | In        | 2020-01-30 12:27:31 | 2020-01-30 12:29:53 | 00:03:42     |
|     1 |        1 | wls2        | Out       | 2020-01-30 12:25:29 | 2020-01-30 12:29:53 | 00:07:04     |
+-------+----------+-------------+-----------+---------------------+---------------------+--------------+
2 rows in set (6.49 sec)

================================================= ============== Спасибо всем, кто ответил на вопрос или прокомментировал. Я принял предложение Ууэрдо о составном индексе и получил следующие результаты:

mysql> create index CamNameCamTime on events (camname,camtimestamp);

mysql> select OUTSIDE.ownerUID as Owner,
           OUTSIDE.propUID as Property,
           OUTSIDE.camname as 'Camera Name',
           OUTSIDE.direction as Direction,
           OUTSIDE.camtimestamp as 'Event Time',
           convert_tz(now(),'UTC','US/Central') as Now,
           sec_to_time(convert_tz(now(),'UTC','US/Central') - OUTSIDE.CamTimeStamp) as 'Elapsed Time' 
      from events OUTSIDE,
         (select camname,max(camtimestamp) as maxtimestamp 
           from events group by camname) as INSIDE
      where OUTSIDE.camname = INSIDE.camname AND OUTSIDE.camtimestamp = INSIDE.maxtimestamp;
+-------+----------+-------------+-----------+---------------------+---------------------+--------------+
| Owner | Property | Camera Name | Direction | Event Time          | Now                 | Elapsed Time |
+-------+----------+-------------+-----------+---------------------+---------------------+--------------+
|     1 |        1 | wls1        | In        | 2020-01-30 18:43:19 | 2020-01-30 18:44:33 | 00:01:54     |
|     1 |        1 | wls2        | Out       | 2020-01-30 18:41:51 | 2020-01-30 18:44:33 | 00:04:42     |
+-------+----------+-------------+-----------+---------------------+---------------------+--------------+
2 rows in set (0.00 sec)

mysql> explain select OUTSIDE.ownerUID as Owner,
    ->            OUTSIDE.propUID as Property,
    ->            OUTSIDE.camname as 'Camera Name',
    ->            OUTSIDE.direction as Direction,
    ->            OUTSIDE.camtimestamp as 'Event Time',
    ->            convert_tz(now(),'UTC','US/Central') as Now,
    ->            sec_to_time(convert_tz(now(),'UTC','US/Central') - OUTSIDE.CamTimeStamp) as 'Elapsed Time'
    ->       from events OUTSIDE,
    ->          (select camname,max(camtimestamp) as maxtimestamp
    ->            from events group by camname) as INSIDE
    ->       where OUTSIDE.camname = INSIDE.camname AND OUTSIDE.camtimestamp = INSIDE.maxtimestamp;
+----+-------------+------------+------------+-------+-------------------------------------+----------------+---------+------------------------------------+------+----------+--------------------------+
| id | select_type | table      | partitions | type  | possible_keys                       | key            | key_len | ref                                | rows | filtered | Extra                    |
+----+-------------+------------+------------+-------+-------------------------------------+----------------+---------+------------------------------------+------+----------+--------------------------+
|  1 | PRIMARY     | <derived2> | NULL       | ALL   | NULL                                | NULL           | NULL    | NULL                               |    2 |   100.00 | Using where              |
|  1 | PRIMARY     | OUTSIDE    | NULL       | ref   | camtimestamp,camname,CamNameCamTime | CamNameCamTime | 263     | INSIDE.camname,INSIDE.maxtimestamp |    1 |   100.00 | NULL                     |
|  2 | DERIVED     | events     | NULL       | range | camname,CamNameCamTime              | CamNameCamTime | 257     | NULL                               |    2 |   100.00 | Using index for group-by |
+----+-------------+------------+------------+-------+-------------------------------------+----------------+---------+------------------------------------+------+----------+--------------------------+
3 rows in set, 1 warning (0.00 sec)




Успех!

Ответы [ 3 ]

1 голос
/ 31 января 2020

MySQL не может использовать более одного индекса для каждой ссылки на таблицу в запросе, поэтому два простых индекса для camname и camtimestamp в отдельности, вероятно, будут иметь ограниченную полезность в этом сценарии. Такие индексы идеально подходят для выбора записей в диапазоне дат или всех записей для имени; но при поиске максимальных значений для каждого имени индекс camname будет иметь сомнительную полезность (поскольку в любом случае он должен проверять каждую временную метку каждого имени), а camstimestamp все равно потребует поиска каждой записи, чтобы убедиться, что все имена камер являются учитывается (наименьшая camtimestamp может быть единственной для определенного имени камеры).

При составном индексе на (camname, camtimestamp) максимальные значения для каждого camname можно быстро определить в подзапросе, а затем index можно использовать снова, чтобы выбрать строки в таблице, соответствующие результатам подзапроса.

0 голосов
/ 30 января 2020

Если предлагаемый составной индекс комментариев (camname, camtimestamp) не помогает, это может быть сценарий добавления триггера в вашу базу данных. Если вы всегда ищете самую последнюю временную метку для данного имени камеры, я бы добавил столбец в таблицу имен камер, например LastEventID. Затем, во время вставки в таблицу событий, просто введите упрощенную

update CameraNameTable set 
      LastEventID = EventIDOfRecordJustInserted 
   where 
      CameraName = CameraNameOfEventInserted

. Тогда ваш запрос может быть таким простым, как

select
      E.*,
      [your additional time, convert value columns]
   from
      CameraTable CT
         JOIN Events E
            on CT.LastEventID = E.ID

. Чтобы заполнить таблицу имен камер после первого добавления. В этом новом столбце вы можете просто

Update CameraTable 
      JOIN ( select CamName, max( ID ) maxEventID
                from Events
                group by CamName ) PQ
         on CameraTable.CamName = PQ.CamName
   set LastEventID = PQ.maxEventID

Ваш триггер может добавить подсекундную синхронизацию к вставке, но даже если вы делаете только 2000 в день, то есть всего 4 в минуту в течение 8 часов в день. Я не вижу в этом ничего значительного. Ваша таблица всегда будет обновляться, как только будет вставлена ​​следующая запись с идентификатором события, поэтому вашему запросу больше не нужно применять max () и присоединяться. Вы присоединяетесь на основе точного идентификатора, который будет частью индекса и будет соответствовать только 1: 1.

0 голосов
/ 30 января 2020

Можете ли вы попробовать ниже sql out?

 SELECT OUTSIDE.owneruid 
       AS Owner, 
       OUTSIDE.propuid 
       AS Property, 
       OUTSIDE.camname 
       AS 'Camera Name', 
       OUTSIDE.direction 
       AS Direction, 
       OUTSIDE.camtimestamp 
       AS 'Event Time', 
       Convert_tz(Now(), 'UTC', 'US/Central') 
       AS Now, 
       Sec_to_time(Convert_tz(Now(), 'UTC', 'US/Central') - 
       OUTSIDE.camtimestamp)
       AS 'Elapsed Time' 
FROM   events OUTSIDE 

WHERE  OUTSIDE.camtimestamp = (SELECT Max(camtimestamp)
        FROM   events WHERE
        events.camname = OUTSIDE.camname); 

====================


Оригинальный квестор здесь. Я выполнил SQL выше, и запрос так и не закончился. Наконец мне пришлось нажать CTRL C, чтобы закончить. Я перепроверил соединение с БД и попробовал другие общие запросы c, которые успешно завершились, и повторил ваш SQL выше. Он сделал то же самое.

Вот ОБЪЯСНЕНИЕ для запроса:

mysql> explain SELECT OUTSIDE.owneruid
    ->        AS Owner,
    ->        OUTSIDE.propuid
    ->        AS Property,
    ->        OUTSIDE.camname
    ->        AS 'Camera Name',
    ->        OUTSIDE.direction
    ->        AS Direction,
    ->        OUTSIDE.camtimestamp
    ->        AS 'Event Time',
    ->        Convert_tz(Now(), 'UTC', 'US/Central')
    ->        AS Now,
    ->        Sec_to_time(Convert_tz(Now(), 'UTC', 'US/Central') -
    ->        OUTSIDE.camtimestamp)
    ->        AS 'Elapsed Time'
    -> FROM   events OUTSIDE
    ->
    -> WHERE  OUTSIDE.camtimestamp = (SELECT Max(camtimestamp)
    ->         FROM   events WHERE
    ->         events.camname = OUTSIDE.camname);
+----+--------------------+---------+------------+------+------------------------+----------------+---------+---------------------+--------+----------+-------------+
| id | select_type        | table   | partitions | type | possible_keys          | key            | key_len | ref                 | rows   | filtered | Extra       |
+----+--------------------+---------+------------+------+------------------------+----------------+---------+---------------------+--------+----------+-------------+
|  1 | PRIMARY            | OUTSIDE | NULL       | ALL  | NULL                   | NULL           | NULL    | NULL                | 264783 |   100.00 | Using where |
|  2 | DEPENDENT SUBQUERY | events  | NULL       | ref  | camname,CamNameCamTime | CamNameCamTime | 257     | lpr.OUTSIDE.camname | 263842 |   100.00 | Using index |
+----+--------------------+---------+------------+------+------------------------+----------------+---------+---------------------+--------+----------+-------------+
2 rows in set, 2 warnings (0.00 sec)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...