2013-07-06 53 views
6

有沒有辦法爲表記錄生成某種有序標識符?按順序生成

假設我們有兩個線程做查詢:

主題1:

begin; 
insert into table1(id, value) values (nextval('table1_seq'), 'hello'); 
commit; 

線程2:

begin; 
insert into table1(id, value) values (nextval('table1_seq'), 'world'); 
commit; 

這是完全可能的(取決於時間),一個外部觀察者會看(2,'world')記錄出現在(1,'hello')之前。

這很好,但我想要一種方法來獲取自上次外部觀察者檢查後出現的'table1'中的所有記錄。

那麼,有沒有辦法按照插入的順序來獲取記錄?也許OID可以提供幫助?

回答

1

號既然有行的數據庫表中沒有自然順序,你有工作在你的表中的值。

那麼,有Postgres specific system columns cmin and ctid可能濫用到一定程度。

元組ID(ctid)包含該塊的文件塊編號和位置。所以這表示磁盤上的當前物理順序。後期增加將有更大的ctid,正常。你的SELECT語句可能看起來像這樣

SELECT *, ctid -- save ctid from last row in last_ctid 
FROM tbl 
WHERE ctid > last_ctid 
ORDER BY ctid 

ctid的數據類型爲tid。例如:'(0,9)'::tid

然而,它是不穩定長期標識符,因爲VACUUM或任何併發UPDATE或一些其它操作可以在任何時間改變的元組的物理位置。但是,在交易期間它是穩定的。如果你只是插入和沒有什麼其他,它應該在本地工作,爲您的目的。

我想補充一個時間戳列與除serial列默認now() ...

我還要讓列默認填充您的id柱(一serialIDENTITY列)。它從後面的階段中檢索數字,而不是明確提取並插入它,從而最小化(但不是消除)競爭條件的窗口 - 稍後插入較低的id的機會。詳細說明:

+0

我不認爲依靠物理行位置是一種有效的策略。中止的事務,丟失的數據庫連接等都可以使後面一行按錯誤順序排列。同樣使用串行列是不夠的,因爲在插入一行和提交一個thread1事務之間可能會有任何延遲,這可能會使thread2事務在它之前被提交和看到。 – Tometzky

+0

@Tometzky:我同意這不可靠。因爲我已經提到了更多的原因。它僅僅排除了一些無序元組的原因(比如單獨從序列中獲取ID),而不是其他的。 –

+0

我試圖濫用CTID,但它不起作用,因爲我也想不時地刪除一些行。有一個自動單調遞增計數器會很好,但我想我必須做明確的鎖定。 – Cyberax

1

如果你的意思是,如果每個查詢它看到world排它也看到hello排那麼你需要做的:

begin; 
lock table table1 in share update exclusive mode; 
insert into table1(id, value) values (nextval('table1_seq'), 'hello'); 
commit; 

share update exclusive mode是最弱的鎖定模式,這是自獨一無二 - 一次只能有一個會話。

請注意,這不會使這個序列無間隔 - 這是一個不同的問題。

3

你想要的是強迫交易犯(使他們的鑲可見)以相同的順序,他們做了刀片。至於其他客戶所關注的插入還沒有發生,直到他們承諾,因爲他們可能會回滾和消失。

即使您未將插入內容包裝在明確的begin/commit中,也是如此。事務提交,即使隱式完成,仍然不一定以與自己插入的行相同的順序運行。它受操作系統CPU調度程序排序決定等的影響。

即使PostgreSQL支持髒讀,這也會使仍爲爲真。僅僅因爲你開始按給定順序的三次插入並不意味着他們會按照該順序完成。

沒有簡單或可靠的方法來做你認爲會保持併發性的東西。您需要按照單個工作人員的順序執行插入操作,或者按照Tometzky的建議使用表鎖,因爲只有一個插入線程可以在任何給定時間執行任何操作,所以它的效果基本相同。

您可以使用建議鎖定,但效果是相同的。

使用時間戳不會有幫助,因爲您不知道知道如果對於任何兩個時間戳,那麼在兩個時間戳之間有一行還沒有被提交。

由於回滾,系統生成的列中的間距是正常的,所以您不能依賴標識列來讀取直到第一個「間隙」的行。

我想你應該退後一步,看看爲什麼你有這個要求,並且,根據這個要求,爲什麼你使用單獨的併發插入。

也許你會更好地從一個會話做小塊批量插入?

+0

我正面臨類似的挑戰,我這樣做的理由是暴露一些事件。每一行代表和事件。 Feed的合同應該是頁面不可變。 客戶端將請求頁面視爲'page?after = &limit = XX'。 但是,如果不按順序插入,可能會導致'page?after = 3'在第一次返回'[5]'並且在提交「4」後返回'[4,5]'。 這是一個僅插入表格,所以它似乎是一個很好的飼料基礎。 但是,序列號出現的順序不正確,我正在考慮是否有更好的方法。我們目前正在回看一些頁面以檢查新條目。 –

+1

這正是邏輯解碼的目的。看看wal2json,pglogical等 –