如您所见,json[b] 值(就像 PostgreSQL 中的任何其他类型一样)只能作为一个整体使用 UPDATEed。
8.14.2. 有效设计 JSON 文档:
当存储在表中时,JSON 数据与任何其他数据类型一样受到相同的并发控制注意事项。尽管存储大文档是可行的,但请记住,任何更新都会获取整行的行级锁。考虑将 JSON 文档限制在可管理的大小,以减少更新事务之间的锁争用。 理想情况下,每个 JSON 文档都应该代表一个原子数据,业务规则规定不能合理地进一步细分为更小的数据可以独立修改。
因此,一个明显的解决方案是划分 JSON 数组并存储其元素(例如,在联结表中,与原始表具有一对多关系)。
但是,您也可以通过其他几种方式避免这些“丢失的更新”(但这些方式确实不是那些理想的方式)。
- 原子
UPDATEs
让我给你介绍一个类比。如果你想在任何 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 语句之间,另一个事务可以更新当前事务之前的值。如果发生这种情况,那些UPDATEs 就会丢失。您最有可能在您的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 您 SELECTed 一行是出于特定原因:FOR UPDATE。前任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 将使用谓词锁定来避免丢失更新。此外,当发生序列化失败时,您应该重试整个事务。