我會盡量詳細解釋我的問題,我將不勝感激任何幫助/建議。我的問題是關於由兩個查詢造成的死鎖(一個插入和一個更新)。我使用的MS-SQL服務器2008更新和插入查詢創建死鎖
我必須使用同一個數據庫的兩個應用:
- 網絡應用程序(在每一次請求多個記錄通過調用存儲過程插入到表印象)
- Windows服務(計算在一分鐘內,每分鐘進行,前一分鐘的所有展示,並設置一個標誌,每個通過一個存儲過程計算爲好印象的)
的Web應用程序中插入的印象記錄無使用交易,而Windows服務應用程序在使用IsolationLevel.ReadUncommitted
交易時計算印象數。在Windows服務應用程序的存儲過程確實是這樣的:
Windows服務的存儲過程:
循環低谷所有具有isCalculated
標誌設置爲false和日期< @now的印象,計數器加一以及另一個表格中與展示表格相關的其他數據,並將isCalculated
標誌設置爲具有日期< @現在的展示次數爲true。由於此存儲過程是相當大的,在粘貼沒有意義,這裏是的PROC做什麼縮短的代碼片段:
DECLARE @nowTime datetime = convert(datetime, @now, 21)
DECLARE dailyCursor CURSOR FOR
SELECT Daily.dailyId,
Daily.spentDaily,
Daily.impressionsCountCache ,
SUM(Impressions.amountCharged) as sumCharged,
COUNT(Impressions.impressionId) as countImpressions
FROM Daily INNER JOIN Impressions on Impressions.dailyId = Daily.dailyId
WHERE Impressions.isCharged=0 AND Impressions.showTime < @nowTime AND Daily.isActive = 1
GROUP BY Daily.dailyId, Daily.spentDaily, Daily.impressionsCountCache
OPEN dailyCursor
DECLARE @dailyId int,
@spentDaily decimal(18,6),
@impressionsCountCache int,
@sumCharged decimal(18,6),
@countImpressions int
FETCH NEXT FROM dailyCursor INTO @dailyId,@spentDaily, @impressionsCountCache, @sumCharged, @countImpressions
WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE Daily
SET spentDaily= @spentDaily + @sumCharged,
impressionsCountCache = @impressionsCountCache + @countImpressions
WHERE dailyId = @dailyId
FETCH NEXT FROM dailyCursor INTO @dailyId,@spentDaily, @impressionsCountCache, @sumCharged, @countImpressions
END
CLOSE dailyCursor
DEALLOCATE dailyCursor
UPDATE Impressions
SET isCharged=1
WHERE showTime < @nowTime AND isCharged=0
Web應用程序的存儲過程:
這個過程是非常簡單,它只是在表格中插入記錄。這裏是一個縮短的代碼片段:
INSERT INTO Impressions
(dailyId, date, pageUrl,isCalculated) VALUES
(@dailyId, @date, @pageUrl, 0)
守則
調用這些存儲過程的代碼非常簡單,它只是創建的SQL命令傳遞所需的參數,並執行它們
//i send the date like this
string date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff",
CultureInfo.InvariantCulture);
SqlCommand comm = sql.StoredProcedureCommand("storedProcName",
parameters, values);
我經常遇到死鎖(在web應用程序中發生異常,而不是windows服務),並且在使用SQL-Profiler之後,我發現死鎖可能是由於se兩個查詢(我沒有太多分析探查器數據的經驗)。
從SQL Server事件探查器收集到的最新跟蹤數據可以在這個問題上
理論上這兩個存儲過程應該能夠共同努力的底部找到,因爲第一個插入記錄的一個個date = DateTime.Now,第二個計算日期爲< DateTime.Now的印象數。
編輯:
這是在Windows服務應用程序代碼的運行:
SQL sql = new SQL();
DateTime endTime = DateTime.Now;
//our custom DAL class that opens a connection
sql.StartTransaction(IsolationLevel.ReadUncommitted);
try
{
List<string> properties = new List<string>() { "now" };
List<string> values = new List<string>() { endTime.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture) };
SqlCommand comm = sql.StoredProcedureCommannd("ChargeImpressions", properties, values);
comm.Transaction = sql.Transaction;
ok = sql.CheckExecute(comm);
}
catch (Exception up)
{
ok = false;
throw up;
}
finally
{
if (ok)
sql.CommitTransaction();
else
sql.RollbackTransactions();
CloseConn();
}
編輯:
我加在兩個表的索引由馬丁·史密斯這樣的建議:
CREATE NONCLUSTERED INDEX [IDX_Daily_DailyId] ON [dbo].[Daily]
(
[daily] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
and
CREATE NONCLUSTERED INDEX [IDX_Impressions_isCharged_showTime] ON [dbo].[Impressions]
(
[isCharged] ASC,
[showTime] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
目前沒有異常,將報告後
編輯:
不幸的是這並沒有解決死鎖問題。我將在分析器中啓動死鎖跟蹤,以查看死鎖是否與以前相同。
編輯:
粘貼新的跟蹤(對我來說,它看起來一樣的前一個),也沒有捕捉到執行計劃的屏幕(其過大),但here is the xml from the execution plan。而這裏是截圖插入查詢的執行計劃:
<deadlock victim="process14e29e748">
<process-list>
<process id="process14e29e748" taskpriority="0" logused="952" waitresource="KEY: 6:72057594045071360 (f473d6a70892)" waittime="4549" ownerId="2507482845" transactionname="INSERT" lasttranstarted="2011-09-05T11:59:16.587" XDES="0x15bef83b0" lockMode="S" schedulerid="1" kpid="2116" status="suspended" spid="65" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2011-09-05T11:59:16.587" lastbatchcompleted="2011-09-05T11:59:16.587" clientapp=".Net SqlClient Data Provider" hostpid="2200" isolationlevel="snapshot (5)" xactid="2507482845" currentdb="6" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="dbo.InsertImpression" line="27" stmtstart="2002" stmtend="2560" sqlhandle="0x03000600550e30512609e200529f00000100000000000000">
INSERT INTO Impressions
(dailyId, languageId, showTime, pageUrl, amountCharged, age, ipAddress, userAgent, portalId, isCharged,isCalculated) VALUES
(@dailyId, @languageId, @showTime, @pageUrl, @amountCharged, @age, @ip, @userAgent, @portalId, 0, 0) </frame>
</executionStack>
<inputbuf>
Proc [Database Id = 6 Object Id = 1362103893] </inputbuf>
</process>
<process id="process6c9dc8" taskpriority="0" logused="335684" waitresource="KEY: 6:72057594045464576 (5fcc21780b69)" waittime="4475" ownerId="2507482712" transactionname="transaction_name" lasttranstarted="2011-09-05T11:59:15.737" XDES="0x1772119b0" lockMode="U" schedulerid="2" kpid="3364" status="suspended" spid="88" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2011-09-05T11:59:15.737" lastbatchcompleted="2011-09-05T11:59:15.737" clientapp=".Net SqlClient Data Provider" hostpid="1436" isolationlevel="read uncommitted (1)" xactid="2507482712" currentdb="6" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="dbo.ChargeImpressions" line="60" stmtstart="4906" stmtend="5178" sqlhandle="0x03000600e3c5474f0609e200529f00000100000000000000">
UPDATE Impressions
SET isCharged=1
WHERE showTime &lt; @nowTime AND isCharged=0
</frame>
</executionStack>
<inputbuf>
Proc [Database Id = 6 Object Id = 1330103779] </inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594045071360" dbid="6" objectname="dbo.Daily" indexname="PK_Daily" id="lock14c6aab00" mode="X" associatedObjectId="72057594045071360">
<owner-list>
<owner id="process6c9dc8" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="process14e29e748" mode="S" requestType="wait"/>
</waiter-list>
</keylock>
<keylock hobtid="72057594045464576" dbid="6" objectname="dbo.Impressions" indexname="IDX_Impressions_isCharged_showTime" id="lock14c901200" mode="X" associatedObjectId="72057594045464576">
<owner-list>
<owner id="process14e29e748" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="process6c9dc8" mode="U" requestType="wait"/>
</waiter-list>
</keylock>
</resource-list>
</deadlock>
編輯:
之後喬恩建議ATHAN迪金森:
- 我改變我添加了存儲過程(除去光標),
- 我改變了IDX_Impressions_isCharged_showTime不允許PAGE_LOCKS和
- -1秒到Windows服務應用程序的屬性@now避免臨界死鎖的情況。
更新:
查詢執行時間的最後更改後下降,但例外的數量也沒有。
希望最後更新:
由馬丁·史密斯提出的變化是現在生活,插入查詢現在使用的非聚集索引,並在理論上這應該解決這個問題。目前沒有例外報告(保持我的手指交叉)
本文介紹了未提交讀和鎖定行爲:http://beyondrelational.com/blogs/jacob/archive/2008/08/28/sql -server-transaction-isolation-level-read-committed.aspx –
由於FK與'dbo.Daily'的關係,這個問題似乎已經出現,你對這兩個表有什麼索引? –
我在這兩個表中都有幾個索引。每日有兩個索引(主要包含「dailyId」和非集羣非唯一「日期」)。展示次數有3個索引(主集羣「impressionId」,非集羣非唯一「impressionId,dailyId,isCalculated,isCharged」和非集羣非唯一「isCharged,portalId」) – Atzoya