首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Postgres‘如果不存在’失败,因为序列存在。

Postgres‘如果不存在’失败,因为序列存在。
EN

Stack Overflow用户
提问于 2016-09-09 00:49:11
回答 2查看 3.5K关注 0票数 6

我在我正在构建的应用程序中有几个计数器,因为我试图让应用程序根据需要动态创建它们。

对于一个简单的例子,如果有人在脚本中键入一个单词,它应该返回该单词先前输入的次数。下面是一个sql示例,如果他们输入这个单词示例,就可以执行这个示例。

代码语言:javascript
运行
复制
CREATE SEQUENCE IF NOT EXISTS example START WITH 1;
SELECT nextval('example')

这将在第一次运行时返回1,第二次返回2,等等。

问题是当两个人同时点击按钮时。首先,请注意,在我的应用程序中发生的不仅仅是这些语句,因此它们重叠的可能性比所有这些语句都要大得多。

代码语言:javascript
运行
复制
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来说是相当新的,所以也许他们还没有解决所有的问题吗?

编辑:我们考虑这样做的主要原因是为了避免过多的锁定。这样的想法是,如果两个人同时递增,使用一个序列将意味着两个用户都不应该等待另一个用户(除了,在这个例子中,最初创建该序列)。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-09-09 01:03:19

序列是数据库架构的一部分。如果您发现自己根据存储在数据库中的数据动态地修改模式,那么您可能做错了什么。这对于具有特殊属性的序列尤其如此,例如,关于它们与事务有关的行为。具体来说,如果在事务中间增加序列(在nextval的帮助下),然后回滚该事务,则序列的值将不会回滚。因此,最有可能的是,这种行为是你不想用你的数据。在您的示例中,假设用户试图添加单词。这将导致相应的序列递增。现在,假设事务由于原因而没有完成(例如,计算机崩溃),然后回滚。最终的结果是,单词没有添加到数据库中,而是增加了序列。

对于您提到的特定示例,有一个简单的解决方案:创建一个普通表来存储所有“序列”。像这样的东西会起作用:

代码语言:javascript
运行
复制
CREATE TABLE word_frequency (
    word text NOT NULL UNIQUE,
    frequency integer NOT NULL
);

现在我知道这只是一个例子,但是如果这种方法不适用于您的实际用例,请告诉我们,我们可以根据您的需要调整它。

编辑:以下是上述解决方案的工作方式。如果添加了一个新单词,请运行以下查询(仅在postgres 9.5+中运行“UPSERT”语法):

代码语言:javascript
运行
复制
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。如果两个事务同时尝试这样做,会发生什么情况?考虑以下情况:

代码语言:javascript
运行
复制
client 1          client 2
--------          --------
BEGIN
                  BEGIN
UPSERT ('foo',1)
                  UPSERT ('foo',1) <====
COMMIT
                  COMMIT

一旦客户端2尝试增加foo的频率(用上面的箭头标记),操作就会阻塞,因为行是由不同的事务修改的。当客户端1提交时,客户端2将被解除阻塞并继续运行,不会出现任何错误。这正是我们希望它发挥作用的方式。还要注意的是,postgresql将使用行级锁定来实现此行为,因此其他插入不会被阻止。

票数 5
EN

Stack Overflow用户

发布于 2016-09-09 04:00:20

编辑:我们考虑这样做的主要原因是为了避免过多的锁定。这样的想法是,如果两个人同时递增,使用一个序列将意味着两个用户都不应该等待另一个用户(除了,在这个例子中,最初创建该序列)。

听起来你在优化一个很可能不存在的问题。当然,如果您有10万个同时使用的用户,它们只插入行(因为序列通常只会被使用),则可能会与该序列发生某种争用,但实际上,在序列发生障碍之前很久还会有其他瓶颈。

我建议你先证明序列是个问题。有了正确的数据库设计(动态DDL不是),序列就不会成为瓶颈。

作为参考,DDL在大多数数据库中并不是事务安全的。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39402212

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档