Проблема продуктивності MySQL з використанням індексованих стовпців часу


15

Я намагався вирішити наступну проблему протягом приблизно однієї години зараз, і все ще не впорався з цим.

Гаразд, у мене є таблиця (MyISAM):

+---------+-------------+------+-----+-------------------+----------------+
| Field   | Type        | Null | Key | Default           | Extra          |
+---------+-------------+------+-----+-------------------+----------------+
| id      | int(11)     | NO   | PRI | NULL              | auto_increment |
| http    | smallint(3) | YES  | MUL | 200               |                |
| elapsed | float(6,3)  | NO   |     | NULL              |                |
| cached  | tinyint(1)  | YES  |     | NULL              |                |
| ip      | int(11)     | NO   |     | NULL              |                |
| date    | timestamp   | NO   | MUL | CURRENT_TIMESTAMP |                |
+---------+-------------+------+-----+-------------------+----------------+

Будь ласка, не заважайте індексам, я розгулював, намагаючись знайти рішення. Тепер ось мій запит.

SELECT http,
COUNT( http )  AS count 
FROM reqs
WHERE DATE(date) >= cast(date_sub(date(NOW()),interval 24 hour) as datetime)
GROUP BY http
ORDER BY count;

таблиця зберігає інформацію про вхідні веб-запити, тому це досить велика база даних.

+-----------+
| count(id) |
+-----------+
|    782412 |
+-----------+

зауважте, що немає кращого способу встановлення первинного ключа, оскільки стовпець id буде єдиним унікальним ідентифікатором, який у мене є. Наведений вище запит займає приблизно 0,6-1,6 секунди.

Який індекс був би розумним? Я подумав, що дата індексації дасть мені «погану» кардинальність, і, отже, MySQL не використовуватиме її. http також є поганим вибором, оскільки існує лише близько 20 різних можливих значень.

Дякую за допомогу!

Оновлення 1 Я додав індекс на (http, дата), як запропонував ypercube:

mysql> CREATE INDEX httpDate ON reqs (http, date);

і використовував його запит, але він виконував однаково погано. Доданий індекс:

+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| reqs  |          0 | PRIMARY  |            1 | id          | A         |      798869 |     NULL | NULL   |      | BTREE      |         |
| reqs  |          1 | httpDate |            1 | http        | A         |          19 |     NULL | NULL   | YES  | BTREE      |         |
| reqs  |          1 | httpDate |            2 | date        | A         |       99858 |     NULL | NULL   |      | BTREE      |         |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

і ПОЯСНЕННЯ

+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+
| id | select_type        | table | type  | possible_keys | key      | key_len | ref  | rows  | Extra                                                     |
+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+
|  1 | PRIMARY            | r     | range | NULL          | httpDate | 3       | NULL |    20 | Using index for group-by; Using temporary; Using filesort |
|  2 | DEPENDENT SUBQUERY | ri    | ref   | httpDate      | httpDate | 3       | func | 41768 | Using where; Using index                                  |
+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+

Версія сервера MySQL:

mysql> SHOW VARIABLES LIKE "%version%";
+-------------------------+---------------------+
| Variable_name           | Value               |
+-------------------------+---------------------+
| protocol_version        | 10                  |
| version                 | 5.1.73              |
| version_comment         | Source distribution |
| version_compile_machine | x86_64              |
| version_compile_os      | redhat-linux-gnu    |
+-------------------------+---------------------+
5 rows in set (0.00 sec)

Чи можете ви також додати версію mysql і що таке двигун таблиці? (myisam or innodb)
ypercubeᵀᴹ

MyISAM та 5.1.73 - усі деталі тепер у пості.
Робін Геллер

Я боюся, що це може мати відношення до того, що httpстовпчик буде нульовим. Я завтра розслідую, якщо знайду час.
ypercubeᵀᴹ

Я боюся, що це може мати відношення до того, що стовпець http є нульовим. Я завтра розслідую, якщо знайду час. Ви можете перевірити, створивши ідентичну таблицю (за винятком http NOT NULL) та скопіювавши в неї всі дані (крім рядків з http NULL, звичайно.)
ypercubeᵀᴹ

Змінивши його на NOT NULL (що цілком можливо, я не сильно заперечував при створенні таблиці), підвищив продуктивність приблизно до ~ 1s - 1,6s для запиту (мій запит). Дякуємо за ваші зусилля до цих пір.
Робін Геллер

Відповіді:


10

У мене є три пропозиції

ПРОПОЗИЦІЯ №1: Перепишіть запит

Вам слід переписати запит наступним чином

SELECT http,
COUNT( http )  AS count 
FROM reqs
WHERE date >= ( DATE(NOW() - INTERVAL 1 DAY) + INTERVAL 0 SECOND )
GROUP BY http
ORDER BY count;

або

SELECT * FROM
(
    SELECT http,
    COUNT( http )  AS count 
    FROM reqs
    WHERE date >= ( DATE(NOW() - INTERVAL 1 DAY) + INTERVAL 0 SECOND )
    GROUP BY http
) A ORDER BY count;

БЕЗ не повинен мати функції з обох сторін знаку рівності. Наявлення дати з лівого боку знака рівності полегшує Оптимізатору запитів використання індексу проти нього.

ПРИЛОЖЕННЯ №2: Підтримка індексу

Я б також запропонував інший індекс

ALTER TABLE reqs ADD INDEX date_http_ndx (date,http); -- not (http,date) 

Я пропоную такий порядок стовпців, оскільки всі dateзаписи будуть суміжними в індексі. Потім запит просто збирає httpзначення, не пропускаючи прогалини http.

ПРИКЛАД №3: Більший ключ буфера (необов'язково)

MyISAM використовує лише кешування індексів. Оскільки запит не повинен торкатися .MYDфайлу, слід скористатися трохи більшим буфером ключів MyISAM.

Щоб встановити його на 256М

SET @newsize = 1024 * 1024 * 256;
SET GLOBAL key_buffer_size = @newsize;

Потім встановіть його my.cnf

[mysqld]
key_buffer_size = 256M

Перезапуск MySQL не потрібно

Спробувати !!!


Я спробував запити, які ви мені дали. # 1 виконується так само добре, як і інша пропозиція чи моя власна, друга - насправді гірше. Те ж саме стосується і індексу підтримки - зробіть зниження продуктивності приблизно на 75 відсотків. Я зараз спробую більш великий буфер ключів, дякую все одно!
Робін Геллер

Я прийняв вашу відповідь, хоча це не вирішило проблему, але більший буфер клавіш, проте виявився дещо кращим. Закриття цього, оскільки це найкраще рішення з усіх даних. Дякую!
Робін Геллер

Щоб пропозиція №2 працювала, можливо, знадобиться додати в запит "USE INDEX" або "FORCE INDEX", принаймні, це мені довелося зробити, щоб прискорити запит після створення подібного індексу.
Johano Fierra

-2

Змініть тип стовпця дати на ціле число. Збережіть дату як дату Unix у цілому. Мітка часу набагато більша за внутрішню. Ви отримаєте щось від цього.


3
Ви жартуєте? І те й іншеINT йTIMESTAMP потрібно 4 байти.
ypercubeᵀᴹ

3
Не кажучи вже про те, що ви зберігаєте всі функції часу, коли ви зберігаєте дати або часові позначки як цілі числа.
ypercubeᵀᴹ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.