SQL性能優化

索引

每當後端技術人員講起SQL的調優時,第一個想到的方案每每是索引。先舉個最簡單的例子,這裏在tb表中給字段tb_name加上普通的索引,由此根據該字段進行SELECT查詢時就無需進行全表遍歷,以加快查詢速度數據庫

CREATE INDEX tb_tb_a ON tb (tb_a);
SELECT tb_id FROM tb WHERE tb_a='Sherry';

如下幾種狀況將沒法使用索引:後端

  1. 使用OR
    帶有OR操做的語句即便其中部分帶了索引字段的也不會使用,如下SELECT操做是使用不了索引的,因此SQL要儘可能少用OR操做服務器

    CREATE INDEX tb_tb_a ON tb (tb_a);
    SELECT tb_id FROM tb WHERE tb_a='Sherry' OR tb_b='Billy';(索引失效)

    若是要想使用OR而且讓索引生效,只能將OR條件裏每一個相關列都加上索引!!!函數

  2. 使用多列索引時測試

    CREATE INDEX tb_tb_a_tb_b ON tb (tb_a,tb_b);

    這個多列索引本質上是建立了兩個索引,分別是tb_atb_a_tb_b(可理解爲字段最左部的某連續部分),則對如下SELECT語句產生不一樣的結果優化

    SELECT tb_id FROM tb WHERE tb_a='Sherry';(索引生效)
    SELECT tb_id FROM tb WHERE tb_b='Sherry';(索引失效)
    SELECT tb_id FROM tb WHERE tb_a='Sherry' AND tb_b='Billy';(索引生效)

    多列索引tb_tb_a_tb_b和分開對tb_atb_b字段建立兩個獨立的索引的區別是多列索引能順序地利用所包含的字段索引,而分開建立的索引則會選擇最嚴格(能夠理解爲所選出結果集最小的索引)的索引來進行檢索,其餘相關的索引也不會被使用,故效果不如多列索引。另外創建多列索引時,須要注意索引所用字段的順序,應將最嚴格的索引放在最前面使索引產生更好的效果設計

  3. 使用LIKE接以%開頭的字符串時code

    CREATE INDEX tb_tb_a ON tb (tb_a);
    SELECT tb_id FROM tb WHERE tb_a LIKE 'She%';(索引生效)
    SELECT tb_id FROM tb WHERE tb_a LIKE '%rry';(索引失效)

    上面這種失效的狀況下,可使用另外一種方式索引

    SELECT tb_id FROM tb WHERE REVERSE(tb_a) LIKE 'yrr%';(索引生效)

    即對所需查詢的字段作一次翻轉而後再進行LIKE操做,就能夠達到利用索引的目的,不過這裏又涉及到在SQL中使用函數的問題可能影響效率,所以最好對實際狀況進行測試而決定使用方式,但這種方式不適用於LIKE '%xxx%'之類的SQL調優字符串

  4. 列是字符串類型時
    假設字段tb_astring類型

    CREATE INDEX tb_tb_a ON tb (tb_a);
    SELECT tb_id FROM tb WHERE tb_a='123456';(索引生效)
    SELECT tb_id FROM tb WHERE tb_a=123456;(索引失效)

    字符串字段的查詢參數不加引號時雖然在某些狀況下能查詢成功,但並不能利用到已建立的索引

字段

  1. SELECT語句中所提取的字段儘可能少,通常只取出須要的字段,千萬不要爲了方便編寫SQL語句而使用如下相似作法

    SELECT * FROM tb WHERE tb_gender=0;

    當你讀取出來的記錄量很大時更要禁止這種作法,這就是爲何我在本篇文章中寫的SQL都是SELECT tb_id之類來做爲例子,而不是SELECT *,這個讀者能夠親測,即便你數據庫的數據量不是不少,你也能發現當你SELECT *SELECT tb_id時的耗時差異有多大(PS:某次項目經歷中我就由於這個問題致使兩個SQL的耗時分別是 1100ms 和 200ms)

  2. VARCHAR類型的字段長度在儘可能合理範圍內分配,無需分配過多

  3. 儘可能使用TINYINTSMALLINTMEDIUM_INT做爲整數字段類型而不是INT

  4. 設計容許的狀況下,儘可能將字段可否爲NULL屬性設置爲NOT NULL,不然將可能致使引擎放棄使用索引而進行全表掃描

鏈接表

有時候爲了取到多個表的字段,編寫SQL時會使用一次甚至屢次JOIN操做,在進行多表鏈接時應使各個表的數據集儘可能少,舉個例子,好比如今tb1表數據量很大

SELECT tb1.tb1_name FROM tb1 LEFT JOIN tb2 ON tb2.tb2_otherid=tb1.tb1_id WHERE tb1.tb1_gender=0;

上面語句JOIN操做時會進行tb1tb2兩個表全部數據集鏈接操做,爲了減少鏈接操做的數據集,可將其改成

SELECT tb1.tb1_name FROM (SELECT tb1.tb1_id, tb1.tb1_name FROM tb1 WHERE tb1.tb1_gender=0) AS tb1 LEFT JOIN tb2 ON tb2.tb2_otherid=tb1.tb1_id;

這樣一來,JOIN左邊的數據集就僅僅是tb1_gender=0篩選出來的數據集而不是tb1全部數據集,從而提升了JOIN操做的執行速度。要注意一點是,JOIN操做的查詢效率要比子查詢高得多,因此可使用JOIN操做的狀況下儘可能減小或杜絕子查詢操做

計算操做

儘可能避免在SQLJOINWHERE部分使用計算操做,由於大多數涉及到在SQL中計算操做的狀況每每會使索引失效而進行了全表遍歷操做或者加大了數據庫的負擔,而這些原本是能夠放到業務服務器上進行處理的,如

CREATE INDEX tb_tb_time ON tb (tb_time);
SELECT tb_id FROM tb WHERE YEAR(tb_time)='2012';(調用YEAR函數本質上也是計算操做)

這種狀況不只不能利用索引,還會給數據庫帶來更大的計算負擔,而這種狀況幾乎不須要給業務服務器帶來更大負擔就能夠進行優化,只須要將SQL修改成

SELECT tb_id FROM tb WHERE tb_time BETWEEN '2012-01-01 00:00:00' AND '2012-12-31 23:59:59'

便可

相關文章
相關標籤/搜索