首页 > Python基础教程 >
-
C#教程之针对多类型数据库,集群数据库的有序
一、背景
常见的一种数据库设计是使用连续的整数为做主键,当新的数据插入到数据库时,由数据库自动生成。但这种设计不一定适合所有场景。
随着越来越多的使用Nhibernate、EntityFramework等ORM(对象关系映射)框架,应用程序被设计成为工作单元(Unit Of Work)模式,需要在数据持久化之前生成主键,为了保证在多线程并发以及站点集群环境中主键的唯一性,最简单最常见的方式是将主键设计成为GUID类型。
(工作单元:是数据库应用程序经常使用的一种设计模式,简单一点来说,就是对多个数据库操作进行打包,记录对象上的所有变化,并在最后提交时一次性将所有变化通过系统事务写入数据库。目的是为了减少数据库调用次数以及避免数据库长事务。)
GUID(全球唯一标识符)也称为UUID,是一种由算法生成的二进制长度为128位的数字标识符。在理想情况下,任何计算机和计算机集群都不会生成两个相同的GUID。GUID 的总数达到了2^128(3.4×10^38)个,所以随机生成两个相同GUID的可能性非常小,但并不为0。GUID一词有时也专指微软对UUID标准的实现。
(RFC 41222描述了创建标准GUID,如今大多数GUID生成算法通常是一个很长的随机数,再结合一些像网络MAC地址这种随机的本地组件信息。)
GUID的优点允许开发人员随时创建新值,而无需从数据库服务器检查值的唯一性,这似乎是一个完美的解决方案。
很多数据库在创建主键时,为了充分发挥数据库的性能,会自动在该列上创建聚集索引。
我们先来说一说什么是聚集索引。集索引确定表中数据的物理顺序。聚集索引类似于电话簿,按姓氏排列数据。由于聚集索引规定数据在表中的物理存储顺序,因此一个表也只能包含一个聚集索引。它能够快速查找到数据,但是如果插入数据库的主键不在列表的末尾,向表中添加新行时就非常缓慢。例如,看下面这个例子,在表中已经存在三行数据:
此时非常简单:数据行按对应ID列的顺序储存。如果我们新添加一行ID为8的数据,不会产生任何问题,新行会追加的末尾。
但如果我们想插入一行的ID为5的数据:
ID为7,8的数据行必须向下移动。虽然在这算什么事儿,但当您的数据量达到数百万行的级别之后,这就是个问题了。如果你还想要每秒处理上百次这种请求,那可真是难上加难了。
这就是GUID主键引发的问题:它是随机产生的,所以在数据插入时,随时都会涉及到数据的移动,导致插入会很缓慢,还会涉及大量不必要的磁盘活动。总结果有两点:
- 空间的浪费以及由此带来的读写效率的下降;
- 更主要的,存储的碎片化以及由此带来的读写效率严重下降。
GUID最关键的问题就是它是随机的。我们需要设计一种有规则的GUID生成方式,在之后生成的GUID类型总是比之前的要大,保证插入数据库的主键是在列表末尾追加的,这种我们称之为有序GUID。
二、GUID排序规则
在讲解有序GUID之前,我们必须先了解一下GUID在.Net中以及各个数据库中的排序规则,排序规则不一样,生成有序GUID的规则也会随之变化。
128位的GUID主要有4部分组成:Data1, Data2, Data3, and Data4,你可以看成下面这样:
11111111-2222-3333-4444-444444444444
Data1占4个字节、Data2占2个字节、 Data3占2个字节、Data4占8个字节。我们分别的对个字节编上序号:
序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Value | 11 | 11 | 11 | 11 | - | 22 | 22 | - | 33 | 33 | - | 44 | 44 | - | 44 | 44 | 44 | 44 | 44 | 44 |
GUID在.Net中的排序规则
在.Net中,GUID默认的排序过段规则是按左到右的,看下面这个示例。
1 var list = new List<Guid> {
2 new Guid("00000000-0000-0000-0000-010000000000"),
3 new Guid("00000000-0000-0000-0000-000100000000"),
4 new Guid("00000000-0000-0000-0000-000001000000"),
5 new Guid("00000000-0000-0000-0000-000000010000"),
6 new Guid("00000000-0000-0000-0000-000000000100"),
7 new Guid("00000000-0000-0000-0000-000000000001"),
8 new Guid("00000000-0000-0000-0100-000000000000"),
9 new Guid("00000000-0000-0000-0010-000000000000"),
10 new Guid("00000000-0000-0001-0000-000000000000"),
11 new Guid("00000000-0000-0100-0000-000000000000"),
12 new Guid("00000000-0001-0000-0000-000000000000"),
13 new Guid("00000000-0100-0000-0000-000000000000"),
14 new Guid("00000001-0000-0000-0000-000000000000"),
15 new Guid("00000100-0000-0000-0000-000000000000"),
16 new Guid("00010000-0000-0000-0000-000000000000"),
17 new Guid("01000000-0000-0000-0000-000000000000")
18 };
19 list.Sort();
20
21 foreach (Guid guid in list) {
22 Console.WriteLine(guid.ToString());
23 }
输出结果:
00000000-0000-0000-0000-000000000001
00000000-0000-0000-0000-000000000100
00000000-0000-0000-0000-000000010000
00000000-0000-0000-0000-000001000000
00000000-0000-0000-0000-000100000000
00000000-0000-0000-0000-010000000000
00000000-0000-0000-0010-000000000000
00000000-0000-0000-0100-000000000000
00000000-0000-0001-0000-000000000000
00000000-0000-0100-0000-000000000000
00000000-0001-0000-0000-000000000000
00000000-0100-0000-0000-000000000000
00000001-0000-0000-0000-000000000000
00000100-0000-0000-0000-000000000000
00010000-0000-0000-0000-000000000000
01000000-0000-0000-0000-000000000000
通过上面的输出结果,我们可以得到排序的权重如下:
序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
权重 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ||||
Value | 11 | 11 | 11 | 11 | - | 22 | 22 | - | 33 | 33 | - | 44 | 44 | - | 44 | 44 | 44 | 44 | 44 | 44 |
这与数字排序规则一致,从右到左进行依次进行排序(数字越小,权重越高,排序的优先级越高)。
GUID在各个数据库中的排序规则
在SQL Server数据库中,我们有一种非常简单的方式来比较两个GUID类型的大小值(其实在SQL Server数据库中称为UniqueIdentifier
类型):
1 With UIDs As (-- 0 1 2 3 4 5 6 7 8 9 A B C D E F
2 Select ID = 1, UID = cast ('00000000-0000-0000-0000-010000000000' as uniqueidentifier)
3 Union Select ID = 2, UID = cast ('00000000-0000-0000-0000-000100000000' as uniqueidentifier)
4 Union Select ID = 3, UID = cast ('00000000-0000-0000-0000-000001000000' as uniqueidentifier)
5 Union Select ID = 4, UID = cast ('00000000-0000-0000-0000-000000010000' as uniqueidentifier)
6 Union Select ID = 5, UID = cast ('00000000-0000-0000-0000-000000000100' as uniqueidentifier)
7 Union Select ID = 6, UID = cast ('00000000-0000-0000-0000-000000000001' as uniqueidentifier)
8 Union Select ID = 7, UID = cast ('00000000-0000-0000-0100-000000000000' as uniqueidentifier)
9 Union Select ID = 8, UID = cast ('00000000-0000-0000-0010-000000000000' as uniqueidentifier)
10 Union Select ID = 9, UID = cast ('00000000-0000-0001-0000-000000000000' as uniqueidentifier)
11 Union Select ID = 10, UID = cast ('00000000-0000-0100-0000-000000000000' as uniqueidentifier)
12 Union Select ID = 11, UID = cast ('00000000-0001-0000-0000-000000000000' as uniqueidentifier)
13 Union Select ID = 12, UID = cast ('00000000-0100-0000-0000-000000000000' as uniqueidentifier)
14 Union Select ID = 13, UID = cast ('00000001-0000-0000-0000-000000000000' as uniqueidentifier)
15 Union Select ID = 14, UID = cast ('00000100-0000-0000-0000-000000000000' as uniqueidentifier)
16 Union Select ID = 15, UID = cast ('00010000-0000-0000-0000-000000000000' as uniqueidentifier)
17 Union Select ID = 16, UID = cast ('01000000-0000-0000-0000-000000000000' as uniqueidentifier)
18 )
19 Select * From UIDs Order By UID, ID
查询结果:
ID | UID |
---|---|
16 | 01000000-0000-0000-0000-000000000000 |
15 | 00010000-0000-0000-0000-000000000000 |
14 | 00000100-0000-0000-0000-000000000000 |
13 | 00000001-0000-0000-0000-000000000000 |
12 | 00000000-0100-0000-0000-000000000000 |
11 | 00000000-0001-0000-0000-000000000000 |
10 | 00000000-0000-0100-0000-000000000000 |
9 | 00000000-0000-0001-0000-000000000000 |
8 | 00000000-0000-0000-0010-000000000000 |
7 | 00000000-0000-0000-0100-000000000000 |
6 | 00000000-0000-0000-0000-000000000001 |
5 | 00000000-0000-0000-0000-000000000100 |
4 | 00000000-0000-0000-0000-000000010000 |
3 | 00000000-0000-0000-0000-000001000000 |
2 | 00000000-0000-0000-0000-000100000000 |
1 | 00000000-0000-0000-0000-010000000000 |