SQL Server 2016 唯一标识符和外键
SQL Server 2016 unique identifier and foreign keys
我有一个简单的数据库 table 用于存储用户执行的操作。
CREATE TABLE [admin].[AuditRecords]
(
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[TypeName] [varchar](200) NOT NULL,
[IssuedBy] [uniqueidentifier] NOT NULL,
[IssuedOn] [datetimeoffset](0) NOT NULL,
[Details] [varchar](max) NOT NULL,
CONSTRAINT [PK_AuditRecords]
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]
GO
ALTER TABLE [admin].[AuditRecords] WITH CHECK
ADD CONSTRAINT [FK_AuditRecords_UserDetails]
FOREIGN KEY([IssuedBy])
REFERENCES [security].[UserDetails] ([Id])
GO
从 auditrecord
table 到用户详细信息 table.
有一个 FK
CREATE TABLE [security].[UserDetails]
(
[Id] [uniqueidentifier] NOT NULL,
[Email] [varchar](256) NOT NULL,
[FirstName] [varchar](256) NOT NULL,
[LastName] [varchar](256) NOT NULL,
[UserStatusId] [int] NOT NULL,
[PhoneNumber] [varchar](20) NOT NULL,
[FaxNumber] [varchar](20) NOT NULL,
[SignatureId] [int] NULL,
[SignatureFilePath] [varchar](256) NOT NULL,
[CreatedDate] [datetime] NOT NULL,
[ModifiedDate] [datetime] NOT NULL,
CONSTRAINT [PK__UserDeta__3214EC070E7DD00C]
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],
CONSTRAINT [UQ_UserDetails_Email]
UNIQUE NONCLUSTERED ([Email] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
我有以下 SQL 语句用于更新数据库
INSERT INTO admin.AuditRecords (TypeName, IssuedBy, IssuedOn, Details)
VALUES (@TypeName, @IssuedBy, @IssuedOn, @Details)
这是 Dapper 代码
var messageDetails = JsonConvert.SerializeObject(data);
var auditRecord = new
{
TypeName = data.GetType().Name,
IssuedBy = _principal.UserId(),
IssuedOn = DateTimeOffset.UtcNow,
Details = messageDetails
};
var tenant = _tenantSettingsFactory.GetCurrentTenant();
using (var sqlConnection = new SqlConnection(tenant.DbConnection))
{
await sqlConnection.OpenAsync();
await sqlConnection.ExecuteAsync(AUDIT_INSERT_SQL, auditRecord);//, transaction);
}
_principle.UserId()
returns 两个 table 中都存在的 Guid 和特定用户 ID。
当我尝试 运行 该查询时,如果我将 guid 以小写形式传递,我会超时。如果我传递大写字母,代码就可以工作。 id 以大写形式存储在数据库中。我已经能够在 SSMS 中复制相同的问题。当 id 传递大写时,记录插入,当传递小写时超时。
这是堆栈跟踪。
System.Data.SqlClient.SqlException (0x80131904): Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
---> System.ComponentModel.Win32Exception (258): The wait operation timed out.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParserStateObject.ThrowExceptionAndWarning(Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParserStateObject.CheckThrowSNIException()
at System.Data.SqlClient.SqlCommand.CheckThrowSNIException()
at System.Data.SqlClient.SqlCommand.EndExecuteNonQueryInternal(IAsyncResult asyncResult)
at System.Data.SqlClient.SqlCommand.EndExecuteNonQuery(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
at Dapper.SqlMapper.ExecuteImplAsync(IDbConnection cnn, CommandDefinition command, Object param) in /_/Dapper/SqlMapper.Async.cs:line 678
at Vortext.RCM.Data.SQL.AuditStore.Append(Object data) in C:\dev\vortext-main-new\vortext-web\Vortext.RCM.Data\SQL\AuditStore.cs:line 48
at Vortext.Common.Domain.Decorators.CommandStoreDecorator`1.HandleAsync(TCommand command)
at Vortext.RCM.Common.Decorators.TransactionCommandDecorator`1.HandleAsync(TCommand command) in C:\dev\vortext-main-new\vortext-web\Vortext.RCM\Common\Decorators\TransactionCommandDecorator.cs:line 23
at CallSite.Target(Closure , CallSite , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at Vortext.RCM.Api.CompositionRoot.CommandHandlerMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) in C:\dev\vortext-main-new\vortext-web\Vortext.RCM.Api\CompositionRoot\CommandHandlerMiddleware.cs:line 52
有什么想法吗?
------------更新----------------
看来原因是 LCK_M_S 锁。我认为因为写入 IssuedBy 的 ID 与在 UserDetails table 上修改的 ID 相同,第一个事务正在阻止第二个事务。
事实证明,真正的问题是我正在尝试 运行 一个单独的 connection/transaction 审计记录而不是编辑用户的主事务。因为第一个事务审计 UserDetails
table 而第二个事务试图更新 AuditRecords
其中 FK 匹配 UserDetails
中正在编辑的行,第二个事务超时等待为完成第一笔交易。删除 FK 即可解决问题。
我有一个简单的数据库 table 用于存储用户执行的操作。
CREATE TABLE [admin].[AuditRecords]
(
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[TypeName] [varchar](200) NOT NULL,
[IssuedBy] [uniqueidentifier] NOT NULL,
[IssuedOn] [datetimeoffset](0) NOT NULL,
[Details] [varchar](max) NOT NULL,
CONSTRAINT [PK_AuditRecords]
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]
GO
ALTER TABLE [admin].[AuditRecords] WITH CHECK
ADD CONSTRAINT [FK_AuditRecords_UserDetails]
FOREIGN KEY([IssuedBy])
REFERENCES [security].[UserDetails] ([Id])
GO
从 auditrecord
table 到用户详细信息 table.
CREATE TABLE [security].[UserDetails]
(
[Id] [uniqueidentifier] NOT NULL,
[Email] [varchar](256) NOT NULL,
[FirstName] [varchar](256) NOT NULL,
[LastName] [varchar](256) NOT NULL,
[UserStatusId] [int] NOT NULL,
[PhoneNumber] [varchar](20) NOT NULL,
[FaxNumber] [varchar](20) NOT NULL,
[SignatureId] [int] NULL,
[SignatureFilePath] [varchar](256) NOT NULL,
[CreatedDate] [datetime] NOT NULL,
[ModifiedDate] [datetime] NOT NULL,
CONSTRAINT [PK__UserDeta__3214EC070E7DD00C]
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],
CONSTRAINT [UQ_UserDetails_Email]
UNIQUE NONCLUSTERED ([Email] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
我有以下 SQL 语句用于更新数据库
INSERT INTO admin.AuditRecords (TypeName, IssuedBy, IssuedOn, Details)
VALUES (@TypeName, @IssuedBy, @IssuedOn, @Details)
这是 Dapper 代码
var messageDetails = JsonConvert.SerializeObject(data);
var auditRecord = new
{
TypeName = data.GetType().Name,
IssuedBy = _principal.UserId(),
IssuedOn = DateTimeOffset.UtcNow,
Details = messageDetails
};
var tenant = _tenantSettingsFactory.GetCurrentTenant();
using (var sqlConnection = new SqlConnection(tenant.DbConnection))
{
await sqlConnection.OpenAsync();
await sqlConnection.ExecuteAsync(AUDIT_INSERT_SQL, auditRecord);//, transaction);
}
_principle.UserId()
returns 两个 table 中都存在的 Guid 和特定用户 ID。
当我尝试 运行 该查询时,如果我将 guid 以小写形式传递,我会超时。如果我传递大写字母,代码就可以工作。 id 以大写形式存储在数据库中。我已经能够在 SSMS 中复制相同的问题。当 id 传递大写时,记录插入,当传递小写时超时。
这是堆栈跟踪。
System.Data.SqlClient.SqlException (0x80131904): Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
---> System.ComponentModel.Win32Exception (258): The wait operation timed out.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParserStateObject.ThrowExceptionAndWarning(Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParserStateObject.CheckThrowSNIException()
at System.Data.SqlClient.SqlCommand.CheckThrowSNIException()
at System.Data.SqlClient.SqlCommand.EndExecuteNonQueryInternal(IAsyncResult asyncResult)
at System.Data.SqlClient.SqlCommand.EndExecuteNonQuery(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
at Dapper.SqlMapper.ExecuteImplAsync(IDbConnection cnn, CommandDefinition command, Object param) in /_/Dapper/SqlMapper.Async.cs:line 678
at Vortext.RCM.Data.SQL.AuditStore.Append(Object data) in C:\dev\vortext-main-new\vortext-web\Vortext.RCM.Data\SQL\AuditStore.cs:line 48
at Vortext.Common.Domain.Decorators.CommandStoreDecorator`1.HandleAsync(TCommand command)
at Vortext.RCM.Common.Decorators.TransactionCommandDecorator`1.HandleAsync(TCommand command) in C:\dev\vortext-main-new\vortext-web\Vortext.RCM\Common\Decorators\TransactionCommandDecorator.cs:line 23
at CallSite.Target(Closure , CallSite , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at Vortext.RCM.Api.CompositionRoot.CommandHandlerMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) in C:\dev\vortext-main-new\vortext-web\Vortext.RCM.Api\CompositionRoot\CommandHandlerMiddleware.cs:line 52
有什么想法吗?
------------更新----------------
看来原因是 LCK_M_S 锁。我认为因为写入 IssuedBy 的 ID 与在 UserDetails table 上修改的 ID 相同,第一个事务正在阻止第二个事务。
事实证明,真正的问题是我正在尝试 运行 一个单独的 connection/transaction 审计记录而不是编辑用户的主事务。因为第一个事务审计 UserDetails
table 而第二个事务试图更新 AuditRecords
其中 FK 匹配 UserDetails
中正在编辑的行,第二个事务超时等待为完成第一笔交易。删除 FK 即可解决问题。