我正在尝试为表的主键生成唯一ID,我正在使用DateTime.Now.Ticks
。这是现在我们不能使用Identity
的要求。
但有时,在循环中它会在连续迭代中生成相同的ID。 我的简化代码看起来像这样
While(IncomingData.Next())
{
IncomingData.ID = DateTime.Now.Ticks;
// Other Operations
.
.
.
InsertInDatabase(IncomingData);
}
是因为我的处理器指令/秒的速度大于Ticks
的精确度吗?
我正在使用I5 2.9GHZ处理器。虽然我已经通过引入计数变量并将其添加到ticks
来解决了我的问题。这不是一个好方法。无论如何,有人可以为我分解它,因为tick
是如何计算的,它是否依赖于cpu循环?谢谢。
答案 0 :(得分:5)
使用DateTime.Now.Ticks
作为数据库身份是一个非常糟糕的主意。即使您解决了问题中的“重复问题”,您的应用程序将来可能会中断,例如,一个非常常见的情况,应用程序部署在多个服务器上。
您可以(1)使用数据库自动生成,自动增加ID,或(2)使用Guid
来解决问题。
编辑根据数据库性能考虑,在大多数情况下,自动增加long
ID的性能优于guid。
编辑将tick
视为时间测量单位,就像年/月/日/小时/分钟/秒/毫秒/纳秒一样。 Tick
介于毫秒和纳秒之间,1毫秒= 100000个滴答。
答案 1 :(得分:1)
TL; DR:
DateTime.Now
的精度约为15ms,如果循环速度快于DateTime.Now
,则GUID
将在不同迭代次数上具有相同的值。
数据存储为无符号64位整数
位01-62:100纳秒的值,其中0代表1/1/0001 12:00 am,直到值12/31/9999 23:59:59.9999999
此外,DateTime.Now非常不精确:
此属性的分辨率取决于系统计时器,在Windows系统上约为15毫秒
对于ID分配部分,如其他所述,使用为此目的而制作的.git/hooks
。
答案 2 :(得分:1)
我认为您的策略有一些优点的唯一情况是您需要在特定日期之间查询数据库中的数据。否则,简单整数计数器 - 从0或1开始 - 可能总是更好。
如果正确实施并按照使用方式使用,那么您的想法并不完全糟糕。它可能只是不必要地复杂化。
我假设你希望你的主键是一个增加的整数,这是一个明智的要求,以保持你的插入快速与一些数据库。 GUID不适合您。 我假设您不能使用自动递增的数据库密钥。 我还假设您不会从多个应用程序 - 也不是多台计算机 - 写入您的数据库。
首先,您需要考虑夏令时:使用DateTime.UtcNow而不是DateTime.Now。那是因为DateTime.Now可以在夏令时的情况下向后跳。
其次,在极少数情况下 - 当调整系统时钟时,你应该期望DateTime.UtcNow无论如何都要向后跳。这意味着您无论如何都需要保存以前分配的值。
第三,正如您所知,系统时钟的精度不是无限的 - 通常是15毫秒 - 因此您需要保存先前分配的值并在DateTime.UtcNow返回相同值两次的情况下递增它。
第四,知道你需要保留一个包含先前分配值的变量,为什么不放弃整个DateTime想法并仅依赖于该变量?我的意思是:在程序开始时,您可以从数据库中读取最大值,将其存储到内存中的计数器中,然后在每次需要新键值时递增计数器。
答案 3 :(得分:0)
据我所知,DateTime.Now.Ticks值在大多数当前计算机上大约每15ms更新一次,即使我已经读过它可以每1ms更新一次。无论如何,它是一个需要由操作系统更新而不是直接从时钟读取的值,因此可以预期在两个不同的读取中获得相同的结果。我建议你使用不同的,作为数据库或GUID的自动增加值。
这篇文章阐述了这个问题:http://www.nullskull.com/articles/20021111.asp
答案 4 :(得分:0)
基于DateTime.Now.Ticks创建唯一的顺序ID是一项挑战。但是,如果您考虑@ZunTzu出色答案中的要点,就可以完成。
Kestrel HTTP server使用以下代码。当Kestrel为跟踪生成请求ID时使用。
internal static class CorrelationIdGenerator
{
private static readonly string _encode32Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
private static long _lastId = DateTime.UtcNow.Ticks;
public static string GetNextId() => GenerateId(Interlocked.Increment(ref _lastId));
private static unsafe string GenerateId(long id)
{
char* charBuffer = stackalloc char[13];
charBuffer[0] = _encode32Chars[(int)(id >> 60) & 31];
charBuffer[1] = _encode32Chars[(int)(id >> 55) & 31];
charBuffer[2] = _encode32Chars[(int)(id >> 50) & 31];
charBuffer[3] = _encode32Chars[(int)(id >> 45) & 31];
charBuffer[4] = _encode32Chars[(int)(id >> 40) & 31];
charBuffer[5] = _encode32Chars[(int)(id >> 35) & 31];
charBuffer[6] = _encode32Chars[(int)(id >> 30) & 31];
charBuffer[7] = _encode32Chars[(int)(id >> 25) & 31];
charBuffer[8] = _encode32Chars[(int)(id >> 20) & 31];
charBuffer[9] = _encode32Chars[(int)(id >> 15) & 31];
charBuffer[10] = _encode32Chars[(int)(id >> 10) & 31];
charBuffer[11] = _encode32Chars[(int)(id >> 5) & 31];
charBuffer[12] = _encode32Chars[(int)id & 31];
return new string(charBuffer, 0, 13);
}
}
关键要点是:
DateTime.UtcNow.Ticks
用作“种子”。当我自己尝试进行IoT项目时,我在阅读this excellent blog post, aptly named Generating IDs in C# 'safely' and efficiently时发现了这段代码。
答案 5 :(得分:-2)
更好的方法是使用自动生成的Id
但是如果你想坚持使用DataTime Tick选项,那么你可以使用这种方法,根据你的时间给你一个唯一的数字。
Random rand = new Random(DateTime.Now.Millisecond);
此外,您可以使用
Guid guid = Guid.NewGuid();
那也会给你一个唯一的号码。
谢谢!