KingbaseES CTID 与 Oracle ROWID
熟悉oracle的人都知道ROWID可用于快速的數(shù)據(jù)訪問,KingbaseES 由于自身MVCC機制的原因,ctid 作為 oracle rowid 的替代方案不合適,但currtid 還是基本可以滿足rowid 的功能的。本文向大家介紹如何通過currtid 實現(xiàn)rowid 的功能。
一、Oracle ROWID
Oracle ROWID 記錄了tuple的物理存儲位置,通過ROWID可以非常快速地訪問tuple。因此,在極致性能的應(yīng)用設(shè)計里,經(jīng)常會使用到ROWID。典型的使用場景如下:
declare
? ? ? ? cursor cur01 as select rowid from tab1;
begin
? ? ? ? ......
? ? ? ? update tab1 where rowid='xxx';
end;
二、KingbaseES CTID
我們知道,對于Oracle ,一條tuple的ROWID正常是不會變化的(引發(fā)row movement的操作除外,如:跨分區(qū)遷移update,表shrink),因此,應(yīng)用設(shè)計上可以方便的使用rowid,加快訪問速度。而對于KingbaseES,也有類似Oracle ROWID的ctid,格式 “(blockid,slotid)”,同樣記錄了tuple存儲的物理位置,通過ctid也能快速的訪問數(shù)據(jù)。但由于KingbaseES的多版本(MVCC)讀實現(xiàn)機制的差異,ctid會隨update操作變化,這種情況下,使用ctid有可能因為tuple被update,導(dǎo)致訪問不到數(shù)據(jù)。為了讓大家對于ctid有直觀認識,舉例如下:
| A用戶 | B用戶 |
| select ctid from t1 where id=1;返回 (0,1) | |
| select ctid from t1 where id=1;返回 (0,1) | |
| update t1 set name='aa' where ctid='(0,1)'; | |
| select ctid from t1 where id=1;返回 (0,2) | |
| select * from t1 where ctid='(0,1)'; 無返回 |
可以看到,在有并發(fā)的情況下,用ctid訪問是不可靠的。例子中,B用戶通過ctid 訪問時,就會發(fā)現(xiàn)找不到數(shù)據(jù)。
三、currtid 函數(shù)
KingbaseES的update操作實際delete and insert 的結(jié)合體。對于update操作完成后,在vacuum 之前,原始tuple是包含指向新tuple的ctid。函數(shù) currtid 可以通過舊ctid 取得updated tuple的最新ctid。具體見以下例子:
test=# insert into t1 values(1,'a'); INSERT 0 1 test=# select ctid from t1 where id=1;ctid -------(0,1) (1 row)test=# update t1 set name='aa' where id=1; UPDATE 1 test=# select ctid from t1 where id=1;ctid -------(0,2) (1 row)test=# select * from t1 where ctid='(0,1)';id | name ----+------ (0 rows)test=# select currtid('t1'::regclass,'(0,1)');currtid ---------(0,2) (1 row)test=# select * from t1 where ctid=currtid('t1'::regclass,'(0,1)');id | name ----+-----------1 | aa (1 row)可以看到,通過將初始的 ctid 傳遞給 currtid 函數(shù),可以取得最新的 ctid 。
Note:currtid 有效的前提是update 后,多版本信息沒有被清理掉,也就是沒有進行vacuum操作。
四、性能問題
從以上例子可以看到,使用currtid 可以避免期間數(shù)據(jù)被修改的問題。但實際上,這里有個性能的問題。請看實際例子:
test=# explain select * from t1 where ctid=currtid('t1'::regclass,'(0,1)');QUERY PLAN --------------------------------------------------------Seq Scan on t1 (cost=0.00..26.95 rows=1 width=44)Filter: (ctid = currtid('16387'::oid, '(0,1)'::tid)) (2 rows)test=# explain select * from t1 where ctid='(0,2)';QUERY PLAN ---------------------------------------------------Tid Scan on t1 (cost=0.00..4.01 rows=1 width=44)TID Cond: (ctid = '(0,2)'::tid) (2 rows)可以看到,對于?ctid=currtid('t1'::regclass,'(0,1)') ,實際上采取的是 seqscan 。問題是currtid('t1'::regclass,'(0,1)') 是在等式右邊的,不涉及 ctid 的轉(zhuǎn)換,為什么無法使用 Tid Scan ??
我們來看currtid 函數(shù)屬性:
test=# select proname,provolatile from pg_proc where proname='currtid';proname | provolatile ---------+-------------currtid | v函數(shù)是 volatile 。volatile 函數(shù)導(dǎo)致無法使用TID scan
五、修改函數(shù)屬性為immutable
把函數(shù)的屬性改成immutable 情況下的執(zhí)行計劃:
test=# update pg_proc set provolatile='i' where proname='currtid'; UPDATE 1 test=# explain select * from t1 where ctid=currtid('t1'::regclass,'(0,1)');QUERY PLAN ---------------------------------------------------Tid Scan on t1 (cost=0.00..4.01 rows=1 width=44)TID Cond: (ctid = '(0,2)'::tid) (2 rows)可以看到,修改函數(shù)的屬性為 immutable后,可以走 Tid Scan了。
附:volatile 函數(shù)與 immutable函數(shù)差異
就本例而言,對于SQL:select * from t1 where ctid=currtid('t1'::regclass,'(0,1)', '(0,1)' )。
如果currtid是volatile 類型的函數(shù),優(yōu)化器采取 Seq Scan,針對每個tuple,都會執(zhí)行一次函數(shù)調(diào)用。函數(shù)調(diào)用是在訪問tuple之后,因此,能夠保證數(shù)據(jù)的絕對準(zhǔn)確性。
如果currtid是immutable 類型的函數(shù),針對整個查詢,只需調(diào)用一次函數(shù)。執(zhí)行SQL時,先執(zhí)行函數(shù),再將結(jié)果以參數(shù)形式傳給SQL。這里的風(fēng)險點是,如果從函數(shù)調(diào)用開始到SQL執(zhí)行完成之前,如果tuple被update,可能導(dǎo)致返回結(jié)果的不準(zhǔn)確。幸運的是,無論函數(shù)調(diào)用,還是TID scan,都是非常快的(微秒級別),基本可以避免影響。
當(dāng)然,如果一定要考慮結(jié)果的絕對準(zhǔn)確,實際不管使用ROWID,還是 ctid , 都不是絕對安全。因為,即使oracle ,ROWID 也有可能發(fā)生變動。
NOTE:以上的例子同時在 PostgreSQL12 和 KingbaseES V8R6進行過驗證。
從計算 currtid('t1'::regclass,'(0,1)') 的結(jié)果,傳給ctid,再執(zhí)行SQL。在這期間(從即使currtid,到訪問到實際的tuple,時間不確定,可能很長,也可能很短,看執(zhí)行計劃),如果該tuple被修改,則可能返回錯誤的結(jié)果(無記錄)。如果采用全表,針對每個tuple,currtid('t1'::regclass,'(0,1)') 都要計算一次(volatile,即使參數(shù)值相同,不同時間返回的值是不同的),函數(shù) currtid('t1'::regclass,'(0,1)') 的結(jié)果運算推遲到tuple訪問的同時進行 ,避免了錯誤的結(jié)果。
總結(jié)
以上是生活随笔為你收集整理的KingbaseES CTID 与 Oracle ROWID的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 学习新技能_如何学习新技能
- 下一篇: 怎么测试唱歌水平的软件,测试一下你的唱功