db2 執行計划


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

問題描述

客戶 A 業務核心數據庫采用 DB2 UDB,業務部門報告其中一個模塊響應緩慢,通過分析該業務模塊代碼可以定位為一條性能較差的 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 原文

將 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 的性能好壞。

步驟三:分析相關索引

索引 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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM