好的。我完全重写了我的问题。
数据库表
CREATE TABLE [dbo].[IntegrationCommandLog](
[Id] [uniqueidentifier] NOT NULL,
[Content] [nvarchar](max) NULL,
[OrderingKey] [bigint] IDENTITY(1,1) NOT NULL,
[RowVersion] [timestamp] NULL,
[Topic] [nvarchar](13) NULL,
CONSTRAINT [PK_IntegrationCommandLog] PRIMARY KEY NONCLUSTERED
(
[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
CREATE CLUSTERED INDEX [IX_IntegrationCommandLog_OrderingKey] ON [dbo].[IntegrationCommandLog]
(
[OrderingKey] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
复制者。无需附加调试器即可运行发行版
必需的软件包:
Install-Package Dapper
Install-Package System.Data.SqlClient
代码:
using Dapper;
using System;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
namespace TestApp
{
class Program
{
private const string Sql = "SELECT * FROM dbo.IntegrationCommandLog WHERE OrderingKey > @OrderingKey ORDER BY OrderingKey";
private const string cs = "Data Source=.;Initial Catalog=Test;Integrated Security=True";
static void Main(string[] args)
{
Task.Run(() => Query());
var tasks = new Task[200];
for (int i = 0; i < tasks.Length; ++i)
tasks[i] = Task.Run(() => Insert());
while (true)
{
int j = Task.WaitAny(tasks);
tasks[j] = Task.Run(() => Insert());
}
}
private async static Task Query()
{
long last = -1;
var connection = new SqlConnection(cs);
await connection.OpenAsync();
while (true)
{
var entries = await connection.QueryAsync<IntegrationLogEntry>(Sql, new { OrderingKey = last });
Console.WriteLine(entries.Count());
if (entries.Any())
{
last = entries.Aggregate((e1, e2) =>
{
if (e1.OrderingKey + 1 != e2.OrderingKey)
Console.WriteLine($"Sequence violation {e1.OrderingKey} {e2.OrderingKey}");
return e2;
}).OrderingKey;
}
await Task.Delay(1000);
}
}
private static async Task Insert()
{
string sql = @"SET NOCOUNT ON;
INSERT INTO [dbo].[IntegrationCommandLog] ([Id], [Content], [Topic])
VALUES ( @Id, @Content, @Topic);
SELECT [OrderingKey], [RowVersion]
FROM [dbo].[IntegrationCommandLog]
WHERE @@ROWCOUNT = 1 AND [Id] = @Id";
var content = new string('a', 1000);
using (var connection = new SqlConnection(cs))
{
await connection.OpenAsync();
await connection.ExecuteAsync(sql, new { Id = Guid.NewGuid(), Content = content, Topic = "SomeTopic" });
}
}
}
public class IntegrationLogEntry
{
public Guid Id { get; private set; }
public string Content { get; private set; }
public string Topic { get; private set; }
public long OrderingKey { get; private set; }
public byte[] RowVersion { get; set; }
}
}
确保没有空隙
SELECT top 100 * FROM (SELECT *, rowid = ROW_NUMBER() OVER (ORDER BY OrderingKey) FROM [dbo].[IntegrationCommandLog]) l1
JOIN (SELECT *, rowid = ROW_NUMBER() OVER (ORDER BY OrderingKey) FROM [dbo].[IntegrationCommandLog]) l2 on l1.rowid + 1 = l2.rowid
WHERE l1.OrderingKey + 1 != l2.OrderingKey
输出
Microsoft SQL Server 2016(SP1)(KB3182545)-13.0.4001.0(X64)2016年10月28日18:17:30版权所有(c)Windows Server 2016 Datacenter 6.3(Build 14393)上的Microsoft Corporation Developer Edition(64位) :)(管理程序)
问题:
答案 0 :(得分:0)
e1.OrderingKey +1!= e2.OrderingKey测试e2仅比前一个密钥高1。数据库无法保证。
很多情况会导致非连续的身份:(不要忘记删除)
交易中的连续值–插入交易 多行不能保证获得行的连续值 因为其他并发插入可能会在表上发生。如果值 必须是连续的,则交易应使用排他锁 或使用SERIALIZABLE隔离级别。
服务器重启或其他故障后的连续值– SQL Server可能会缓存 出于性能原因和一些已分配的标识值 数据库故障或服务器重新启动期间,这些值可能会丢失。这个 插入时可能会导致身份值出现空白。如果没有差距 接受,那么应用程序应使用自己的机制 生成键值。将序列生成器与NOCACHE一起使用 选项可以将差距限制为从未提交的事务。
值的重用–对于具有特定标识的给定标识属性 种子/增量,标识值不会被引擎重用。如果一个 特定的插入语句失败或插入语句已滚动 然后,消耗的身份值将丢失,并且不会 再次产生。这可能会导致在后续标识时出现空白 值生成。