1. 程式人生 > >高水位線引起的查詢變慢解決方法

高水位線引起的查詢變慢解決方法

眾所周知,隨著不斷地進行表記錄的DML操作,會不斷提高表的高水位線(HWM),DELETE操作之後雖然表的資料刪除了,但是並沒有降低表的高水位,除非你使用TRUNCATE操作,進行表查詢的時候,Oracle會掃表高水位以下的資料塊,也就是說,掃描的時間並不會有所減少。所以DELETE刪除資料以後並不會提高表的查詢效率。

面通過這個例子,用來解決高水位引起的查詢變慢問題:

--例子中測試表佔用表空間大小為:128M
SQL> SELECT a.bytes/1024/1024 || 'M' FROM user_segments a WHERE a.segment_name = 'TC_RES_PHY_EQP_PRO_MID_517';

A.BYTES/1024/1024||'M'
-----------------------------------------
128M

--查詢一條記錄成本為:4357,一致性讀為:15730 耗時 0.53 秒
SQL> set autotrace  on
SQL> SELECT 1 FROM TC_RES_PHY_EQP_PRO_MID_517 a WHERE a.obj_id = 17202000000001;

         1
----------
         1


執行計劃
----------------------------------------------------------
Plan hash value: 854298875

------------------------------------------------------------------------------------------------
| Id  | Operation         | Name                       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                            |   175 |  2275 |  4357   (2)| 00:00:53 |
|*  1 |  TABLE ACCESS FULL| TC_RES_PHY_EQP_PRO_MID_517 |   175 |  2275 |  4357   (2)| 00:00:53 |
------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("A"."OBJ_ID"=17202000000001)

Note
-----
   - dynamic sampling used for this statement (level=2)


統計資訊
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      15730  consistent gets
          0  physical reads
          0  redo size
        520  bytes sent via SQL*Net to client
        520  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

--現在刪除大部分資料,只剩下一條測試資料:
SQL> DELETE FROM TC_RES_PHY_EQP_PRO_MID_517 a WHERE a.obj_id <> 17202000000001;

已刪除1172857行。

--查詢該段佔用的表空間仍然為128M
SQL> set autotrace off
SQL> SELECT a.bytes/1024/1024 || 'M' FROM user_segments a WHERE a.segment_name = 'TC_RES_PHY_EQP_PRO_MID_517';

A.BYTES/1024/1024||'M'
-----------------------------------------
128M
SQL> COMMIT;

提交完成。

SQL> SELECT a.bytes/1024/1024 || 'M' FROM user_segments a WHERE a.segment_name = 'TC_RES_PHY_EQP_PRO_MID_517';

A.BYTES/1024/1024||'M'
-----------------------------------------
128M

--查詢一條記錄消耗的成本為:4316,一致性讀為:15730 耗時 0.52 秒
SQL> set autotrace on
SQL> SELECT 1 FROM TC_RES_PHY_EQP_PRO_MID_517 a WHERE a.obj_id = 17202000000001;

         1
----------
         1


執行計劃
----------------------------------------------------------
Plan hash value: 854298875

------------------------------------------------------------------------------------------------
| Id  | Operation         | Name                       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                            |     1 |    13 |  4316   (1)| 00:00:52 |
|*  1 |  TABLE ACCESS FULL| TC_RES_PHY_EQP_PRO_MID_517 |     1 |    13 |  4316   (1)| 00:00:52 |
------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("A"."OBJ_ID"=17202000000001)

Note
-----
   - dynamic sampling used for this statement (level=2)


統計資訊
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      15730  consistent gets
          0  physical reads
          0  redo size
        520  bytes sent via SQL*Net to client
        520  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

--一般情況下,表的rowid是不會變的,我們通過ALTER TABLE TABLE_NAME ENABLE ROW MOVEMENT;來開啟行遷移
SQL> ALTER TABLE TC_RES_PHY_EQP_PRO_MID_517 ENABLE ROW MOVEMENT;

表已更改。

--整理碎片並回收空間
--此操作相比於ALTER TABLE MOVE:
--1.不會消耗更多的表空間
--2.可以線上執行,不會使索引失效
--3.可以使用引數CASCADE,同時收縮表上的索引
--4.ALTER TABLE MOVE之後表空間的位置肯定會發生變化,而SHRINK表空間的位置沒有發生變化
SQL> ALTER TABLE TC_RES_PHY_EQP_PRO_MID_517 SHRINK SPACE;

表已更改。
--查詢一條記錄消耗的成本為:2,一致性讀為:4 耗時 0.01 秒
SQL> SELECT 1 FROM TC_RES_PHY_EQP_PRO_MID_517 a WHERE a.obj_id = 17202000000001;

         1
----------
         1


執行計劃
----------------------------------------------------------
Plan hash value: 854298875

------------------------------------------------------------------------------------------------
| Id  | Operation         | Name                       | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                            |     1 |    13 |     2   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| TC_RES_PHY_EQP_PRO_MID_517 |     1 |    13 |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("A"."OBJ_ID"=17202000000001)

Note
-----
   - dynamic sampling used for this statement (level=2)


統計資訊
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
        520  bytes sent via SQL*Net to client
        520  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
--此時佔用表空間只有4M
SQL> SELECT a.bytes/1024/1024 || 'M' FROM user_segments a WHERE a.segment_name = 'TC_RES_PHY_EQP_PRO_MID_517';

A.BYTES/1024/1024||'M'
-----------------------------------------
4M

當然ENABLE ROW MOVEMENT對系統性能也有影響,在TOM的部落格中找到這個關於ROW MOVEMENT的問答:

You Asked

Hi Tom 

I have seen your posting on ENABLE ROW MOVEMENT which is available in 10g. It looks a 
very nice option since we can relocate and reorganize  the heap tables without any outage 
since it does not invalidate indexes. But is there any performance hit or any other 
disadvantages for using this. I would like to use this in our new application.

Rgds
Anil 
 
and we said...

Well, the tables have to be in an ASSM (Automatic Segment Space Managment) tablespace for 
this to work (so if they are not, you have to move them there first in order to do this 
over time).

It will necessarily consume processing resources on your machine while running (it will 
read the table, it will delete/insert the rows at the bottom of the table to move them 
up, it will generate redo, it will generate undo).

I would suggest benchmarking -- collect performance metrics about the table before and 
after performing the operation.  You would expect full scans to operate more efficiently 
after, you would expect index range scans to either be unchanged or "better" as you have 
more rows per block packed together (less data spread).  You would be looking for that to 
happen -- statspack or the tools available in dbconsole would be useful for measuring 
that (the amount of work performed by your queries over time) 

也就是說,ENABLE ROW MOVEMENT也會有副作用,因為表開啟行遷移之後,如果對資料進行UPDATE操作,那麼系統會對資料進行DELETE操作

之後再進行INSERT操作,導致產生更多的redo和undo,並且rowid也會發生變化。

附行遷移和行連線的解釋:

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.