天天看點

Oracle行内連結不會引起USER_TABLES中CHAIN_CNT值變化

    問題描述:下面SQL,建立一個超過255列的表(實際為256列),然後插入幾條資料,然後對表做ANALYZE分析過後,但是發現user_tables的CHAIN_CNT字段值為0,chained_rows表中沒有記錄,為什麼會這樣?

<a href="http://images2015.cnblogs.com/blog/73542/201607/73542-20160710100709405-1669637749.png"></a>

在分析這個問題前,我們要先了解一下Oracle資料庫當中的Row Migration (行遷移) &amp; Row Chaining (行連結)概念:

    當表中一行的資料不能在一個資料block中放入的時候,這個時候就會發生兩種情況,一種是行連結(Row Chaining),另外一種就是行遷移(Row Migration)了。

   行連結産生在第一次插入資料的時候如果一個block不能存放一行記錄的情況下。這種情況下,Oracle将使用連結一個或者多個在這個段中保留的block存儲這一行記錄,行連結比較容易發生在比較大的行上,例如行上有LONG、LONG RAW、LOB等資料類型的字段,這種時候行連結是不可避免的會産生的。

   當一行記錄初始插入的時候事可以存儲在一個block中的,由于更新操作導緻行長增加了,而block的自由空間已經完全滿了,這個時候就産生了行遷移。在這種情況下,Oracle将會遷移整行資料到一個新的block中(假設一個block中可以存儲下整行資料),Oracle會保留被遷移行的原始指針指向新的存放行資料的block,這就意味着被遷移行的ROW ID是不會改變的。

當發生了行遷移或者行連結,對這行資料操作的性能就會降低,因為Oracle必須要掃描更多的block來獲得這行的資訊

row chain:When a row is too large to fit into any block, row chaining occurs. In this case, the Oracle devide the row into smaller chunks. each chunk is stored in a block along with the necessary poiters to retrive and assemble the entire row.

row migration:when a row is to be updated and it cannot find the necessary free space in its block, the Oracle will move the entire row into a new block and leave a pointer from the orginal block to the new location. This process is called row migration.

那麼現在回到這個問題,我們先來看看表t_chain1的rowid,以及對應的檔案号等資訊:

<a href="http://images2015.cnblogs.com/blog/73542/201607/73542-20160710100712733-378963099.png"></a>

<b>Row Format and Size</b>

<a></a>

Oracle stores each row of a database table containing data for less than 256 columns as one or more row pieces. If an entire row can be inserted into a single data block, then Oracle stores the row as one row piece. However, if all of a row's data cannot be inserted into a single data block or if an update to an existing row causes the row to outgrow its data block, then Oracle stores the row using multiple row pieces. A data block usually contains only one row piece for each row. When Oracle must store a row in more than one row piece, it is chained across multiple blocks.

<a href="http://images2015.cnblogs.com/blog/73542/201607/73542-20160710100714561-1793163873.png"></a>

<a href="http://images2015.cnblogs.com/blog/73542/201607/73542-20160710100716717-750429806.png"></a>

去$ORACLE_BASE下面的udmp找到對應的trc檔案,我實驗中生成的檔案為scm2_ora_20850.trc

<a href="http://images2015.cnblogs.com/blog/73542/201607/73542-20160710100718030-2045535667.png"></a>

<a href="http://images2015.cnblogs.com/blog/73542/201607/73542-20160710100719655-475940888.png"></a>

cc:表示列數,fb:H是指行記錄的頭,L是指行記錄的最後一列,F是指行記錄的第一列. 實驗結果跟理論是一緻的。到這裡似乎一直沒有回到我們的問題來,那麼我們先來看看官方文檔對AVG_ROW_LEN的解釋:

Number of rows in the table that are <b>chained from one data block to another</b>,<b> or which have migrated to a new block</b>, requiring a link to preserve the old ROWID

下面我們來構造一個行連結的案例,如下所示,建立表t_chain,使其一行的記錄無法插入到一個block裡面,那麼當插入的時候,就會産生行連結,此時對表做ANALYZE分析過後,但是發現user_tables的CHAIN_CNT字段值不為0了,chained_rows表中也會有相關記錄

<a href="http://images2015.cnblogs.com/blog/73542/201607/73542-20160710100721139-167411997.png"></a>

參考資料:

<a href="https://docs.oracle.com/cd/B19306_01/server.102/b14237/statviews_2105.htm#REFRN20286">https://docs.oracle.com/cd/B19306_01/server.102/b14237/statviews_2105.htm#REFRN20286</a>

<a href="http://docs.oracle.com/cd/B19306_01/server.102/b14220/schema.htm#i4383">http://docs.oracle.com/cd/B19306_01/server.102/b14220/schema.htm#i4383</a>

<a href="http://docs.oracle.com/cd/B28359_01/server.111/b28318/schema.htm#CNCPT1129">http://docs.oracle.com/cd/B28359_01/server.111/b28318/schema.htm#CNCPT1129</a>