sql - 如何选择事务隔离级别?

标签 sql postgresql concurrency transactions acid

我在数据库中有一个表,负责存储已排序/可重新排序的列表。它具有以下形状:

| id | listId | index | title | ... |

其中id是主键,listId是外键,标识该项目属于哪个列表,title等列是内容项目。 index 属性负责项目在列表中的位置。它是一个整数计数器(从 0 开始),在列表范围内是唯一的,但可以在列表中重复。示例数据:

| id      | listId  | index | title    | ...
---------------------------------------------
| "item1" | "list1" | 0     | "title1" | ...
| "item2" | "list1" | 1     | "title2" | ...
| "item3" | "list1" | 2     | "title3" | ...
| "item4" | "list2" | 0     | "title4" | ...
| "item5" | "list2" | 1     | "title5" | ...

用户可以创建/删除项目,在列表内或跨列表移动它们。 为确保运行这些操作时索引的一致性,我执行以下操作:

创建项目:

  1. 计算此列表中的项目
SELECT COUNT(DISTINCT "Item"."id") as "cnt" 
FROM "item" "Item" 
WHERE "Item"."listId" = ${listId}
  1. 插入新项目,索引设置为从第 1 步开始计数:
INSERT INTO "item"("id", "listId", "index", "title", ...) 
VALUES (${id}, ${listId}, ${count}, ${title})

这样索引会随着插入列表中的每个项目而增长。

移动项目:

  1. 检索项目的当前 listIdindex:
SELECT "Item"."listId" AS "Item_listId", "Item"."index" AS "Item_index" 
FROM "item" "Item" 
WHERE "Item"."id" = ${id}
  1. 如有必要,更改“转移”项目的索引,以便顺序一致,例如鉴于项目向前移动,其当前位置(不包括)和其下一个位置(包括)之间的所有项目都需要将其索引减少1:
UPDATE "item" 
  SET "index" = "index" - 1 
WHERE "listId" = ${listId} 
  AND "index" BETWEEN ${sourceIndex + 1} AND ${destinationIndex}

我将省略列表间移动的变化,因为它非常相似。

  1. 更新项目本身:
UPDATE "item" 
   SET "index" = ${destinationIndex} 
WHERE "id" = ${id}

删除项目:

  1. 检索项目的索引和 listId

  2. 将同一列表中与此项目相邻的所有项目向后移动 1 步,以消除间隙

UPDATE "item" 
  SET "index" = "index" - 1 
WHERE "listId" = ${listId} 
  AND "index" > ${itemIndex}
  1. 删除项目:
DELETE FROM "item" 
WHERE "id" = ${id}

问题是:

我应该为每个操作提供什么事务隔离级别?保持索引列一致,没有间隙,最重要的是 - 没有重复对我来说非常重要。 create item 操作会受到幻读的影响,因为它按某些标准对项目进行计数,而且它应该是可序列化的,我的理解是否正确?其他操作呢?

最佳答案

在不了解您的特定应用程序的更多信息的情况下,最安全的做法确实是在您访问该表时使用 serializable 作为隔离级别,但即使是该级别也可能不足以满足您的特定情况。

(listId, index) 上的unique 约束可以防止重复(标题怎么样?可以在同一个列表中重复吗?),一些精心制作的 < strong>“看门狗”查询可以进一步缓解问题和数据库序列,或者存储过程可以确保没有间隙,但事实是机制本身似乎很脆弱。

只了解您的具体问题,您似乎遇到了用户级别的并发问题,因为多个用户可以同时访问相同的对象并对它们进行更改。假设这是具有无状态后端(因此本质上是分布式的)的典型 Web 应用程序,这可能会在反射(reflect)架构甚至功能需求的用户体验方面产生大量影响。例如,用户 Foo 将项目 Car 移动到 List B,用户 Bar 当前正在处理该项目。可以合理地假设 Bar 需要在操作完成后立即查看项目 Car,但除非有某种机制立即通知,否则不会发生这种情况列表 B 的用户。您在同一组列表上工作的用户越多,即使有通知也会变得越糟,因为您会收到越来越多的通知,直到用户看到事情一直在变化并且无法跟上它.

任何人都会做出很多假设来为您提供答案。我自己的意思是,您可能需要修改该应用程序的要求,或者确保管理层了解一些限制并且他们接受这些限制。 这种类型的问题在分布式应用程序中很常见。通常在某些数据集上放置“锁”(通过数据库或共享内存池),以便在任何给定时间只有一个用户可以更改它们,或者,提供一个工作流来管理冲突操作(很像版本控制系统) .如果两者均未完成,则会保留操作日志以了解发生了什么,并在以后检测到问题时纠正问题。

关于sql - 如何选择事务隔离级别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57187476/

相关文章:

mysql - 如何更新mysql中新添加的列

mysql - 如何在 Xampp 上使用 MariaDB 将 XML 文件转换为行和列?

java - batchStore不加载生成的 key

c# - List<T> 并发删除和添加

mysql - "Repeatable read"与乐观

mongodb - 在单个 Postgresql 实例上结合 NoSQL 和关系数据库

python - 在 python 中存储和查询序列

concurrency - 在处理 3rd 方代码时,如何知道 Go 中会同时发生什么

Java 执行器服务 :- Notify a thread to wake up when an event occurs

sql - OrientDB:如何使用选择查询更新列