db2 執行計劃

SQL 語句優化貫穿於數據庫類應用程序的整個生命週期,包括前期程序開發,產品測試以及後期生產維護。針對於不一樣類型的 SQL 性能問題有不一樣的優化方法。索引對於改善數據庫 SQL 查詢操做性能相當重要,如何選擇合適的列以及正確的組合所選擇的列建立索引對查詢語句的性能有着極大的影響,本文將結合具體案例進行解釋。html

問題描述

客戶 A 業務核心數據庫採用 DB2 UDB,業務部門報告其中一個模塊響應緩慢,經過分析該業務模塊代碼能夠定位爲一條性能較差的 SQL 語句。sql

清單 1. 影響性能的 SQL 語句
1
2
3
db2fox@bivm:~/test> cat t1.sql
select name,location,address from t1 where name=16123
db2fox@bivm:~/test>

問題分析與解決

步驟一:分析該 SQL 語句的執行計劃數據庫

DB2 提供了能分析 SQL 執行計劃的工具:db2expln,經過分析 SQL 執行計劃咱們將瞭解 DB2 優化器選擇了什麼樣的「途徑」來訪問數據,執行計劃的優劣將直接影響 SQL 的性能。工具

清單 2. 執行計劃輸出結果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
db2fox@bivm:~/test> db2expln -database fox -i -g -stmtfile t1.sql -terminator ';' -output t1.exp
db2fox@bivm:~/test> cat t1.exp
DB2 Universal Database Version 10.5, 5622-044 (c) Copyright IBM Corp. 1991, 2012
Licensed Material - Program Property of IBM
IBM DB2 Universal Database SQL and XQUERY Explain Tool
******************** DYNAMIC ***************************************
==================== STATEMENT ==========================================
     Isolation Level = Cursor Stability
     Blocking = Block Unambiguous Cursors
     Query Optimization Class = 5
     Partition Parallel = No
     Intra-Partition Parallel = No
     SQL Path = "SYSIBM", "SYSFUN", "SYSPROC", "SYSIBMADM",
  "DB2FOX"
Statement:
  select name, location, address
  from t1
  where name=16123
Section Code Page = 1208
Estimated Cost = 3517.214111 --此處咱們能夠看到行計劃的 COST 值
Estimated Cardinality = 3600.000000
( 2) Access Table Name = DB2FOX.T1 ID = 4,513
  | #Columns = 3
  | Skip Inserted Rows
  | Avoid Locking Committed Data
  | Currently Committed for Cursor Stability
  | May participate in Scan Sharing structures
  | Scan may start anywhere and wrap, for completion
  | Fast scan, for purposes of scan sharing management
  | Scan can be throttled in scan sharing management
  | Relation Scan
  | | Prefetch: Eligible
  | Lock Intents
  | | Table: Intent Share
  | | Row : Next Key Share
  | Sargable Predicate(s)
  | | #Predicates = 1
( 1) | | Return Data to Application
  | | | #Columns = 3
( 1) Return Data Completion
End of section
Optimizer Plan:
  Rows
  Operator
  (ID)
  Cost
  3600
  RETURN
  ( 1)
  3517.21
  |
  3600
  TBSCAN --> 該執行計劃選擇了全表掃描
  ( 2)
  3517.21
  |
  90000
  Table:
  DB2FOX
  T1

這是一條很是簡單的 SQL 語句,其執行計劃選擇了「全表掃描」,通常狀況下全表掃描的「代價」較高而執行效率較差,相對而言,使用索引的效率要高的多,但在一些特殊狀況下「全表掃描」的效率要優於「使用索引」,影響優化器選擇的因素有不少,包括:表的大小,查詢結果集的大小,有無索引,I/O 預讀等。性能

清單 3. 表的統計信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
db2fox@bivm:~/test> db2 "select count(*) from t1"
1
-----------
  90000
  1 record(s) selected.
fox@bivm:~/test> db2 "select substr(indname,1,10),substr(tabname,1,20),
substr(colnames,1,20) from syscat.indexes where tabname='T1'"
1 2 3
---------- -------------------- --------------------
I_T1 T1 +LOCATION+NAME
db2fox@bivm:~/test> db2 "select firstkeycard, first2keycard from syscat.indexes
> where indname='I_T1'"
FIRSTKEYCARD FIRST2KEYCARD
-------------------- ---------------------------------------------------
  3 -->重複值很是的多59093 -->重複值很是的少
  1 record(s) selected.
db2fox@bivm:~/test>
db2fox@bivm:~/test> db2 "describe table t1"
  Data type Column
Column name schema Data type name Length Scale Nulls
------------------------------- --------- ------------------- ---------- ----- ------
NAME SYSIBM CHARACTER 40 0 Yes
LOCATION SYSIBM CHARACTER 50 0 Yes
ADDRESS SYSIBM VARCHAR 130 0 Yes
  3 record(s) selected.
db2fox@bivm:~/test>

T1 表上有一個名爲「I_T1」的索引,該表有大概 9 萬條記錄,並且 NAME 列的重複值很是的少,這種狀況下影響業務性能的 SQL 語句很是適合使用索引,但當前的執行計劃卻選擇了「全表掃描」!咱們再仔細觀察一下該 SQL 語句的原文:select name,location,address from t1 where name=16123 請注意 where 條件 name=16123 這是一個「數值」類型,而 t1 表中 NAME 列定義的是「字符」類型的,這多是影響執行化選擇的緣由!測試

步驟二:修改 SQL 原文fetch

將 SQL 原文中 where 條件部分加「引號」以使得「優化器」能夠選擇索引。優化

清單 4. 從新生成執行計劃,驗證優化效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
db2fox@bivm:~/test> cat t1.sql
select name,location,address from t1 where name='16123'
db2fox@bivm:~/test>
db2fox@bivm:~/test> db2expln -database fox -i -g -stmtfile t1.sql -terminator ';' -output t1.exp
DB2 Universal Database Version 10.5, 5622-044 (c) Copyright IBM Corp. 1991, 2012
Licensed Material - Program Property of IBM
IBM DB2 Universal Database SQL and XQUERY Explain Tool
Output is available in "t1.exp".
db2fox@bivm:~/test> cat t1.exp
DB2 Universal Database Version 10.5, 5622-044 (c) Copyright IBM Corp. 1991, 2012
Licensed Material - Program Property of IBM
IBM DB2 Universal Database SQL and XQUERY Explain Tool
******************** DYNAMIC ***************************************
==================== STATEMENT ==========================================
     Isolation Level = Cursor Stability
     Blocking = Block Unambiguous Cursors
     Query Optimization Class = 5
     Partition Parallel = No
     Intra-Partition Parallel = No
     SQL Path = "SYSIBM", "SYSFUN", "SYSPROC", "SYSIBMADM",
  "DB2FOX"
Statement:
  select name, location, address
  from t1
  where name='16123'
Section Code Page = 1208
Estimated Cost = 132.665771 -->COST 值比優化前改善很是明顯
Estimated Cardinality = 2.596810
( 2) Access Table Name = DB2FOX.T1 ID = 4,513
  | Index Scan: Name = DB2FOX.I_T1 ID = 1
  | | Regular Index (Not Clustered)
  | | Index Columns:
  | | | 1: LOCATION (Ascending)
  | | | 2: NAME (Ascending)
  | #Columns = 2
  | Skip Inserted Rows
  | Avoid Locking Committed Data
  | Currently Committed for Cursor Stability
  | Evaluate Predicates Before Locking for Key
  | #Key Columns = 2
  | | Start Key: Inclusive Value
  | | | 1: [GAP Unconstrained]
  | | | 2: '16123 ...'
  | | Stop Key: Inclusive Value
  | | | 1: [GAP Unconstrained]
  | | | 2: '16123 ...'
  | Data Prefetch: Sequential(2), Readahead
  | Index Prefetch: Sequential(4), Readahead
  | Lock Intents
  | | Table: Intent Share
  | | Row : Next Key Share
  | Sargable Predicate(s)
( 1) | | Return Data to Application
  | | | #Columns = 3
( 1) Return Data Completion
End of section
Optimizer Plan:
  Rows
  Operator
  (ID)
  Cost
  2.59681
  RETURN
  ( 1)
  132.666
  |
  2.59681
  FETCH
  ( 2)
  132.666
  / \
  2.59681 90000
  IXSCAN Table:
  ( 3) DB2FOX
  115.093 T1
  |
  59093
  Index:
  DB2FOX -->已經使用了索引
  I_T1
db2fox@bivm:~/test>

從新執行該 SQL 語句驗證其優化效果,能夠看出該 SQL 已經有明顯的改善,但依然沒有知足業務指望。SQL 的性能很大程度上是與「索引」相關的, 正確的使用索引以及合理的設計「索引」是改善 SQL 性能的最主要手段,「索引」質量的高低也將直接影響 SQL 的性能好壞。lua

步驟三:分析相關索引spa

索引 I_T1 是由 LOCATION 列和 NAME 列聯合構成的「組合索引」,一般狀況下「組合索引」的「引導列」(排在最左邊的列)對查詢語句中的 where 條件影響最大,而索引 I_T1 的引導列爲 LOCATION, 所以能夠考慮新建立一個索引只有 NAME 列或者建立一個新的由 NAME 列爲引導列的組合索引。

清單 5. 建立以 NAME 列爲引導列的索引
1
2
3
4
5
6
7
8
9
db2fox@bivm:~> db2 "create index i_t1_name on t1(name)"
DB20000I The SQL command completed successfully.
db2fox@bivm:~> db2 "describe indexes for table t1"
Index Index Unique Number of Index Index Null
schema name rule columns type partitioning keys
------------------------------- ------------------- --------------
DB2FOX I_T1 D 2 RELATIONAL DATA - Y
DB2FOX I_T1_NAME  D 1 RELATIONAL DATA - Y
  2 record(s) selected.
清單 6. 從新生成執行計劃,驗證優化效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
db2fox@bivm:~/test> db2expln -database fox -i -g -stmtfile t1.sql -terminator ';' -output t1.exp
DB2 Universal Database Version 10.5, 5622-044 (c) Copyright IBM Corp. 1991, 2012
Licensed Material - Program Property of IBM
IBM DB2 Universal Database SQL and XQUERY Explain Tool
Output is available in "t1.exp".
db2fox@bivm:~/test> cat t1.exp
DB2 Universal Database Version 10.5, 5622-044 (c) Copyright IBM Corp. 1991, 2012
Licensed Material - Program Property of IBM
IBM DB2 Universal Database SQL and XQUERY Explain Tool
******************** DYNAMIC ***************************************
==================== STATEMENT ==========================================
     Isolation Level = Cursor Stability
     Blocking = Block Unambiguous Cursors
     Query Optimization Class = 5
     Partition Parallel = No
     Intra-Partition Parallel = No
     SQL Path = "SYSIBM", "SYSFUN", "SYSPROC", "SYSIBMADM",
  "DB2FOX"
Statement:
  select name, location, address
  from t1
  where name='16123'
Section Code Page = 1208
Estimated Cost = 27.005688 -->COST 值比優化前改善很是明顯
Estimated Cardinality = 2.898831
( 2) Access Table Name = DB2FOX.T1 ID = 4,513
  | Index Scan: Name = DB2FOX.I_T1_NAME ID = 2
  | | Regular Index (Not Clustered)
  | | Index Columns:
  | | | 1: NAME (Ascending)
  | #Columns = 2
  | Skip Inserted Rows
  | Avoid Locking Committed Data
  | Currently Committed for Cursor Stability
  | Evaluate Predicates Before Locking for Key
  | #Key Columns = 1
  | | Start Key: Inclusive Value
  | | | 1: '16123 ...'
  | | Stop Key: Inclusive Value
  | | | 1: '16123 ...'
  | Data Prefetch: Sequential(1), Readahead
  | Index Prefetch: Sequential(1), Readahead
  | Lock Intents
  | | Table: Intent Share
  | | Row : Next Key Share
  | Sargable Predicate(s)
( 1) | | Return Data to Application
  | | | #Columns = 3
( 1) Return Data Completion
End of section
Optimizer Plan:
  Rows
  Operator
  (ID)
  Cost
  2.89883
  RETURN
  ( 1)
  27.0057
  |
  2.89883
  FETCH
  ( 2)
  27.0057
  / \
  2.89883 90000
  IXSCAN Table:
  ( 3) DB2FOX
  13.5494 T1
  |
  30731
  Index:
  DB2FOX
  I_T1_NAME -->優化器選擇新建立的索引
db2fox@bivm:~/test>

從以上的執行計劃中能夠看到 COST 值從最初的3517.214111最終下降到27.005688,該 SQL 語句的性能提高很是明顯。

索引設計原則

索引一般用於加速對錶的訪問。可是,邏輯數據設計也可使用索引。例如,惟一索引不容許列中存在重複值的條目,從而保證了一個表中不會有兩行相同的記錄。還能夠建立索引,以將一列中的值按升序或降序進行排序。

要點: 在建立索引時要記住,雖然它們能夠提升查詢性能,但會對寫性能產生負面影響。出現此負面影響是由於對於數據庫管理器寫入表中的每行,它還必須更新任何受影響的索引。所以,只有在可以明顯提升總體性能時,才應建立索引。

在建立索引時,還應考慮表結構和最常對這些表執行查詢的類型。例如,頻繁發出的查詢的 WHERE 子句中出現的列很適合做爲索引。可是,在較少運行的查詢中,索引對 INSERT 和 UPDATE 語句的性能產生的負面影響可能超過所帶來的好處。

一樣,在常常運行的查詢的 GROUP BY 子句中出現的列可能會從建立索引中獲益,尤爲在用於分組行的值的數目小於要分組的行數時。

在建立索引時, 也能夠進行壓縮。以後,您可使用 ALTER INDEX 語句來修改索引,從而啓用或禁用壓縮功能。

要刪除索引,可使用 DROP INDEX 命令。

設計索引時的準則和注意事項

  1. 雖然構成一個索引鍵的列的順序不會影響索引鍵的建立,可是當它決定是否使用索引時就可能影響優化器。例如,若是查詢包含 ORDER BY col1,col2 子句,那麼可使用對 (col1,col2) 建立的索引,但對 (col2,col1) 建立的索引沒什麼幫助。一樣,若是查詢指定了條件,例如 where col1 >= 50 and col1 <= 100 或 where col1=74,那麼對 (col1) 或 (col1,col2) 建立的索引將起做用,但基於 (col2,col1) 的索引的做用不大。
  2. 能夠對特定的表定義任意數目的索引(最大數目爲 32767),這些索引能提升查詢性能。
  3. 索引管理器必須在更新、刪除和插入操做期間維護索引。爲有不少更新內容的表建立大量索引可能減慢請求的處理速度。一樣,大型索引鍵也會減慢處理請求的速度。所以,僅當頻繁訪問明顯有利之時,才使用索引。
  4. 不是惟一索引鍵的一部分但要在該索引中存儲或維護的列數據稱爲包含列。只能爲惟一索引指定包含列。當用包含列建立索引時,僅對惟一鍵列進行排序並考慮其惟一性。使用包含列能夠啓用僅訪問索引來進行數據檢索,從而提升性能。
  5. 若是要創建索引的表是空的,那麼仍會建立索引,可是在裝入該表或插入行以前,不會創建任何索引條目。若是該表不爲空,那麼數據庫管理器將在處理 CREATE INDEX 語句時建立索引條目。
  6. 索引會消耗磁盤空間。該磁盤空間大小取決於鍵列的長度和要創建索引的行數。隨着插入到表中的數據增多,索引大小也會增長。所以,在規劃數據庫大小時,應考慮正在創建索引的數據量。

注: 都應該按重複值最少到重複值最多的順序對索引鍵中的列進行排序。此排序提供最佳性能。

結束語

本案例中經過修改了兩 SQL 原文並從新設計了一個索引達到了優化目的,知足了業務要求,當數據庫出現性能問題時,經過現象分析其本質,最終找到優化的具體方法。數據庫優化是一個系統化的過程,有時沒法一蹴而就,須要按部就班。深入的理解數據庫的運行機制和原理是迅速判斷性能問題的基礎。

 

 

參考:https://www.ibm.com/developerworks/cn/data/library/techarticle/dm-1511-db2sql-optmize/

相關文章
相關標籤/搜索