DateTime.Now.Ticks repeating inside a loop

前端 未结 6 1097
小鲜肉
小鲜肉 2021-01-27 02:55

I am trying to generate a unique ID for the primary key of a table and I am using DateTime.Now.Ticks for it. That\'s a requirement for now we can\'t use Ident

相关标签:
6条回答
  • 2021-01-27 03:23

    TL;DR:

    The precision of DateTime.Now is around 15ms, if you loop faster than that, DateTime.Now will have the same value over differents iterations.


    As described in the .Net source code:

    The data is stored as an unsigned 64-bit integeter

    Bits 01-62: The value of 100-nanosecond ticks where 0 represents 1/1/0001 12:00am, up until the value 12/31/9999 23:59:59.9999999

    Furthermore, the DateTime.Now is quite imprecise:

    The resolution of this property depends on the system timer, which is approximately 15 milliseconds on Windows systems


    For the ID assignation part, as other stated, use GUID which have been made for this purpose.

    0 讨论(0)
  • 2021-01-27 03:26

    Using DateTime.Now.Ticks as database identity is a very bad idea. Even you resolved the "repeating issue" in your question, your application will probably break in the future, for example, a very common scenario, the application is deployed on multiple servers.

    You can either (1) use a database auto-generated, auto-increased id, or (2) use Guid to fix the issue.

    EDIT Under the database performance consideration, auto-increased long id has a better performance than guid in most circumstances.

    EDIT Treat tick as an unit of time measurement just like year/month/day/hour/minute/second/millisecond/nanosecond. Tick is between millisecond and nanosecond, 1 millisecond = 100000 ticks.

    0 讨论(0)
  • 2021-01-27 03:29

    Creating a unique sequential Id in C#

    Creating a unique sequential Id based of DateTime.Now.Ticks is challenging. But can be done if you consider the points in @ZunTzu's excellent answer.

    The code below is used by the Kestrel HTTP server. It's used when Kestrel generates request IDs for traces.

    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);
        }
    }
    

    The key take aways are:

    • Use DateTime.UtcNow.Ticks as a 'seed'.
    • Use Interlocked.Increment to increment
    • In an effort to avoid collisions when called from different machines. You could consider prefixing with the machine name hash (Not perfect but will work for most use cases).

    When trying to do this myself for a IoT project I discovered this code when reading this excellent blog post, aptly named Generating IDs in C# 'safely' and efficiently.

    0 讨论(0)
  • 2021-01-27 03:33

    The better approach is use auto-generated Id

    but If you want to stick with DataTime Tick option, then you can use this approach that will give you a unique number base on your time.

    Random rand = new Random(DateTime.Now.Millisecond);
    

    Also, you can use

    Guid guid = Guid.NewGuid();
    

    That'll also give you a unique number.

    Thanks!

    0 讨论(0)
  • 2021-01-27 03:38

    To my knowledge, DateTime.Now.Ticks value is updated roughly every 15ms on most current computers, even though I've read that it can be updated every 1ms. Anyhow it is a value that needs to be updated by the OS and not directly read from the clock, so getting the same result in two different reads is expectable. I'd suggest that you use a different, as an auto increased value on the database or a GUID.

    This articles sheds some light on the matter: http://www.nullskull.com/articles/20021111.asp

    0 讨论(0)
  • 2021-01-27 03:50

    The only case where I see some merit in your strategy is if you need to query your database for data between specific dates. Otherwise a simple integer counter - starting from 0 or 1 - will probably always be better.

    Your idea is not utterly bad if implemented properly and used the way it is meant to be used. It may just be needlessly complicated.

    I am assuming that you want you primary key to be an increasing integer, a sensible requirement to keep your inserts fast with some databases. A GUID will not work for you. I am assuming that you cannot use an auto-incremented database key. I am also assuming that you will not write into your database from multiple applications - nor multiple computers.

    First, you need to take daylight saving into account: use DateTime.UtcNow instead of DateTime.Now. That's because DateTime.Now can jump backwards in case of daylight saving.

    Second, you should expect DateTime.UtcNow to jump backwards anyway in rare occasions - when the system clock is adjusted. It means you need to save the previously allocated value anyway.

    Third, as you already know, the precision of the system clock is not infinite - typically it is 15 ms -, so you need to save the previously allocated value and increment it in case DateTime.UtcNow returns the same value twice.

    Fourth, knowing that you will need to keep a variable holding the previously allocated value, why not drop the whole DateTime idea and rely only on that variable? What I mean is: at the start of your program, you could read the greatest value from the database, store it into your counter in memory and then increment the counter everytime you need a new key value.

    0 讨论(0)
提交回复
热议问题