UuidCreateSequential 函数的包裹

标题陈述


不改变的GUID质量相比较,堪比自增ID integer

图片 1

 

三个大神告诉自身NEWSEQUENTIALID()
在数额迁移的时候会有标题(感谢大神带领),所以作者就深挖一下那一个函数。

    关于NEWSEQUENTIALID()
的用法 参照  NEWSEQUENTIALID()

NEWSEQUENTIALID 是对 Windows
UuidCreateSequential 函数的卷入。

https://msdn.microsoft.com/zh-cn/library/ms189786(v=sql.120).aspx.aspx)

作者们系统中对UuidCreateSequential
方法的强大是那般写的,代码如下:

 

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

        public static Guid CreateRpcrt4Guid()
        {
            Guid guid;
            int result = UuidCreateSequential(out guid);
            if (result == RPC_S_OK)
            {
                byte[] guidBytes = guid.ToByteArray();
                Array.Reverse(guidBytes, 0, 4);
                Array.Reverse(guidBytes, 4, 2);
                Array.Reverse(guidBytes, 6, 2);

                return new Guid(guidBytes);
            }
            else
                return Guid.NewGuid();
        }

    }

 

  有以下多少个毛病:

  1、暴漏MAC地址:NEWSEQUENTIALID函数最终6个字符是网卡的MAC地址

  能够试行看一下

create table #t
(
    id uniqueidentifier not null  default newsequentialid()
    ,name varchar(100)
)
go

insert into #t(name)
output inserted.id
values('a')

 

  2、假使进展多少迁移,到另一台机械上,MAC地址更动就能引起页的争用。

    因为GUID在的SQL
Server的值大小的比对是这么的:

with uids as (
            select id =  1, uuid = cast ('00000000-0000-0000-0000-010000000000' as uniqueidentifier)
    union   select id =  2, uuid = cast ('00000000-0000-0000-0000-000100000000' as uniqueidentifier)
    union   select id =  3, uuid = cast ('00000000-0000-0000-0000-000001000000' as uniqueidentifier)
    union   select id =  4, uuid = cast ('00000000-0000-0000-0000-000000010000' as uniqueidentifier)
    union   select id =  5, uuid = cast ('00000000-0000-0000-0000-000000000100' as uniqueidentifier)
    union   select id =  6, uuid = cast ('00000000-0000-0000-0000-000000000001' as uniqueidentifier)
    union   select id =  7, uuid = cast ('00000000-0000-0000-0100-000000000000' as uniqueidentifier)
    union   select id =  8, uuid = cast ('00000000-0000-0000-0010-000000000000' as uniqueidentifier)
    union   select id =  9, uuid = cast ('00000000-0000-0001-0000-000000000000' as uniqueidentifier)
    union   select id = 10, uuid = cast ('00000000-0000-0100-0000-000000000000' as uniqueidentifier)
    union   select id = 11, uuid = cast ('00000000-0001-0000-0000-000000000000' as uniqueidentifier)
    union   select id = 12, uuid = cast ('00000000-0100-0000-0000-000000000000' as uniqueidentifier)
    union   select id = 13, uuid = cast ('00000001-0000-0000-0000-000000000000' as uniqueidentifier)
    union   select id = 14, uuid = cast ('00000100-0000-0000-0000-000000000000' as uniqueidentifier)
    union   select id = 15, uuid = cast ('00010000-0000-0000-0000-000000000000' as uniqueidentifier)
    union   select id = 16, uuid = cast ('01000000-0000-0000-0000-000000000000' as uniqueidentifier)
)
select * from uids order by uuid desc

 

输出结果:

图片 2  类似
汉字的三点水偏旁(为了好记)


从此间能够看出,MAC地址对GUID的大大小小有那最高的决定性,那就导致在多少迁移的时候出标题。

 

COMB解决方案


 

 COMB 类型的GUID
基本陈设思路是那样的:既然GUID数据变动是自由的形成索引功效低下,影响了系统的个性,那么能无法经过整合的办法,保留GUID的前10个字节,用后6个字节表示GUID生成的光阴(DateTime),那样我们将时刻音讯与GUID组合起来,在保留GUID的唯一性的还要增加了有序性,以此来做实索引功用。

 

前十二个字节是通过随机数变化

private static readonly RNGCryptoServiceProvider RandomGenerator = new RNGCryptoServiceProvider();

      byte[] randomBytes = new byte[10];
      RandomGenerator.GetBytes(randomBytes);

UuidCreateSequential 函数的包裹。UuidCreateSequential 函数的包裹。 

UuidCreateSequential 函数的包裹。 

后多个字节用时间退换

      long timestamp = DateTime.UtcNow.Ticks / 10000L;
      byte[] timestampBytes = BitConverter.GetBytes(timestamp);

      if (BitConverter.IsLittleEndian)
      {
        Array.Reverse(timestampBytes);
      }

 

 

末段结合起来

    byte[] guidBytes = new byte[16];
    Buffer.BlockCopy(randomBytes, 0, guidBytes, 0, 10);
    Buffer.BlockCopy(timestampBytes, 2, guidBytes, 10, 6); 

    return new Guid(guidBytes);

 

 

UuidCreateSequential 函数的包裹。UuidCreateSequential 函数的包裹。那几个消除方法是被我们所确认的,唯一以为不好的地方是,在快速获得多数的GUID的时候,时间是一致的,加上自由变化的数据,这一组数据是大小不一的。假若数据Curry有无数数额,这一组数据明确比他们都大,品质应该没失常。

github地址:

 

自己的消除措施


 

总计地点的法子,UuidCreateSequential
前边13个字节有序,后6个是MAC地址。COMBO解决方案是前方11个随机,后三个是时刻。作者是将那多个结合起来

前12个去UuidCreateSequential
方法的值,后6个取时间

UuidCreateSequential 函数的包裹。代码:

public static Guid NewSequentialGuid()
    {
        const int RPC_S_OK = 0;
        Guid guid;
        int result = UuidCreateSequential(out  guid);

        if (result != RPC_S_OK)
        {
            throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
        }
        else
        {
       //这里把UuidCreateSequential函数返回的数据做处理
            byte[] guidBytes = guid.ToByteArray();
            Array.Reverse(guidBytes, 0, 4);
            Array.Reverse(guidBytes, 4, 2);
            Array.Reverse(guidBytes, 6, 2);

       //这里用时间
            long timestamp = DateTime.UtcNow.Ticks / 10000L;
            byte[] timestampBytes = BitConverter.GetBytes(timestamp);

            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(timestampBytes);
            }
       //最后把时间赋值给后6位
            Buffer.BlockCopy(timestampBytes, 2, guidBytes, 10, 6);
            return new Guid(guidBytes);
        }


    }

    [System.Runtime.InteropServices.DllImport("rpcrt4.dll", SetLastError = true)]
    private static extern int UuidCreateSequential(out Guid guid);

 

这里能够在程序调用,作为DBA在数据库使用的话能够将这些法子增加到程序集里,供给有个别退换

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlTypes;

public class FunctionNewGuid
{
  //这里需要添加SqlFunction属性
  //返回类型是数据库类型

    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlGuid NewSequentialGuid()
    {
        const int RPC_S_OK = 0;
        Guid guid;
        int result = UuidCreateSequential(out  guid);

        if (result != RPC_S_OK)
        {
            throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
        }
        else
        {
            byte[] guidBytes = guid.ToByteArray();
            Array.Reverse(guidBytes, 0, 4);
            Array.Reverse(guidBytes, 4, 2);
            Array.Reverse(guidBytes, 6, 2);

            long timestamp = DateTime.UtcNow.Ticks / 10000L;
            byte[] timestampBytes = BitConverter.GetBytes(timestamp);

            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(timestampBytes);
            }
            Buffer.BlockCopy(timestampBytes, 2, guidBytes, 10, 6);
            return new SqlGuid(guidBytes);
        }


    }

    [System.Runtime.InteropServices.DllImport("rpcrt4.dll", SetLastError = true)]
    private static extern int UuidCreateSequential(out Guid guid);
}

 

编写翻译生成DLL后,注册到数据库

--设置数据库是可信任
 ALTER DATABASE TEST SET TRUSTWORTHY ON

 --创建程序集
 CREATE ASSEMBLY SQLCLR FROM 'D:\SQLCLR.DLL'
 WITH PERMISSION_SET = UNSAFE


 --用程序集方法创建函数
 CREATE FUNCTION func_NewSequentialGuid()
    RETURNS uniqueidentifier
AS external name SQLCLR.FunctionNewGuid.NewSequentialGuid

 

    

测量试验代码:

 批量诉求:


 

select dbo.func_NewSequentialGuid() 
union
select dbo.func_NewSequentialGuid() 
union
select dbo.func_NewSequentialGuid() 
union
select dbo.func_NewSequentialGuid() 
union
select dbo.func_NewSequentialGuid() 

 结果:

 图片 3

 

 多次伸手:


 

create table #t
(
    uuid uniqueidentifier 
    ,id int identity
)
go

insert into #t(uuid)
values(dbo.func_NewSequentialGuid())
go 10

select * from #t

 

 图片 4

 

git地址

https://gitee.com/wangzhanbo/cms/tree/master/Library

 

若果反常,希望大家指正。。。

 

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注