我在数据库中有一个表,负责存储已排序/可重新排序的列表。它具有以下形状:
| 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" | ...
用户可以创建/删除项目,在列表内或跨列表移动它们。 为确保运行这些操作时索引的一致性,我执行以下操作:
创建项目:
- 计算此列表中的项目
SELECT COUNT(DISTINCT "Item"."id") as "cnt"
FROM "item" "Item"
WHERE "Item"."listId" = ${listId}
- 插入新项目,索引设置为从第 1 步开始计数:
INSERT INTO "item"("id", "listId", "index", "title", ...)
VALUES (${id}, ${listId}, ${count}, ${title})
这样索引会随着插入列表中的每个项目而增长。
移动项目:
- 检索项目的当前 listId 和 index:
SELECT "Item"."listId" AS "Item_listId", "Item"."index" AS "Item_index"
FROM "item" "Item"
WHERE "Item"."id" = ${id}
- 如有必要,更改“转移”项目的索引,以便顺序一致,例如鉴于项目向前移动,其当前位置(不包括)和其下一个位置(包括)之间的所有项目都需要将其索引减少1:
UPDATE "item"
SET "index" = "index" - 1
WHERE "listId" = ${listId}
AND "index" BETWEEN ${sourceIndex + 1} AND ${destinationIndex}
我将省略列表间移动的变化,因为它非常相似。
- 更新项目本身:
UPDATE "item"
SET "index" = ${destinationIndex}
WHERE "id" = ${id}
删除项目:
检索项目的索引和 listId
将同一列表中与此项目相邻的所有项目向后移动 1 步,以消除间隙
UPDATE "item"
SET "index" = "index" - 1
WHERE "listId" = ${listId}
AND "index" > ${itemIndex}
- 删除项目:
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/