基於索引的sql優化 上

2021-08-15 05:50:54 字數 4001 閱讀 4793

客服業務受到sql語句的影響非常大,在規模比較大的局點,往往因為乙個小的sql語句不夠優化,導致資料庫效能急劇下降,小型機idle所剩無幾,應用伺服器斷連、超時,嚴重影響業務的正常執行。

資料庫的優化方法有很多種,在應用層來說,主要是基於索引的優化。

常用建立索引的規則如下:

1、表的主鍵、外來鍵必須有索引;

2、資料量超過300的表應該有索引;

3、經常與其他表進行連線的表,在連線欄位上應該建立索引;

4、經常出現在where子句中的字段,特別是大表的字段,應該建立索引;

5、索引應該建在選擇性高的字段上;

6、索引應該建在小字段上,對於大的文字字段甚至超長字段,不要建索引;

7、復合索引的建立需要進行仔細分析;盡量考慮用單字段索引代替:

a、正確選擇復合索引中的主列字段,一般是選擇性較好的字段;

b、復合索引的幾個字段是否經常同時以and方式出現在where子句中?單字段查詢是否極少甚至沒有?如果是,則可以建立復合索引;否則考慮單字段索引;

c、如果復合索引中包含的字段經常單獨出現在where子句中,則分解為多個單字段索引;

d、如果復合索引所包含的字段超過3個,那麼仔細考慮其必要性,考慮減少復合的字段;

e、如果既有單字段索引,又有這幾個欄位上的復合索引,一般可以刪除復合索引;

8、頻繁進行資料操作的表,不要建立太多的索引;

9、刪除無用的索引,避免對執行計畫造成負面影響;

以上是一些普遍的建立索引時的判斷依據。一言以蔽之,索引的建立必須慎重,對每個索引的必要性都應該經過仔細分析,要有建立的依據。因為太多的索引與不充分、不正確的索引對效能都毫無益處:在表上建立的每個索引都會增加儲存開銷,索引對於插入、刪除、更新操作也會增加處理上的開銷。 另外,過多的復合索引,在有單字段索引的情況下,一般都是沒有存在價值的;相反,還會降低資料增加刪除時的效能,特別是對頻繁更新的表來說,負面影響更大。

任何對列的操作都可能導致全表掃瞄,這裡所謂的操作包括資料庫函式、計算表示式等等,查詢時要盡可能將操作移至等式的右邊,甚至去掉函式。   

例1:下列sql條件語句中的列都建有恰當的索引,但30萬行資料情況下執行速度卻非常慢:

select * from

record

where substrb(cardno,1,4)='5378'(13秒)

select * from

record

where amount/30

< 1000(11秒)

select * from

record

where to_char(actiontime,'yyyymmdd')='19991201'(10秒)

由於where子句中對列的任何操作結果都是在sql執行時逐行計算得到的,因此它不得不進行表掃瞄,而沒有使用該列上面的索引;如果這些結果在查詢編譯時就能得到,那麼就可以被sql優化器優化,使用索引,避免表掃瞄,因此將sql重寫如下:

select * from

record

where cardno like '5378%'(< 1秒)

select * from

record

where amount < 1000*30(< 1秒)

select * from

record

where actiontime= to_date ('19991201' ,'yyyymmdd')(< 1秒)

差別是很明顯的!

需要注意的是,盡量避免潛在的資料型別轉換。如將字元型資料與數值型資料比較,oracle會自動將字元型用to_number()函式進行轉換,從而導致全表掃瞄。

例2:表tab1中的列col1是字元型(char),則以下語句存在型別轉換:

select col1,col2 from tab1 where col1>10,

應該寫為: select col1,col2 from tab1 where col1>'10'。

增加查詢的範圍限制,避免全範圍的搜尋。

例3:以下查詢表record 中時間actiontime小於2023年3月1日的資料:

select * from

record

where actiontime < to_date ('20010301' ,'yyyymm')

查詢計畫表明,上面的查詢對錶進行全表掃瞄,如果我們知道表中的最早的資料為2023年1月1日,那麼,可以增加乙個最小時間,使查詢在乙個完整的範圍之內。修改如下: select * from

record

where

actiontime < to_date ('20010301' ,'yyyymm')

and actiontime > to_date ('20010101' ,'yyyymm')

後一種sql語句將利用上actiontime欄位上的索引,從而提高查詢效率。把'20010301'換成乙個變數,根據取值的機率,可以有一半以上的機會提高效率。同理,對於大於某個值的查詢,如果知道當前可能的最大值,也可以在where子句中加上 「and 列名< max(最大值)」。

含有"in"、"or"的where子句常會使用工作表,使索引失效;如果不產生大量重複值,可以考慮把子句拆開;拆開的子句中應該包含索引。   

例4: select

count(*) from stuff where id_no in('0','1')(23秒)

可以考慮將or子句分開:

select

count(*) from stuff where id_no='0'

select

count(*) from stuff where id_no='1'

然後再做乙個簡單的加法,與原來的sql語句相比,查詢速度更快。

盡量去掉 "<>",避免全表掃瞄,如果資料是列舉值,且取值範圍固定,則修改為"or"方式。

例5: update serviceinfo set state=

0where state<>

0;以上語句由於其中包含了"<>",執行計畫中用了全表掃瞄(table access full),沒有用到state欄位上的索引。實際應用中,由於業務邏輯的限制,欄位state為列舉值,只能等於0,1或2,而且,值等於=

1,2的很少,因此可以去掉"<>",利用索引來提高效率。

修改為:update serviceinfo set state=

0where state =

1or state =

2 。進一步的修改可以參考第4種方法。

where字句中的is

null和is

notnull將不會使用索引而是進行全表搜尋,因此需要通過改變查詢方式,

分情況討論等方法,去掉where子句中的is

null和is

notnull。

索引的選擇性低,但資料的值分布差異很大時,仍然可以利用索引提高效率。a、資料分布不均勻的特殊情況下,選擇性不高的索引也要建立。

表serviceinfo中資料量很大,假設有一百萬行,其中有乙個欄位disposalcourseflag,取值範圍為列舉值:[0,1,2,3,4,5,6,7]。按照前面說的索引建立的規則,「選擇性不高的字段不應該建立索引,該欄位只有8種取值,索引值的重複率很高,索引選擇性明顯很低,因此不建索引。然而,由於該字段上資料值的分布情況非常特殊,具體如下表:

而且,常用的查詢中,查詢disposalcourseflag<6 的情況既多又頻繁,毫無疑問,如果能夠建立索引,並且被應用,那麼將大大提高這種情況的查詢效率。因此,我們需要在該字段上建立索引。

基於索引的SQL語句優化之一

避免對列的操作 任何對列的操作都可能導致全表掃瞄,這裡所謂的操作包括資料庫函式 計算表示式等等,查詢時要盡可能將操作移至等式的右邊,甚至去掉函式。例1 下列sql條件語句中的列都建有恰當的索引,但30萬行資料情況下執行速度卻非常慢 select from record where substrb c...

SQL優化 索引優化

一 發現哪些sql語句有效能問題 開啟mysql慢查詢日誌對sql語句進行監控 show variables like slow query log 檢視是否開啟慢查詢日誌 set global slow query log on 開啟慢查詢日誌 set global log queries not...

SQL優化(SQL 索引)

檢視表定義 show create table users 檢視表的索引 show index from users 你要獲取第乙個表的所有資訊,你說全表掃瞄快呢還是索引掃瞄快呢?所以當你查詢庫 包括left join中的臨時庫 的所有資訊時,資料庫會選擇最優方法 全表掃瞄!s表dept id na...