共計 5057 個字符,預計需要花費 13 分鐘才能閱讀完成。
這篇文章將為大家詳細講解有關 Oracle 如何修改壓縮數據,丸趣 TV 小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
我們將看到只有在直接路徑加載、CTAS(create table as select)和 alter table move 時,基礎表壓縮機制才可以生效。同時當表啟用了壓縮時,Oracle 會默認的將該表中數據塊的 pctfree 設置為 0,這也暗示了我們基礎壓縮應該作為一種只讀數據的壓縮策略。
當我們查看一個對應塊的 dump 文件時,會發現 Oracle 并不是“壓縮”數據,他所做的是在每個塊上創建重復值列表(即字典表),然后通過一些標志來代替那些重復值從而達到塊級別的去重。并且,Oracle 可以重新排列塊中的字段順序,從而增加用一個標志來代替多個字段的機會。這告訴我們,Oracle 在讀取塊時并不需要“解壓”數據,他需要做的僅僅是通過指針來重構數據,當然這是一個 CPU 密集型操作。
在這篇文章中,我們將討論如果不遵從只讀原則將會發生什么。然后,我們將會在第三篇文章中探討需要另外授權的 OLTP 壓縮。如前所述,以下所有示例都來自 Oracle 11.2.0.3 的實例。
去重與刪除
你可以回憶下上篇文章中,我把一個包含組合標志的數據塊的一行 dump 出來,然后 Oracle 遞歸的向上查找這個標志代表的意義,最終確定該組合標志由兩個單獨的標志和兩個額外的字段值組合而成,下面就是我們測試的那行:
tab 1, row 0, @0x1b28
tl: 5 fb: –H-FL– lb: 0x0 cc: 4
col 0: [4] 41 41 41 41
col 1: [10] 41 41 41 41 41 41 41 41 41 41
col 2: [2] c1 02
col 3: [10] 20 20 20 20 20 20 20 20 20 31
bindmp: 2c 00 01 04 31
這是我們在查找引用的單個標志的值時所發現的 **49 號標志 **:
Tab 0, row 49, @0x1ed0
tl: 19 fb: –H-FL– lb: 0x0 cc: 4
col 0: [4] 41 41 41 41
col 1: [10] 41 41 41 41 41 41 41 41 41 41
col 2: [2] c1 02
col 3: [10] 20 20 20 20 20 20 20 20 20 31
bindmp: 00 08 04 36 40 ca c1 02 d2 20 20 20 20 20 20 20 20 20 31
bindmp 中的前 5 個字節告訴我們這個標志在這個塊中使用了 8 次 (00 08), 由 4 個列組成,然后我們來看看 54(0x36) 和 64(0x40)號標志:
tab 0, row 54, @0x1f74
tl: 7 fb: –H-FL– lb: 0x0 cc: 1
col 0: [4] 41 41 41 41
bindmp: 00 0a cc 41 41 41 41
tab 0, row 64, @0x1f7b
tl: 13 fb: –H-FL– lb: 0x0 cc: 1
col 0: [10] 41 41 41 41 41 41 41 41 41 41
bindmp: 00 05 d2 41 41 41 41 41 41 41 41 41 41
從上面的 dump 數據我們可以猜到,如果想要刪除原始行,就必須進行額外的工作。\
有兩件事必然會發生:
1. 該行必須標志為已刪除(以正常的方式),
2. **49 號標志 ** 的“使用計數”也必須減少 1。
在刪除一行之后,這里有一個小的片段,首先是行條目本身:
tab 1, row 0, @0x1b28
tl: 2 fb: –HDFL– lb: 0x2
bindmp: 3c 02
以下是 **49 號標志 ** 的二進制轉儲,注意,第二個字節:
bindmp: 00 07 04 36 40 ca c1 02 d2 20 20 20 20 20 20 20 20 20 31
所以我們可以意識到,即使是刪除簡單的一行,也會使維護塊數據的工作增加。但是這個標志同時也在塊的其他 7 行中使用,所以如果我刪除這些行,會發生什么? 答案取決于刪除的并發會話數量。如果我使用一個進程來刪除所有 8 行,在刪除第 8 行時,Oracle 刪除了標志,此時 63 號標志和 64 號標志必須更新,以顯示它們缺少了一個依賴項。如果我重復測試使用多個會話來刪除行,并且在每次刪除后不提交,那么我就可以看到一個場景,標志顯示為零,但不會消失。(也有可能我還沒有觀察到的一些后續的塊清理操作將會清除這個狀態的標志。)
在我提到并發測試之前,我沒有提到任何關于提交或回滾的內容。標志的改變發生在 delete 這個動作上,并且之后并沒有提交。如果我提交或回滾會發生什么?
在提交時,可能會發生通常的提交清除操作,用提交時的 SCN 更新事務的 ITL 插槽(換句話說,沒有新的或特別的事情發生)。在回滾時,數據根據 undo 信息恢復,任何已經被刪除的標志也將被重新創建,任何相關標志的使用數都會增加。
但重點是,回滾之后,壓縮依然會保留。雖然這些行會在回滾后寫入塊的空閑空間,但在原始塊和回滾后的塊之間還是有一些區別。因為這樣的操作需要塊經過空閑空間碎片的整合操作。所以如果你再次將塊 dump 出來,你可以看到塊的內容已經被移動了。在我的例子里(刪除了引用 49 號標志的 8 行記錄,然后回滾),我看到了如下的區別:
tab 0, row 49, @0x1ed0 — original position of token 0
tab 0, row 49, @0x134a — position of token 0 after rollback
tab 1, row 0, @0x1b28 — original position of row 0
tab 1, row 0, @0x1322 — position of row 0 after rollback
壓縮與空閑空間
當你刪除然后回滾數據后,行就會移動,這個現象引出了關于空閑空間非常有趣的一點——當你的表是基礎壓縮的時候,默認的 pctfree 就是 0 了。沒有空閑空間,但有空間給我在回滾后移動數據用?
我發現 Oracle 確實會保留一點點空間(大約幾十 byte,但對于我測試用例里的兩整行也是絕對足夠了)。這一小部分空間允許 Oracle 恢復那些已被刪除的行。有些情況,這部分剩余空間甚至能讓你做 update 操作。
我來微調下我的初始數據集,每一行看起來如下:
(1000001, AAAA , AAAAAAAAAA , nbsp; nbsp; nbsp; nbsp; nbsp; nbsp; nbsp; nbsp; nbsp;1)
第一列是一個序列,第二列從 AAAA 到 EEEE 循環,第三列從 AAAAAAAAAA 到 JJJJJJJJJJ 循環,最后一列是 10 個字符,從 1 -50 循環(占位符用 \ nbsp; 表示)。然后我生成 800 行數據。由于我創建數據的方法問題,第一個數據塊中有 11 行數據,第二第三列都是 A,所以我需要運行如下 sql 然后 dump 表中的第一個塊來觀察發生了什么。
update t1
set
vc_rep = BBBB
where
vc_rep = AAAA
and vc_cycle = AAAAAAAAAA
and rownum = 4
;
這證明了這個塊里有足夠的空間來更新這兩行記錄,而且始終在同一個塊里,但是我的行還是發生了遷移。這里有這個數據塊中某行在操作前后的 dump 數據對比:
tab 1, row 0, @0x1bb8 — before
tl: 11 fb: –H-FL– lb: 0x0 cc: 4
col 0: [4] 41 41 41 41
col 1: [10] 41 41 41 41 41 41 41 41 41 41
col 2: [10] 20 20 20 20 20 20 20 20 20 31
col 3: [5] c4 02 01 01 02
bindmp: 2c 00 02 03 1b cd c4 02 01 01 02
tab 1, row 0, @0x4f3 — after
tl: 37 fb: –H-FL– lb: 0x2 cc: 4
col 0: [4] 42 42 42 42
col 1: [10] 41 41 41 41 41 41 41 41 41 41
col 2: [10] 20 20 20 20 20 20 20 20 20 31
col 3: [5] c4 02 01 01 02
bindmp: 2c 02 04 00 cc 42 42 42 42 d2 41 41 41 41 41 41 41 41 41 41 d2 20 20 20 20 20 20 20 20 20 31 cd c4 02 01 01 02
在 update 操作后,Oracle 將該行擴展成了完整的四列數據。有兩個標志在字典表中,可以被用來替換更新的這行記錄的前兩個字段。但是 Oracle 并沒有去試圖尋找并使用這些標志。所以,這么看來,好像 update 壓縮的數據就會造成整體的混亂,一行壓縮的記錄可能會擴展的及其巨大,微不足道的那點空閑空間無法裝下這些數據,最終引發了行的遷移。
雖然我們現在看來,當出現擴展的行以及遷移的行之后,數據會有點混亂。但當我們執行回滾操作時,Oracle 會把這些混亂清理干凈,而且剩余的行也都會在原始壓縮的、未遷移的位置。
所以 update 操作到底能造成多么糟糕的影響?回答這個問題之前,我們可以先看下我所做的 update 操作。我修改了一個標志可以代替的值,而且該值在很多行中都存在。但如果我修改了一個標志無法代替的值呢?Oracle 還會因為這個 update 來擴展這行記錄嗎?答案是否定的。如果我們修改了 ID(序列類型,不重復,無法標志化)的值。下面是修改前會的 dump 數據對比:
tab 1, row 0, @0x1bb8 — before
tl: 11 fb: –H-FL– lb: 0x0 cc: 4
col 0: [4] 41 41 41 41
col 1: [10] 41 41 41 41 41 41 41 41 41 41
col 2: [10] 20 20 20 20 20 20 20 20 20 31
col 3: [5] c4 02 01 01 02
bindmp: 2c 00 02 03 1b cd c4 02 01 01 02
tab 1, row 0, @0x1bb8 — after
tl: 10 fb: –H-FL– lb: 0x2 cc: 4
col 0: [4] 41 41 41 41
col 1: [10] 41 41 41 41 41 41 41 41 41 41
col 2: [10] 20 20 20 20 20 20 20 20 20 31
col 3: [4] c3 64 64 64
bindmp: 2c 02 02 03 1b cc c3 64 64 64
update 操作后的數據依然在原來的位置,并未發生遷移。但是請注意該行由一個可代表前三行的標志和一個實際的值組成。行擴展并未發生。
我初始測試的那行數據實際上整行都可以被一個標志所代替。如果我更新一個被多個標志組合起來的行中的某個標志化的字段會怎樣?Oracle 并不會擴展整行——它只會擴展 update 操作影響的那列的數據。這里是操作前后的 dump 數據:
tab 1, row 18, @0x1ac2
tl: 13 fb: –H-FL– lb: 0x0 cc: 4
col 0: [4] 44 44 44 44
col 1: [10] 58 58 58 58 58 58 58 58 58 58
col 2: [10] 20 20 20 20 20 20 20 20 33 34
col 3: [5] c4 02 01 01 14
bindmp: 2c 00 04 03 32 37 45 cd c4 02 01 01 14
tab 1, row 18, @0x1ab8
tl: 23 fb: –H-FL– lb: 0x2 cc: 4
col 0: [4] 44 44 44 44
col 1: [10] 59 59 59 59 59 59 59 59 59 59
col 2: [10] 20 20 20 20 20 20 20 20 33 34
col 3: [5] c4 02 01 01 14
bindmp: 2c 02 04 00 32 d2 59 59 59 59 59 59 59 59 59 59 45 cd c4 02 01 01 14
在這個測試的最開始,dump 數據就表明了這行由三個獨立的標志 (0x32, 0x37 和 0x45) 和一個實際數值組成。我將第一列的值‘XXXXXXXXXX’更新為‘YYYYYYYYYY’,正如你所見,最后一塊 dump 數據依然包含標志 0x32 和 0x45,但是標志 0x37 已經被實際值所替換掉。你也可以看到行的長度增加了 10 字節(從 13b 增加到 23b),這意味著 Oracle 不得不把它移動到那很小的一部分空閑空間中,所以最終行的地址發生了變化。
所以當你試圖更新基礎表壓縮中的數據時,Oracle 可能將標志擴展為實際值,但它會盡可能的做最小化的擴展。即使數據在壓縮后 pctfree 為 0 的情況下數據塊中依然有一小部分空間。所以雖然你可以在不造成大量擴展以及行遷移的情況下做一些極小量的 update 操作,但這些副作用幾乎不可能被預知。
如果你確實需要對已壓縮的數據做一些小量的維護操作,就需要對實際數據做足夠多的測試來尋找最合適的 pctfree 的值,以將行遷移率控制在可接受的范圍。
關于“Oracle 如何修改壓縮數據”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。