我需要在对同一行执行更新逻辑时阻止行级别的数据库读取。漂亮干净的解决方案会是什么样子?这是我正在处理的一些代码:
using (SqlConnection conn = new SqlConnection(Configuration.ConnectionString)) {
conn.Open();
using (SqlTransaction tran = conn.BeginTransaction(IsolationLevel.Serializable)) {
//TODO lock table row with ID=primaryKey (block other reads and updates)
using (SqlCommand cmd = new SqlCommand("SELECT Data FROM MyTable WHERE ID=@primaryKey", conn)) {
cmd.Parameters.AddWithValue("@primaryKey", primaryKey);
using (var reader = cmd.ExecuteReader()) {
data = PopulateData(reader);
};
}
if (isUpdateNeeded(data)) {
ChangeExternalSystemStateAndUpdateData(data) //EDIT - concurrent calls not allowed
WriteUpdatesToDatabase(conn, tran, data); //EDIT
tran.Commit();
}
} //realease lock and allow other processes to read row with ID=primaryKey
}
编辑:
我有以下限制:
- 代码可以同时在不同的应用程序池中执行。所以内存锁定不是一种选择
- ChangeExternalSystemStateAndUpdateData(( 只能在 MyTable 行的范围内执行一次。并发呼叫将导致问题
通常,这里的问题不是行锁定,而是: 其他 SPID 在您的读取和更新之间获取读锁定 - 这可能导致死锁情况; 此处的常见解决方法是在初始读取中获取写锁定:
SELECT Data FROM MyTable WITH (UPDLOCK) WHERE ID=@primaryKey
请注意,如果另一个 SPID 显式使用NOLOCK
那么他们仍然会闪电般地通过它。
您也可以尝试添加ROWLOCK
,即WITH (UPDLOCK, ROWLOCK)
- 就我个人而言,最初我会保持简单。由于您处于可序列化的上下文中,因此尝试过于精细可能是一个失败的原因 - 键范围锁等。