避免向資料庫請求不需要的資料
在訪問資料庫時,應該只請求需要的行和列。請求多餘的行和列會消耗mysql伺服器的cpu和記憶體資源,並增加網路開銷。
例如在處理分頁時,應該使用limit限制mysql只返回一頁的資料,而不是向應用程式返回全部資料後,再由應用程式過濾不需要的行。
當一行資料被多次使用時可以考慮將資料行快取起來,避免每次使用都要到mysql查詢。
避免使用select *這種方式進行查詢,應該只返回需要的列。
查詢資料的方式
查詢資料的方式有全表掃瞄、索引掃瞄、範圍掃瞄、唯一索引查詢、常數引用等。這些查詢方式,速度從慢到快,掃瞄的行數也是從多到少。可以通過explain語句中的type列反應查詢採用的是哪種方式。
通常可以通過新增合適的索引改善查詢資料的方式,使其盡可能減少掃瞄的資料行,加快查詢速度。
例如,當發現查詢需要掃瞄大量的資料行但只返回少數的行,那麼可以考慮使用覆蓋索引,即把所有需要用到的列都放到索引中。這樣儲存引擎無須回表獲取對應行就可以返回結果了。
分解大的查詢
可以將乙個大查詢切分成多個小查詢執行,每個小查詢只完成整個查詢任務的一小部分,每次只返回一小部分結果
刪除舊的資料是乙個很好的例子。如果只用一條語句一次性執行乙個大的刪除操作,則可能需要一次鎖住很多資料,佔滿整個事務日誌,耗盡系統資源、阻塞很多小的但重要的查詢。將乙個大的刪除操作分解成多個較小的刪除操作可以將伺服器上原本一次性的壓力分散到多次操作上,盡可能小地影響mysql效能,減少刪除時鎖的等待時間。同時也減少了mysql主從複製的延遲。
另乙個例子是分解關聯查詢,即對每個要關聯的表進行單錶查詢,然後將結果在應用程式中進行關聯。下面的這個查詢:
select * from tag
join tag_post on tag_post.tag_id=tag.id
join post on tag_post.post_id=post.id
where tag.tag = 'mysql';
可以分解成下面這些查詢來代替:
select * from tag where tag = 'mysql';
select * from tag_post where tag_id = 1234;
select * from post where post.id in (123,456,567,9098,8904);
將乙個關聯查詢拆解成多個單錶查詢有如下有點:
讓快取的效率更高。如果快取的是關聯查詢的結果,那麼其中的乙個表發生變化,整個快取就失效了。而拆分後,如果只是某個表很少的改動,並不會破壞所有的快取。
可以減少鎖的競爭
更容易對資料庫進行拆分,更容易做到高效能和可擴充套件。
查詢本身的效率也有可能會有所提公升。例如上面用in()代替關聯查詢比隨機的關聯更加高效。
優化min()和max()
新增索引可以優化min()和max()表示式。例如,要找到某一列的最小值,只需要查詢對應b-tree索引的最左端的記錄即可。類似的,如果要查詢列中的最大值,也只需要讀取b-tree索引的最後一條記錄。對於這種查詢,explain中可以看到"select tables optimized away",表示優化器已經從執行計畫中移除了該錶,並以乙個常數取而代之。
用in()取代or
在mysql中,in()先將自己列表中的資料進行排序,然後通過二分查詢的方式確定列的值是否在in()的列表中,這個時間複雜度是o(logn)。如果換成or操作,則時間複雜度是o(n)。所以,對於in()的列表中有大量取值的時候,用in()替換or操作將會更快。
優化關聯查詢
在mysql中,任何乙個查詢都可以看成是乙個關聯查詢,即使只有乙個表的查詢也是如此。
mysql對任何關聯都執行巢狀迴圈的關聯操作,例如對於下面的sql語句:
select tbl1.col1,tbl2.col2
from tbl1 inner join tbl2 using(col3)
where tbl1.col1 in(5,6);
下面的偽**表示mysql將如何執行這個查詢:
//先從第乙個表中取出符合條件的所有行
out_iter = iterator over tbl1 where col1 in(5,6)
outer_row = out_iter.next
//在while迴圈中遍歷第乙個表結果集的每一行
while outer_row
//對於第乙個表結果集中的每一行,在第二個表中找出符合條件的所有行
inner_iter = iterator over tbl2 where col3 = outer_row.col3
inner_row = inner_iter.next
while inner_row
//將第乙個表的結果列和第二個表的結果列拼裝在一起作為結果輸出
output[outer_row.col1, inner_row.col2]
inner_row = inner_iter.next
end//回溯,再根據第乙個表結果集的下一行,繼續上面的過程
outer_row = outer_iter.next
end對於單錶查詢,那麼只需要完成上面外層的基本操作。
優化關聯查詢,要確保on或者using子句中的列上有索引,並且在建立索引時需要考慮到關聯的順序。通常來說,只需要在關聯順序中的第二個表的相應列上建立索引。例如,當表a和表b用列c關聯的時候,假設關聯的順序是b、a,那麼就不需要在b表的c列上建立索引。沒有用到的索引只會帶來額外的負擔。
此外,確保任何的group by和order by中的表示式只涉及到乙個表中的列,這樣才能使用索引來優化這個過程。
臨時表的概念
上面提到在mysql中,任何乙個查詢實質上都是乙個關聯查詢。那麼對於子查詢或union查詢是如何實現關聯操作的呢。
對於union查詢,mysql先將每乙個單錶查詢結果放到乙個臨時表中,然後再重新讀出臨時表資料來完成union查詢。mysql讀取結果臨時表和普通表一樣,也是採用的關聯方式。
當遇到子查詢時,先執行子查詢並將結果放到乙個臨時表中,然後再將這個臨時表當做乙個普通表對待。
mysql的臨時表是沒有任何索引的,在編寫複雜的子查詢和關聯查詢的時候需要注意這一點。
臨時表也叫派生表。
排序優化
應該盡量讓mysql使用索引進行排序。當不能使用索引生成排序結果的時候,mysql需要自己進行排序。如果資料量小於「排序緩衝區」的大小,則mysql使用記憶體進行「快速排序」操作。如果資料量太大超過「排序緩衝區」的大小,那麼mysql只能採用檔案排序,而檔案排序的演算法非常複雜,會消耗很多資源。
無論如何排序都是乙個成本很高的操作,所以從效能角度考慮,應盡可能避免排序。所以讓mysql根據索引構造排序結果非常的重要。
子查詢優化
mysql的子查詢實現的非常糟糕。最糟糕的一類查詢是where條件中包含in()的子查詢語句。
應該盡可能用關聯替換子查詢,可以提高查詢效率。
優化count()查詢
count()有兩個不同的作用:
統計某個列值的數量,即統計某列值不為null的個數。
統計行數。
當使用count(*)時,統計的是行數,它會忽略所有的列而直接統計所有的行數。而在括號中指定了乙個列的話,則統計的是這個列上值不為null的個數。
可以考慮使用索引覆蓋掃瞄或增加彙總表對count()進行優化。
優化limit分頁
處理分頁會使用到limit,當翻頁到非常靠後的頁面的時候,偏移量會非常大,這時limit的效率會非常差。例如對於*limit 10000,20*這樣的查詢,mysql需要查詢10020條記錄,將前面10000條記錄拋棄,只返回最後的20條。這樣的代價非常高,如果所有的頁面被訪問的頻率都相同,那麼這樣的查詢平均需要訪問半個表的資料。
優化此類分頁查詢的乙個最簡單的辦法就是盡可能地使用索引覆蓋掃瞄,而不是查詢所有的列。然後根據需要與原表做一次關聯操作返回所需的列。對於偏移量很大的時候,這樣的效率會提公升非常大。考慮下面的查詢:
select film_id, description from sakila.film order by title limit 50, 5;
如果這個表非常大,那麼這個查詢最好改寫成下面的這樣子:
select film.film_id, film.description from sakila.film
inner join
(select film_id from sakila.film order by title limit 50,5) as lim
using(film_id);
注意優化中關聯的子查詢,因為只查詢film_id乙個列,資料量小,使得乙個記憶體頁可以容納更多的資料,這讓mysql掃瞄盡可能少的頁面。在獲取到所需要的所有行之後再與原表進行關聯以獲得需要的全部列。
limit的優化問題,其實是offset的問題,它會導致mysql掃瞄大量不需要的行然後再拋棄掉。可以借助書籤的思想記錄上次取資料的位置,那麼下次就可以直接從該書籤記錄的位置開始掃瞄,這樣就避免了使用offset。可以把主鍵當做書籤使用,例如下面的查詢:
select * from sakila.rental order by rental_id desc limit 20;
select * from sakila.rental where rental_id < 16030
order by rental_id desc limit 20;
該技術的好處是無論翻頁到多麼後面,其效能都會很好。
此外,也可以用關聯到乙個冗餘表的方式提高limit的效能,冗餘表只包含主鍵列和需要做排序的資料列。
優化union查詢
除非確實需要伺服器消除重複的行,否則一定要使用union all。如果沒有all關鍵字,mysql會給臨時表加上distinct選項,這會導致對整個臨時表的資料做唯一性檢查。這樣做的代價非常高。
mysql離散查詢 如何寫出高效能的MySQL查詢
想寫這樣一篇文章很久了,但始終沒有下手。最近幫同事看了幾個查詢,而且自己也在考慮乙個索引系統的問題,所以今天就把這個寫了。介紹一下mysql的索引機制,還有一些mysql查詢的優化策略。鄙人才疏學淺,很可能說的不對,請路過的各位大俠批評指正,獻醜了。首先,說說mysql的索引儲存方式。mysql的索...
mysql查詢效能優化 MySQL 查詢效能優化
在日常開發中,程式設計師寫的最多的除了bug之外,應該算是sql語句了。sql的質量影響了程式的響應速度,只有利用mysql的特性,才能讓mysql更有效的執行查詢sql,充分發揮mysql的優勢,並避開它的弱點。為什麼查詢速度會慢?在編寫sql之前,需要清楚一點 真正重要的是響應時間。如果我們把查...
mysql子查詢效能 MySQL子查詢效能
1 語法 子查詢有in和exists兩種,哪種速度更快呢?主要取決於兩張表的大小關係。select from a where cc in select cc from b select from a where exists select cc from b where b.cc a.cc in會先...