1. 程式人生 > >mysql語句優化總結(一)

mysql語句優化總結(一)

Sql語句優化和索引

1.Innerjoin和左連線,右連線,子查詢

A.     inner join內連線也叫等值連線是,left/rightjoin是外連線。

SELECT A.id,A.name,B.id,B.name FROM A LEFT JOIN B ON A.id =B.id;

SELECT A.id,A.name,B.id,B.name FROM A RIGHT JOIN ON B A.id= B.id;

SELECT A.id,A.name,B.id,B.name FROM A INNER JOIN ON A.id =B.id;

經過來之多方面的證實inner join效能比較快,因為inner join是等值連線,或許返回的行數比較少。但是我們要記得有些語句隱形的用到了等值連線,如:

SELECT A.id,A.name,B.id,B.name FROM A,B WHERE A.id = B.id;

推薦:能用inner join連線儘量使用inner join連線

B.子查詢的效能又比外連線效能慢,儘量用外連線來替換子查詢。

  Select* from A where exists (select * from B where id>=3000 and A.uuid=B.uuid);

A表的資料為十萬級表,B表為百萬級表,在本機執行差不多用2秒左右,我們可以通過explain可以檢視到子查詢是一個相關子查詢(DEPENDENCE SUBQUERY);Mysql是先對外表A執行全表查詢,然後根據uuid逐次執行子查詢,如果外層表是一個很大的表,我們可以想象查詢效能會表現比這個更加糟糕。

  一種簡單的優化就是用innerjoin的方法來代替子查詢,查詢語句改為:

   Select* from A inner join B using(uuid) where b.uuid>=3000;

  這個語句執行測試不到一秒;

C.在使用ON 和 WHERE 的時候,記得它們的順序,如:

SELECT A.id,A.name,B.id,B.name FROM A LEFT JOIN B ON A.id =B.id WHERE B.NAME=’XXX’

執行過程會先執行ON 後面先過濾掉B表的一些行數。然而WHERE是後再過濾他們兩個連線產生的記錄。

不過在這裡提醒一下大家:ON後面的條件只能過濾出B表的條數,但是連線返回的記錄的行數還是A表的行數是一樣。如:

SELECT A.id,A.name,B.id,B.name FROM A LEFT JOIN B ON A.id =B.id;

返回的記錄數是A表的條數,ON後面的條件只起到過濾B表的記錄數,而

SELECT A.id,A.name,B.id,B.name FROM A ,B WHERE A.id = B.id

返回的條數,是笛卡爾積後,符合A.id = B.id這個條件的記錄

D.使用JOIN時候,應該用小的結果驅動打的結果(left join 左邊表結果儘量小,如果有條件應該放到左邊先處理,right join同理反向),同事儘量把牽涉到多表聯合的查詢拆分多個query(多個表查詢效率低,容易鎖表和阻塞)。如:

Select * from A left join B ona.id=B.ref_id where B.ref_id>10;

可以優化為:select * from (select * from A wehre id >10) T1 left join B onT1.id=B.ref_id;

2.建立索引,加快查詢效能.

A.在建立複合索引的時候,在where條件中用到的欄位在複合索引中,則最好把這個欄位放在複合索引的最左端,這樣才能使用索引,才能提高查詢。

B.保證連線的索引是相同的型別,意思就是A表和B表相關聯的欄位,必須是同類型的。這些型別都建立了索引,這樣才能兩個表都能使用索引,如果型別不一樣,至少有一個表使用不了索引。

C.索引,不僅僅是主鍵和唯一鍵,也可以是其他的任何列。在使用like其中一個有索引的欄位列的時候。

如: select *from A name like ‘xxx%’;

這個sql會使用name的索引(前提name建立了索引);而下面的語句就使用不了索引

Select * from A name like ‘%xxx’;

因為‘%’代表任何字元,%xxx不知道怎麼去索引的,所以使用不了索引。

D.複合索引

比如有一條語句這樣的:select* from users where area =’beijing’ and age=22;

如果我們是在area和age上分別建立索引的話,由於mysql查詢每次只能使用一個索引,所以雖然這樣已經相對不做索引時全表掃描提高了很多效率,但是如果area,age兩列上建立複合索引的話將帶來更高的效率。如果我們建立了(area,age,salary)的複合索引,那麼其實相當於建立了(area,age,salary),(area,age),(area)三個索引,這樣稱為最佳左字首特性。因此我們在建立複合索引的應該將最常用作限制條件的列放在最左邊,依次遞減。

E.索引不會包含有NULL值的列

只要列中包含有NULL值都將不會被包含在索引中(除非是唯一值的域,可以存在一個NULL),複合索引中只要有一列含有NULL值,那麼這一列對於此複合索引是無效的。所以我們在資料庫設計時不要讓欄位的預設值為NULL.

F.使用短索引

對串列進行索引,如果可能應該指定一個字首長度。例如,如果有一個CHAR(255)的列,如果在錢10個或者20字元內,多數值是唯一的,那麼就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁碟空間和I/O操作。

G.排序的索引問題

Mysql查詢只是用一個索引,因此如果where子句中已經使用了索引的話,那麼order by中的列是不會使用索引的。因此資料庫預設排序可以符合要求情況下不要使用排序操作;儘量不要包含多個列的排序,如果需要最好給這些列建立複合索引。

3.limit千萬級分頁的時候優化。

A.在我們平時用limit,如:

Select * from A order by id limit 1,10;

這樣在表資料很少的時候,看不出什麼效能問題,倘若到達千萬級,如:

Select * from A order by id limit10000000,10;

雖然都是隻查詢10記錄,但是這個就效能就讓人受不了了。所以為什麼當表資料很大的時候,我們還繼續用持久層框架如hibernate,ibatis就會有一些效能問題,除非持久層框架對這些大資料表做過優化。

B.在遇見上面的情況,我們可以用另外一種語句優化,如:

Select * from A where id>=(Select idfrom a limit 10000000,1) limit 10;

確實這樣快了很多,不過前提是,id欄位建立了索引。也許這個還不是最優的,其實還可以這樣寫:

Select * from A where id between 10000000and 10000010;

這樣的效率更加高。

4.儘量避免Select * 命令

A.從表中讀取越多的資料,查詢會變得更慢。它會增加磁碟的操作時間,還是在資料庫伺服器與web伺服器是獨立分開的情況下,你將會經歷非常漫長的網路延遲。僅僅是因為資料不必要的在伺服器之間傳輸。

5.儘量不要使用BY RAND()命令

 A.如果您真需要隨機顯示你的結果,有很多更好的途徑實現。而這個函式可能會為表中每一個獨立的行執行BY RAND()命令—這個會消耗處理器的處理能力,然後給你僅僅返回一行。

6.利用limit 1取得唯一行

A.有時要查詢一張表時,你要知道需要看一行,你可能去查詢一條獨特的記錄。你可以使用limit 1.來終止資料庫引擎繼續掃描整個表或者索引,如:

Select * from A  where namelike ‘%xxx’ limit 1;

這樣只要查詢符合like ‘%xxx’的記錄,那麼引擎就不會繼續掃描表或者索引了。

7.儘量少排序

A.排序操作會消耗較多的CPU資源,所以減少排序可以在快取命中率高等

8.儘量少OR

A.當where子句中存在多個條件以“或”並存的時候,Mysql的優化器並沒有很好的解決其執行計劃優化問題,再加上mysql特有的sql與Storage分層架構方式,造成了其效能比較地下,很多時候使用union all或者union(必要的時候)的方式代替“or”會得到更好的效果。

9.儘量用union all 代替union

A.union和union all的差異主要是前者需要將兩個(或者多個)結果集合並後再進行唯一性過濾操作,這就會涉及到排序,增加大量的cpu運算,加大資源消耗及延遲。所以當我們可以確認不可能出現重複結果集或者不在乎重複結果集的時候,儘量使用union all而不是union.

10.避免型別轉換

A.這裡所說的“型別轉換”是指where子句中出現column欄位的型別和傳入的引數型別不一致的時候發生的型別轉換。人為的上通過轉換函式進行轉換,直接導致mysql無法使用索引。如果非要轉型,應該在傳入引數上進行轉換。

11.不要在列上進行運算

A. 如下面:select * fromusers where YEAR(adddate)<2007;將在每個行進行運算,這些導致索引失效進行全表掃描,因此我們可以改成:

Select * from users where adddate<’2007-01-01’;

12.儘量不要使用NOT IN和<>操作

A. NOT IN和<>操作都不會使用索引,而是將會進行全表掃描。NOT IN可以NOT EXISTS代替,id<>3則可以使用id>3 or id <3;如果NOT EXISTS是子查詢,還可以儘量轉化為外連線或者等值連線,要看具體sql的業務邏輯。

B.把NOT IN轉化為LEFT JOIN如:

SELECT * FROM customerinfo WHERE CustomerIDNOT in (SELECT CustomerID FROM salesinfo );

優化:

SELECT * FROM customerinfo LEFT JOINsalesinfoON customerinfo.CustomerID=salesinfo. CustomerID WHEREsalesinfo.CustomerID IS NULL;

13.使用批量插入節省互動(最好是使用儲存過程)

A. 儘量使用insert intousers(username,password) values(‘test1’,’pass1’), (‘test2’,’pass2’), (‘test3’,’pass3’);

14. 鎖定表

A. 儘管事務是維護資料庫完整性的一個非常好的方法,但卻因為它的獨佔性,有時會影響資料庫的效能,尤其是在很多的應用系統中.由於事務執行的過程中,資料庫將會被鎖定,因此其他的使用者請求只能暫時等待直到該事務結算.如果一個數據庫系統只有少數幾個使用者來使用,事務造成的影響不會成為一個太大問題;但假設有成千上萬的使用者同時訪問一個數據庫系統,例如訪問一個電子商務網站,就會產生比較嚴重的響應延遲.其實有些情況下我們可以通過鎖定表的方法來獲得更好的效能.如:

LOCK TABLE inventory write

Select quanity from inventory whereitem=’book’;

Update inventory set quantity=11 whereitem=’book’;

UNLOCK TABLES;

這裡,我們用一個select語句取出初始資料,通過一些計算,用update語句將新值更新到列表中。包含有write關鍵字的LOCK TABLE語句可以保證在UNLOCK TABLES命令被執行之前,不會有其他的訪問來對inventory進行插入,更新或者刪除的操作。

15.對多表關聯的查詢,建立檢視

A.對多表的關聯可能會有效能上的問題,我們可以對多表建立檢視,這樣操作簡單話,增加資料安全性,通過檢視,使用者只能查詢和修改指定的資料。且提高表的邏輯獨立性,檢視可以遮蔽原有表結構變化帶來的影響。