VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > Python基础教程 >
  • C#教程之针对多类型数据库,集群数据库的有序(2)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

     通过上面可以得于是如下结果:

  1. 先按每1-8从左到右进行排序;
  2. 接着按第9-10位从右到左进行排序;
  3. 最后按后11-16位从右到左进行排序;

     通过分析,我们可得到如下权重列表:

序号 1 2 3 4   5 6   7 8   9 10   11 12 13 14 15 16
权重 16 15 14 13   12 11   10 9   7 8   1 2 3 4 5 6
Value 11 11 11 11 - 22 22 - 33 33 - 44 44 - 44 44 44 44 44 44

 

 

 

     在Microsoft官方文档中,有一篇文档关于GUID与uniqueidentifier的值比较:Comparing GUID and uniqueidentifier Values。

     不同的数据库处理GUID的方式也是不同的:

          1)在SQL Server存在内置GUID类型,没有原生GUID支持的数据库通过模拟方式来实现的;

          2)在Oracle保存为raw bytes类型,具体类型为raw(16)

          3)在MySql中通常将GUID储存为char(36)的字符串形式;

     关于Oracle、MySql数据库的排序规则与.Net中排序规则,不过篇章的限制,这里不再做具体的演示。在github上提供了示例SQL语句:https://gist.github.com/tangdf/f0aed064ba10bfa0050e4344b9236889。我们在这里只给出最终的结论:

小结:

  1. .Net中GUID的排序规则是从左到右依次进行排序,与数字排序规则一致;
  2. Sql Server数据库提供对GUID类型的支持,在数据库中称为UniqueIdentifier类型,但是排序规则比较复杂:Oracle数据库未提供对GUID类型的支持,使用的是raw bytes类型保存数据raw(16),具体类型为,排序规则与GUID在.Net中规则一致;
    • 先按每1-8从左到右进行排序;
    • 接着按第9-10位从右到左进行排序;
    • 最后按后11-16位从右到左进行排序;
  3. Oracle数据库未提供对GUID类型的支持,使用的是raw bytes类型保存数据raw(16),具体类型为,排序规则与GUID在.Net中规则一致;
  4. MySql数据未提供对GUID类型的支持,使用的是字符串的类型保存数据,使用是的char(36)类型,由于使用的是字符串类型,排序规则与GUID在.Net中的规则一致。

 

三、有序GUID

     有序GUID是有规则的生成GUID,保存在之后生成的GUID类型总是比之前的要大。不过在上一节中,已经提到过各个数据库对GUID支持不一样,而且排序的规则也不一样,所以我们需要为每一个数据库提供不一致的有序GUID生成规则。

     UuidCreateSequential函数

     我们都知道SQL Server数据库有一个NewSequentialId()函数,用于创建有序GUID。在创建表时,可以将它设置成为GUID类型字段的默认值,在插入新增数据时自动创建主键的值(该函数只能做为字段的默认值,不能直接在SQL中调用)。

     示例:

1 Create Table TestTable
2        (
3          ID UniqueIdentifier Not Null Default ( NewSequentialId() ) ,
4          Number Int
5        );

     NewSequentialId()函数只能在数据库使用,不过在 Microsoft 的 MSDN 文档中有说明,NEWSEQUENTIALID 是对 Windows UuidCreateSequential 函数的包装,https://msdn.microsoft.com/zh-cn/library/ms189786(v=sql.120).aspx。这样我们可以在C#通过非托管方法调用:

复制代码
 1 [System.Runtime.InteropServices.DllImport("rpcrt4.dll", SetLastError = true)]
 2    private static extern int UuidCreateSequential(out Guid guid);
 3 
 4    public static Guid NewSequentialGuid()
 5    {
 6        const int RPC_S_OK = 0;
 7 
 8        int result = UuidCreateSequential(out var guid);
 9        if (result != RPC_S_OK) {
10            throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
11        }
12 
13        return guid;
14    }
复制代码

     不这个方法也存在三个问题:

  1. 这个方法涉及到安全问题,UuidCreateSequential函数依赖的计算硬件,该方法的后12位其实是网卡的MAC地址。这是我电脑生成的一组有序GUID。

{A2A93393-C8DC-11E7-B133-2C56DC497A97}
{A2A93394-C8DC-11E7-B133-2C56DC497A97}
{A2A93395-C8DC-11E7-B133-2C56DC497A97}
{A2A93396-C8DC-11E7-B133-2C56DC497A97}
{A2A93397-C8DC-11E7-B133-2C56DC497A97}
{A2A93398-C8DC-11E7-B133-2C56DC497A97}
{A2A93399-C8DC-11E7-B133-2C56DC497A97}
{A2A9339A-C8DC-11E7-B133-2C56DC497A97}
{A2A9339B-C8DC-11E7-B133-2C56DC497A97}
{A2A9339C-C8DC-11E7-B133-2C56DC497A97}

     这是我电脑的网卡的MAC地址:

     

  1. 由于UuidCreateSequential函数生成的有序GUID中包括MAC地址,所以如果在服务器集群环境中,肯定存在一台服务器A上生成的有序GUID总比另一台服务器B生成要更小,服务器A产生的数据插入到数据库时,由于聚集索引的问题,总是会移动服务器B已经持久化到数据库中的数据。集群的服务器越多,产生的IO问题更严重。在服务器群集环境中,需要自行实现有序GUID。

  2. UuidCreateSequential函数生成的GUID规则与SQL Server中排序的规则存在不一致,这样仍然会导致严重的IO问题,所以需要将GUID重新排序后再持久化到数据库。例如上面列出生成的GUID列表,依次生成的数据可以看出,是第4位字节在自增长,在这与任何一个数据库的排序规则都不一致;关于该函数生成的规则,可以见此链接:https://stackoverflow.com/questions/5585307/sequential-guids。

     下面的方法是将生成的GUID调整成为适合Sql Server使用的有序GUID(针对其它数据库支持,您可以按排序规则自行修改):

复制代码
 1 [System.Runtime.InteropServices.DllImport("rpcrt4.dll", SetLastError = true)]
 2 static extern int UuidCreateSequential(byte[] buffer);
 3 
 4 static Guid NewSequentialGuid() {
 5 
 6     byte[] raw = new byte[16];
 7     if (UuidCreateSequential(raw) != 0)
 8         throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
 9 
10     byte[] fix = new byte[16];
11 
12     // reverse 0..3
13     fix[0x0] = raw[0x3];
14     fix[0x1] = raw[0x2];
15     fix[0x2] = raw[0x1];
16     fix[0x3] = raw[0x0];
17 
18     // reverse 4 & 5
19     fix[0x4] = raw[0x5];
20     fix[0x5] = raw[0x4];
21 
22     // reverse 6 & 7
23     fix[0x6] = raw[0x7];
24     fix[0x7] = raw[0x6];
25 
26     // all other are unchanged
27     fix[0x8] = raw[0x8];
28     fix[0x9] = raw[0x9];
29     fix[0xA] = raw[0xA];
30     fix[0xB] = raw[0xB];
31     fix[0xC] = raw[0xC];
32     fix[0xD] = raw[0xD];
33     fix[0xE] = raw[0xE];
34     fix[0xF] = raw[0xF];
35 
36     return new Guid(fix);
37 }
复制代码

 小结:
     UuidCreateSequential函数存在隐私的问题,不适合集群环境,并且需要重新排序后再提交到数据库;

     COMB解决方案

     COMB 类型的GUID 是由Jimmy Nilsson在他的“The Cost of GUIDs as Primary Keys”一文中设计出来的。
     基本设计思路是这样的:既然GUID数据生成是随机的造成索引效率低下,影响了系统的性能,那么能不能通过组合的方式,保留GUID的前10个字节,用后6个字节表示GUID生成的时间(DateTime),这样我们将时间信息与GUID组合起来,在保留GUID的唯一性的同时增加了有序性,以此来提高索引效率(这是针对Sql Server数据库来设计的)。

     在NHibernate框架中已经实现该功能,可以在github上看到实现方式:https://github.com/nhibernate/nhibernate-core/blob/master/src/NHibernate/Id/GuidCombGenerator.cs#L25-L72。

     在EF以及EF Core也同样实现了类似的解决方案,EF Core的实现方式:https://github.com/aspnet/EntityFrameworkCore/blob/f7f6d6e23c8e47e44a61983827d9e41f2afe5cc7/src/EFCore/ValueGeneration/SequentialGuidValueGenerator.cs#L25-L44

     在这里介绍一下使用的方式,由EF Core框架自动生成有序GUID的方式:

复制代码
 1 public class SampleDbContext : DbContext
 2 {
 3     protected override void OnModelCreating(ModelBuilder modelBuilder)
 4     {
 5         modelBuilder.Entity<GuidEntity>(b =>
 6         {
 7             b.Property(e => e.Id).HasValueGenerator<SequentialGuidValueGenerator>();
 8         });
 9     }
10 }
复制代码

 但是请注意,这两个ORM的解决方案只针对Sql Server数据库,因为只保证了最后几位字节是按顺序来生成的。

     SequentialGuid框架

     SequentialGuid框架也是我要推荐给您,因为它提供了常见数据库生成有序Guid的解决方案。

     关于该框架的设计思路以及针对各个数据库的性能测试,见链接:https://www.codeproject.com/Articles/388157/GUIDs-as-fast-primary-keys-under-multiple-database。

     使用方式,建议您参考ABP框架,在ABP中使用SequentialGuid框架来生成有序GUID,关键代码链接:https://github.com/aspnetboilerplate/aspnetboilerplate/blob/b36855f0c238c3592203f058c641862844a0614e/src/Abp/SequentialGuidGenerator.cs#L36-L51。

总结

我们来总结一下:

  1. 在数据库中最好不要使用随机的GUID,它会影响性能;
  2. 在SQL Server中提供了NewSequentialId函数来生成有序GUID;
  3. 各个数据库对GUID支持的不一样,而且排序的规则也不一样;
  4. UuidCreateSequential函数存在隐私的问题,不适合集群环境,并且需要重新排序后再提交到数据库;
  5. 各ORM框架提供了有序GUID的支持,但是其实只是针对Sql Server数据库设计的;
  6. 推荐您使用SequentialGuid框架,它解决了多数据库以及集群环境的问题。

 

 转载链接:http://www.cnblogs.com/tdfblog/p/SequentialGuid.html


相关教程