我正在尝试建立一个类似维基百科的东西,多人可以在其中编辑内容。有特权的人也可以恢复更改。我不希望恢复有损(意味着真正放弃人们所做的编辑。它应该只 stash 它),所以这似乎需要一个像数据结构的 git 分支来存储编辑,并带有指向“当前”的指针。
我试过这个设计:
CREATE TABLE article (
id serial PRIMARY KEY,
content text NOT NULL,
author integer NOT NULL REFERENCES "user",
path text NOT NULL,
relationship ltree NOT NULL
);
CREATE TABLE current_article (
article_id NOT NULL REFERENCES article
);
relationship
记录的是新文章还是现有文章的编辑:
id | content | path | author | relationship
---+---------+------+--------+-------------
1 | foo | /a1 | 1 | 'root'
2 | bar | /a1 | 2 | 'root.1'
3 | baz | /a2 | 3 | 'root'
这里的意思是,作者2把文章/a1
从foo改成了bar,文章/a2
是新的。
current_article
记录哪篇文章是“当前”文章,通常它只指向最新的一篇。还原后,它可以指向旧的:
article_id
----------
2
3
当有编辑出现时,我会这样插入:
INSERT INTO article (content, path, author) VALUES ('qux', '/a2', 4);
并依靠插入前触发器为该路径查找当前文章并填写关系,并依靠插入后触发器更新当前文章指针。
你觉得这个设计怎么样?我在这个设计中遇到的问题是难以处理并发。
在插入前触发器中,当它找到当前文章时,它可能已经被更改,而在插入后触发器中,它可能会错误地覆盖当前文章并指向另一篇文章。
这方面我有3个问题:
- serializable Isolation 能解决问题吗? (我对 MVCC 的概念很陌生,仍在努力思考)如果不是,我应该如何解决它?
- 是否有更好的设计不必处理并发?
- 如果我确实需要处理并发问题,我该如何在不同的竞争条件下对我的设计进行单元测试(或者这样的单元测试是否必要)?
谢谢。
最佳答案
并发发生在两个层面:应用程序和数据库。
在应用程序级别,多个用户可能有重叠的编辑 session 。在某个时候,用户会保存他们的版本,然后下一个用户也会保存,但是在您当前的设计中似乎没有办法确定最后一次编辑是从哪个版本分支:此信息无处可寻提到的 INSERT
。
数据库级别的并发是一个不同的问题,它涉及同时运行的事务。
如果您尝试使用处理并发性的数据库原语来解决应用程序并发性问题,您将必须保持打开事务直到用户完成编辑,这意味着在任意长时间内,这在数据库中是行不通的设计。
首先您需要找出一个应用程序和设计策略来处理并发编辑,然后您需要找出一个数据库策略来处理并发事务,即当人们同时点击“保存”并更新数据的事务并行运行。这些是完全不同的东西。
关于并发事务,避免麻烦的一种通用方法是在写入事务开始时锁定文章,然后再做任何其他事情,这样任何其他尝试执行相同操作的事务都会被阻塞,直到并发更改被提交(或回滚)。这是序列化更新的最简单方法,但它假定要锁定的内容具有足够的粒度,以便不会同时阻止对其他文章的更新。
理想情况下,应该有一个article
表,每个path
只有一行(独立于修订,将存储在不同的表中)。然后用 SELECT ... FOR UPDATE
锁定该行将足以保证执行分支或任何复杂更新的查询集可以工作,而不会被对同一篇文章的并发更改打扰。
另一种(更粗略的)方法是使用可序列化隔离级别并重试任何失败的事务,并返回指示序列化失败的 SQLSTATE
。
关于git - 如何在postgresql中存储类似git分支的数据结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46498949/