1. 程式人生 > >有關Oracle統計資訊的知識點

有關Oracle統計資訊的知識點

一、什麼是統計資訊

統計資訊主要是描述資料庫中表,索引的大小,規模,資料分佈狀況等的一類資訊。例如,表的行數,塊數,平均每行的大小,索引的leaf blocks,索引欄位的行數,不同值的大小等,都屬於統計資訊。CBO正是根據這些統計資訊資料,計算出不同訪問路徑下,不同join 方式下,各種計劃的成本,最後選擇出成本最小的計劃。 統計資訊是存放在資料字典表中的,如tab$,一般可通過察看某些檢視來獲取統計資訊狀況,如DBA_TABLES,DBA_INDEXES,DBA_TAB_COL_STATISTICS, DBA_TAB_HISTOGRAMS等。在這些檢視中包含表示統計資訊的一些欄位,這些欄位只有蒐集過統計資訊之後才有值,否則是空的。例如,last_analyzed 欄位表示上次統計資訊蒐集的時間,可以根據這個欄位,快速的瞭解最近一次統計資訊蒐集的時間。

二、收集統計資訊的方法

  1. 使用gather_stats_job自動收集是在建立資料庫時自動建立的,並由排程程式進行管理。他會收集資料庫中優化程式統計資訊缺失或已過時的所有物件的統計資訊。
  2. 使用dbms_stats 程式包手動收集收集的是系統統計資訊。
  3. 通過設定資料庫初始化引數進行收集。
  4. 通過從另一個數據庫匯入統計資訊進行收集。

三、Oracle自動收集統計資訊的原理

統計資訊對於Oracle資料庫來說至關重要,尤其是在使用CBO(基於成本的優化器)模式的時候,統計資訊包括表的使用塊數、空閒塊數、平均行長度、統計資訊收集時間等。在Oracle9i資料庫中,兩種優化器模式RBO和CBO並存,在預設情況下,optimizer_mode引數的值是choose,choose不是優化器模式,它表示在分析資料庫中的語句時,如果在物件上有統計資訊,就是用CBO方式生成執行計劃,如果物件上沒有統計資訊,是使用RBO模式。

從總體上來說,CB的準確度高於RBO,但是它要求要有統計資訊和統計資訊必須準確,否則Oracle可能會做出錯誤的判斷。所以在Oracle9i資料庫中,我們會自己來規劃在什麼樣的時間採用什麼樣的策略來收集統計資訊。也就是說,Oracle9i的統計資訊收集工作必須通過手工方式來實現。

到了Oracle10g,預設情況下,optimizer_mode=all_rows,也就是採用了CBO的方式,為了保證執行計劃的準確,在週一到週五(晚22:00-次日6:00),通過一個job(gather_stat_job)自動收集物件的統計資訊。這種自動收集統計資訊的方式並不是收集所有物件的統計資訊,而是收集沒有統計資訊的物件和統計資訊過舊的物件。

Automatic Statistics Gathering是由Scheduler排程GATHER_STATS_JOB作業來完成的,在GATHER_STATS_JOB作業中則呼叫DBMS_STATS.GATHER_DATABASE_STATS_JOB_PROC儲存過程。GATHER_DATABASE_STATS_JOB_PROC是一個內部的儲存過程,基本上跟DBMS_STATS.GATHER_DATABASE_STATS的功能一樣,但在其內部有優先順序的考慮,更新量(變化量)越多的表將會越優先收集統計資訊。為物件收集統計資訊的條件是,之前從來沒有收集過的或者是更新的(包括insert,update,delete,truncate)記錄數超過當前總記錄數10%的表(在Oracle11g中則提供了SET_TABLE_PREFS函式修改10%這個閾值)。記錄數的更改量由Oracle資料庫自動監控,在初始化引數statistics_level設定為TYPICAL或者ALL時,自動監控即會生效。

3.1 調整當更新量達(變化量)達到多少時開始統計資訊收集任務

1

2

3

4

BEGIN

DBMS_STATS.SET_TABLE_PREFS ( ownname =>'XXXXX', tabname =>'T1', pname =>'STALE_PERCENT', pvalue =>'5');

END;

/

3.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

select t1.window_name,t1.repeat_interval,t1.duration from dba_scheduler_windows t1,dba_scheduler_wingroup_members t2

where t1.window_name=t2.window_name and t2.window_group_name in ('MAINTENANCE_WINDOW_GROUP','BSLN_MAINTAIN_STATS_SCHED');

WINDOW_NAME                    REPEAT_INTERVAL                                              DURATION

------------------------------ ------------------------------------------------------------ --------------------

MONDAY_WINDOW                  freq=daily;byday=MON;byhour=22;byminute=0; bysecond=0        +000 04:00:00

TUESDAY_WINDOW                 freq=daily;byday=TUE;byhour=22;byminute=0; bysecond=0        +000 04:00:00

WEDNESDAY_WINDOW               freq=daily;byday=WED;byhour=22;byminute=0; bysecond=0        +000 04:00:00

THURSDAY_WINDOW                freq=daily;byday=THU;byhour=22;byminute=0; bysecond=0        +000 04:00:00

FRIDAY_WINDOW                  freq=daily;byday=FRI;byhour=22;byminute=0; bysecond=0        +000 04:00:00

SATURDAY_WINDOW                freq=daily;byday=SAT;byhour=6;byminute=0; bysecond=0         +000 20:00:00

SUNDAY_WINDOW                  freq=daily;byday=SUN;byhour=6;byminute=0; bysecond=0         +000 20:00:00

# WINDOW_NAME:任務名

# REPEAT_INTERVAL:任務重複間隔時間

# DURATION:持續時間

# 1.停止任務

BEGIN

DBMS_SCHEDULER.DISABLE(

name=>'"SYS"."FRIDAY_WINDOW"',

force=>TRUE);

END;

# 2.修改任務的持續時間,單位是分鐘

BEGIN

DBMS_SCHEDULER.SET_ATTRIBUTE(

name=>'"SYS"."FRIDAY_WINDOW"',

attribute=>'DURATION',

value=>numtodsinterval(180, 'minute'));

END;

# 3.開始執行時間,BYHOUR=2,表示2點開始執行

BEGIN

DBMS_SCHEDULER.SET_ATTRIBUTE(

name=>'"SYS"."FRIDAY_WINDOW"',

attribute=>'REPEAT_INTERVAL',

value=>'FREQ=WEEKLY;BYDAY=MON;BYHOUR=2;BYMINUTE=0;BYSECOND=0');

END;

# 4.開啟任務

BEGIN

DBMS_SCHEDULER.ENABLE(

name=>'"SYS"."FRIDAY_WINDOW"');

END;

3.3 禁用統計資訊自動收集

1

2

3

4

BEGIN

DBMS_SCHEDULER.DISABLE('GATHER_STATS_JOB');

END;

/

四、DBMS_STATS包

DBMS_STATS包,主要提供了蒐集(gather),刪除(delete),匯出(export),匯入(import),修改(set)統計資訊的方法。

dbms_stats與analyze的區別:

dbms_stats是Oracle9i及後續版本中用於收集統計資訊的包,雖然analyze命令也一直可以使用,但是現在已經不推薦使用analyze命令來收集統計資訊,而是使用dbms_stats。兩者之間有很大的不同,dbms_stats能正確收集分割槽表的統計資訊,也就是說能夠收集global statistic,而analyze只能收集最低層次物件的統計資訊,然後推導和彙總出高一級物件的統計資訊,如果分割槽表只會收集分割槽統計資訊,然後再彙總出所有分割槽的統計資訊,得到表一級的統計資訊。

4.1 什麼是golbal statistic

golbal statistic是指直接從物件本身收集到的統計資訊,而不是從下一級物件“推導”和“彙總”出來的統計資訊,golbal statistic對於優化器來說非常重要,一個SQL,除非其查詢條件限制了資料只在分割槽上,否則大多數情況下需要golbal statistic才能得到正確的執行計劃。有的統計值可以從下一級物件進行彙總後得到,如表的總行數,可以通過各分割槽的行數相加得到。但有的統計值不能通過下一級物件得到,比如列上的唯一值數量(distinct value)以及密度值(density)。

4.2 使用DBMS_STATS.GATHER_DATABASE_STATS收集整個資料庫的統計資訊

1

2

3

4

5

6

7

BEGIN

dbms_stats.gather_database_stats(estimate_percent => dbms_stats.AUTO_SAMPLE_SIZE,

method_opt       => 'for all indexed columns',

options          => 'GATHER AUTO',

cascade          => TRUE);

END;

/

引數說明:

1.estimate_percent:取樣的百分比,使用dbms_stats.auto_sample_size選項允許Oracle自動估算要取樣的一個segment的最佳百分比。

2.method_opt選項適合在表和索引資料發生變化時重新整理統計資料:

  • for table:只統計表
  • for all indexed columns:只統計有索引的表列
  • for all indexes:只分析統計相關索引
  • for all columns:分析所有的列

dbms_stats的method_opt引數尤其適合在表和索引資料發生變化時重新整理統計資料。method_opt引數也適合用於判斷哪些列需要直方圖(histograms)。某些情況下,索引內的各個值的分佈會影響CBO是使用一個索引還是執行一次全表掃描的決策。例如,假如在where子句中指定的值的數量不對稱,全表掃描就顯得比索引訪問更經濟。

如果有一個高度傾斜的索引(某些值的行數不對稱),就可建立Oracle直方圖統計。但在現實世界中,出現這種情況的機率相當小。使用CBO時,最常見的錯誤之一就是在CBO統計中不必要地引入直方圖。為了智慧地生成直方圖,Oracle為dbms_stats準備了method_opt引數。在method_opt子句中,還有一些重要的選項,包括skewonly,repeat和auto:

  • method_opt=>'for all columns size skewonly'
  • method_opt=>'for all columns size repeat'
  • method_opt=>'for all columns size auto'

(1).skewonly選項會耗費大量處理時間,因為它要檢查每個索引中的每個列的值的分佈情況。如果dbms_stat發現一個索引的各個列分佈得不均勻,那麼就會為該索引建立直方圖,幫助基於成本的SQL優化器決定是進行索引訪問,還是進行全表掃描訪問。 (2).repeat選項在重新分析任務所消耗的資源就會少一些。使用repeat選項時,只會為現有的直方圖重新分析索引,不再搜尋其他直方圖機會。定期重新分析統計資料時,應該採取這種方式。 (3).auto選項根據資料分佈以及應用程式訪問列的方式來建立直方圖。

3.options控制Oracle統計資訊的重新整理方式:

  • gather:重新分析整個架構
  • gather empty:只分析目前還沒有統計的表
  • gather stale:只重新分析修改量超過10%的表(包括插入、更新和刪除)
  • gather auto:重新分析當前沒有統計的物件,以及統計資料過期(變髒)的物件。使用gather auto類似於組合使用gather stale和gather empty

4.3 使用DBMS_STATS.GATHER_SCHEMA_STATS收集整個使用者下物件的統計資訊

1

2

3

4

5

6

7

exec dbms_stats.gather_schema_stats(

ownname => 'SCOTT',

options => 'GATHER AUTO'

estimate_percent => dbms_stats.auto_sample_size,

method_opt => 'for all columns size repeat',

degree =>15

)

4.4 使用DBMS_STATS.GATHER_TABLE_STATS收集表、列、索引的統計資訊

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

dbms_stats.gather_table_stats(

owner VARCHAR2,

tablename VARCHAR2,

partname VARCHAR2,

estimate_percent NUMBER,

block_sample BOOLEAN,

method_opt VARCHAR2,

degree NUMBER,

granularity VARCHAR2,

cascade BOOLEAN,

stattab VARCHAR2,

statid VARCHAR2,

statown VARCHAR2,

no_invalidate BOOLEAN,

force BOOLEAN

)

引數說明:

1.owner:要分析表的所有者

2.tablename:要分析的表的表名

3.partname:分割槽名

4.estimate_percent:取樣行的百分比,從0.000001-100,null為全部分析,不採樣。常量DBMS_STATS.AUTO_SAMPLE_SIZE是預設值,由Oracle決定最佳取樣率。

5.block_sample:是否用塊取樣代替行取樣。

6.method_opt:決定histograms資訊是怎樣被統計的,method_opt的取值如下:

  • for all columns:統計所有的histograms
  • for all indexed columns:統計所有index列的histograms
  • for all hidden coloumns:統計hidden列的histograms
  • for columns <list> SIZE <N> | REPEAT | AUTO | SKEWONLY 統計指定列的histograms,N的取值範圍是0-254

7.degree:設定統計資訊收集的並行度,預設值為null。

8.cascade:收集索引的統計資訊,預設為false

9.stattab:指定儲存統計資訊的表。

10.statid:如果多個表的統計資訊儲存在一個stattab中時,statid用作分割槽條件。

11.statown:儲存統計資訊表的所有著。

如果不指定上述三個引數,則統計資訊會被更新到資料字典。

12.force:即使表鎖住了也收集統計資訊。

4.5 統計資訊的匯出匯入刪除操作

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

# 1.建立統計資訊歷史保留表

exec dbms_stats.create_stat_table(

ownname => '',

stattab => ''

)

# 2.匯出整個scheme的統計資訊

exec dbms_stats.export_schema_stats(

ownname => '',

stattab => ''

)

# 3.分析scheme

Exec dbms_stats.gather_schema_stats(

ownname => ''

options => 'GATHER AUTO',

estimate_percent => dbms_stats.auto_sample_size,

method_opt => 'for all indexed columns ',

degree => 6

)  

# 4.分析表

exec dbms_stats.gather_table_stats(

ownname => '',

tabname =>'',

estimate_percent => 10,

method_opt=> 'for all indexed columns'

)

# 5.分析索引

exec dbms_stats.gather_index_stats(

ownname => '',

indname => '',

estimate_percent => 10,

degree => 6

)

# 6.如果發現執行計劃走錯,刪除表的統計資訊

exec dbms_stats.delete_table_stats(

ownname => '',

tabname => ''

)

# 7.匯入錶的歷史統計資訊

exec dbms_stats.import_table_stats(

ownname => '',

tabname => '',

stattab => ''

4.6 鎖住統計資訊

將一個表的統計資訊鎖住,以防止錯誤的統計資訊將此正確的資訊覆蓋掉時需要用到LOCK_TABLE_STATS包:

1

2

3

4

DBMS_STATS.LOCK_TABLE_STATS(

ownname    VARCHAR2,

tabname    VARCHAR2

);

參考文件: