表熱塊的處理手段分析

尛樣兒發表於2010-09-11

前言:
熱塊在我們的日常資料庫中,是最常見的問題之一,怎樣來解決問題,沒有一個統一的方法,只有透過實驗,結合實際情況來減少熱塊的發生,下面來分析一下幾種能夠解決熱塊的手段之間的不同。
熱塊是由於同時訪問某些塊太過頻繁而出現的效能問題。從這一點來說,我們可以讓塊所包含的資料少一點,那麼訪問資料的使用者就會分散到不同的塊上。還有就是我們可以透過hash演算法來將連續插入的資料分散儲存在不同的塊中,這樣使用者訪問資料也會分散到不同的塊上。

//建立普通表
SQL> create table test111
  2  (id number,
  3  name varchar2(20));

//指定pctfree的表
create table test222
(id number,
name varchar2(20))
pctfree 60
;

//建立hash表
CREATE TABLE test333
(id number,
name varchar2(20))
PARTITION BY HASH (id)(
PARTITION pid1,
PARTITION pid2,
PARTITION pid3,
PARTITION pid4,
PARTITION pid5,
PARTITION pid6,
PARTITION pid7,
PARTITION pid8,
PARTITION pid9,
PARTITION pid10
);

//建立普通表,指定minimize records_per_block屬性
create table test444
(id number,
name varchar2(20));
alter table test444 minimize records_per_block;
Table created.

SQL>   2    3    4    5
Table created.

SQL>   2    3    4    5    6    7    8    9   10   11   12   13   14   15
Table created.

SQL>   2    3
Table created.

SQL> alter table test444 minimize records_per_block;
alter table test444 minimize records_per_block
            *
ERROR at line 1:
ORA-28603: statement not permitted on empty tables

SQL> insert into test444 values (111,'111');

1 row created.

SQL> commit;

Commit complete.

SQL> alter table test444 minimize records_per_block;

Table altered.

SQL> truncate table test444;

Table truncated.

//建立模擬資料
SQL> create or replace procedure p_test
  2  as
  3  begin
  4  for i in 1..100000 loop
  5  insert into test111 values (i,to_char(i));
  6  insert into test222 values (i,to_char(i));
  7  insert into test333 values (i,to_char(i));
  8  insert into test444 values (i,to_char(i));
  9  commit;
 10  end loop;
 11  end p_test;
 12  /

Procedure created.

SQL> exec p_test;

PL/SQL procedure successfully completed.

SQL> commit;

Commit complete.

//分析1:4種不同型別的表,佔用的塊數
SQL> select count(distinct dbms_rowid.ROWID_BLOCK_NUMBER(rowid)) from test111 ;

COUNT(DISTINCTDBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID))
---------------------------------------------------
                                                218

SQL> select count(distinct dbms_rowid.ROWID_BLOCK_NUMBER(rowid)) from test222;

COUNT(DISTINCTDBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID))
---------------------------------------------------
                                                492

SQL> select count(distinct dbms_rowid.ROWID_BLOCK_NUMBER(rowid)) from test333;

COUNT(DISTINCTDBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID))
---------------------------------------------------
                                                221

SQL> select count(distinct dbms_rowid.ROWID_BLOCK_NUMBER(rowid)) from test444;

COUNT(DISTINCTDBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID))
---------------------------------------------------
                                              50000

//4種不同型別的表,塊包含的行數分析
SQL> select tt.block_rows,count(1) from (
select dbms_rowid.ROWID_BLOCK_NUMBER(rowid) blockid,count(1) block_rows from test111 group by dbms_rowid.ROWID_BLOCK_NUMBER(rowid))
tt group by tt.block_rows;  2    3

BLOCK_ROWS   COUNT(1)
---------- ----------
       574          1
       155          1
       461          1
       549          1
       519         17
       454        197

6 rows selected.

SQL> select tt.block_rows,count(1) from (
select dbms_rowid.ROWID_BLOCK_NUMBER(rowid) blockid,count(1) block_rows from test222 group by dbms_rowid.ROWID_BLOCK_NUMBER(rowid))
tt group by tt.block_rows;
  2    3
BLOCK_ROWS   COUNT(1)
---------- ----------
       247          3
       209          1
       229         39
       262          1
       201        447
        10          1

6 rows selected.

SQL>
SQL> select tt.block_rows,count(1) from (
select dbms_rowid.ROWID_BLOCK_NUMBER(rowid) blockid,count(1) block_rows from test333 group by dbms_rowid.ROWID_BLOCK_NUMBER(rowid))
tt group by tt.block_rows;
  2    3
BLOCK_ROWS   COUNT(1)
---------- ----------
       399          1
        91          1
       485          1
       472          1
       470          1
       529          2
       155          1
       481          1
       523          1
       230          1
       331          1

BLOCK_ROWS   COUNT(1)
---------- ----------
       532          1
       467          2
       464          1
       528          1
       519          6
       483          1
       525          2
       277          1
       473          1
       381          1
       454        185

BLOCK_ROWS   COUNT(1)
---------- ----------
       530          2
       203          1
       480          1
       524          1
       406          2

27 rows selected.

SQL> select tt.block_rows,count(1) from (
select dbms_rowid.ROWID_BLOCK_NUMBER(rowid) blockid,count(1) block_rows from test444 group by dbms_rowid.ROWID_BLOCK_NUMBER(rowid))
tt group by tt.block_rows;
  2    3
BLOCK_ROWS   COUNT(1)
---------- ----------
         2      50000

SQL>

分析:從上面的分析來看,普通表,每個塊都包含了400~500行記錄。使用了pctfree的塊大部分只包含了200~250行記錄。hash表的大部分塊也包含了400~500行記錄。指定指定minimize records_per_block屬性的表最規矩,沒個塊只包含了2條記錄。
從以上的資料統計來看,指定minimize records_per_block屬性的方式不可取,這樣會導致讀塊的個數增加數倍,對io壓力太大。使用pctfree達到了我們的目的,減少塊所包含的資料量。hash表跟普通表大部分塊所包含的資料量都是相近的。但是我們要注意一點,hash表所包含的資料並不是有序的,從這一點來說,對熱塊的解決也是有幫助的。
從以上的分析來看,pctfree可以減少塊包含的行數,hash表可以打亂連續插入資料的分佈情況,這2種手段都可以緩解熱塊情況。我們何不把這2種手段結合起來用呢?

SQL>
SQL>
SQL> create table test555
(id number,
name varchar2(20))
pctfree 60
PARTITION BY HASH (id)(
PARTITION pid1,
PARTITION pid2,
PARTITION pid3,
PARTITION pid4,
PARTITION pid5,
PARTITION pid6,
PARTITION pid7,
PARTITION pid8,
PARTITION pid9,
PARTITION pid10
);  2    3    4    5    6    7    8    9   10   11   12   13   14   15   16

Table created.

SQL>
create or replace procedure p_test
as
begin
for i in 1..100000 loop
insert into test555 values (i,to_char(i));
commit;
end loop;
end p_test;
/
SQL>   2    3    4    5    6    7    8    9
Procedure created.

SQL> exec p_test

PL/SQL procedure successfully completed.

 

SQL> select count(distinct dbms_rowid.ROWID_BLOCK_NUMBER(rowid) ) from test555;

COUNT(DISTINCTDBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID))
---------------------------------------------------
                                                496

SQL> select tt.block_rows,count(1) from (
select dbms_rowid.ROWID_BLOCK_NUMBER(rowid) blockid,count(1) block_rows from test555 group by dbms_rowid.ROWID_BLOCK_NUMBER(rowid))
tt group by tt.block_rows;  2    3

BLOCK_ROWS   COUNT(1)
---------- ----------
       153          1
       213          1
       235          1
       218          2
       229         27
       205          1
       224          1
       201        388
       240          2
       161          1
       230          1

BLOCK_ROWS   COUNT(1)
---------- ----------
       135          1
       242          1
       239          2
       206          1
        75          1
         8          1
       200         50
       214          1
       107          1
       241          1
        99          1

BLOCK_ROWS   COUNT(1)
---------- ----------
       221          2
       216          1
       236          1
       158          2
         9          1
       234          2

28 rows selected.

用這種方式,既保證了每個塊包含的行數減少,也保證了連續的資料分佈在不同的塊中儲存。筆者覺得用這種方式來實現表的建立能夠最大程度的把對資料的訪問分散到表所包含的所有塊上,也減少了塊包含的資料。這需要實踐來檢驗。至於pctfree和hash分割槽的個數,也沒有什麼固定值,需要dba透過經驗和系統的情況來調整,當然最有效的辦法就是測試了。透過測試來找到一個最佳值。

RAC全域性熱塊的處理手段可參考文章:http://blog.csdn.net/csucxcc/article/details/5896309

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/23135684/viewspace-673241/,如需轉載,請註明出處,否則將追究法律責任。

相關文章