[英]MySQL MyISAM table performance… painfully, painfully slow
我有一個表結構,可以總結如下:
pagegroup
* pagegroupid
* name
有3600行
page
* pageid
* pagegroupid
* data
引用頁組; 有10000行; 每頁組可以有1-700行之間的任何內容; 數據列的類型為mediumtext,該列每行包含100k - 200kbytes數據
userdata
* userdataid
* pageid
* column1
* column2
* column9
參考頁面; 有大約300,000行; 每頁可以有大約1-50行
上面的結構是相當直接的轉發,問題是從userdata到頁面組的連接非常非常慢,即使我已經索引了應該被索引的所有列。 運行此類連接的查詢所需的時間(userdata inner_join page inner_join pagegroup)超過3分鍾。 考慮到我根本沒有選擇數據列這一事實,這非常慢。 查詢示例耗時太長:
SELECT userdata.column1, pagegroup.name
FROM userdata
INNER JOIN page USING( pageid )
INNER JOIN pagegroup USING( pagegroupid )
請幫助解釋為什么需要這么長時間,我該怎么做才能讓它更快。
在胡言亂語之后解釋回復:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE userdata ALL pageid 372420
1 SIMPLE page eq_ref PRIMARY,pagegroupid PRIMARY 4 topsecret.userdata.pageid 1
1 SIMPLE pagegroup eq_ref PRIMARY PRIMARY 4 topsecret.page.pagegroupid 1
SELECT
u.field2, p.pageid
FROM
userdata u
INNER JOIN page p ON u.pageid = p.pageid;
/*
0.07 sec execution, 6.05 sec fecth
*/
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE u ALL pageid 372420
1 SIMPLE p eq_ref PRIMARY PRIMARY 4 topsecret.u.pageid 1 Using index
SELECT
p.pageid, g.pagegroupid
FROM
page p
INNER JOIN pagegroup g ON p.pagegroupid = g.pagegroupid;
/*
9.37 sec execution, 60.0 sec fetch
*/
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE g index PRIMARY PRIMARY 4 3646 Using index
1 SIMPLE p ref pagegroupid pagegroupid 5 topsecret.g.pagegroupid 3 Using where
如果遇到諸如此類的性能問題,請將中/長文本列保留在單獨的表中。
userdata表中columnX的數據類型和用途是什么? 應該注意的是,任何文本數據類型(即排除char,varchar)都會強制在磁盤上創建任何臨時表。 既然你在沒有條件,分組或排序的情況下進行直接連接,它可能不需要任何臨時表,除了聚合最終結果。
如果您向我們展示如何創建索引,我認為這也將非常有用。 需要記住的一點是,雖然InnoDB將表的主鍵連接到每個索引,但MyISAM卻沒有。 這意味着如果您索引列名並使用LIKE搜索它,但仍希望獲取頁組的ID ; 然后查詢仍然需要訪問表來獲取id,而不是能夠從索引中檢索它。
這意味着,在您的情況下,如果我理解您對apphacker的正確評論,那就是獲取每個用戶頁面組的名稱。 查詢優化器希望使用索引進行連接,但是對於每個結果,它還需要訪問該表以檢索頁組名稱。 如果您的數據類型名稱不大於中等varchar,即沒有文本,您還可以創建一個索引(id,name),使索引能夠直接從索引中獲取名稱。
作為最后的嘗試,您指出如果mediumtext不在頁表中,整個查詢可能會更快。
這有望使您能夠更快地加入,因為Pages中的列沒有占用太多空間。 然后,當您需要顯示某個頁面時,您將使用pageId-column上的PageData表連接以獲取顯示特定頁面所需的數據。
找出MySQL對您的查詢執行的操作的簡單方法是讓它向您解釋查詢。 運行此命令並查看輸出:
EXPLAIN SELECT userdata.column1, pagegroup.name
FROM userdata
INNER JOIN page USING( pageid )
INNER JOIN pagegroup USING( pagegroupid )
MySQL將告訴您它處理查詢的順序以及它使用的索引。 您創建索引的事實並不意味着MySQL實際使用它們。
另請參閱使用EXPLAIN優化查詢
編輯
EXPLAIN的輸出看起來很好。 它在userdata表上執行全表掃描,但這是正常的,因為您要返回其中的所有行。 優化此方法的最佳方法是重新考慮您的應用程序。 你真的需要返回所有372K行嗎?
我假設userdata表非常大並且不適合內存。 MySQL必須從硬盤讀取整個表,即使它只需要兩個小列。
您可以嘗試通過定義包含查詢所需內容的索引來消除掃描整個表的需要。 這樣,索引不是一種方便搜索主表的方法,但它是表本身的簡寫版本。 MySQL只需要從磁盤讀取速記表。
索引可能如下所示:
column1, pageid
這必須是非群集的,否則它將成為大表的一部分,從而破壞其目的。 有關MySQL如何決定群集索引的想法,請參閱此頁面 。 最簡單的方法似乎是確保在pageid上有一個主鍵,它將被群集化,因此輔助column1 + pageid索引將是非群集的。
一個可能的問題是MySQL每個查詢只使用一個索引,也許你沒有這些列的單個索引 - 或者MySQL的查詢優化器沒有選擇它。 EXPLAIN SELECT
&c在這里告訴你什么?
我會先打破查詢,弄清楚是否有一個慢速和一個快速部分,或者兩者都很慢(對不起,我不喜歡USING語法,所以我將使用ON):
SELECT
u.userdata, p.pageid
FROM
userdata u
INNER JOIN page p ON u.pageid = p.pageid
SELECT
p.pageid, g.pagegroupid
FROM
page
INNER JOIN pagegroup g ON p.pagegroupid = g.pagegroupid
這給你帶來了什么? 使用EXPLAIN EXTENDED
運行這些將提供額外的提示。
看起來你正在對userdata
上的所有行進行連接,然后嘗試選擇所有內容。 這是每一個page
的pagegroup
與userdata
。 WHERE
子句在WHERE
? 沒有LIMIT
,你想要多少結果? 你為什么不得到你的行數倒在userdata
在連續explain
結果,應加快查詢。 嘿。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.