🌹歡迎來到《小5講堂》🌹
🌹這是《C#》系列文章,每篇文章將以博主理解的角度展開講解。🌹
🌹溫馨提示:博主能力有限,理解水平有限,若有不對之處望指正!🌹
目錄
- 前言
- 鎖定分析
- Lock鎖對象
- 事務鎖
- 重復鍵
- DeepSeek解決方案
- 死鎖的原因
- 解決方案
- 1. **減少事務范圍**
- 2. **優化 SQL 語句**
- 3. **使用鎖提示**
- 4. **重試機制**
- 5. **檢查索引**
- 6. **分析死鎖圖**
- 示例代碼改進
- 總結
- 相關文章
前言
最近博主在開發一個項目時,用到的都是基本框架功能,有些造輪子的意思。
如果不留意,那么很容易犯一些代碼邏輯上的錯誤。
自己封裝框架和使用現有成熟框架最大區別就是容易踩坑,除非你有比較多的框架開發經驗。
鎖定分析
Lock鎖對象
這里博主犯了個小細節錯誤,鎖的對象和范圍邏輯不對,導致重復數據出現,大事務里存在同表查詢和添加,即使在小量并發下,出現爭搶資源,導致鎖。
實例化鎖對象
下面是錯誤寫法,有發現那里不對嗎?
namespace MvcMyNameSpace
{public class GetClass{private object lockObj = new object();public string GetNo(string tableName, string prefix){lock (lockObj){// 查詢表string sql = "select top 1 * from Test"}}}
靜態實例化
正確寫法,確保鎖對象是當前唯一
namespace MvcMyNameSpace
{public class GetClass{private static object lockObj = new object();public string GetNo(string tableName, string prefix){lock (lockObj){// 查詢表string sql = "select top 1 * from Test"}}}
鎖范圍
博主在上面獨立方法代碼塊加上鎖后,運行一段時間任務后,發現還是會出現重復索引。
后來排查發現,是并發會在最外層接口方法沒有鎖定。
之后在接口API整個方法加了鎖后,就再也沒有出現了重復索引。
事務鎖
博主這里是在一個大事務內進行了表查詢,在高并發下就會出現,多個事務可能同時嘗試更新或讀取相同的數據行或表,導致資源競爭。
重復鍵
對于sql server數據庫而言,可對表的字段設置唯一索引,確保數據庫表唯一性。
如果表設置了唯一索引,那么還會出現重復,說明程序端代碼邏輯不對,寫法有問題。
博主此處出現的錯誤就是在并發情況,至少有兩個并發同一時間獲取到同一條記錄。
DeepSeek解決方案
從你提供的代碼和錯誤信息來看,程序在執行 SQL 命令時遇到了死鎖問題。死鎖通常發生在多個事務同時競爭相同的資源,并且每個事務都在等待其他事務釋放鎖時發生。SQL Server 檢測到死鎖后,會選擇其中一個事務作為“犧牲品”并終止它,以便其他事務可以繼續執行。
死鎖的原因
在你的代碼中,死鎖可能由以下原因引起:
- 并發事務競爭相同的資源:多個事務可能同時嘗試更新或讀取相同的數據行或表,導致資源競爭。
- 事務范圍過大:如果事務涉及多個表或大量數據,死鎖的風險會增加。
- 鎖的順序不一致:如果不同的事務以不同的順序鎖定資源,可能會導致死鎖。
解決方案
以下是幾種可能的解決方案:
1. 減少事務范圍
- 盡量減少事務的持續時間,確保事務只包含必要的操作。
- 如果可能,將事務拆分為多個較小的事務,減少鎖的持有時間。
2. 優化 SQL 語句
- 確保 SQL 語句盡可能高效,減少鎖的持有時間。
- 使用
WITH (NOLOCK)
提示來避免讀取操作時的鎖競爭(但要注意臟讀的風險)。 - 確保更新操作只鎖定必要的行,而不是整個表。
3. 使用鎖提示
- 在 SQL 語句中使用鎖提示來控制鎖的行為。例如,使用
UPDLOCK
或ROWLOCK
來減少鎖的粒度。 - 例如:
SELECT * FROM TableName WITH (UPDLOCK) WHERE ...
4. 重試機制
- 在捕獲到死鎖異常后,實現重試機制。死鎖通常是暫時的,重試后事務可能會成功執行。
- 例如:
int retryCount = 3; while (retryCount > 0) {try{// 執行 SQL 命令cmd.ExecuteNonQuery();break;}catch (SqlException ex){if (ex.Number == 1205) // 1205 是死鎖錯誤代碼{retryCount--;if (retryCount == 0)throw; // 重試次數用盡,拋出異常Thread.Sleep(1000); // 等待一段時間后重試}else{throw; // 其他異常直接拋出}} }
5. 檢查索引
- 確保表上有適當的索引,以減少鎖的競爭。缺少索引可能導致全表掃描,增加鎖的沖突。
6. 分析死鎖圖
- 使用 SQL Server 的死鎖圖(Deadlock Graph)來分析死鎖的原因。你可以通過 SQL Server Profiler 或 Extended Events 來捕獲死鎖圖,了解哪些資源和事務導致了死鎖。
示例代碼改進
以下是一個簡單的重試機制示例,你可以在代碼中添加:
int retryCount = 3;
while (retryCount > 0)
{try{using (SqlConnection connection = new SqlConnection(connectionString)){connection.Open();using (SqlCommand cmd = new SqlCommand()){cmd.Connection = connection;foreach (String str in List){sqlText = "";// 添加參數ArrayList paramsArray = listParams[i];foreach (Dictionary<string, object> paramsItem in paramsArray){foreach (var keyItem in paramsItem){if (string.IsNullOrEmpty($"{keyItem.Value}")){cmd.Parameters.AddWithValue(keyItem.Key, DBNull.Value);}else{cmd.Parameters.AddWithValue(keyItem.Key, $"{keyItem.Value}");}if (str.Contains("update")){sqlText += $"{keyItem.Key.Replace("@", "")}='{keyItem.Value}',";}else{sqlText += $"'{keyItem.Value}',";}}}allSql = $"{str}";// 在這里執行你的 SQL 命令cmd.CommandText = str;cmd.ExecuteNonQuery();cmd.Parameters.Clear();i++;}rows += 1;}}break; // 執行成功,退出循環}catch (SqlException ex){if (ex.Number == 1205) // 1205 是死鎖錯誤代碼{retryCount--;if (retryCount == 0)throw; // 重試次數用盡,拋出異常Thread.Sleep(1000); // 等待一段時間后重試}else{throw; // 其他異常直接拋出}}
}
總結
死鎖是數據庫系統中常見的問題,通常通過優化事務范圍、SQL 語句、鎖策略以及實現重試機制來解決。通過分析死鎖圖和調整代碼邏輯,可以有效減少死鎖的發生。
相關文章
【C#】事務(進程 ID 64)與另一個進程被死鎖在鎖資源上,并且已被選作死鎖犧牲品。請重新運行該事務。不能在具有唯一索引“XXX_Index”的對象“dbo.Test”中插入重復鍵的行。
【C#】使用DeepSeek幫助評估數據庫性能問題,C# 使用定時任務,每隔一分鐘移除一次表,再重新創建表,和往新創建的表追加5萬多條記錄
【C#】合理使用DeepSeek相關AI應用為我們提供強有力的開發工具,在.net core 6.0框架下使用JsonNode動態解析json字符串,如何正確使用單問號和雙問號做好空值處理
【C#】已經實體類和動態實體類的反射使用方法,兩分鐘回顧,碼上就懂
【C#】使用vue3的axios發起get和post請求.net framework部署的API顯示跨域
【C#】.net core 6.0 webapi 使用core版本的NPOI的Excel讀取數據以及保存數據
【C#】pdf按頁分割文件,以及分頁合并,效果還不錯,你值得擁有
【C#】未能加載文件或程序集“CefSharp.Core.Runtime.dll”或它的某一個依賴項。找不到指定的模塊。
【C#】.net core 6.0 在program時間格式統一json格式化,并列舉program默認寫法和簡化寫法
【C#】.net core 6.0 ApiController,API控制器方法,API接口以實體類作為接收參數應該注意的點
【C#】 SortedDictionary,查找字典中是否存在給定的關鍵字
【C#】.net core 6.0 MVC返回JsonResult顯示API接口返回值不可被JSON反序列化
【C#】.net core 6.0 使用第三方日志插件Log4net,配置文件詳細說明
【C#】使用代碼實現龍年春晚撲克牌魔術(守歲共此時),代碼實現篇
【C#】使用代碼實現龍年春晚撲克牌魔術(守歲共此時),流程描述篇