Problem wydajności MySQL przy użyciu kolumny indeksowanej daty / godziny

15

Próbowałem rozwiązać następujący problem przez około godzinę i nadal nie dotarłem dalej.

Ok, mam stolik (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 |                |
+---------+-------------+------+-----+-------------------+----------------+

Nie przejmuj się indeksami, bawiłem się, próbując znaleźć rozwiązanie. Oto moje zapytanie.

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;

tabela przechowuje informacje o przychodzących żądaniach internetowych, więc jest to dość duża baza danych.

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

zauważ, że nie ma lepszego sposobu na ustawienie klucza podstawowego, ponieważ kolumna id będzie jedynym unikalnym identyfikatorem, jaki mam. Wyżej wymienione zapytanie trwa około 0,6-1,6 sekundy.

Który indeks byłby sprytny? Doszedłem do wniosku, że data indeksowania da mi „złą” liczność, a zatem MySQL go nie użyje. http jest również złym wyborem, ponieważ istnieje tylko około 20 różnych możliwych wartości.

Dzięki za pomoc!

Aktualizacja 1 Dodałem indeks (http, data) zgodnie z sugestią ypercube:

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

i użył swojego zapytania, ale działało równie źle. Dodano indeks:

+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| 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      |         |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

i WYJAŚNIĆ

+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+
| 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                                  |
+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+

Wersja serwera 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)
Robin Heller
źródło
Czy możesz również dodać wersję mysql i jaki jest silnik tabeli? (myisam lub innodb)
ypercubeᵀᴹ
MyISAM i 5.1.73 - wszystkie szczegóły teraz w poście.
Robin Heller,
Obawiam się, że może to mieć związek z httpkolumną zerowalną. Zbadam jutro, jeśli znajdę czas.
ypercubeᵀᴹ
Obawiam się, że może to mieć związek z zerowaniem kolumny http. Zbadam jutro, jeśli znajdę czas. Możesz przetestować, tworząc identyczną tabelę (oprócz z http NOT NULL) i kopiując do niej wszystkie dane (oczywiście oprócz wierszy z http NULL).
ypercubeᵀᴹ
Zmiana go na NOT NULL (co jest całkowicie możliwe, nie przeszkadzało mi to zbytnio podczas tworzenia tabeli) zwiększyła wydajność do około ~ 1s - 1.6s dla zapytania (mojego zapytania). Dzięki za twój wysiłek do tej pory.
Robin Heller,

Odpowiedzi:

10

Mam trzy sugestie

SUGGESTION # 1: Przepisz zapytanie

Powinieneś przepisać zapytanie w następujący sposób

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

lub

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;

GDZIE nie powinno mieć funkcji po obu stronach znaku równości. Posiadanie daty po lewej stronie znaku równości ułatwia Optymalizatorowi zapytań użycie indeksu przeciwko niemu.

SUGGESTION # 2: Supporting Index

Sugerowałbym również inny indeks

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

Sugeruję tę kolejność kolumn, ponieważ wszystkie datewpisy będą ciągłe w indeksie. Następnie zapytanie po prostu zbiera httpwartości bez pomijania lukhttp .

SUGESTIA # 3: Większy bufor kluczy (opcjonalnie)

MyISAM używa tylko buforowania indeksu. Ponieważ zapytanie nie powinno dotykać.MYD pliku, należy użyć nieco większego bufora kluczy MyISAM.

Aby ustawić go na 256M

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

Następnie ustaw my.cnf

[mysqld]
key_buffer_size = 256M

Ponowne uruchomienie MySQL nie jest wymagane

Spróbuj !!!

RolandoMySQLDBA
źródło
Próbowałem zapytań, które mi zadałeś. # 1 działał mniej więcej tak dobrze, jak inne sugestie lub moje, drugi faktycznie gorzej. To samo dotyczy wskaźnika pomocniczego - spraw, aby wydajność spadła o około 75 procent. Wypróbuję teraz większy bufor kluczy, i tak dziękuję!
Robin Heller,
Przyjąłem twoją odpowiedź, chociaż to nie rozwiązało problemu, z większym buforem klucza, jednak działał nieco lepiej. Zamknięcie tego, ponieważ jest to najlepsze rozwiązanie ze wszystkich podanych. Dziękuję Ci!
Robin Heller,
Aby sugestia nr 2 zadziałała, może być konieczne dodanie do zapytania „USE INDEX” lub „FORCE INDEX”, przynajmniej tak musiałem zrobić, aby przyspieszyć moje zapytanie po utworzeniu takiego indeksu.
Johano Fierra
-2

Zmień typ kolumny daty na liczbę całkowitą. Przechowuj datę jako datę uniksową w liczbach całkowitych. Znacznik czasu Jest znacznie większy niż int. Dostaniesz z tego trochę huku.

Apachebeard
źródło
3
Żartujesz? Oba INTi TIMESTAMPpotrzebują 4 bajtów.
ypercubeᵀᴹ
3
Nie wspominając o tym, że tracisz wszystkie funkcje daty i godziny, gdy przechowujesz daty lub znaczniki czasu jako liczby całkowite.
ypercubeᵀᴹ