Dizinlenmiş tarih-saat sütununu kullanan MySQL performans sorunu


15

Yaklaşık bir saat boyunca aşağıdaki sorunu çözmeye çalıştım ve hala daha fazla alamadım.

Tamam, bir masam var (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 |                |
+---------+-------------+------+-----+-------------------+----------------+

Lütfen dizinlere aldırmayın, bir çözüm bulmaya çalışıyorum. Şimdi, işte sorgum.

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;

tablo gelen web istekleri hakkında bilgi depolamak böylece oldukça büyük bir veritabanı.

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

kimlik sütunu sahip olduğum tek benzersiz tanımlayıcı olacağından birincil anahtarı ayarlamanın daha iyi bir yolu olmadığını unutmayın . Yukarıda belirtilen sorgunun çalışması yaklaşık 0.6-1.6 saniye sürer.

Hangi indeks akıllı olur? İndeksleme tarihinin bana "kötü" kardinalite vereceğini düşündüm ve böylece MySQL bunu kullanmayacak.http sadece kötü bir seçimdir, çünkü sadece yaklaşık 20 farklı olası değer vardır.

Yardımın için teşekkürler!

Güncelleme 1 Ypercube'ün önerdiği gibi (http, tarih) üzerine bir dizin ekledim:

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

ve sorgusunu kullandı, ama aynı derecede kötü performans gösterdi. Eklenen dizin:

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

ve EXPLAIN

+----+--------------------+-------+-------+---------------+----------+---------+------+-------+-----------------------------------------------------------+
| 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 sunucu sürümü:

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)

Ayrıca mysql sürümünü ekleyebilir ve tablonun motoru nedir? (myisam or innodb)
ypercubeᵀᴹ

MyISAM ve 5.1.73 - tüm detaylar artık yayında.
Robin Heller

Korkarım httpsütun null edilebilir olması ile ilgili olabilir . Zaman bulursam yarın araştıracağım.
ypercubeᵀᴹ

Korkarım http sütununun geçersiz olmasıyla ilgili olabilir. Zaman bulursam yarın araştıracağım. Özdeş bir tablo oluşturarak (hariç http NOT NULL) ve tüm verileri bu tabloya kopyalayarak test edebilirsiniz (elbette http NULL içeren satırlar hariç.)
ypercubeᵀᴹ 18

DEĞİL DEĞİL DEĞİŞTİRME (tamamen mümkün, ben tablo oluştururken çok umursamadım) sorgu için yaklaşık ~ 1s - 1.6s performansı artırdı (benim sorgu). Şimdiye kadar gösterdiğiniz çaba için teşekkürler.
Robin Heller

Yanıtlar:


10

Üç önerim var

ÖNERİ # 1: Sorguyu yeniden yazın

Sorguyu aşağıdaki gibi yeniden yazmalısınız

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

veya

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;

NEREDE eşittir işaretinin her iki tarafında bir işlev olmamalıdır. Eşittir işaretinin sol tarafında tarih olması, Sorgu Optimize Edici'nin buna karşı bir dizin kullanmasını kolaylaştırır.

ÖNERİ # 2: Destekleyici Endeks

Ayrıca farklı bir indeks öneririm

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

Bu sütun sırasını öneririm çünkü dategirdilerin tümü dizinde bitişik olacaktır. Ardından, sorgu httpboşlukları atlamadan değerleri toplar http.

ÖNERİ # 3: Daha Büyük Anahtar Arabelleği (İsteğe Bağlı)

MyISAM yalnızca dizin önbellekleme kullanır. Sorgu .MYDdosyaya dokunmaması gerektiğinden, biraz daha büyük bir MyISAM Anahtar Arabelleği kullanmalısınız.

256M olarak ayarlamak için

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

Ardından, my.cnf

[mysqld]
key_buffer_size = 256M

MySQL'in yeniden başlatılması gerekmez

Bir şans ver !!!


Bana verdiğin sorguları denedim. # 1 diğer öneri veya benimki kadar iyi performans gösterdi, ikincisi aslında daha kötü performans gösterdi. Destekleyici Endeks için aynı şey - performansın yaklaşık yüzde 75 düşmesini sağlayın. Şimdi daha büyük anahtar arabelleğini deneyeceğim, yine de teşekkürler!
Robin Heller

Sorunu çözmeme rağmen cevabınızı kabul ettim, ancak daha büyük bir anahtar tamponuyla, ancak biraz daha iyi performans gösterdi. Bu, tüm verilenlerin en iyi çözümü olduğu için kapatılıyor. Teşekkür ederim!
Robin Heller

Öneri # 2'nin çalışması için, sorguya "KULLANIM ENDEKSİ" veya "FORCE İNDEKSİ" eklemek gerekebilir, en azından böyle bir dizin oluşturduktan sonra sorgumu hızlandırmak için yapmam gereken buydu.
Johano Fierra

-2

Tarih sütunu türünüzü bir tamsayı olarak değiştirin. Tarihi tamsayı olarak Unix tarihi olarak saklayın. Zaman Damgası Bir int'den çok daha büyük. Bundan biraz patlarsınız.


3
Dalgamı geçiyorsun? Hem INTve TIMESTAMP4 bayt gerekir.
ypercubeᵀᴹ

3
Tarihleri ​​veya zaman damgalarını tamsayı olarak saklarken tüm datetime işlevlerini kaybettiğinizden bahsetmiyoruz.
ypercubeᵀᴹ
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.