我正在构建的应用程序中有几个计数器,因为我试图让应用程序根据需要动态创建它们。
举一个简单的例子,如果有人在脚本中输入一个单词,它应该返回之前输入单词的次数。以下是在键入单词example时可以执行的sql示例。
CREATE SEQUENCE IF NOT EXISTS example START WITH 1;
SELECT nextval('example')
这将在第一次运行时返回1
,第二次返回2
,等等。
问题是当2个人同时点击按钮时。 首先,请注意我的应用程序中发生的事情远不止这些声明,因此重叠的可能性比发生这种情况时更重要。
1> BEGIN;
2> BEGIN;
1> CREATE SEQUENCE IF NOT EXISTS example START WITH 1;
2> CREATE SEQUENCE IF NOT EXISTS example START WITH 1; -- is blocked by previous statement
1> SELECT nextval('example') -- returns 1 to user.
1> COMMIT; -- unblocks second connection
2> ERROR: duplicate key value violates unique constraint
"pg_type_typname_nsp_index"
DETAIL: Key (typname, typnamespace)=(example, 109649) already exists.
我的印象是,通过使用“IF NOT EXISTS”,声明应该只是一个无操作,如果它确实存在,但它似乎有这种竞争条件,而事实并非如此。我说竞争条件,因为如果这两个没有同时执行,它就像人们期望的那样工作。
我注意到IF NOT EXISTS
对于postgres是相当新的,所以也许他们还没有解决所有的问题呢?
编辑: 我们考虑以这种方式做事的主要原因是为了避免过度锁定。想法是,如果两个人同时增加,使用序列将意味着用户不应该等待另一个(除了,例如,在该示例中,用于该序列的初始创建)
答案 0 :(得分:4)
序列是数据库模式的一部分。如果您发现自己根据存储在数据库中的数据动态修改架构,那么您可能做错了什么。对于具有特殊性质的序列尤其如此。关于他们在交易方面的行为。具体来说,如果在事务中间递增序列(在The expression contains invalid date constant '#_ofE_Employees, 'Sytem.String') LIKE '1')OR
的帮助下)然后回滚该事务,则不会回滚序列的值。所以很有可能,这种行为是您不想要的数据行为。在您的示例中,假设用户尝试添加单词。这导致相应的序列递增。现在想象一下,事务没有完成(例如计算机崩溃)并且它被回滚。您最终会将这个词添加到数据库中,但序列会增加。
对于您提到的特定示例,有一个简单的解决方案;创建一个普通的表来存储所有"序列"。这样的事情会这样做:
nextval
现在我知道这只是一个例子,但如果这种方法对您的实际用例不起作用,请告诉我们,我们可以根据您的需要进行调整。
修改:以下是上述解决方案的工作原理。如果添加了新单词,请运行以下查询(" UPSERT"仅在postgres 9.5+中使用语法):
CREATE TABLE word_frequency (
word text NOT NULL UNIQUE,
frequency integer NOT NULL
);
此查询将在频率为1的INSERT INTO word_frequency(word,frequency)
VALUES ('foo',1)
ON CONFLICT (word)
DO UPDATE
SET frequency = word_frequency.frequency + excluded.frequency
RETURNING frequency;
中插入一个新单词,或者如果单词已存在,则会将现有频率增加1.现在如果两个事务同时尝试执行此操作会发生什么?请考虑以下情形:
word_frequency
一旦客户端2尝试增加foo的频率(用上面的箭头标记),将会发生阻塞,因为该行被另一个事务修改。当客户端1提交时,客户端2将被取消阻止并继续而没有任何错误。这正是我们希望它发挥作用的方式。另请注意,postgresql将使用行级锁定来实现此行为,因此不会阻止其他插入。
答案 1 :(得分:2)
编辑:我们考虑以这种方式做事的主要原因是 避免过度锁定。想到如果有两个人去 同时增加,使用序列意味着两者都没有 用户应该等待另一个(除了在这个例子中, 用于初始创建该序列)
听起来你正在针对可能不存在的问题进行优化。当然,如果你有100,000个并发用户只插入行(因为序列只会正常使用),可能会对序列产生一些争用,但实际上会有其他瓶颈长在序列阻碍之前。
我建议你首先证明序列是一个问题。通过适当的数据库设计(动态DDL不是),序列将不再是瓶颈。
作为参考,DDL在大多数数据库中不是事务安全的。