Générateur De Guid Séquentiel

y a-t-il un moyen d'obtenir la fonctionnalité de Sql Server 2005+ Sequential Guid generator sans insérer d'enregistrements pour le relire lors d'un aller-retour ou en invoquant un win dll natif? J'ai vu quelqu'un répondre avec une façon d'utiliser rpcrt4.dll mais je ne suis pas sûr que ce serait en mesure de travailler à partir de mon environnement hébergé pour la production.

Edit: en travaillant avec la réponse de @John Boker, j'ai essayé de la transformer en un générateur de GuidComb à la place d'être dépendant de la dernière directive générée autre que recommencer. C'est pour la graine au lieu de commencer avec Guid.Vide que j'utilise

public SequentialGuid()
{
    var tempGuid = Guid.NewGuid();
    var bytes = tempGuid.ToByteArray();
    var time = DateTime.Now;
    bytes[3] = (byte) time.Year;
    bytes[2] = (byte) time.Month;
    bytes[1] = (byte) time.Day;
    bytes[0] = (byte) time.Hour;
    bytes[5] = (byte) time.Minute;
    bytes[4] = (byte) time.Second;
    CurrentGuid = new Guid(bytes);
}

je me suis basé que les commentaires sur

// 3 - the least significant byte in Guid ByteArray 
        [for SQL Server ORDER BY clause]
// 10 - the most significant byte in Guid ByteArray 
        [for SQL Server ORDERY BY clause]
SqlOrderMap = new[] {3, 2, 1, 0, 5, 4, 7, 6, 9, 8, 15, 14, 13, 12, 11, 10};

est-ce que cela ressemble à la façon dont je voudrais lancer une Gide avec le DateTime ou est-ce que cela ressemble à ce que je devrais faire à l'envers et travailler à l'envers à partir de la fin des index SqlOrderMap? Je ne suis pas trop préoccupé par leur être une pause de paging à tout moment un le guide initial serait créé car il ne se produirait que lors de l'application recycle.

38
demandé sur John Saunders 2009-11-18 00:36:40

11 réponses

cette personne est venue avec quelque chose pour faire des guids séquentiels, voici un lien

http://developmenttips.blogspot.com/2008/03/generate-sequential-guids-for-sql.html

code pertinent:

public class SequentialGuid {
    Guid _CurrentGuid;
    public Guid CurrentGuid {
        get {
            return _CurrentGuid;
        }
    }

    public SequentialGuid() {
        _CurrentGuid = Guid.NewGuid();
    }

    public SequentialGuid(Guid previousGuid) {
        _CurrentGuid = previousGuid;
    }

    public static SequentialGuid operator++(SequentialGuid sequentialGuid) {
        byte[] bytes = sequentialGuid._CurrentGuid.ToByteArray();
        for (int mapIndex = 0; mapIndex < 16; mapIndex++) {
            int bytesIndex = SqlOrderMap[mapIndex];
            bytes[bytesIndex]++;
            if (bytes[bytesIndex] != 0) {
                break; // No need to increment more significant bytes
            }
        }
        sequentialGuid._CurrentGuid = new Guid(bytes);
        return sequentialGuid;
    }

    private static int[] _SqlOrderMap = null;
    private static int[] SqlOrderMap {
        get {
            if (_SqlOrderMap == null) {
                _SqlOrderMap = new int[16] {
                    3, 2, 1, 0, 5, 4, 7, 6, 9, 8, 15, 14, 13, 12, 11, 10
                };
                // 3 - the least significant byte in Guid ByteArray [for SQL Server ORDER BY clause]
                // 10 - the most significant byte in Guid ByteArray [for SQL Server ORDERY BY clause]
            }
            return _SqlOrderMap;
        }
    }
}
20
répondu John Boker 2015-06-15 18:51:51

vous pouvez simplement utiliser la même fonction Win32 API que SQL Server utilise :

UuidCreateSequential

et appliquer un peu de bit-shifting pour mettre les valeurs dans l'ordre big-endian.

et puisque vous le voulez en C#:

private class NativeMethods
{
   [DllImport("rpcrt4.dll", SetLastError=true)]
   public static extern int UuidCreateSequential(out Guid guid);
}

public static Guid NewSequentialID()
{
   //Code is released into the public domain; no attribution required
   const int RPC_S_OK = 0;

   Guid guid;
   int result = NativeMethods.UuidCreateSequential(out guid);
   if (result != RPC_S_OK)
      return Guid.NewGuid();

   //Endian swap the UInt32, UInt16, and UInt16 into the big-endian order (RFC specified order) that SQL Server expects
   //See https://stackoverflow.com/a/47682820/12597
   //Short version: UuidCreateSequential writes out three numbers in litte, rather than big, endian order
   var s = guid.ToByteArray();
   var t = new byte[16];

   //Endian swap UInt32
   t[3] = s[0];
   t[2] = s[1];
   t[1] = s[2];
   t[0] = s[3];
   //Endian swap UInt16
   t[5] = s[4];
   t[4] = s[5];
   //Endian swap UInt16
   t[7] = s[6];
   t[6] = s[7];
   //The rest are already in the proper order
   t[8] = s[8];
   t[9] = s[9];
   t[10] = s[10];
   t[11] = s[11];
   t[12] = s[12];
   t[13] = s[13];
   t[14] = s[14];
   t[15] = s[15];

   return new Guid(t);
}

voir aussi


Microsoft UuidCreateSequential est juste une mise en œuvre d'un type 1 uuid de RFC 4122 .

Un uuid comporte trois parties:

  • node : (6 bytes) - l'adresse MAC de l'ordinateur
  • timestamp : (7 octets) - nombre de 100 ns intervalles, depuis 00: 00: 00.00, 15 octobre 1582 (la date de la réforme grégorienne au calendrier chrétien)
  • clockSequenceNumber (2 octets) - counter dans le cas où vous générez un guid plus rapide que 100ns, ou vous changez votre adresse mac

l'algorithme de base est:

  1. obtenir un système de verrouillage
  2. lire le dernier node , timestamp et clockSequenceNumber provenant du stockage persistant (registre / fichier)
  3. obtenez le courant node (c'est à dire l'adresse MAC)
  4. obtenez le courant timestamp
    • a) Si l'état sauvegardé n'était pas disponible ou corrompu, ou si l'adresse mac a changé, générer un aléatoire clockSequenceNumber
    • b) si l'État était disponible, mais que le courant timestamp est le même ou plus ancien que l'horodatage enregistré, incrémenter le clockSequenceNumber
  5. enregistrer node , timestamp et clockSequenceNumber retour vers le stockage persistant
  6. libérer le verrouillage global
  7. formater la structure de guidage selon le rfc

il y a un numéro de version 4 bits, et 2 bits variante qui doivent également être Andés dans les données:

guid = new Guid(
      timestamp & 0xFFFFFFFF,  //timestamp low
      (timestamp >> 32) & 0xFFFF, //timestamp mid
      ((timestamp >> 40) & 0x0FFF), | (1 << 12) //timestamp high and version (version 1)
      (clockSequenceNumber & 0x3F) | (0x80), //clock sequence number and reserved
      node[0], node[1], node[2], node[3], node[4], node[5], node[6]);

Note : complètement non testé; je viens de l'apercevoir du RFC.

  • l'ordre des octets pourrait devoir être modifié ( Voici l'ordre des octets pour le serveur sql )
  • vous pourriez vouloir créer votre propre version, par exemple la Version 6 (la version 1-5 est définie). De cette façon, vous êtes garanti d'être universellement unique
64
répondu Ian Boyd 2017-12-13 19:47:12

Ici est de savoir comment NHibernate met en œuvre le Guid.Algorithme de peigne:

private Guid GenerateComb()
{
    byte[] guidArray = Guid.NewGuid().ToByteArray();

    DateTime baseDate = new DateTime(1900, 1, 1);
    DateTime now = DateTime.Now;

    // Get the days and milliseconds which will be used to build the byte string 
    TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks);
    TimeSpan msecs = now.TimeOfDay;

    // Convert to a byte array 
    // Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333 
    byte[] daysArray = BitConverter.GetBytes(days.Days);
    byte[] msecsArray = BitConverter.GetBytes((long) (msecs.TotalMilliseconds / 3.333333));

    // Reverse the bytes to match SQL Servers ordering 
    Array.Reverse(daysArray);
    Array.Reverse(msecsArray);

    // Copy the bytes into the guid 
    Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2);
    Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4);

    return new Guid(guidArray);
}
14
répondu Moslem Ben Dhaou 2014-08-24 14:41:21

un guid séquentiel qui se met à jour souvent (au moins 3 fois par milliseconde), peut être trouvé ici . Il est créé avec le code C# régulier (pas d'appel de code natif).

7
répondu Alex Siepman 2013-11-01 21:04:22

C # Version

    public static Guid ToSeqGuid()
    {
        Int64 lastTicks = -1;
        long ticks = System.DateTime.UtcNow.Ticks;

        if (ticks <= lastTicks)
        {
            ticks = lastTicks + 1;
        }

        lastTicks = ticks;

        byte[] ticksBytes = BitConverter.GetBytes(ticks);

        Array.Reverse(ticksBytes);

        Guid myGuid = new Guid();
        byte[] guidBytes = myGuid.ToByteArray();

        Array.Copy(ticksBytes, 0, guidBytes, 10, 6);
        Array.Copy(ticksBytes, 6, guidBytes, 8, 2);

        Guid newGuid = new Guid(guidBytes);

        string filepath = @"C:\temp\TheNewGuids.txt";
        using (StreamWriter writer = new StreamWriter(filepath, true))
        {
            writer.WriteLine("GUID Created =  " + newGuid.ToString());
        }

        return newGuid;

    }

}

}

3
répondu Charles 2013-06-05 23:11:36

ma solution (en VB mais facile à convertir). Il modifie le plus significatif (pour le tri de serveur SQL) les 8 premiers octets du GUID à DateTime.UtcNow.Tics et a également du code supplémentaire pour aider à la question d'obtenir les mêmes tics plusieurs fois si vous appelez pour un nouveau GUID plus rapide que les mises à jour de l'horloge du système.

Private ReadOnly _toSeqGuidLock As New Object()
''' <summary>
''' Replaces the most significant eight bytes of the GUID (according to SQL Server ordering) with the current UTC-timestamp.
''' </summary>
''' <remarks>Thread-Safe</remarks>
<System.Runtime.CompilerServices.Extension()> _
Public Function ToSeqGuid(ByVal guid As Guid) As Guid

    Static lastTicks As Int64 = -1

    Dim ticks = DateTime.UtcNow.Ticks

    SyncLock _toSeqGuidLock

        If ticks <= lastTicks Then
            ticks = lastTicks + 1
        End If

        lastTicks = ticks

    End SyncLock

    Dim ticksBytes = BitConverter.GetBytes(ticks)

    Array.Reverse(ticksBytes)

    Dim guidBytes = guid.ToByteArray()

    Array.Copy(ticksBytes, 0, guidBytes, 10, 6)
    Array.Copy(ticksBytes, 6, guidBytes, 8, 2)

    Return New Guid(guidBytes)

End Function
3
répondu Ronny Heuschkel 2014-03-07 09:48:21

autant que je sache, les NHibernate ont un générateur spécial, appelé GuidCombGenerator. Vous pouvez le regarder.

2
répondu Mike Chaliy 2009-11-17 21:53:35

peut-être intéressant à comparer avec les autres suggestions:

Entitefram Framework Core implémente également un guide de validation séquentielle. Ils génèrent des guids de randoms pour chaque valeur et ne changent que les octets les plus significatifs basés sur un timestamp et des incréments de thread-safe pour le tri dans le serveur SQL.

source du lien de

cela conduit à des valeurs qui sont toutes très différentes mais avec une estampille temporelle sortable.

2
répondu Schwarzie2478 2016-10-03 15:52:14

j'ai juste pris la NHibernate de réponse par Musulman Ben Dhaou et en fait une extension de la fonction:

using System;

namespace Atlas.Core.Kernel.Extensions
{
  public static class Guids
  {
    public static Guid Comb(this Guid source)
    {
      byte[] guidArray = source.ToByteArray();

      DateTime baseDate = new DateTime(1900, 1, 1);
      DateTime now = DateTime.Now;

      // Get the days and milliseconds which will be used to build the byte string 
      TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks);
      TimeSpan msecs = now.TimeOfDay;

      // Convert to a byte array 
      // Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333 
      byte[] daysArray = BitConverter.GetBytes(days.Days);
      byte[] msecsArray = BitConverter.GetBytes((long)(msecs.TotalMilliseconds / 3.333333));

      // Reverse the bytes to match SQL Servers ordering 
      Array.Reverse(daysArray);
      Array.Reverse(msecsArray);

      // Copy the bytes into the guid 
      Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2);
      Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4);

      return new Guid(guidArray);
    }
  }
}
2
répondu toddmo 2017-05-23 12:18:02

je viens de voir cette question... Il se trouve que je suis l'auteur d'une petite bibliothèque open-source.Net pour générer des GUIDs de style COMB.

la bibliothèque supporte à la fois la méthode originale (compatible avec le type datetime de SQL Server) et une méthode utilisant des horodateurs Unix, qui ont plus de précision temporelle. Il comprend également une variante qui fonctionne mieux pour PostgrSQL:

https://github.com/richardtallent/RT.Comb

2
répondu richardtallent 2017-07-05 04:18:51

N'est pas spécifiquement guid, mais j'utilise maintenant normalement un générateur d'id séquentiel de style Flocon De Neige. Les mêmes avantages d'un guid tout en ayant encore mieux la compatibilité des index groupés qu'un guid séquentiel.

Excentriques .NET de Base

IdGen for. NET Framework

1
répondu Chris Marisic 2016-10-03 20:37:29