2017-05-12 89 views
0

是否有人使用Rails 4.x或Rails 5.x以及Postgres(9.4+)jsonb列?我瞭解使用jsonb + Postgres的好處 - 您可以將無模式數據與結構化數據混合使用。有人可能會稱這是一個快樂的媒介。僅在Rails + Postgres中追加jsonb列

我們注意到更新行時(意外或有意)覆蓋jsonb數據很容易。有沒有人看到任何技術,使jsonb列只能附加?

假設一行有一個jsonb列,我們稱之爲「調查問卷」。問卷列具有以下的生命週期:

  • 發生另一個UPDATE操作這次是JSON數組
  • 另一個追加更多JSON

    1. 開始作爲null
    2. 一些JSON被保存(經由UPDATE操作) UPDATE操作發生,但是這次使用「{}」,這是有效的json
    3. 存儲在「調查問卷」中的所有數據現在都是無效的。

    這很糟糕,因爲我們基本上已經丟失了所有數據。

    更廣泛地說,其他人如何實現在Rails或Postgres中僅添加表(或列)?這個問題在db層或應用層最好解決嗎?

    欣賞這些想法。提前致謝!

  • +0

    如何更容易覆蓋'jsonb'列而不是'varchar'或'int4'列? – Brian

    回答

    0

    正如你注意到的那樣,json[b]的值(就像PostgreSQL中的其他類型一樣)只能作爲一個整體被編輯爲UPDATE

    8.14.2.有效地設計JSON文件:存儲在表中時

    JSON數據受到相同的併發控制的考慮任何其它數據類型。儘管存儲大型文檔是切實可行的,但請記住,任何更新都會獲得整行上的行級鎖定。考慮將JSON文檔限制爲可管理的大小,以減少更新事務之間的鎖爭用。 理想情況下,JSON文檔應該分別代表原子數據,商業規則指定的數據不能合理地進一步細分爲更小的數據庫,這些數據可以獨立修改

    所以,你一個顯而易見的解決方案是將您的JSON陣列&店它的元素,而不是(f.ex.在接線表,其中一對多關係到你的原始表)。

    但是,您也可以通過其他幾種方式避免這些「丟失的更新」(但這些確實不是那些理想的方式)。

    1. 原子UPDATE小號

    讓我給你介紹一個類比。如果你想在任何RDBMS的計數器,你通常會做這樣的:

    UPDATE counter SET value = value + 1 
    

    這是(當然)不受丟失更新。但是,當你做

    SELECT value FROM counter 
    -- do something in client & bind the selected value + 1 to the next query: 
    UPDATE counter SET value = ? 
    

    受到丟失更新。因爲,在SELECT & UPDATE聲明之間,另一個交易可能會在當前值之前更新該值。如果發生這種情況,那些UPDATE就會丟失。您最有可能在jsonb列中執行此類UPDATE

    第一條語句的jsonb對方可能看起來像這些之一:

    -- to append a JSON array element to the root JSON array 
    UPDATE t SET jsonb_col = jsonb_col || '[{"a":1}]'; 
    -- to append a JSON array element to an array, located on the path: 'a' (requires 9.6+) 
    UPDATE t SET jsonb_col = jsonb_insert(jsonb_col, ARRAY['a', '-1'], '{"a":1}', TRUE); 
    -- Notes: TRUE means that insert AFTER ... -1 (in the path) means after the LAST ELEMENT 
    

    然而,這些(通常情況下)很難實現與奧姆斯。

  • 鎖定
  • 如果您不能使用上面的查詢,則必須確保只有一個事務可以UPDATE同時在表中的行。

    2/A。 悲觀鎖

    這樣一來,你告訴RDBMS明確指出你SELECT編行特定原因:FOR UPDATE。 F.ex. ActiveRecord supports this

    2/B。 樂觀鎖

    有了這個,你必須使用/在您UPDATE一個version列,即:

    UPDATE t 
    SET jsonb_col = ?, 
         t_version = t_version + 1 
    WHERE t_version = ? 
    

    這樣一來,就沒有辦法寬鬆的UPDATE,但你的陳述可能不會做什麼都沒有。如果它沒有更新任何行,您必須自己檢查行數(在您的客戶端)&重試。

    F.ex. ActiveRecord supports this too

    瞭解更多關於這些:Optimistic vs. Pessimistic locking

  • 串行事務樂觀鎖系溶液,除了它不
  • Serializable transactions作品需要特殊的版本列。相反,RDBMS將使用謂詞鎖定來避免丟失的更新。此外,當序列化失敗發生時,您需要重新嘗試整個事務。