mysq查詢l優化
指標:執行時間 檢查的行數 回傳的行數
1. count的優化
比如:計算id大于5的城市 a. select count(*) from world.city where id > 5; b. select (select count(*) from world.city) – count(*) from world.city where id <= 5; a陳述句當行數超過11行的時候需要掃描的行數比b陳述句要多, b陳述句掃描了6行,此種情況下,b陳述句比a陳述句更有效率,當沒有where陳述句的時候直接select count(*) from world.city這樣會更快,因為mysql總是知道表的行數,
案例:
# 建庫 CREATE DATABASE IF NOT EXISTS db3 DEFAULT CHARSET=utf8; USE db3; # 建表 CREATE TABLE IF NOT EXISTS cnt(id INT , NAME VARCHAR(10),age INT ,tel VARCHAR(10)); # 創建存盤程序 procedrue DELIMITER $ CREATE PROCEDURE cnt() BEGIN #定義變數 declare DECLARE i INT DEFAULT 0; WHILE(i<100000) DO BEGIN SELECT i; SET i=i+1; INSERT INTO cnt(id,NAME)VALUES(i,'zs'); END; END WHILE; END $ DELIMITER ; #呼叫存盤程序 CALL cnt(); SELECT COUNT(*)FROM cnt; #執行耗時 : 0 sec 傳送時間 : 0.051 sec 總耗時 : 0.051 sec #資料庫知道表內有多少條資料 SELECT COUNT(*) FROM cnt WHERE id > 5; # 執行耗時 : 0.130 sec 傳送時間 : 0 sec 總耗時 : 0.130 sec # 使用where 掃描了全表 id > 5的所有資料 總查詢99995條 SELECT (SELECT COUNT(*) FROM cnt) - COUNT(*) FROM cnt WHERE id <=5 ; # 執行耗時 : 0.080 sec 傳送時間 : 0 sec 總耗時 : 0.081 sec # 使用where 掃描全表 id <= 5 的五條資料 加上 (SELECT COUNT(*) FROM cnt) 一條資料 總查詢 6 條
2. 避免使用不兼容的資料型別,
例如float和int、char和varchar、binary和varbinary是不兼容的,資料型別的不兼容可能使優化器無法執行一些本來可以進行的優化操作, 在程式中,保證在實作功能的基礎上,盡量減少對資料庫的訪問次數;通過搜索引數,盡量減少對表的訪問行數,最小化結果集,從而減輕網路負擔;
能夠分開的操作盡量分開處理,提高每次的回應速度;
在資料視窗使用SQL時,盡量把使用的索引放在選擇的首列;演算法的結構盡量簡單;在查詢時,不要過多地使用通配符如 SELECT * FROM T1陳述句,要用到幾列就選擇幾列如:SELECT COL1,COL2 FROM T1;
在可能的情況下盡量限制盡量結果集行數如:SELECT TOP 300 COL1,COL2,COL3 FROM T1,因為某些情況下用戶是不需要那么多的資料的,不要在應用中使用資料庫游標,游標是非常有用的工具,但比使用常規的、面向集的SQL陳述句需要更大的開銷;按照特定順序提取資料的查找,
案例:
# 創建一個表測驗了一下 int 型別資料插入float型別的資料 結果:插入數度變慢 會四舍五入 CREATE TABLE sss(id INT); INSERT INTO sss VALUES(12.4); INSERT INTO sss VALUES(12.5);
3. 索引欄位上進行運算會使索引失效,
盡量避免在WHERE子句中對欄位進行函式或運算式操作,這將導致引擎放棄使用索引而進行全表掃描,
如: SELECT * FROM T1 WHERE F1/2=100 應改為: SELECT * FROM T1 WHERE F1=100*2
案例:
#創建索引 CREATE INDEX index_id ON cnt(id); SELECT * FROM cnt WHERE id > 50000; #行耗時 : 0.009 sec 傳送時間 : 0 sec 總耗時 : 0.010 sec SELECT * FROM cnt WHERE id*2 > 100000; #執行耗時 : 0.031 sec 傳送時間 : 0 sec 總耗時 : 0.032 sec # 使用了索引進行運算 使得索引失效 效率變慢了
4. 避免使用!=或<>、IS NULL或IS NOT NULL、IN ,NOT IN等這樣的運算子.
因為這會使系統無法使用索引,而只能直接搜索表中的資料,例如: SELECT id FROM employee WHERE id != “B%” 優化器將無法通過索引來確定將要命中的行數,因此需要搜索該表的所有行,在in陳述句中能用exists陳述句代替的就用exists.
5. 盡量使用數字型欄位.
一部分開發人員和資料庫管理人員喜歡把包含數值資訊的欄位 設計為字符型,這會降低查詢和連接的性能,并會增加存盤開銷,這是因為引擎在處理查詢和連接回逐個比較字串中每一個字符,而對于數字型而言只需要比較一次就夠了,
6. 合理使用EXISTS,NOT EXISTS子句,如下所示:
SELECT SUM(T1.C1) FROM T1 WHERE (SELECT COUNT(*)FROM T2 WHERE T2.C2=T1.C2>0) 2.SELECT SUM(T1.C1) FROM T1WHERE EXISTS(SELECT * FROM T2 WHERE T2.C2=T1.C2) 兩者產生相同的結果,但是后者的效率顯然要高于前者,
因為后者不會產生大量鎖定的表掃描或是索引掃描,如果你想校驗表里是否存在某條紀錄,不要用count(*)那樣效率很低,而且浪費服務器資源,可以用EXISTS代替,如: IF (SELECT COUNT(*) FROM table_name WHERE column_name = ‘xxx’)
可以寫成:IF EXISTS (SELECT * FROM table_name WHERE column_name = ‘xxx’)
7. 能夠用BETWEEN的就不要用IN
8. 能夠用DISTINCT的就不用GROUP BY
9. 盡量不要用SELECT INTO陳述句,SELECT INTO 陳述句會導致表鎖定,阻止其他用戶訪問該表,
10. 必要時強制查詢優化器使用某個索引
SELECT * FROM T1 WHERE nextprocess = 1 AND processid IN (8,32,45)
改成: SELECT * FROM T1 (INDEX = IX_ProcessID) WHERE nextprocess = 1 AND processid IN (8,32,45)
則查詢優化器將會強行利用索引IX_ProcessID 執行查詢,
案例:
# 10.必要時強制查詢優化器使用索引 #建表 create table if not exists T1(processid int, nextprocess int); #建索引 create index index_processID on T1(processid); # 10.1:不使用索引 select * from T1 where nextprocess = 1 and processid in (8,32,45); # 10.2: 強制使用索引 select * from T1 froce index(index_processID) where nextprocess = 1 and process = 1 and processid in (8,32,45);
11. 消除對大型表行資料的順序存取
盡管在所有的檢查列上都有索引,但某些形式的WHERE子句強迫優化器使用順序存取,如: SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008 解決辦法可以使用并集來避免順序存取: SELECT * FROM orders WHERE customer_num=104 AND order_num>1001 UNION SELECT * FROM orders WHERE order_num=1008 這樣就能利用索引路徑處理查詢,【jacking 資料結果集很多,但查詢條件限定后結果集不大的情況下,后面的陳述句快】
案例:
# 11.消除對大型表 行資料的順序存取 #建表 create table if not exists orders(customer_num int, order_num int); # 消除順序索引 #11.1 沒有使用索引 select * from orders where (customer_num=104 and order_num > 100) or order_num=1005; #11.2 使用索引 將or 拆開 避免使用or select * from orders where custoner_num = 104 and order_num > 100 union # union 默認去重,排序 union all 不去重,不會排序 select * from orders where order_num=1005;
12. 盡量避免在索引過的字符資料中,使用非打頭字母搜索,這也使得引擎無法利用索引,
見如下例子: SELECT * FROM T1 WHERE NAME LIKE ‘%L%’ SELECT * FROM T1 WHERE SUBSTING(NAME,2,1)=’L’ SELECT * FROM T1 WHERE NAME LIKE ‘L%’ 即使NAME欄位建有索引,前兩個查詢依然無法利用索引完成加快操作,引擎不得不對全表所有資料逐條操作來完成任務,而第三個查詢能夠使用索引來加快操作,不要習慣性的使用 ‘%L%’這種方式(會導致全表掃描),如果可以使用`L%’相對來說更好;
案例:
# 12. 模糊查詢where like ,字母打頭'1%' 會使用索引 即%在右 create table if not exists T2(name varchar(20)); create index index_name on T2(name); # 12.1 不會使用索引 select * from T2 where name like '%L%'; select * from T2 where name like '%L'; #substring 進行字串截取 引數:字串,起始位置,結束位置 select * from T2 where substring(name,2,1)='L'; # 12.2 使用到索引 select * from T2 where name like 'L%';
13. 雖然UPDATE、DELETE陳述句的寫法基本固定,但是還是對UPDATE陳述句給點建議:
a) 盡量不要修改主鍵欄位, b) 當修改VARCHAR型欄位時,盡量使用相同長度內容的值代替, c) 盡量最小化對于含有UPDATE觸發器的表的UPDATE操作, d) 避免UPDATE將要復制到其他資料庫的列, e) 避免UPDATE建有很多索引的列, f) 避免UPDATE在WHERE子句條件中的列,
14. 能用UNION ALL就不要用UNION
UNION ALL不執行SELECT DISTINCT函式,這樣就會減少很多不必要的資源 在跨多個不同的資料庫時使用UNION是一個有趣的優化方法,UNION從兩個互不關聯的表中回傳資料,這就意味著不會出現重復的行,同時也必須對資料進行排序,我們知道排序是非常耗費資源的,特別是對大表的排序, UNION ALL可以大大加快速度,如果你已經知道你的資料不會包括重復行,或者你不在乎是否會出現重復的行,在這兩種情況下使用UNION ALL更適合,此外,還可以在應用程式邏輯中采用某些方法避免出現重復的行,這樣UNION ALL和UNION回傳的結果都是一樣的,但UNION ALL不會進行排序,
15. 欄位資料型別優化:
a. 避免使用NULL型別:NULL對于大多數資料庫都需要特殊處理,MySQL也不例外,它需要更多的代碼,更多的檢查和特殊的索引邏輯,有些開發人員完全沒有意識到,創建表時NULL是默認值,但大多數時候應該使用NOT NULL,或者使用一個特殊的值,如0,-1作為默認值, b. 盡可能使用更小的欄位,MySQL從磁盤讀取資料后是存盤到記憶體中的,然后使用cpu周期和磁盤I/O讀取它,這意味著越小的資料型別占用的空間越小,從磁盤讀或打包到記憶體的效率都更好,但也不要太過執著減小資料型別,要是以后應用程式發生什么變化就沒有空間了,修改表將需要重構,間接地可能引起代碼的改變,這是很頭疼的問題,因此需要找到一個平衡點, c. 優先使用定長型
16. 關于大資料量limit分布的優化見下面鏈接(當偏移量特別大時,limit效率會非常低):
http://ariyue.iteye.com/blog/553541 附上一個提高limit效率的簡單技巧,在覆寫索引(覆寫索參考通俗的話講就是在select的時候只用去讀取索引而取得資料,無需進行二次select相關表)上進行偏移,而不是對全行資料進行偏移,可以將從覆寫索引上提取出來的資料和全行資料進行聯接,然后取得需要的列,會更有效率,看看下面的查詢: mysql> select film_id, description from sakila.film order by title limit 50, 5; 如果表非常大,這個查詢最好寫成下面的樣子: mysql> select film.film_id, film.description from sakila.film inner join(select film_id from sakila.film order by title liimit 50,5) as film usinig(film_id);
17. 程式中如果一次性對同一個表插入多條資料,比如以下陳述句:
insert into person(name,age) values(‘xboy’, 14); insert into person(name,age) values(‘xgirl’, 15); insert into person(name,age) values(‘nia’, 19); 把它拼成一條陳述句執行效率會更高. insert into person(name,age) values(‘xboy’, 14), (‘xgirl’, 15),(‘nia’, 19);
18. 不要在選擇的欄位上放置索引,這是無意義的,應該在條件選擇的陳述句上合理的放置索引,比如where,order by,
SELECT id,title,content,cat_id FROM article WHERE cat_id = 1;
19. ORDER BY陳述句的MySQL優化: a. ORDER BY + LIMIT組合的索引優化,如果一個SQL陳述句形如:
SELECT [column1],[column2],…. FROM [TABLE] ORDER BY [sort] LIMIT [offset],[LIMIT]; 這個SQL陳述句優化比較簡單,在[sort]這個欄位上建立索引即可, b. WHERE + ORDER BY + LIMIT組合的索引優化,形如: SELECT [column1],[column2],…. FROM [TABLE] WHERE [columnX] = [VALUE] ORDER BY [sort] LIMIT [offset],[LIMIT]; 這個陳述句,如果你仍然采用第一個例子中建立索引的方法,雖然可以用到索引,但是效率不高,更高效的方法是建立一個聯合索引(columnX,sort) c. WHERE + IN + ORDER BY + LIMIT組合的索引優化,形如: SELECT [column1],[column2],…. FROM [TABLE] WHERE [columnX] IN ([value1],[value2],…) ORDER BY [sort] LIMIT [offset],[LIMIT]; 這個陳述句如果你采用第二個例子中建立索引的方法,會得不到預期的效果(僅在[sort]上是using index,WHERE那里是using where;using filesort),理由是這里對應columnX的值對應多個, 目前哥還木有找到比較優秀的辦法,等待高手指教, d.WHERE+ORDER BY多個欄位+LIMIT,比如: SELECT * FROM [table] WHERE uid=1 ORDER x,y LIMIT 0,10; 對于這個陳述句,大家可能是加一個這樣的索引:(x,y,uid),但實際上更好的效果是(uid,x,y),這是由MySQL處理排序的機制造成的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/463550.html
標籤:其他
上一篇:各公司用戶畫像技術案例分享
下一篇:mysql查詢優化
