SQL> insert into test(x) values (1);1 row created.SQL> commit;Commit complete.
查找這行記錄所在的block,并dump出來:
SQL> select dbms_rowid.rowid_block_number(rowid) from test;DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)34SQL> alter system dump datafile 23 block 34;System altered.
我們不難看出,nrid出現了值,指向了下一個row id,證實剛剛的update操作使這行記錄產生了行鏈接或者行? 二、行遷移/行鏈接的檢測 通過前面的介紹我們知道,行鏈接主要是由于數據庫的db_block_size不夠大,對于一些大的字段沒法在一個block中存儲下,從而產生了行鏈接。對于行鏈接我們除了增大db_block_size之外沒有別的任何辦法去避免,但是因為數據庫建立后db_block_size是不可改變的(在9i之前),對于Oracle9i的數據庫我們可以對不同的表空間指定不同的db_block_size,因此行鏈接的產生幾乎是不可避免的,也沒有太多可以調整的地方。行遷移則主要是由于更新表的時候,由于表的pctfree參數設置太小,導致block中沒有足夠的空間去容納更新后的記錄,從而產生了行遷移。對于行遷移來說就非常有調整的必要了,因為這個是可以調整和控制清除的。 如何檢測數據庫中存在有了行遷移和行鏈接呢?我們可以利用Oracle數據庫自身提供的腳本utlchain.sql(在$ORACLE_HOME/rdbms/admin目錄下)生成chained_rows表,然后利用ANALYZE TABLE table_name LIST CHAINED ROWS INTO chained_rows命令逐個分析表,將分析的結果存入chained_rows表中。從utlchain.sql腳本中我們看到chained_rows的建表腳本,對于分區表,cluster表都是適用的。然后可以使用拼湊語句的辦法生成分析所需要的表的腳本,并執行腳本將具體的分析數據放入Chained_rows表中,例如下面是分析一個用戶下所有表的腳本:
SPOOL list_migation_rows.sqlSET ECHO OFFSET HEADING OFFSELECT 'ANALYZE TABLE ' table_name ' LIST CHAINED ROWS INTO chained_rows;' FROM user_tables;SPOOL OFF
然后查詢chained_rows表,可以具體查看某張表上有多少的行鏈接和行遷移。
SELECT table_name, count(*) from chained_rows GROUP BY table_name;
當然,也可以查詢v$sysstat視圖中的’table fetch continued row’列得到當前的行鏈接和行遷移數量。
SELECT name, value FROM v$sysstat WHERE name = 'table fetch continued row';
可以使用如下的腳本來直接查找存在有行鏈接和行遷移的表,自動完成所有的分析和統計。
accept owner prompt " Enter the schema name to check for Row Chaining (RETURN for All): "promptpromptaccept table prompt " Enter the table name to check (RETURN for All tables owned by &owner): "promptpromptset head off serverout on term on feed off veri off echo off!clearprompt declarev_owner varchar2(30);v_table varchar2(30);v_chains number;v_rows number;v_count number := 0;sql_stmt varchar2(100);dynamicCursor INTEGER;dummy INTEGER;cursor chains isselect count(*) from chained_rows;cursor analyze isselect owner, table_namefrom sys.dba_tables where owner like upper('%&owner%')and table_name like upper('%&table%')order by table_name;begindbms_output.enable(64000);open analyze;fetch analyze into v_owner, v_table;while analyze%FOUND loopdynamicCursor := dbms_sql.open_cursor;sql_stmt := 'analyze table 'v_owner'.'v_table' list chained rows into chained_rows';dbms_sql.parse(dynamicCursor, sql_stmt, dbms_sql.native);dummy := dbms_sql.execute(dynamicCursor);dbms_sql.close_cursor(dynamicCursor);open chains;fetch chains into v_chains;if (v_chains != 0) thenif (v_count = 0) thendbms_output.put_line(CHR(9)CHR(9)CHR(9)'<<<<< Chained Rows Found >>>>>');v_count := 1;end if;dynamicCursor := dbms_sql.open_cursor;sql_stmt := 'Select count(*) v_rows'' From 'v_owner'.'v_table;dbms_sql.parse(dynamicCursor, sql_stmt, dbms_sql.native);dbms_sql.DEFINE_COLUMN(dynamicCursor, 1, v_rows);dummy := dbms_sql.execute(dynamicCursor);dummy := dbms_sql.fetch_rows(dynamicCursor);dbms_sql.COLUMN_VALUE(dynamicCursor, 1, v_rows);dbms_sql.close_cursor(dynamicCursor);dbms_output.put_line(v_owner'.'v_table);dbms_output.put_line(CHR(9)'---> Has 'v_chains' Chained Rows and 'v_rows' Num_Rows in it!');dynamicCursor := dbms_sql.open_cursor;sql_stmt := 'truncate table chained_rows';dbms_sql.parse(dynamicCursor, sql_stmt, dbms_sql.native);dummy := dbms_sql.execute(dynamicCursor);dbms_sql.close_cursor(dynamicCursor);v_chains := 0;end if;close chains;fetch analyze into v_owner, v_table;end loop;if (v_count = 0) thendbms_output.put_line('No Chained Rows found in the 'v_owner' owned Tables!');end if;close analyze;end;/set feed on head onprompt
SQL>@$ORACLE_HOME/rdbms/admin/utlchain.sqlTable created.SQL> ANALYZE TABLE CUSTOMER LIST CHAINED ROWS INTO chained_rows;Table analyzed.SQL>SELECT count(*) from chained_rows;TABLE_NAME COUNT(*)CUSTOMER 213061 rows selected.
查看在CUSTOMER表上存在的限制:
SQL>select CONSTRAINT_NAME,CONSTRAINT_TYPE,TABLE_NAME from USER_CONSTRAINTS where TABLE_NAME='CUSTOMER';CONSTRAINT_NAME C TABLE_NAME------------------------------ - --PK_CUSTOMER1 P CUSTOMERSQL>select CONSTRAINT_NAME,CONSTRAINT_TYPE,TABLE_NAME from USER_CONSTRAINTS where R_CONSTRAINT_NAME='PK_CUSTOMER1';no rows selectedSQL> CREATE TABLE CUSTOMER_temp ASSELECT * FROM CUSTOMER WHERE rowid IN(SELECT head_rowid FROM chained_rowsWHERE table_name = 'CUSTOMER'); Table created.SQL>select count(*) from CUSTOMER;COUNT(*)----------338299SQL> DELETE CUSTOMER WHERE rowid IN(SELECT head_rowidFROM chained_rowsWHERE table_name = 'CUSTOMER');21306 rows deleted.SQL> INSERT INTO CUSTOMER SELECT * FROM CUSTOMER_temp;21306 rows created.SQL> DROP TABLE CUSTOMER_temp;Table dropped.SQL> commit;Commit complete.SQL> select count(*) from CUSTOMER;COUNT(*)----------338299SQL> truncate table chained_rows;Table truncated.SQL> ANALYZE TABLE CUSTOMER LIST CHAINED ROWS INTO chained_rows;Table analyzed.SQL> select count(*) from chained_rows; COUNT(*)----------0