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 即可解决问题。