ruby-on-rails - 在 Rails + Postgres 中仅附加 jsonb 列

标签 ruby-on-rails json postgresql jsonb

有人使用 Rails 4.x 或 Rails 5.x 以及 Postgres (9.4+) jsonb 列吗?我了解使用 jsonb + Postgres 的好处 - 您可以将无模式数据与结构化数据混合在一起。有些人可能称之为折衷方案。

我们注意到更新行时很容易(无意或有意)覆盖 jsonb 数据。有没有人见过任何使 jsonb 列仅附加的技术?

假设一行有一个jsonb列,我们称之为“调查问卷”。问卷栏的生命周期如下:

  1. null 开头
  2. 保存了一些 json(通过 UPDATE 操作)
  3. 发生另一个 UPDATE 操作来追加更多 json,这次它是一个 json 数组
  4. 发生另一个 UPDATE 操作,但这次使用“{}”,它是有效的 json
  5. “调查问卷”中存储的所有数据现已无效。

这很糟糕,因为我们基本上丢失了所有数据。

更广泛地说,其他人如何在 Rails 或 Postgres 中实现仅追加表(或列)?这个问题最好在数据库层还是应用程序层解决?

欣赏这些想法。提前致谢!

最佳答案

正如您所注意到的,json[b] 值(就像 PostgreSQL 中的任何其他类型一样)只能作为一个整体进行UPDATE编辑。

<强> 8.14.2.有效地设计 JSON 文档:

JSON data is subject to the same concurrency-control considerations as any other data type when stored in a table. Although storing large documents is practicable, keep in mind that any update acquires a row-level lock on the whole row. Consider limiting JSON documents to a manageable size in order to decrease lock contention among updating transactions. Ideally, JSON documents should each represent an atomic datum that business rules dictate cannot reasonably be further subdivided into smaller datums that could be modified independently.

因此,对您来说,一个显而易见的解决方案是划分 JSON 数组并存储其元素(例如,在联结表中,与原始表具有一对多关系)。

但是,您也可以通过其他几种方式来避免这些“丢失的更新”(但这些确实不是那些理想的方式)。

  1. 原子更新

让我向您介绍一个类比。如果您想在任何 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 = ?

可能会丢失更新。因为,在 SELECTUPDATE 语句之间,另一个事务可能更新当前事务之前的值。如果发生这种情况,那些 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

但是,这些(通常)很难通过 ORM 实现。

  • 锁定
  • 如果您无法使用上述查询,则必须确保一次只有一个事务可以UPDATE表中的一行。

    2/A。 悲观锁定

    这样,您就可以明确地告诉 RDBMS 您出于特定原因SELECT一行:FOR UPDATE 。 F. 前。 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 将使用谓词锁定来避免丢失更新。此外,当发生序列化失败时,您应该重试整个事务。

    关于ruby-on-rails - 在 Rails + Postgres 中仅附加 jsonb 列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43929857/

    相关文章:

    ruby-on-rails - 在 docker 上安装 gem

    ruby-on-rails - Rails Proper Syntax 用于访问不同 Controller 中模型的错误

    json - Alamofire 4.0 JSOn 解析 Swift

    php - 从 PHPMyadmin 导出 int 作为 json

    java - 如何在 java 中访问 WrappedArray 的 scala.collection.mutable.WrappedArray 中的值

    ruby-on-rails - rails 4 : organize rails models in sub path without namespacing models?

    sql - SELECT 查询合并/加入 PostgreSQL 中的两个表

    json - 避免从表行构建的 JSON 中的匿名字段

    postgresql - 我如何销毁 PostgreSQL 中模式的所有表

    ruby-on-rails - $undefined 和 $end 在 Ruby 中指的是什么?