1. 程式人生 > 其它 >達夢sql優化之執行計劃

達夢sql優化之執行計劃

執行計劃是什麼呢?比如你執行一條sql語句,查詢優化器會為這條sql語句設計執行方式,交給執行器去執行,查詢優化器設計的執行方式就是執行計劃。

EXPLAIN可以打印出語句的執行計劃。

那麼,執行計劃主要是由什麼組成的呢?答案是操作符(個人理解)。

執行計劃是由各類操作符組成的一顆樹,從內到外依次執行,縮排越多的越先執行,同樣縮排的上面的先執行,下面的後執行,上下的優先順序高於內外。

達夢執行計劃涉及到的一些主要操作符有:

CSCN :基礎全表掃描(a),從頭到尾,全部掃描

SSCN :二級索引掃描(b), 從頭到尾,全部掃描

SSEK :二級索引範圍掃描(b) ,通過鍵值精準定位到範圍或者單值

CSEK :聚簇索引範圍掃描© ,通過鍵值精準定位到範圍或者單值

BLKUP :根據二級索引的ROWID 回原表中取出全部資料(b + a)

一、執行計劃解讀

SQL> explain select * from SYSOBJECTS;

1   #NSET2: [0, 1531, 396]
2     #PRJT2: [0, 1531, 396]; exp_num(17), is_atom(FALSE)
3       #CSCN2: [0, 1531, 396]; SYSINDEXSYSOBJECTS(SYSOBJECTS as SYSOBJECTS)

從上面的執行計劃中我們可以看到哪些資訊呢?

首先,一個執行計劃由若干個計劃節點組成,如上面的1、2、3。

然後我們看到,每個計劃節點中包含操作符(CSCN2)和它的代價([0, 1711, 396])等資訊。

代價由一個三元組組成[代價,記錄行數,位元組數]。

代價的單位是毫秒,記錄行數表示該計劃節點輸出的行數,位元組數表示該計劃節點輸出的位元組數。

拿上面第三個計劃節點舉例:操作符是CSCN2即全表掃描,代價估算是0ms,掃描的記錄行數是1711行,輸出位元組數是396個。

二、舉例說明操作符

1、準備測試表和資料

DROP TABLE T1;
DROP TABLE T2;
CREATE TABLE T1(C1 INT ,C2 CHAR(1),C3 VARCHAR(10) ,C4 VARCHAR(10) );
CREATE TABLE T2(C1 INT ,C2 CHAR(1),C3 VARCHAR(10) ,C4 VARCHAR(10) );
INSERT INTO T1 SELECT LEVEL C1,CHR(65+MOD(LEVEL,57)) C2,'TEST',NULL FROM DUAL CONNECT BY LEVEL
<=10000; INSERT INTO T2 SELECT LEVEL C1,CHR(65+MOD(LEVEL,57)) C2,'TEST',NULL FROM DUAL CONNECT BY LEVEL<=10000; CREATE INDEX IDX_C1_T1 ON T1(C1); SP_INDEX_STAT_INIT(USER,'IDX_C1_T1');-- 收集指定的索引的統計資訊

這裡說明一下SP_INDEX_STAT_INIT的兩個引數分別是模式名和索引名。我這裡指定的是USER,會預設查詢當前登入使用者同名的模式,如果這個使用者下有多個模式,查不到其他模式。

2、NSET:收集結果集

說明:用於結果集收集的操作符, 一般是查詢計劃的頂層節點。

EXPLAIN SELECT * FROM T1;

3、PRJT:投影

說明:關係的“投影”(project)運算,用於選擇表示式項的計算;廣泛用於查詢,排序,函式索引建立等。

EXPLAIN SELECT * FROM T1;

4、SLCT:選擇

說明:關係的“選擇” 運算,用於查詢條件的過濾。

EXPLAIN SELECT * FROM T1 WHERE C2='TEST';

5、AAGR:簡單聚集

說明:用於沒有group by的count,sum,age,max,min等聚集函式的計算。

EXPLAIN SELECT COUNT(*) FROM T1 WHERE C1 = 10;

6、FAGR:快速聚集

說明:用於沒有過濾條件時從表或索引快速獲取 MAX/MIN/COUNT值,DM資料庫是世界上單表不帶過濾條件下取COUNT值最快的資料庫。

EXPLAIN SELECT COUNT(*) FROM T1;

7、HAGR:HASH分組聚集

說明:用於分組列沒有索引只能走全表掃描的分組聚集,C2列沒有建立索引。

EXPLAIN SELECT COUNT(*) FROM T1 GROUP BY C2;

8、SAGR:流分組聚集

說明:用於分組列是有序的情況下可以使用流分組聚集,C1上已經建立了索引,SAGR2效能優於HAGR2。

EXPLAIN SELECT COUNT(*) FROM T1 GROUP BY C1;

官方解釋是:如果輸入流是有序的,則使用流分組,並計算聚集函式。

9、BLKUP:二次掃描

說明:先使用2級別索引定位,再根據表的主鍵、聚集索引、 rowid等資訊定位資料行。

EXPLAIN SELECT * FROM T1 WHERE C1=10;

bookmark lookup 翻譯成中文是書籤查詢

BLKUP2 官方文件說明是:定位查詢

10、CSCN:全表掃描

說明:CSCN2是CLUSTER INDEX SCAN的縮寫即通過聚集索引掃描全表,全表掃描是最簡單的查詢,如果沒有選擇謂詞,或者沒有索引可以利用,則系統一般只能做全表掃描。在一個高併發的系統中應儘量避免全表掃描。

EXPLAIN SELECT * FROM T1;

11、SSEK、CSEK、SSCN:索引掃描

1) SSEK

說明:SSEK2是二級索引掃描即先掃描索引,再通過主鍵、聚集索引、ROWID等資訊去掃描表;

EXPLAIN SELECT * FROM T1 WHERE C1=10;

2)CSEK

說明:CSEK2是聚集索引掃描只需要掃描索引,不需要掃描表;

CREATE CLUSTER INDEX IDX_C1_T2 ON T2(C1);

EXPLAIN SELECT * FROM T2 WHERE C1=10;

3)SSCN

說明:SSCN是索引全掃描,不需要掃描表。

官方解釋是:直接使用二級索引進行掃描。

CREATE INDEX IDX_C1_C2_T1 ON T1(C1,C2);

EXPLAIN SELECT C1,C2 FROM T1;

三、簡單連線查詢例子

CREATE TABLE T1(C1 INT,C2 CHAR);
CREATE TABLE T2(D1 INT,D2 CHAR);
CREATE INDEX IDX_T1_C1 ON T1(C1);
INSERT INTO T1 VALUES(1,'A');
INSERT INTO T1 VALUES(2,'B');
INSERT INTO T1 VALUES(3,'C');
INSERT INTO T1 VALUES(4,'D');
INSERT INTO T2 VALUES(1,'A');
INSERT INTO T2 VALUES(2,'B');
INSERT INTO T2 VALUES(5,'C');
INSERT INTO T2 VALUES(6,'D');

SELECT A.C1+1,B.D2 FROM T1 A, T2 B WHERE A.C1 = B.D1;
EXPLAIN SELECT A.C1+1,B.D2 FROM T1 A, T2 B WHERE A.C1 = B.D1;

用到的操作符說明:

NSET2 結果集(result set)收集,一般是查詢計劃的頂層節點

PRJT2 關係的―投影‖(project)運算,用於選擇表示式項的計算

NEST LOOP INDEX JOIN2 索引內連線

CSCN2 聚集索引掃描

SSEK2 二級索引資料定位

該計劃的大致執行流程如下:

1) CSCN2: 掃描 T2 表的聚集索引,資料傳遞給父節點索引連線;

2) NEST LOOP INDEX JOIN2: 當左孩子有資料返回時取右側資料;

3) SSEK2: 利用 T2 表當前的 D1 值作為二級索引 IDX_T1_C1 定位查詢的 KEY,返回結果給父節點;

4) NEST LOOP INDEX JOIN2: 如果右孩子有資料則將結果傳遞給父節點 PRJT2,否則繼續取左孩子的下一條記錄;

5) PRJT2: 進行表示式計算 C1+1, D2;

6) NSET2: 輸出最後結果;

7) 重複過程 1) ~ 4)直至左側 CSCN2 資料全部取完。

應該是這麼個意思,取右側的一個值,去左側匹配,然後到PRJT2去計算,接著再去取右側一個值,再去左側匹配,如此迴圈,直到匹配完,不知道我理解的對不對。

四、單表

CREATE TABLE T1(C1 INT,C2 INT);

insert into t1 select level,level from dual connect by level < 10000;

1、全表掃描(無索引時)

explain select * from t1 where c1 = 5;

用到的操作符說明:

NSET2 結果集(result set)收集,一般是查詢計劃的頂層節點

PRJT2 關係的―投影‖(project)運算,用於選擇表示式項的計算

SLCT2 關係的―選擇‖(select)運算,用於查詢條件的過濾

CSCN2 聚集索引掃描

說明:建立了一個普通表,沒有任何索引,過濾,從T1中取出資料只能走全表掃描CSCN

2、t1(c1)加索引i_test1

create index i_test1 on t1(c1);

1)直接使用二級索引掃描

explain select c1 from t1;

用到的操作符說明:

NSET2 結果集(result set)收集,一般是查詢計劃的頂層節點

PRJT2 關係的―投影‖(project)運算,用於選擇表示式項的計算

SSCN 直接使用二級索引進行掃描

說明:這個時候T1存在兩個入口,CSCN T1基表(全表掃描T1),或者SSCN 二級索引I_TEST1,本例只要求獲取C1,二級索引上存在C1,且資料長度比基礎表要少(基表多出一個C2),索引選擇SSCN。

2)全表掃描(有索引時)

explain select c2 from t1;

用到的操作符說明:

NSET2 結果集(result set)收集,一般是查詢計劃的頂層節點

PRJT2 關係的―投影‖(project)運算,用於選擇表示式項的計算

CSCN2 聚集索引掃描

說明:依然沒有更好的入口,還是選擇CSCN全表

3)定位查詢

explain select * from t1 where c1 = 5;

用到的操作符說明:

NSET2 結果集(result set)收集,一般是查詢計劃的頂層節點

PRJT2 關係的―投影‖(project)運算,用於選擇表示式項的計算

BLKUP2 定位查詢

SSEK2 二級索引資料定位

說明:查詢條件C1 = 多少,存在C1索引,需要注意的是操作符後面的描述scan_range[5,5],表示精準定位到5,無疑,多數情況下這樣是比較有效率的。

另外一點,SSEK 上面出現了BLKUP操作符,由於I_TEST1上沒有C2的資料,而查詢需要SELECT *,索引需要BLKUP回原表查詢整行資料。

很容易的,我們可以想到如果只查詢C1,那麼BLKUP操作符應該不存在,驗證一下。

explain select c1 from t1 where c1 = 5;

3、聚簇索引

1)ROWID聚簇索引

聚簇索引是比較特殊的索引(對應操作符CSEK),在DM7上,同一張表的聚簇索引只允許存在一個,預設建表時(不建堆表的情況下),基表就是一個ROWID聚簇索引,可以預見到對ROWID的精準定位應該會走CSEK。

explain select c1 from t1 where rowid = 6;

用到的操作符說明:

NSET2 結果集(result set)收集,一般是查詢計劃的頂層節點

PRJT2 關係的―投影‖(project)運算,用於選擇表示式項的計算

CSEK2 聚集索引資料定位

2) t1(c2)自定義聚簇索引i_index2

create cluster index i_index2 on t1(c2);

那麼ROWID這個聚簇索引就不存在了,取而代指的是按C2為順序的聚簇索引

explain select c1 from t1 where rowid = 6;

用到的操作符說明:

NSET2 結果集(result set)收集,一般是查詢計劃的頂層節點

PRJT2 關係的―投影‖(project)運算,用於選擇表示式項的計算

SLCT2 關係的―選擇‖(select)運算,用於查詢條件的過濾

SSCN 直接使用二級索引進行掃描

說明:這裡查詢中需要C1以及ROWID,而普通二級索引I_TEST1上正好都有,且比聚簇索引的長度要短,所以選擇SSCN I_TEST1

explain select c1 from t1 where c2 = 6;

用到的操作符說明:

NSET2 結果集(result set)收集,一般是查詢計劃的頂層節點

PRJT2 關係的―投影‖(project)運算,用於選擇表示式項的計算

CSEK2 聚集索引資料定位

五、複雜連線查詢

CREATE TABLE TEST5(ID INT);
CREATE TABLE TEST6(ID INT);
CREATE TABLE TEST7(ID INT);
CREATE TABLE TEST8(ID INT);
insert into test5 values(3);
insert into test6 values(4);
insert into test7 select level %100 from dual connect by level < 10000;
insert into test8 select level %100 from dual connect by level < 10000;
 
explain select /*+no_use_cvt_var*/* from
       (select test5.id from test5,test6 where test5.id = test6.id)a,
       (select id from
              (select test7.id from test7,test8 where test7.id = test8.id) group by id
       ) b
where a.id = b.id;

用到的操作符說明:

NSET2 結果集(result set)收集,一般是查詢計劃的頂層節點

PRJT2 關係的―投影‖(project)運算,用於選擇表示式項的計算

HASH2 INNER JOIN HASH 內連線

PRJT2 關係的―投影‖(project)運算,用於選擇表示式項的計算

HASH2 INNER JOIN HASH 內連線

PRJT2 關係的―投影‖(project)運算,用於選擇表示式項的計算

HASH2 INNER JOIN HASH 內連線

CSCN2 聚集索引掃描

CSCN2 聚集索引掃描

PRJT2 關係的―投影‖(project)運算,用於選擇表示式項的計算

HAGR2 HASH 分組,並計算聚集函式

PRJT2 關係的―投影‖(project)運算,用於選擇表示式項的計算

HASH2 INNER JOIN HASH 內連線

CSCN2 聚集索引掃描

CSCN2 聚集索引掃描

no_use_cvt_var 不考慮變數改寫方式實現連線,僅 OPTIMIZER_MODE=1 有效。

執行順序 6->7->5->12->13->11->9->3

首先執行TEST5和TEST6的HASH連線,然後執行TEST7,TEST8的HASH連線並將連線結果進行HASH分組,再將兩個結果再次進行HASH連線得到最終結果集。

六、補充下索引的知識點

聚集(clustered)索引,也叫聚簇索引。聚簇索引的索引和資料是儲存在一起的。

定義:資料行的物理順序與列值(一般是主鍵的那一列)的邏輯順序相同,一個表中只能擁有一個聚集索引。

說實話,看著有點拗口,用大白話說就是,我們的sql資料庫是行資料庫,資料是一行一行儲存的,而聚集索引是個特殊的索引,相當於這一行行記錄的物理編號,描述這一行行資料的物理儲存順序。所以,一張表只會有一個聚集索引。

除了聚集索引外的其他索引型別都屬於二級索引。

非聚集索引和聚集索引的區別在於, 通過聚集索引可以查到需要查詢的資料, 而通過非聚集索引可以查到記錄對應的主鍵值 , 再使用主鍵的值通過聚集索引查詢到需要的資料

更多資訊請上達夢技術社群瞭解: https://eco.dameng.com