我正在构建一个应用程序,其中有几个计数器,我正在尝试让它们根据需要动态创建。
举个简单的例子,如果有人在脚本中输入单词,它应该返回此单词之前出现的次数。以下是可能执行的SQL示例,如果他们输入了单词“example”。
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",如果存在该语句应该只是一个空操作,但似乎存在这种竞争条件,情况并非如此。我称之为竞争条件,因为如果这两个语句不同时执行,则会按照预期工作。
我注意到"IF NOT EXISTS"在Postgres中相对较新,所以也许他们还没有解决所有问题?
编辑:我们考虑以这种方式处理的主要原因是避免过多的锁定。想法是如果两个人同时增加,使用序列意味着两个用户都不必等待对方(除了像这个例子中一样,初始创建该序列)。