2017.12.11更新
The Secret Life of SQL: How to Optimize Database Performance by Bryana Knight
查詢性能低下的原因是訪問了太多的數(shù)據(jù)
- 多表連接時返回了所有的列
select * from sakila.actor
inner join sakila.file_actor using(actior_id)
inner join sakila.film using(film_id)
where sakila.film.title = 'AronMan'
<font color = red>正確的做法是這樣</font>
select sakila.actor.* from sakila.actor
inner join sakila.file_actor using(actior_id)
inner join sakila.film using(film_id)
where sakila.film.title = 'AronMan'
- 分解連接技術
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,789)
分解連接看上去比較浪費畜挥,但是有巨大優(yōu)勢
- 緩存效率高
- MyISAM引擎下介却,鎖住表的時間短
- 在應用程序端連接可以更方便擴展數(shù)據(jù)庫林束,把表放在不同的數(shù)據(jù)庫服務器上
- 查詢本身更高效
- 減少多余行的訪問
什么時候使用分解連接负懦?
- 可以緩存大量查詢
- 使用了多個MyISAM表
- 數(shù)據(jù)分布在不同服務器
- 對于大表使用in替換連接
- 一個連接引用了同一個表多次
優(yōu)化連接
- 確保on或者using的列有索引
- 確保group by 或者order by只引用一個列诵竭,這樣可以使用索引
悲觀鎖
select chairid from seat where booked is null for update
update seat set booked='x' where chairid=1
commit
索引及查詢優(yōu)化
摘取部分自mysql性能優(yōu)化-慢查詢分析蜘醋、優(yōu)化索引和配置
索引的類型
? 普通索引:這是最基本的索引類型碟绑,沒唯一性之類的限制剖膳。
? 唯一性索引:和普通索引基本相同抛猖,但所有的索引列值保持唯一性政钟。
? 主鍵:主鍵是一種唯一索引路克,但必須指定為”PRIMARY KEY”。
? 全文索引:MYSQL從3.23.23開始支持全文索引和全文檢索养交。在MYSQL中精算,全文索引的索引類型為FULLTEXT。全文索引可以在VARCHAR或者TEXT類型的列上創(chuàng)建碎连。
使用多列索引 要注意最左前綴問題
有時MySQL不使用索引灰羽,即使有可用的索引。一種情形是當優(yōu)化器估計到使用索引將需要MySQL訪問表中的大部分行時鱼辙。(在這種情況下廉嚼,表掃描可能會更快些)。然而倒戏,如果此類查詢使用LIMIT只搜索部分行怠噪,MySQL則使用索引,因為它可以更快地找到幾行并在結果中返回杜跷。
合理的建立索引的建議:
(1) 越小的數(shù)據(jù)類型通常更好:越小的數(shù)據(jù)類型通常在磁盤傍念、內(nèi)存和CPU緩存中都需要更少的空間,處理起來更快葛闷。
(2) 簡單的數(shù)據(jù)類型更好:整型數(shù)據(jù)比起字符憋槐,處理開銷更小,因為字符串的比較更復雜淑趾。在MySQL中阳仔,應該用內(nèi)置的日期和時間數(shù)據(jù)類型,而不是用字符串來存儲時間扣泊;以及用整型數(shù)據(jù)類型存儲IP地址近范。
(3) 盡量避免NULL:應該指定列為NOT NULL,除非你想存儲NULL延蟹。在MySQL中评矩,含有空值的列很難進行查詢優(yōu)化,因為它們使得索引等孵、索引的統(tǒng)計信息以及比較運算更加復雜。你應該用0蹂空、一個特殊的值或者一個空串代替空值
這部分是關于索引和寫SQL語句時應當注意的一些瑣碎建議和注意點俯萌。
當結果集只有一行數(shù)據(jù)時使用LIMIT 1
避免SELECT *,始終指定你需要的列
從表中讀取越多的數(shù)據(jù)上枕,查詢會變得更慢咐熙。他增加了磁盤需要操作的時間,還是在數(shù)據(jù)庫服務器與WEB服務器是獨立分開的情況下辨萍。你將會經(jīng)歷非常漫長的網(wǎng)絡延遲棋恼,僅僅是因為數(shù)據(jù)不必要的在服務器之間傳輸返弹。
使用連接(JOIN)來代替子查詢(Sub-Queries)。 連接(JOIN)之所以更有效率一些爪飘,是因為MySQL不需要在內(nèi)存中創(chuàng)建臨時表來完成這個邏輯上的需要兩個步驟的查詢工作义起。
使用ENUM、CHAR 而不是VARCHAR师崎,使用合理的字段屬性長度
盡可能的使用NOT NULL
固定長度的表會更快
拆分大的DELETE 或INSERT 語句
查詢的列越小越快
Where條件
在查詢中默终,WHERE條件也是一個比較重要的因素,盡量少并且是合理的where條件是很重要的犁罩,盡量在多個條件的時候齐蔽,把會提取盡量少數(shù)據(jù)量的條件放在前面,減少后一個where條件的查詢時間床估。
有些where條件會導致索引無效:
? where子句的查詢條件里有含滴!=,MySQL將無法使用索引丐巫。
? where子句使用了Mysql函數(shù)的時候谈况,索引將無效,比如:select * from tb where left(name, 4) = ‘xxx’
? 使用LIKE進行搜索匹配的時候鞋吉,這樣索引是有效的:select * from tbl1 where name like ‘xxx%’鸦做,而like ‘%xxx%’ 時索引無效
技巧整理
1、應盡量避免在 where 子句中使用!=或<>操作符谓着,否則將引擎放棄使用索引而進行全表掃描泼诱。
2、對查詢進行優(yōu)化赊锚,應盡量避免全表掃描治筒,首先應考慮在 where 及 order by 涉及的列上建立索引。
3舷蒲、應盡量避免在 where 子句中對字段進行 null 值判斷耸袜,否則將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where num is null
可以在num上設置默認值0牲平,確保表中num列沒有null值堤框,然后這樣查詢:
select id from t where num=0
4、盡量避免在 where 子句中使用 or 來連接條件纵柿,否則將導致引擎放棄使用索引而進行全表掃描蜈抓,如:
select id from t where num=10 or num=20
可以這樣查詢:
select id from t where num=10
union all
select id from t where num=20
5、下面的查詢也將導致全表掃描:(不能前置百分號)
select id from t where name like '%abc'
若要提高效率昂儒,可以考慮全文檢索沟使。
6、in 和 not in 也要慎用渊跋,否則會導致全表掃描腊嗡,如:
select id from t where num in(1,2,3)
對于連續(xù)的數(shù)值着倾,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
7、如果在 where 子句中使用參數(shù)燕少,也會導致全表掃描卡者。因為SQL只有在運行時才會解析局部變量,但優(yōu)化程序不能將訪問計劃的選擇推遲到運行時棺亭;它必須在編譯時進行選擇虎眨。然 而,如果在編譯時建立訪問計劃镶摘,變量的值還是未知的嗽桩,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:
select id from t where num=@num
可以改為強制查詢使用索引:
select id from t with(index(索引名)) where num=@num
8凄敢、應盡量避免在 where 子句中對字段進行表達式操作碌冶,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where num/2=100
應改為:
select id from t where num=100*2
9涝缝、應盡量避免在where子句中對字段進行函數(shù)操作扑庞,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where substring(name,1,3)=’abc’
應改為:
select id from t where name like ‘a(chǎn)bc%’
select id from t where createdate>=’2005-11-30′ and createdate<’2005-12-1′
10拒逮、不要在 where 子句中的“=”左邊進行函數(shù)罐氨、算術運算或其他表達式運算,否則系統(tǒng)將可能無法正確使用索引滩援。
11纫骑、在使用索引字段作為條件時盏触,如果該索引是復合索引钱烟,那么必須使用到該索引中的第一個字段作為條件時才能保證系統(tǒng)使用該索引琼了,否則該索引將不會被使 用,并且應盡可能的讓字段順序與索引順序相一致恩袱。
12泣棋、不要寫一些沒有意義的查詢,如需要生成一個空表結構:
select col1,col2 into #t from t where 1=0
這類代碼不會返回任何結果集畔塔,但是會消耗系統(tǒng)資源的潭辈,應改成這樣:
create table #t(…)
13、很多時候用 exists 代替 in 是一個好的選擇:
select num from a where num in(select num from b)
用下面的語句替換:
select num from a where exists(select 1 from b where num=a.num)
14澈吨、并不是所有索引對查詢都有效把敢,SQL是根據(jù)表中數(shù)據(jù)來進行查詢優(yōu)化的,當索引列有大量數(shù)據(jù)重復時棚辽,SQL查詢可能不會去利用索引技竟,如一表中有字段 sex冰肴,male屈藐、female幾乎各一半榔组,那么即使在sex上建了索引也對查詢效率起不了作用。
15联逻、索引并不是越多越好搓扯,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率包归,因為 insert 或 update 時有可能會重建索引锨推,所以怎樣建索引需要慎重考慮,視具體情況而定公壤。一個表的索引數(shù)最好不要超過6個换可,若太多則應考慮一些不常使用到的列上建的索引是否有 必要。
16.應盡可能的避免更新 clustered 索引數(shù)據(jù)列厦幅,因為 clustered 索引數(shù)據(jù)列的順序就是表記錄的物理存儲順序沾鳄,一旦該列值改變將導致整個表記錄的順序的調(diào)整,會耗費相當大的資源确憨。若應用系統(tǒng)需要頻繁更新 clustered 索引數(shù)據(jù)列译荞,那么需要考慮是否應將該索引建為 clustered 索引。
17休弃、盡量使用數(shù)字型字段吞歼,若只含數(shù)值信息的字段盡量不要設計為字符型,這會降低查詢和連接的性能塔猾,并會增加存儲開銷篙骡。這是因為引擎在處理查詢和連接時會 逐個比較字符串中每一個字符,而對于數(shù)字型而言只需要比較一次就夠了桥帆。
18医增、盡可能的使用 varchar/nvarchar 代替 char/nchar ,因為首先變長字段存儲空間小老虫,可以節(jié)省存儲空間叶骨,其次對于查詢來說,在一個相對較小的字段內(nèi)搜索效率顯然要高些祈匙。
19忽刽、任何地方都不要使用 select * from t ,用具體的字段列表代替“*”跪帝,不要返回用不到的任何字段。
20些阅、盡量使用表變量來代替臨時表伞剑。如果表變量包含大量數(shù)據(jù),請注意索引非常有限(只有主鍵索引)市埋。
21黎泣、避免頻繁創(chuàng)建和刪除臨時表恕刘,以減少系統(tǒng)表資源的消耗。
22抒倚、臨時表并不是不可使用褐着,適當?shù)厥褂盟鼈兛梢允鼓承├谈行В缤信唬斝枰貜鸵么笮捅砘虺S帽碇械哪硞€數(shù)據(jù)集時含蓉。但是,對于一次性事件项郊,最好使 用導出表馅扣。
23、在新建臨時表時着降,如果一次性插入數(shù)據(jù)量很大岂嗓,那么可以使用 select into 代替 create table,避免造成大量 log 鹊碍,以提高速度厌殉;如果數(shù)據(jù)量不大,為了緩和系統(tǒng)表的資源侈咕,應先create table公罕,然后insert。
24耀销、如果使用到了臨時表楼眷,在存儲過程的最后務必將所有的臨時表顯式刪除,先 truncate table 熊尉,然后 drop table 罐柳,這樣可以避免系統(tǒng)表的較長時間鎖定。
25狰住、盡量避免使用游標张吉,因為游標的效率較差,如果游標操作的數(shù)據(jù)超過1萬行催植,那么就應該考慮改寫肮蛹。
26、使用基于游標的方法或臨時表方法之前创南,應先尋找基于集的解決方案來解決問題伦忠,基于集的方法通常更有效。
27稿辙、與臨時表一樣昆码,游標并不是不可使用。對小型數(shù)據(jù)集使用 FAST_FORWARD 游標通常要優(yōu)于其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數(shù)據(jù)時赋咽。在結果集中包括“合計”的例程通常要比使用游標執(zhí)行的速度快笔刹。如果開發(fā)時 間允許,基于游標的方法和基于集的方法都可以嘗試一下冬耿,看哪一種方法的效果更好。
28萌壳、在所有的存儲過程和觸發(fā)器的開始處設置 SET NOCOUNT ON 亦镶,在結束時設置 SET NOCOUNT OFF 。無需在執(zhí)行存儲過程和觸發(fā)器的每個語句后向客戶端發(fā)送 DONE_IN_PROC 消息袱瓮。
29缤骨、盡量避免向客戶端返回大數(shù)據(jù)量,若數(shù)據(jù)量過大尺借,應該考慮相應需求是否合理绊起。
30、盡量避免大事務操作燎斩,提高系統(tǒng)并發(fā)能力虱歪。