Оптимизировать и ускорить выбор запросов MySQL - PullRequest
0 голосов
/ 20 ноября 2018

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

У меня есть 2 таблицы MySQL с отношением один-ко-многим .Одним из них является таблица user, которая содержит уникальный список пользователей и имеет около 22krows .Одним из них является таблица linedata, которая содержит все возможные координаты для каждого пользователя и имеет около 490k строк .

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

Структура CREATE STMT

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `isActive` tinyint(4) NOT NULL,
  `userId` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `gender` varchar(45) COLLATE utf8_unicode_ci NOT NULL,
  `age` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21938 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


CREATE TABLE `linedata` (
  `id` int(11) NOT NULL,
  `userId` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `timestamp` datetime NOT NULL,
  `x` float NOT NULL,
  `y` float NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Запрос выбора

SELECT 
        u.id, 
        u.isActive, 
        u.userId,
        u.name,
        u.gender,
        u.age,
        GROUP_CONCAT(CONCAT_WS(', ',timestamp,x, y)
                     ORDER BY timestamp ASC SEPARATOR '; '
                    ) as linedata_0

        FROM user u 
        JOIN linedata l
        ON u.id=l.id
        WHERE DATEDIFF(l.timestamp, '2018-02-28T20:00:00.000Z') >= 0
          AND DATEDIFF(l.timestamp, '2018-11-20T09:20:08.218Z') <= 0
        GROUP BY userId;

Вывод EXPLAIN

+-------+---------------+-----------+-----------+-------------------+-----------+---------------+-----------+-----------+------------------------------------------------------------+
    |   ID  |   SELECT_TYPE |   TABLE   |   TYPE    |   POSSIBLE_KEYS   |   KEY     |   KEY_LEN     |   REF     |   ROWS    |       EXTRA                                                |
    +-------+---------------+-----------+-----------+-------------------+-----------+---------------+-----------+-----------+------------------------------------------------------------+
    |   1   |   SIMPLE      |   l      |   ALL   |   NULL         |   NULL |      NULL        |   NULL    |   491157   |   "Using where; Using temporary; Using filesort" |
    +-------+---------------+-----------+-----------+-------------------+-----------+---------------+-----------+-----------+------------------------------------------------------------+
    |   1   |   SIMPLE      |   u      |   eq_ref  |   PRIMARY         |   PRIMARY |      4        |   l.id   |   1       |     NULL                                                   |
    +-------+---------------+-----------+-----------+-------------------+-----------+---------------+-----------+-----------+------------------------------------------------------------+

Запрос выбора работает, если, например, я добавлю еще одно условие WHERE для фильтрации отдельных пользователей.Допустим, я хочу выбрать только 200 пользователя, тогда я получил около 14 секунд в качестве времени выполнения.Около 7 секунд , если я выберу только первых 100 пользователей .Но в случае наличия только условия диапазона даты и времени загрузка кажется без конечной точки.Есть какие-нибудь предложения?

ОБНОВЛЕНИЕ

После выполнения рекомендаций Рика тестовый запрос составляет около 14 секунд.Здесь ниже EXPLAIN EXTENDED:

id, select_type, таблица, тип, возможные_ключи, ключ, key_len, ref, строки, отфильтрованные, Extra 1, PRIMARY, u, index, PRIMARY,PRIMARY, 4, NULL, 21959,100.00, NULL 1, PRIMARY, l, ref, id_timestamp_index, id_timestamp_index, 4, u.id, 14,100.00, «Использование условия индекса» 2, «ЗАВИСИМАЯ ПОДПИСЬ», NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, «Таблицы не используются»

Я немного изменил некоторые значения таблиц:

linedata table user table

Где id в пользовательской таблице может быть объединен с userId в таблице линеаданных.И теперь они целые.У нас будет строковый тип только для значения userId в пользовательской таблице, потому что это своего рода длинный строковый идентификатор, такой как 0000309ab2912b2fd34350d7e6c079846bb6c5e1f97d3ccb053d15061433e77a_0 .

Итак, просто для краткого примера мы будем иметь в user и в linedata таблице:

+-------+-----------+-----------+-------------------+--------+---+
|   id  | isActive  |   userId  |       name        | gender |age|
+-------+-----------+-----------+-------------------+--------+---+
|   1   |   1       |  x4by4d   |   john            | m      | 22|
|   2   |   1       |  3ub3ub   |   bob             | m      | 50|
+-------+-----------+-----------+-------------------+--------+---+



+-------+-----------+-----------+------+---+
|   id  | userId    |timestamp  |  x   | y |
+-------+-----------+-----------+------+----+
|   1   |   1       | somedate  |  30  | 10 |
|   2   |   1       | somedate  |  45  | 15 |
|   3   |   1       | somedate  |  50  | 20 |
|   4   |   2       | somedate  |  20  |  5 |
|   5   |   2       | somedate  |  25  | 10 |
+-------+-----------+-----------+------+----+

Я добавил составной индекс madeuserId и timestamp значений в таблице linedata.

Возможно, вместо того, чтобы иметь в качестве первичного ключа значение ai id для таблицы linedata, если я добавлю составной первичный ключ , сделанныйuserId + timestamp?Должно ли повысить производительность или нет?

1 Ответ

0 голосов
/ 21 ноября 2018

Мне нужно помочь вам исправить несколько ошибок, прежде чем обсуждать производительность.

Прежде всего, '2018-02-28T20:00:00.000Z' не будет работать в MySQL.Это должно быть '2018-02-28 20:00:00.000' и что-то должно быть сделано с часовым поясом.

Тогда не «скрывайте столбец в функции».То есть DATEDIFF(l.timestamp ...) не может использовать индексирование для timestamp.

Так что вместо

    WHERE  DATEDIFF(l.timestamp, '2018-02-28T20:00:00.000Z') >= 0
      AND  DATEDIFF(l.timestamp, '2018-11-20T09:20:08.218Z') <= 0

сделайте что-то вроде

    WHERE  l.timestamp >= '2018-02-28 20:00:00.000'
      AND  l.timestamp  < '2018-11-20 09:20:08.218'

Я в замешательстведве таблицы.Оба имеют id и userid, но вы присоединяетесь к id.Возможно, вместо

CREATE TABLE `linedata` (
  `id` int(11) NOT NULL,
  `userId` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  ...

вы имели в виду

CREATE TABLE `linedata` (
  `id` int(11) NOT NULL  AUTO_INCREMENT,  -- (the id for `linedata`)
  `userId` int NOT NULL,   -- to link to the other table
  ...
  PRIMARY KEY(id)
...

Тогда может быть несколько linedata строк для каждого user.

На этом этапе это

    JOIN  linedata l  ON u.id=l.id

становится

    JOIN  linedata l  ON u.id=l.userid

Теперь для производительности: linedata нужно INDEX(userid, timestamp) - в таком порядке.

Теперь подумайте о выводе.Вы запрашиваете до 22 тыс. Строк, возможно, в одном из столбцов нанизаны сотни «ts, x, y».Что получит столько данных?Будет ли он подавиться?

И GROUP_CONCAT имеет ограничение по умолчанию в 1024 байта.Это позволит получить около 50 баллов.Если «пользователь» может находиться в более чем 50 местах за 9 дней, попробуйте увеличить group_concat_max_len перед выполнением запроса.

Чтобы заставить его работать еще быстрее, переформулируйте его следующим образом:

SELECT  u.id, u.isActive, u.userId, u.name, u.gender, u.age,
        ( SELECT  GROUP_CONCAT(CONCAT_WS(', ',timestamp, x, y)
                      ORDER BY timestamp ASC
                      SEPARATOR '; ')
        ) as linedata_0
    FROM  user u
    JOIN  linedata l  ON u.id = l.userid
    WHERE  l.timestamp >= '2018-02-28 20:00:00.000'
      AND  l.timestamp  < '2018-11-20 09:20:08.218';

Другое дело.Возможно, вы захотите найти пользователя по name;так что добавьте INDEX(name)

О, какого черта VARCHAR(255) для userID ??Идентификаторы обычно целые числа.

...