【发布时间】:2019-12-02 21:02:15
【问题描述】:
我在数据库中有一个表,负责存储已排序/可重新排序的列表。它具有以下形状:
| 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}
- 如有必要,更改“移位”项目的索引,以便顺序一致,例如鉴于项目向前移动,其当前位置(不包括)和下一个位置(包括)之间的所有项目都需要将它们的 index 减少 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)上的唯一索引强制执行最重要的约束 - 没有重复项。唯一索引约束很容易实现,并且将始终保持不变。 -
删除记录时,降低所有ID大于删除记录1的记录的ID,在数据量较大时可能会导致性能问题。我的建议是,1. 创建一个序列对象并将其用于新 ID。不要对 Id 列进行“自动递增”并使用序列手动管理它们。 2. 删除记录时,将Id大于被删除记录Id的记录的Ids减1。同一个事务中的Sequence值减1。 3.使用'Read uncommitted'类型作为隔离级别。
-
我警告你,如果你在 UI 上使用 Id 进行操作,在删除记录时更新 Id 会给你带来巨大的操作问题。因为,其他人可以同时编辑另一个具有更大 Id 值的记录。
-
@Gurcan 我需要 ids 是持久的/顺序独立的,并且还希望它们是 uuids 而不是整数。所以 id 和 index 是独立的列。递增/递减记录索引对我来说不是问题,因为一列不打算包含超过 10 个项目。
-
所以,基本设计看起来还不错。索引序列机制可以是我建议的方式。你同意吗?
标签: sql postgresql concurrency transactions acid