今天面某家公司,然后問我SQL優化,感覺有點忘了,今天特此總結一下:
總結得是分兩方面:索引優化和查詢優化;
一. 索引優化:
1. 獨立的列
在進行查詢時,索引列不能是表達式的一部分,也不能是函數的參數,否則無法使用索引。
例如下面的查詢不能使用 actor_id 列的索引:
#這是錯誤的 SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;
優化方式:可以將表達式、函數操作移動到等號右側。如下:
SELECT actor_id FROM sakila.actor WHERE actor_id = 5 - 1;
2. 多列索引
SELECT film_id, actor_ id FROM sakila.film_actor WHERE actor_id = 1 AND film_id = 1;
3. 索引列的順序
索引的選擇性是指:不重復的索引值和記錄總數的比值。最大值為 1,此時每個記錄都有唯一的索引與其對應。選擇性越高,每個記錄的區分度越高,查詢效率也越高。
例如下面顯示的結果中 customer_id 的選擇性比 staff_id 更高,因此最好把 customer_id 列放在多列索引的前面。
SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity, COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity, COUNT(*) FROM payment; #結果如下 staff_id_selectivity: 0.0001 customer_id_selectivity: 0.0373 COUNT(*): 16049
4. 前綴索引
5. 覆蓋索引
-
-
一些存儲引擎(例如 MyISAM)在內存中只緩存索引,而數據依賴於操作系統來緩存。因此,只訪問索引可以不使用系統調用(通常比較費時)。
-
對於 InnoDB 引擎,若輔助索引能夠覆蓋查詢,則無需訪問主索引。
以下為優先使用索引,避免全表掃描:
6. mysql在使用like進行模糊查詢的時候把%放后面,避免開頭模糊查詢
因為mysql在使用like查詢的時候只有使用后面的%時,才會使用到索引。
#進行全表查詢,沒有用到索引 EXPLAIN SELECT * FROM `user` WHERE username LIKE '%ptd_%'; EXPLAIN SELECT * FROM `user` WHERE username LIKE '%ptd_'; #有用到索引 EXPLAIN SELECT * FROM `user` WHERE username LIKE 'ptd_%';
再比如:經常用到的查詢數據庫中姓張的所有人:
SELECT * FROM `user` WHERE username LIKE '張%';
7. 在表中建立索引,優先考慮where、group by使用到的字段
8. 盡量避免使用in 和not in,會導致數據庫引擎放棄索引進行全表掃描。
比如:
SELECT * FROM t WHERE id IN (2,3) SELECT * FROM t1 WHERE username IN (SELECT username FROM t2)
優化方式:如果是連續數值,可以用between代替。如下:
SELECT * FROM t WHERE id BETWEEN 2 AND 3
如果是子查詢,可以用exists代替。如下:
SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t2 WHERE t1.username = t2.username)
9. 盡量避免使用or,會導致數據庫引擎放棄索引進行全表掃描
如:
SELECT * FROM t WHERE id = 1 OR id = 3
優化方式:可以用union代替or。如下:
SELECT * FROM t WHERE id = 1 UNION SELECT * FROM t WHERE id = 3
10. 盡量避免進行null值的判斷,會導致數據庫引擎放棄索引進行全表掃描
SELECT * FROM t WHERE score IS NULL
優化方式:可以給字段添加默認值0,對0值進行判斷。如下:
SELECT * FROM t WHERE score = 0
11. 盡量避免在where條件中等號的左側進行表達式、函數操作,會導致數據庫引擎放棄索引進行全表掃描
同第1個,單獨的列;
SELECT * FROM t2 WHERE score/10 = 9 SELECT * FROM t2 WHERE SUBSTR(username,1,2) = 'li'
優化方式:可以將表達式、函數操作移動到等號右側。如下:
SELECT * FROM t2 WHERE score = 10*9 SELECT * FROM t2 WHERE username LIKE 'li%'
12. 當數據量大時,避免使用where 1=1的條件。通常為了方便拼裝查詢條件,我們會默認使用該條件,數據庫引擎會放棄索引進行全表掃描
SELECT * FROM t WHERE 1=1
優化方式:用代碼拼裝sql時進行判斷,沒where加where,有where加and。
索引的缺點:在數據庫進行DML操作的時候,除了維護數據表之外,還需要維護索引表,運維成本增加。
應用場景:數據量比較大,查詢字段較多的情況。
索引規則:
1.選用選擇性高的字段作為索引,一般unique的選擇性最高;
2.復合索引:選擇性越高的排在越前面。(左前綴原則);
3.如果查詢條件中兩個條件都是選擇性高的,最好都建索引;
參考:
2. SQL優化面試
二、查詢優化
1.
-
-
key : 使用的索引;
-
rows : 掃描的行數;
2.
-
只返回必要的列:最好不要使用 SELECT * 語句。
-
只返回必要的行:使用 LIMIT 語句來限制返回的數據。
-
緩存重復查詢的數據:使用緩存可以避免在數據庫中進行查詢,特別在要查詢的數據經常被重復查詢時,緩存帶來的查詢性能提升將會是非常明顯的。
-
減少服務器端掃描的行數
-
最有效的方式是使用索引來覆蓋查詢。
3.
將一個大連接查詢分解成對每一個表進行一次單表查詢,然后在應用程序中進行關聯,這樣做的好處有:
-
讓緩存更高效:對於連接查詢,如果其中一個表發生變化,那么整個查詢緩存就無法使用。而分解后的多個查詢,即使其中一個表發生變化,對其它表的查詢緩存依然可以使用。
-
分解成多個單表查詢,這些單表查詢的緩存結果更可能被其它查詢使用到,從而減少冗余記錄的查詢。
-
減少鎖競爭;
-
在應用層進行連接,可以更容易對數據庫進行拆分,從而更容易做到高性能和可伸縮。
-
查詢本身效率也可能會有所提升。例如下面的例子中,使用 IN() 代替連接查詢,可以讓 MySQL 按照 ID 順序進行查詢,這可能比隨機的連接要更高效。
SELECT * FROM tab 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);
