为什么使用 .net 并行更新数据库会导致一致的结果
Why Parallel update database with .net leads to consistent result
我正在尝试运行以下代码以在并行更新数据库(使用多个线程)时显示“不一致”,但是当我运行此代码时 table 总是一致的,即 500。它是如何工作的?
static void Main(string[] args)
for (int i = 0; i < 500; i++) {
Thread t = new Thread(() => Update());
t.Start();
}
await Task.Delay(100000);
}
public static void Update() {
using (var conn = new SqlConnection("mssql-connection-string")) {
conn.Execute("update [table] set counter = counter +1;");
}
}
您拥有的代码不会导致竞争条件,因为您想要 UPDATE
的行将被锁定、递增,然后释放锁;这意味着任何其他试图同时成为 运行 的 UPDATE
语句将不得不“等待” first UPDATE
完成.
如果要模拟竞态条件,先给一个变量赋值;那应该给你行为:
DECLARE @c int = (SELECT counter FROM dbo.[table]); --I assume table only has 1 row
UPDATE dbo.[table] SET Counter = @c + 1;
这意味着 counter
的值将首先分配给变量,这不会锁定其他进程读取它的行。因此,多个并发线程可能同时读取该行,然后 UPDATE
将其设为各自的 +1 值。因此,5 个线程可能都将值读取为 7
,并且将 UPDATE
的值读取为 7+1
(8
).
如果出于某种原因,您确实需要先将 table 中的值赋值给一个变量,然后再 UPDATE
它们,那么您需要使用 UPDLOCK
在您的查询中提示:
DECLARE @c int = (SELECT counter FROM dbo.[table] WITH (UPDLOCK)); --I assume table only has 1 row
UPDATE dbo.[table] SET Counter = @c + 1;
您可能还需要将事务的隔离级别更改为 SERIALIZABLE
。
我正在尝试运行以下代码以在并行更新数据库(使用多个线程)时显示“不一致”,但是当我运行此代码时 table 总是一致的,即 500。它是如何工作的?
static void Main(string[] args)
for (int i = 0; i < 500; i++) {
Thread t = new Thread(() => Update());
t.Start();
}
await Task.Delay(100000);
}
public static void Update() {
using (var conn = new SqlConnection("mssql-connection-string")) {
conn.Execute("update [table] set counter = counter +1;");
}
}
您拥有的代码不会导致竞争条件,因为您想要 UPDATE
的行将被锁定、递增,然后释放锁;这意味着任何其他试图同时成为 运行 的 UPDATE
语句将不得不“等待” first UPDATE
完成.
如果要模拟竞态条件,先给一个变量赋值;那应该给你行为:
DECLARE @c int = (SELECT counter FROM dbo.[table]); --I assume table only has 1 row
UPDATE dbo.[table] SET Counter = @c + 1;
这意味着 counter
的值将首先分配给变量,这不会锁定其他进程读取它的行。因此,多个并发线程可能同时读取该行,然后 UPDATE
将其设为各自的 +1 值。因此,5 个线程可能都将值读取为 7
,并且将 UPDATE
的值读取为 7+1
(8
).
如果出于某种原因,您确实需要先将 table 中的值赋值给一个变量,然后再 UPDATE
它们,那么您需要使用 UPDLOCK
在您的查询中提示:
DECLARE @c int = (SELECT counter FROM dbo.[table] WITH (UPDLOCK)); --I assume table only has 1 row
UPDATE dbo.[table] SET Counter = @c + 1;
您可能还需要将事务的隔离级别更改为 SERIALIZABLE
。