+ -
当前位置:首页 → 问答吧 → 如何有顺序的生成GUID

如何有顺序的生成GUID

时间:2010-07-01

来源:互联网

想用Guid做表的主键,并在此字段上建立聚簇索引。 因为Guid是随机生成的,生成的值大小是不确定的,每次生成的数可能很大,也可能很小。这样会影响插入的效率,在网上看到有一种方法可以生成顺序的GUID,称为COMB, 描述是这样的:

--------------------------------------------------------------------

使用“COMB(Combine)”类型

  COMB数据类型的基本设计思路是这样的:保留UniqueIdentifier的前10个字节,用后6个字节表示GUID生成的时间(DateTime),这样我们将时间信息与UniqueIdentifier组合起来,在保留UniqueIdentifier的唯一性的同时增加了有序性,以此来提高索引效率。也许有人会担心UniqueIdentifier减少到10字节会造成数据出现重复,其实不用担心,后6字节的时间精度可以达到1/300秒,两个COMB类型数据完全相同的可能性是在这1/300秒内生成的两个GUID前10个字节完全相同,这几乎是不可能的!在SQL Server中用SQL命令将这一思路实现出来便是:

DECLARE @aGuid UNIQUEIDENTIFIER
SET @aGuid = CAST(CAST(NEWID() AS BINARY(10)) 
+ CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER)

  经过测试,使用COMB做主键比使用INT做主键,在检索、插入、更新、删除等操作上仍然显慢,但比Unidentifier类型要快上一些。

------------------------------------------------------------------------------------------------------

  对于上面的实现方法在很多网站都有描述,国外的英文文章也是这样说的(估计都是从中文翻译过来的)。 

  但这里我有一个疑问, 上面说保留 GUID 的前10个字节,然后用当前时间作为后6个字节,组合一个新的GUID。 这样做还是没有顺序啊? 因为前10个字节有时大有时小啊。 但我搜了半天,没有人对此提出异议。我想如果把时间的6个字节加在前面是不是可以呢? 像下面这样:

DECLARE @aGuid UNIQUEIDENTIFIER
SET @aGuid = CAST(
  CAST(GETDATE() AS BINARY(6))
  + CAST(NEWID() AS BINARY(10)) 
  AS UNIQUEIDENTIFIER)

测试了一下,结果不理想,因为后面的10个字节大小不是顺序的,相加依然不是顺序的。

难道是我误入歧途了吗? 谁能指点一下啊。 谢谢了。


   


作者: luckbird   发布时间: 2010-07-01

Guid效率低,能不用还是不要用
说说你必用的理由好么?

作者: thinclient   发布时间: 2010-07-01

SQL code
NEWSEQUENTIALID() 


在指定计算机上创建大于先前通过该函数生成的任何 GUID 的 GUID

作者: OrchidCat   发布时间: 2010-07-01

引用 2 楼 orchidcat 的回复:
SQL code
NEWSEQUENTIALID()


在指定计算机上创建大于先前通过该函数生成的任何 GUID 的 GUID

好!
以下我引用帮助文档,望对你有帮助:
SQL code
NEWSEQUENTIALID() (Transact-SQL)

在启动 Windows 后在指定计算机上创建大于先前通过该函数生成的任何 GUID 的 GUID。在重新启动 Windows 后,GUID 可以再次从一个较低的范围开始,但仍是全局唯一的。在 GUID 列用作行标识符时,使用 NEWSEQUENTIALID 可能比使用 NEWID 函数的速度更快。其原因在于,NEWID 函数导致随机行为并且使用更少的缓存数据页。使用 NEWSEQUENTIALID 还有助于完全填充数据和索引页。
NEWSEQUENTIALID() 不能在查询中引用。

您可以使用 NEWSEQUENTIALID() 生成 GUID 以减少叶级别索引上的页争用。

使用 NEWSEQUENTIALID() 生成的每个 GUID 在该计算机上都是唯一的。仅当源计算机具有网卡时,使用 NEWSEQUENTIALID() 生成的 GUID 在多台计算机上才是唯一的。有关 GUID 的详细信息,请参阅使用 uniqueidentifier 数据。

作者: thinclient   发布时间: 2010-07-01

作者: feixianxxx   发布时间: 2010-07-01

学习了..

作者: wujinyuan   发布时间: 2010-07-01

不能以 GUID 排序,没有顺序的,

一般随机取数据时用newid(),所以他是没有顺序的

作者: xys_777   发布时间: 2010-07-01

[Quote=引用楼主 luckbird 的回复:]
难道是我误入歧途了吗? 谁能指点一下啊。 谢谢了。Quote]
有点

作者: xys_777   发布时间: 2010-07-01

NEWSEQUENTIALID() 我知道,但据说他不能保证重启服务器后得到的还是大小排列的。同时我的项目以后可能移植到ORACLE,不能绑死在SQLSERVER上。

我之所以要用GUID,因为我有多个服务器同时在不同的地域使用,将来这些数据会汇总到同一个服务器上,所以要用GUID,这样不会导致主键重复。 

我希望有人能帮我解答一下,
DECLARE @aGuid UNIQUEIDENTIFIER
SET @aGuid = CAST(CAST(NEWID() AS BINARY(10))  
+ CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER)
这段代码,在很多网站流传,说是可以生成顺序的编码。我认为不可以,但却没有人对此提出过异议。是不是我哪里理解错了。

我不需要绝对顺序的编码,只要大多数是顺序的就可以。

作者: luckbird   发布时间: 2010-07-01

搞定了, DECLARE @aGuid UNIQUEIDENTIFIER
SET @aGuid = CAST(CAST(NEWID() AS BINARY(10))  
+ CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER) 虽然时间是加在后6位,但实际上可能由于高低位倒置的问题,实际上是加在最前面的,这样就影响到排序了。这个方法是对的。

之前没有在 SqlServer 里对生成的结果做比较,仅从表面上来看,感觉后生成的数值小于之前生成的。作比较才发现,确实是按大小顺序生成的。

另外发现奇怪的是,对GUID类型的数据,.NET的比较和SQLSERVER的比较 是不一样的,就是说两个guid1 和 guid2 ,在.net比较,guid2<guid1. 但在SQLSERVER 里比较 guid2>guid1. 这也是比较诡异的地方,不过不影响我的使用,我只要能做到在数据库是从小到大的使用就可以了。




作者: luckbird   发布时间: 2010-07-01

取 系统时间(yyyymmddhhmmsszzz)+guid 做sguid,肯定是有序而且唯一的
只是不同服务器的时间如果不准,还是会顺序不严格,但是唯一是仍然的

作者: sz_haitao   发布时间: 2010-07-01

“同时我的项目以后可能移植到ORACLE,不能绑死在SQLSERVER上。”
如果这样的话,楼主为何不考虑让.net来生成GUID,而不是利用SQLSERVER的内置机制
因为如果在楼主说的sqlserver转oracle的情况,或是sqlserver与oracle共存的情况下
那,,,就会出现.net,sqlserver,oracle三种不同的生成以及比较机制了

作者: zhangweiit   发布时间: 2010-07-01

另外,好像看到过sql2008(?)有一种新的guid,就是有序的。。。。。。。。。

作者: sz_haitao   发布时间: 2010-07-01

引用 11 楼 zhangweiit 的回复:
“同时我的项目以后可能移植到ORACLE,不能绑死在SQLSERVER上。”
如果这样的话,楼主为何不考虑让.net来生成GUID,而不是利用SQLSERVER的内置机制
因为如果在楼主说的sqlserver转oracle的情况,或是sqlserver与oracle共存的情况下
那,,,就会出现.net,sqlserver,oracle三种不同的生成以及比较机制了


是的,参照网上前辈提供的代码,已经实现了同样的算法用.NET来生成顺序的GUID,以后用ORACLE应该也没有问题了(如果ORACLE对guid的大小比较和SqlServer一致的话,应该不会不一样吧?明天搭个环境测试一下)。

作者: luckbird   发布时间: 2010-07-01

恭喜

鼓掌!

作者: claro   发布时间: 2010-07-02

引用 1 楼 thinclient 的回复:

Guid效率低,能不用还是不要用
说说你必用的理由好么?

你为什么说GUID效率低

作者: ws_hgo   发布时间: 2010-07-02

今天又测试了一下,发现对GUID字段oracle的排序规则和SqlServer又是不一样的,Oracle是用RAW(16)类型来保存的。 于是,昨天想好的解决方案又有问题了。

  又经过一些分析和测试,发现SQLSERVER的BINARY(16)和Oracle的排序规则是一样的,因为都是二进制,于是决定在SQLSERVER里不使用UniqueIdentifier类型来保存GUID,而是用Binary(16)来保存,这样就和ORACLE排序规则一样了。

  使用这个规则的话,之前用当前时间替换GUID后六个字节的方法就不对了,因为二进制是以第一个字节开始排序的。 这里需要改为将原来的GUID向右移位6个字节,然后将当前时间作为前6个字节。

  这样的话从数据库取出时是byte[]类型,需要用下面的语句转换一下,将byte数组转为GUID.
Guid gid = new Guid(((byte[]) obj));

上面说得有点复杂了,总结一下其实就是这样的:

1、生成有顺序的GUID: (方法:生成一个GUID, 向右移位6个字节,然后用当前时间作为前6个字节附加上去)
2、在SQLSERVER中,用BINARY(16)来保存GUID,而不是UniqueIdentifier
3、将来在Oracle中用RAW(16)来保存GUID。其实就是在数据库中直接保存GUID的字节数组。





作者: luckbird   发布时间: 2010-07-02

再次过来,学习

作者: xys_777   发布时间: 2010-07-02

只要涉及在不同服务器运行生成,最后又要汇总的情况,你这些方法都无法绝对保证不会重复。虽然重复机率极少极少,但不代表就是零。

所以不能仅依靠时间,还需要加上服务器编号。

作者: theforever   发布时间: 2010-07-02

学习了

作者: xfhtfsp   发布时间: 2011-11-06

该回复于2011-11-06 22:04:04被版主删除

  • 对我有用[0]
  • 丢个板砖[0]
  • 引用
  • 举报
  • 管理
  • TOP
#21楼 得分:0回复于:2011-11-06 22:20:19
看不太懂,请教一个楼主,
"1、生成有顺序的GUID:(方法:生成一个GUID, 向右移位6个字节,然后用当前时间作为前6个字节附加上去)"

难道就是这句吗?
DECLARE @aGuid UNIQUEIDENTIFIER
SET @aGuid = CAST(CAST(NEWID() AS BINARY(10))  
+ CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER)

何来"右移位6个字节,然后用当前时间作为前6个字节附加上去"这样的处理逻辑?

作者: roy_88   发布时间: 2011-11-06

Guid字段不适合建立聚簇索引
本来Guid字段就是个唯一码,基本无实际意义,你硬要给他次序概念,属于没事找抽型

16楼可能实现了,但是我觉得处理后可能失去原来的唯一性,不同机器同一时间产生的GUID经过处理可能增加了重复的几率,所以看起来可以,实际绝不可取。

作者: ap0405140   发布时间: 2011-11-06