我有下表:
CREATE TABLE [dbo].[Notifications](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Fk_institutionId] [int] NOT NULL,
[Fk_userId] [int] NOT NULL,
[Read] [bit] NOT NULL,
[CategoryId] [int] NOT NULL,
[Title] [nvarchar](150) NULL,
[NotificationText] [text] NULL,
[CreateDate] [datetime] NOT NULL,
[ReadDate] [datetime] NULL,
[DisplayDate] [datetime] NULL,
[ReadBy] [nvarchar](100) NULL,
CONSTRAINT [PK_Notifications] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
一个简单的select * from Notifications
需要30秒,而此表中只有800,000行。
这种实际情况是每个用户执行查询select * from notifications where DisplayDate is null and Fk_userId = [the user id]
,这会在我的.NET Web应用程序中创建一个服务器错误,说明死锁。
我做错了什么?或者为什么会造成死锁错误?
答案 0 :(得分:2)
您已经询问了两个不同的问题。
更重要的似乎是
SELECT *
FROM notifications
WHERE DisplayDate IS NULL
AND Fk_userId = [the user id]
执行计划显示您缺少任何有用的索引,因此正在对整个表进行并行扫描。
如果你提供一个索引,那么它应该表现得更好,并且只需要阅读更少的数据就可以减少死锁。
该查询的示例索引是
CREATE INDEX IX_Fk_userId_Where_DisplayDate_Is_Null
ON notifications(Fk_userId)
INCLUDE (DisplayDate)
WHERE DisplayDate IS NULL;
答案 1 :(得分:0)
可能是由于很多原因:
也许有人正在访问此表并锁定它。在这种情况下,试试这个:
SELECT * FROM Notifications WITH(NOLOCK)
有时问题与缺少索引有关。因为你有一个主键,我不认为它与索引有关。但是,您需要检查该索引的碎片程度,并在必要时重建它。
另一个关键点可能是query optimization statistics。您可以通过执行以下操作来更新它们:
UPDATE STATISTICS Notifications;
根据您上次更新,您可以使用此查询来处理阻止:
-- Detect blocking (run multiple times) (Query 62) (Detect Blocking)
SELECT t1.resource_type AS [lock type], DB_NAME(resource_database_id) AS [database],
t1.resource_associated_entity_id AS [blk object],t1.request_mode AS [lock req], --- lock requested
t1.request_session_id AS [waiter sid], t2.wait_duration_ms AS [wait time], -- spid of waiter
(SELECT [text] FROM sys.dm_exec_requests AS r WITH (NOLOCK) -- get sql for waiter
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle])
WHERE r.session_id = t1.request_session_id) AS [waiter_batch],
(SELECT SUBSTRING(qt.[text],r.statement_start_offset/2,
(CASE WHEN r.statement_end_offset = -1
THEN LEN(CONVERT(nvarchar(max), qt.[text])) * 2
ELSE r.statement_end_offset END - r.statement_start_offset)/2)
FROM sys.dm_exec_requests AS r WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(r.[sql_handle]) AS qt
WHERE r.session_id = t1.request_session_id) AS [waiter_stmt], -- statement blocked
t2.blocking_session_id AS [blocker sid], -- spid of blocker
(SELECT [text] FROM sys.sysprocesses AS p -- get sql for blocker
CROSS APPLY sys.dm_exec_sql_text(p.[sql_handle])
WHERE p.spid = t2.blocking_session_id) AS [blocker_stmt]
FROM sys.dm_tran_locks AS t1 WITH (NOLOCK)
INNER JOIN sys.dm_os_waiting_tasks AS t2 WITH (NOLOCK)
ON t1.lock_owner_address = t2.resource_address OPTION (RECOMPILE);
-- Helps troubleshoot blocking and deadlocking issues
-- The results will change from second to second on a busy system
-- You should run this query multiple times when you see signs of blocking
答案 2 :(得分:0)
您的表格中有一个文本字段(NotificationText)。 "文本"在SQL Server中是一种BLOB(二进制大对象)。这些Field类型很难优化。您可以使用全文索引,但它会变得复杂。
我会尝试排除该字段的选择。或者,问问自己是否真的需要使用Text。 Nvarchar(8000)会满足吗?
我敢打赌,如果删除该文本字段,或将其重新定义为nvarchar,您的查询将突然开始飞行。