2017-09-30 65 views
1

我試圖建立一個維基百科喜歡的東西,在那裏多人可以編輯的內容。有特權的人也可以恢復更改。我不希望反轉是有損的(意味着真的放棄了人們編輯的編輯,它只應該隱藏它),所以這似乎需要像數據結構這樣的git分支用指向「當前」的指針存儲編輯。如何在postgresql中存儲像數據結構的git分支?

我想這樣的設計:

CREATE TABLE article (
    id serial PRIMARY KEY, 
    content text NOT NULL, 
    author integer NOT NULL REFERENCES "user", 
    path text NOT NULL, 
    relationship ltree NOT NULL 
); 

CREATE TABLE current_article (
    article_id NOT NULL REFERENCES article 
); 

relationship記錄,如果它是一個新的文章或現有項目的編輯:

id | content | path | author | relationship 
---+---------+------+--------+------------- 
1 | foo  | /a1 | 1  | 'root' 
2 | bar  | /a1 | 2  | 'root.1' 
3 | baz  | /a2 | 3  | 'root' 

在這裏,它的意思,筆者2從改變文章/a1 foo來吧,文章/a2是一個新的。

current_article記錄哪篇文章是「當前」文章,通常只是指向最新文章。迴歸後,它可以指向一老一:

article_id 
---------- 
2 
3 

當編輯進來,我插入這樣的:

INSERT INTO article (content, path, author) VALUES ('qux', '/a2', 4); 

並依靠一個INSERT觸發器之前找到當前文章該路徑和填充關係以及後插入觸發器來更新當前文章指針。

您對這種設計有什麼看法?我在設計這個問題時遇到了併發問題。

在之前的插入觸發器中,到它找到當前項目時,它可能已被更改,並且在插入後觸發器中,它可能會錯誤地覆蓋當前項目,並且已指向不同的項目。

我在這方面有三個問題:

  1. 將串行隔離解決這個問題? (我對MVCC的概念相當陌生,仍然試圖繞過它的頭)如果不是,我該如何解決它?
  2. 有沒有更好的設計,不必處理併發?
  3. 如果確實需要處理併發性,我該如何在不同的競爭條件下單元測試我的設計(或者甚至是必要的單元測試)?

謝謝。

回答

2

併發發生在兩個級別:應用程序和數據庫。

在應用程序級別,多個用戶可能有重疊的編輯會話。在某些時候,用戶會保存他們的版本,然後下一個會保存,但是在當前的設計中似乎沒有辦法找出最後一個編輯是從哪個版本分支的:這個信息是無處提到INSERT

數據庫級別的併發性是一個不同的問題,它涉及到同時運行的事務。

如果您試圖解決處理併發性的數據庫原語的應用程序併發性問題,則必須保持打開事務,直到用戶完成編輯爲止,這意味着在任意長時間內,這是非啓動器數據庫設計。

首先,您需要找出處理併發編輯的應用程序和設計策略,然後您需要找出數據庫策略來處理併發事務,即人們同時點擊「保存」並且更新數據的事務並行運行。這些是完全不同的東西。


關於併發事務,一個通用的方式,以避免麻煩的是別人做任何事情之前鎖定在寫入事務開始的文章,讓其他任何交易,將嘗試做同樣的會直到併發更改被提交(或回滾)。這是序列化更新的最簡單方法,但它假定有一些要鎖定的內容,具有足夠的粒度,以便更新其他文章不被同時阻止。

理想情況下,應該有一個article表,每個path(獨立的修訂版本,將存儲在不同的表中)只有一行。然後使用SELECT ... FOR UPDATE鎖定該行將足以保證執行分支或任何複雜更新的查詢集可以工作,而不會受到對同一文章的併發更改的困擾。

另一個(更粗糙的)方法是使用可序列化的隔離級別,並重試任何失敗的事務,並指示SQLSTATE表示串行化失敗。

+0

你是對的,編輯不知道它基於哪個版本。客戶也應該發送他們基於的文章ID,我會更新。但是,我不太瞭解應用程序會話的相關性。兩個用戶可以同時進行編輯,如果先保存,另一個將在保存時通知衝突。所以我不應該只需要處理數據庫併發性?這是我遇到的困難。 – hgl

+0

@hgl:編輯後添加更多關於數據庫併發性方面的信息。 –