我在我正在构建的应用程序中有几个计数器,因为我试图让应用程序根据需要动态创建它们。
对于一个简单的例子,如果有人在脚本中键入一个单词,它应该返回该单词先前输入的次数。下面是一个sql示例,如果他们输入这个单词示例,就可以执行这个示例。
CREATE SEQUENCE IF NOT EXISTS example START WITH 1;
SELECT nextval('example')
这将在第一次运行时返回1
,第二次返回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
对postgres来说是相当新的,所以也许他们还没有解决所有的问题吗?
编辑:我们考虑这样做的主要原因是为了避免过多的锁定。这样的想法是,如果两个人同时递增,使用一个序列将意味着两个用户都不应该等待另一个用户(除了,在这个例子中,最初创建该序列)。
发布于 2016-09-09 01:03:19
序列是数据库架构的一部分。如果您发现自己根据存储在数据库中的数据动态地修改模式,那么您可能做错了什么。这对于具有特殊属性的序列尤其如此,例如,关于它们与事务有关的行为。具体来说,如果在事务中间增加序列(在nextval
的帮助下),然后回滚该事务,则序列的值将不会回滚。因此,最有可能的是,这种行为是你不想用你的数据。在您的示例中,假设用户试图添加单词。这将导致相应的序列递增。现在,假设事务由于原因而没有完成(例如,计算机崩溃),然后回滚。最终的结果是,单词没有添加到数据库中,而是增加了序列。
对于您提到的特定示例,有一个简单的解决方案:创建一个普通表来存储所有“序列”。像这样的东西会起作用:
CREATE TABLE word_frequency (
word text NOT NULL UNIQUE,
frequency integer NOT NULL
);
现在我知道这只是一个例子,但是如果这种方法不适用于您的实际用例,请告诉我们,我们可以根据您的需要调整它。
编辑:以下是上述解决方案的工作方式。如果添加了一个新单词,请运行以下查询(仅在postgres 9.5+中运行“UPSERT”语法):
INSERT INTO word_frequency(word,frequency)
VALUES ('foo',1)
ON CONFLICT (word)
DO UPDATE
SET frequency = word_frequency.frequency + excluded.frequency
RETURNING frequency;
这个查询将在word_frequency
中插入一个频率为1的新单词,或者如果这个单词已经存在,它将将现有的频率增加1。如果两个事务同时尝试这样做,会发生什么情况?考虑以下情况:
client 1 client 2
-------- --------
BEGIN
BEGIN
UPSERT ('foo',1)
UPSERT ('foo',1) <====
COMMIT
COMMIT
一旦客户端2尝试增加foo的频率(用上面的箭头标记),操作就会阻塞,因为行是由不同的事务修改的。当客户端1提交时,客户端2将被解除阻塞并继续运行,不会出现任何错误。这正是我们希望它发挥作用的方式。还要注意的是,postgresql将使用行级锁定来实现此行为,因此其他插入不会被阻止。
发布于 2016-09-09 04:00:20
编辑:我们考虑这样做的主要原因是为了避免过多的锁定。这样的想法是,如果两个人同时递增,使用一个序列将意味着两个用户都不应该等待另一个用户(除了,在这个例子中,最初创建该序列)。
听起来你在优化一个很可能不存在的问题。当然,如果您有10万个同时使用的用户,它们只插入行(因为序列通常只会被使用),则可能会与该序列发生某种争用,但实际上,在序列发生障碍之前很久还会有其他瓶颈。
我建议你先证明序列是个问题。有了正确的数据库设计(动态DDL不是),序列就不会成为瓶颈。
作为参考,DDL在大多数数据库中并不是事务安全的。
https://stackoverflow.com/questions/39402212
复制相似问题