1、優化器
爲SQL選擇一個最優的執行計劃的工具分爲RBO和CBO。兩種優化器的區別在於:RBO不會計算cost,但是CBO會計算cost。
2、統計信息
表中的數據量與數據分佈等信息。查看統計信息,是在sys下的視圖中查看。
|-- num_rows
table -> user_tables --|-- blocks
|-- last_analyzed
|-- num_rows
index -> user_indexs --|-- leaf_blocks
|-- distinct_keys
|-- last_analyzed
|-- num_rows
row -> user_columns ---|-- low_values
|-- hight_values
|-- last_analyzed
如果沒有統計信息,CBO會動態採樣,取一定比例的數據分析得到一個統計信息。
3、表連接
實際上應該用row source來代替表。表連接並不是整張表連接。典型的表連接包括:嵌套循環(nested loop)、哈希連接(hash join)、排序合併連接(sort merge join)。驅動表(在表連接中先存取的表)。表連接一次只能連接兩個表,即使有多個表的連接,也只能兩個兩個地連接,所以,兩個表的連接很重要,表的連接順序也很重要。
4、nested loops(嵌套循環)
eg:
select /*+ use_nl(ab) full(a) full(b) leading(b)*/ *
from t_userinfo a,t_userserviceinfo b
where a.phonenumber = b.phonenumber
leading(b)以b爲驅動表
(1)分析該查詢語句的索引:
這種查詢就會採用nested loops(嵌套循環),就像java中的a.forEach(ele -> b.forEach())
執行計劃中的第一個表爲驅動表,既:外層循環。
/*+ use_nl(ab) full(a) full(b) leading(b)*/ 是查詢語句中的提示。
注意:在選擇驅動表的時候,以小表爲驅動表,小表不是真的小表,而是row source小的表。並且,大表上要有索引。
(2)提示:
“提示”是用來提示優化器採用什麼樣的執行計劃。
當出現以下情況的時候,就需要使用提示了:
a、沒有統計信息
b、臨時表上不能收集統計信息
c、CBO選擇的執行計劃不好
(3)提示的分類:
a、指定連接順序:
leading -> 指定驅動表:no_unnest(以主查詢爲驅動)、unnest(以子查詢爲驅動)
ordered -> 指定順序
b、指定連接方式:
use_nl -> 嵌套循環
use_hash -> 哈希連接
c、指定訪問路徑:
full -> 全表掃描
index -> 索引掃描
d、nl_sj,nl_aj,hash_sj,hash_aj
用於子查詢,sj->in/exists
aj->not in/not exists
(4)如何選擇驅動表:
a、當兩個表都是全表掃描時,以小表爲驅動表
b、內表的查詢列一定要能命中索引
c、當b表上有固定查詢條件,可以縮小b表與a表的連接的row source時,以b表爲驅動表。
所以,應該以參與連接的row source比較小的表爲驅動表
(5)帶子查詢的nested loops:
nl-sj -> 以主查詢爲驅動
in/exist -> 以主查詢爲驅動
no_unnest -> 與nl_sj一樣,但是當10G以後,需要使用no_unnest
(6)多表 nested loops:
a、在多表連接的時候,實際上是兩個表兩個表地連接。當使用多表連接的時候,可以使用order(a b c)來指定連接順序,
兩個表連接以後的row source越少,就應該越靠前。
b、帶外連接的nested loop都是以左表爲驅動表。
c、多表連接默認情況下,是根據from後面的表的順序從右向左開始連接,但是,如果RBO認爲有更好的
執行計劃,也會改變。
5、hash join
(1)nested loop 與 hash join的比較:
a、nested loop之適合小數據量
b、hash join適合大數據量
(2)hash join的連接過程:
a、生產hash表,以驅動表數據生成。爲了使hash表小一點,要以小表爲驅動表。
b、生成hash表以後,掃描探測表(探測表是另外一張表),與hash表匹配,匹配上就將數據存入結果集。
c、hash連接結束,就可以得到結果
(3)默認優化器會使用 index fast full scan 而不是table full scan
(4)update技巧
sql語句:
update t_useinfo a
set a.username = (select b.oprinitinfo from t_userservice_info b where a.phone=b.phone);
分析sql語句:
a、這裏會使用nested loop(嵌套循環)
b、視圖update
update(
select a.username,b.opinfo from a,b where a.phone=b.phone
)
set username=opinfo;
c、PL/SQL更新
begin
for item in(
select a.rowid,b.opinfo from a,b where a.phone=b.phone
)
loop
update a set username=item.opinfo where rowid=item.rowid;
end loop;
end;
(5)hash join總結
a、適用於大數據,連接條件必須是等值連接
b、選擇小表爲驅動表
c、設置較大的GPA,可以加快hash join
d、注意順序,中間結果集儘量小
e、hash join與nested loop比較,實際上是用空間換時間,當內存不足的時候,就會報錯
(6)分區表
physical上分離,logical上一體。這樣可以加速查詢
6、oracle 索引
(1)B+樹索引
B+樹索引的數據結果是一個樹,樹的葉子節點是表中的數據行,葉子節點的父節點叫“索引葉子節點塊”,在索引葉子節點塊中會保留相鄰的索引葉子節點的地址。
在B+樹中,索引節點是有序的,而這種有序是針對每一層而言的。B+樹中,每一層可以有200個節點塊,每一個節點塊中,可以對應400個下一級節點塊。但是,葉子節點塊是無序的(葉子節點塊中存的是數據行)。
(2)查看錶的統計信息
可以分析表的一些基本數據。注意:數據被delete,數據佔用的塊不會被釋放,但是,新insert的數據會優先佔用這些空間。
(3)查看錶索引的統計信息
可以分析表的索引的一些信息。
(4)關於索引的一些注意點
a、空字段值是不會被加入索引的,因此,查某個字段爲空是不能在索引中查到的
b、不要在長字段上創建索引,這會導致B+樹層高太大,影響索引掃描,使索引掃描時間增加。
分析:
1)爲什麼在長字段上創建索引會導致B+樹層高太大:因爲每一個索引葉子節點塊能夠存的數
據量是有限的。索引字段越長,索引葉子節點塊中的索引數量越少,葉子節點塊的數量就
越多,B+樹層高就越大。
2)爲什麼B+樹層高越大,索引掃描越慢:因爲B+樹層高越大,需要掃描的葉子節點塊的數量
就越多,所以,掃描時間越大。
c、分配索引空間和表空間時,兩者因該是差不多的。
d、索引鍵是可以重複的,也是可以不重複的(設置索引爲unique),但是不能爲空。
e、索引的重複度越高,查詢越慢:
B+樹的高度決定了從根節點到葉子節點查詢的節點數量。重複度決定了在葉子節點中查詢的節點數
量。所以,B+樹高度越高,查詢的節點樹越多。重複度越高,查詢的節點數越多。
(5)索引的查詢
索引掃描的數度是由以下兩個參數決定的:層高、索引重複度
層高:層高由“索引塊數”、“索引字段長度”這兩個參數決定。因爲塊的大小是固定的,所以,索引字段
的長度可以決定索引的塊數。因爲每一層索引塊數固定,所以,索引塊數可以決定層高。
索引重複度:索引重複度描述了同一個索引佔用的索引塊的數量。索引重複度越高,在同一層掃描的索
引塊樹越多,索引掃描的效率越低。
7、數據庫性能調優
(1)數據庫性能的影響因子:
IO、CPU、網絡。如果數據庫做的計算越多,則,CPU使用率越高,網絡使用率越低。如果數據庫做的計
算越少,則,CPU使用率越低,網絡使用率越高。
所以,要合理地安排數據庫的計算,儘量使IO、CPU、網絡的使用率相當。
(2)減少數據塊的讀取
(3)數據塊是數據行存儲的位置,一個數據塊可以存多少行的數據,由行大小來決定。數據塊大小由磁盤文
件系統決定,也可以設置。磁盤格式化時,會將磁盤劃分爲指定大小的塊。
8、數據的訪問路徑
(1)執行計劃的三個方面:
a、訪問路徑:全表掃描、各種索引掃描
b、連接方式:單表查詢不考慮
c、連接順序:單表查詢不考慮
(2)autotrace工具
查看執行計劃、訪問量多少數據塊
(3)delete不會降低表的高水位
即使數據塊是空塊,全表掃描也會掃描
(4)執行計劃是從下向上看的
(5)select /*+full(a)*/ count(*) from user a;
/*+full(a)*/ : 是告訴優化器對a表執行全表掃描,a爲表user的別名
(6)index unique scan
因爲是唯一索引,所以,查詢的索引塊數量爲層高。
table access by index rowid,通過行號(rowid)訪問表
主要分析:consistent gets 和 physical reads的數量
select /*+index(a indexName)*/ field1 from table a where field2='a' and field3='b';
/*+index(a indexName)*/:指定命中索引a爲table別名,index爲field2與field3的複合索引
9、index range scan
索引範圍掃描:對於組合索引,key爲多個字段的拼接。索引重複度高或範圍查詢
注意:不能命中 index range scan
(1)全模糊查詢
username like '%abc%' / '%abc%'不能比對,因爲不知道'abc'是第幾位
(2)沒有使用索引中的第一個字段(前導列)
(3)使用“不等號”或“not in”
(4)查詢列上有表達式或函數
(5)index range scan的性能分析:
通過分析該查詢訪問的塊來衡量性能,塊越少,性能越好。查詢的塊 = 索引塊 + 數據塊
注意:查詢條件可以分爲:定位條件、過濾條件
如果定位條件先起作用,則,訪問的塊數就會很少,然後用過濾條件過濾
10、如何確定哪個索引好
標準:訪問的數據塊越少越好,訪問的數據塊 = 索引塊 + 表塊
(1)葉子節點的訪問量對性能影響巨大。查詢條件和索引的重複率對該因素影響巨大
(2)因爲表數據是不連續的,所以,通過葉子節點來訪問表是需要來回地從葉子節點獲得rowid。這也是爲
什麼,當命中不了索引時,全表掃描比強制使用index快。自增字段在入表時是相對接近的,因爲數據
入表時間是相近的。
(3)索引“聚集因子”:它描述了數據在表中的聚集情況。但是,聚集因子的計算因爲出現物理讀的可能性很
大,所以,開銷也很大。
(4)一般,如果索引命中超過5%,就不用index range scan
11、index full scan
索引全掃描:掃描所有的葉子節點
特點:
(1)有序,葉子節點是有序的
(2)只能單塊讀(全表掃描可以多塊讀),葉子節點之間是指針相連,只能沿指針遍歷
(3)效率比全表掃描低
使用場景:
(1)排序取Top N
全表掃描排序因爲需要將數據讀入內存,所以,非常消耗性能。但是,index full scan(索引全
掃描)只能掃描Top N就返回
12、index fast full scan
索引快速全掃描:根據索引在磁盤的物理序掃描,不會順着葉子節點指針掃描
特點:
並行、無序、可多塊讀、只適用於CBO(CBO是一種優化器)
13、index skip scan
定義:跳過索引前導列的索引掃描
14、索引設計原則
(1)分區表不創建全局索引,否則,當delete分區時,索引會失效
(2)不要創建無用的索引,否則,會降低DML的性能
(3)t_1_r_1 與 t_1 是一樣功能的索引
(4)key不要太長,否則,B+樹會很高
(5)重複度低的應該靠前
(6)索引和表要建在不同的表空間中,可以提高IO性能