2017-05-12 140 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将使用谓词锁定来避免丢失的更新。此外,当序列化失败发生时,您需要重新尝试整个事务。