我有一个 Postgres 数据库,其中包含有关服务器集群的详细信息,例如服务器状态(“活动”、“待机”等)。活动服务器在任何时候都可能需要故障转移到备用服务器,我不在乎特别使用哪个备用服务器。
我想要一个数据库查询来更改备用服务器的状态 - 只有一个 - 并返回要使用的服务器 IP。选择可以是任意的:因为服务器的状态随着查询而改变,所以选择哪个备用数据库并不重要。
是否可以将我的查询限制为一次更新?
这是我到目前为止所拥有的:
UPDATE server_info SET status = 'active'
WHERE status = 'standby' [[LIMIT 1???]]
RETURNING server_ip;
Run Code Online (Sandbox Code Playgroud)
Postgres 不喜欢这样。我可以做些什么不同的事情?
这很简单,但我对 PG 所做的(v9.0)感到困惑。我们从一个简单的表开始:
CREATE TABLE test (id INT PRIMARY KEY);
Run Code Online (Sandbox Code Playgroud)
和几行:
INSERT INTO TEST VALUES (1);
INSERT INTO TEST VALUES (2);
Run Code Online (Sandbox Code Playgroud)
使用我最喜欢的 JDBC 查询工具 (ExecuteQuery),我将两个会话窗口连接到该表所在的数据库。它们都是事务性的(即 auto-commit=false)。我们称它们为 S1 和 S2。
每个相同的代码位:
1:DELETE FROM test WHERE id=1;
2:INSERT INTO test VALUES (1);
3:COMMIT;
Run Code Online (Sandbox Code Playgroud)
现在,以慢动作运行它,在窗口中一次执行一个。
S1-1 runs (1 row deleted)
S2-1 runs (but is blocked since S1 has a write lock)
S1-2 runs (1 row inserted)
S1-3 runs, releasing the write lock
S2-1 runs, now that it can get the lock. But reports …Run Code Online (Sandbox Code Playgroud) 我尝试编写数据库代码以确保它不受竞争条件的影响,以确保我锁定了正确的行或表。但我经常想:我的代码正确吗?是否有可能强制任何现有的竞争条件出现?我想确保如果它们确实发生在生产环境中,我的应用程序会做正确的事情。
我通常确切地知道哪个并发查询可能会导致问题,但我不知道如何强制它们并发运行以查看是否发生了正确的行为(例如,我使用了正确类型的锁),正确的错误是抛出等。
注意:我使用 PostgreSQL 和 Perl,所以如果这不能被一般地回答,它可能应该被重新标记。
更新:如果解决方案是程序化的,我更喜欢它。这样我就可以编写自动化测试来确保没有回归。
TL;DR:下面的问题归结为:插入行时,在生成新Identity值和锁定聚集索引中的相应行键之间是否存在机会窗口,外部观察者可以在其中看到更新的值 Identity并发事务插入的值?(在 SQL Server 中。)
详细版
我有一个 SQL Server 表,其中有一个Identity名为的列CheckpointSequence,它是该表的聚集索引(它还具有许多其他非聚集索引)的键。行由多个并发进程和线程(在隔离级别和没有)插入到表中。同时,有进程定期从聚集索引中读取行,按该列排序(也在隔离级别,关闭该选项)。READ COMMITTEDIDENTITY_INSERTCheckpointSequenceREAD COMMITTEDREAD COMMITTED SNAPSHOT
我目前依赖于读取过程永远不能“跳过”检查点的事实。我的问题是:我可以依赖这个属性吗?如果没有,我该怎么做才能使它成为现实?
示例:当插入标识值为 1、2、3、4 和 5的行时,读者在看到值为 4 的行之前不得看到值为 5 的行。测试表明该查询包含一个ORDER BY CheckpointSequence子句 (和WHERE CheckpointSequence > -1子句),当第 4 行被读取但尚未提交时可靠地阻塞,即使第 5 行已经提交。
我相信至少在理论上,这里可能存在竞争条件,可能会导致这个假设被打破。不幸的是,Identity关于Identity在多个并发事务的上下文中如何工作的文档并没有太多说明,它只说“每个新值都是基于当前的种子和增量生成的”。和“特定事务的每个新值都不同于表上的其他并发事务。” (微软)
我的推理是,它必须以某种方式工作:
我认为在第 2 …
假设您有以下代码(请忽略它很糟糕):
BEGIN TRAN;
DECLARE @id int
SELECT @id = id + 1 FROM TableA;
UPDATE TableA SET id = @id; --TableA must have only one row, apparently!
COMMIT TRAN;
-- @id is returned to the client or used somewhere else
Run Code Online (Sandbox Code Playgroud)
在我看来,这不是正确管理并发性。仅仅因为您有一个事务并不意味着其他人不会读取您在获取更新语句之前所做的相同值。
现在,让代码保持原样(我意识到这作为单个语句更好地处理,甚至使用自动增量/标识列更好)有哪些确定的方法可以使其正确处理并发并防止允许两个客户端获得相同条件的竞争条件身份证价值?
我很确定将 a 添加WITH (UPDLOCK, HOLDLOCK)到 SELECT 会解决问题。该SERIALIZABLE事务隔离级别(因为它拒绝任何人阅读你做了什么,直到移植是在将似乎工作,以及UPDATE:这是假见马丁的答案)。真的吗?它们会同样有效吗?一个比另一个更受欢迎吗?
想象一下做一些比 ID 更新更合法的事情——一些基于你需要更新的读取的计算。可能涉及许多表,其中一些您会写入,而另一些则不会。这里的最佳做法是什么?
写完这个问题后,我认为锁定提示更好,因为这样你只锁定了你需要的表,但我很感激任何人的意见。
PS 不,我不知道最好的答案,真的很想得到更好的理解!:)
如果可以的话,有什么理由(性能/稳定性)我不应该这样做?
假设我有两个查询,在 SSMS 中的两个单独会话中运行:
第一节:
UPDATE Person
SET Name='Jonny', Surname='Cage'
WHERE Id=42
Run Code Online (Sandbox Code Playgroud)
第二场:
SELECT Name, Surname
FROM Person WITH(NOLOCK)
WHERE Id > 30
Run Code Online (Sandbox Code Playgroud)
SELECT语句是否有可能读取半更新的行,例如带有Name = 'Jonny'和的行Surname = 'Goody'?
查询几乎在不同的会话中同时执行。
我有两张桌子。一个是日志表;另一个本质上包含只能使用一次的优惠券代码。
用户需要能够兑换优惠券,这将在日志表中插入一行并将优惠券标记为已使用(通过将used列更新为true)。
当然,这里存在明显的竞争条件/安全问题。
我过去在 mySQL 的世界里做过类似的事情。在那个世界中,我会全局锁定两个表,在知道这一次只能发生一次的情况下执行逻辑安全,然后在我完成后解锁表。
Postgres 有没有更好的方法来做到这一点?特别是,我担心锁是全局的,但不是必须的——我真的只需要确保没有其他人试图输入那个特定的代码,所以也许一些行级锁会起作用?
我有一个 Web 服务(http api),它允许用户安静地创建资源。在身份验证和验证之后,我将数据传递给 Postgres 函数,并允许它检查授权并在数据库中创建记录。
我今天发现了一个错误,即在同一秒内发出了两个 http 请求,导致使用相同的数据两次调用此函数。函数内部有一个子句,它在表上进行选择以查看值是否存在,如果存在,则获取 ID 并在下一个操作中使用该 ID,如果不存在,则插入数据,获取返回 ID,然后在下一个操作中使用它。下面是一个简单的例子。
select id into articleId from articles where title = 'my new blog';
if articleId is null then
insert into articles (title, content) values (_title, _content)
returning id into articleId;
end if;
-- Continue, using articleId to represent the article for next operations...
Run Code Online (Sandbox Code Playgroud)
正如您可能猜到的那样,我对数据进行了幻读,其中两个事务都进入了if articleId is null then块并试图插入到表中。一个成功了,另一个失败了,因为一个领域的独特限制。
我已经环顾四周,看看如何抵御这种情况,并找到了一些不同的选择,但由于某些原因,它们似乎都不适合我们的需求,我正在努力寻找任何替代方案。
insert ... on conflict do nothing/update...我首先查看了on conflict看起来不错的选项,但是唯一的选项是do nothing不返回导致冲突的记录的 ID,并且do update不会工作,因为它会导致触发器在实际数据时被触发没有改变。在某些情况下,这不是问题,但在许多情况下,这可能会使会话用户会话无效,这是我们无法做到的。 …我在存储过程中的插入有并发问题。该程序的相关部分是这样的:
select @_id = Id from table1 where othervalue = @_othervalue
IF( @_id IS NULL)
BEGIN
insert into table1 (othervalue) values (@_othervalue)
select @_id = Id from table1 where othervalue = @_othervalue
END
Run Code Online (Sandbox Code Playgroud)
当我们同时运行 3 个或 4 个这些存储过程时,我们有时会得到多个插入。
我打算像这样解决这个问题:
insert into table1 (othervalue)
select TOP(1) @_othervalue as othervalue from table1 WITH(UPDLOCK)
where NOT EXISTS ( select * from table1 where othervalue = @_othervalue )
select @_id = Id from table1 where othervalue = @_othervalue
Run Code Online (Sandbox Code Playgroud)
问题是,如何在sql server中并发插入而不重复?我必须使用 TOP 只插入一次的事实让我感到不安。
concurrency ×10
locking ×4
postgresql ×4
sql-server ×4
update ×2
identity ×1
index ×1
mysql ×1
plpgsql ×1
queue ×1
testing ×1
transaction ×1
upsert ×1