GUID 是个根据网络卡MAC Address 及时间等因素随机产生的 128 Bits(=16 Bytes) 数字, 因为 2 的128 次方是个极大的数目, 因此发生重复的机率非常非常.... 低 (乐观地说: 乐透中奖的机率远远高于 GUID 重复, 我们可以假设 "不可能" 发生)!
摘录几个网络上流传的例子来说明 GUID 如何地"不太可能" 发生重复: 一. The probability of an accidental match is (theoretically - and I use that term guardedly) the same as throwing a set of 128 pennies,and having them all land in the same heads/tails combination twice.(把128 个硬币往上拋出落地后, 所有硬币必须出现两次完全相同 "人头" 与 "数字" 的组合) 二. 如果一台机器每秒钟产生10,000,000 个GUID, 则可以保证(机率意义上) 3,240 年不会发生重复! 三. 全世界有60 亿人口, 每个人每秒分配10 亿个号码, 那么需要分配1800 亿年! 反正等到地球毁灭了都不会用完的! 把 GUID 以字符串形式表达就会像 "12345678-1234-1234-1234-123456789012" (4 Bits 代表一个字符, 则有32 个字符, 加上4 条短线共有36 个字符的长度)! The challenges of designing database. 在传统信息系统的数据库端普遍存在几个设计上的难题, 为了解决这些题目, 开发团队经常需要付出较高代价! 这些难题包括: 一. ID (在此指概指在资料表中用来代表资料唯一性的字段) 值是否允许变更(例如: 部门编号, 产品编号, .....) ? ID 字段总是被使用与其它资料表关联, 因此, 需要先判断关联是否已经存在, 以做为 ID 字段可否变更的依据! 当 ID 字段关联的对象数目过多时, 需要在判断上付出更多成本! 二. 由复合字段形成资料表关联时, 是否需要对更多字段进行变更前的判断或处理? 复合字段组成索引的典型之一是单据明细 (或称 "分录", 或指 Detail data, 其主索引可能由单据编号及明细序号(流水号) 组成)! 当某笔明细资料前方插入或是删除另一笔明细时, 原有的明细序号是否需要变更? 不变更时明细序号是否连号且不重复? 变更后是否会破坏原有与其它资料的关联? 这可能是个取舍两难的问题: 由于明细资料前方插入或删除另一笔明细资料时, 在后方的所有明细资料的序号 "被迫" 递增或递减 (就像棒球在满垒状况下, 触身球会造成三垒跑者被 "强迫" 挤回本垒得分), 则索引值会发生变化, 而系于该索引上的关联就会错乱! 三. 无 ID 或主索引字段的资料, 能否与其它资料建立关联? 在自然情况下有些资料并不需要 ID 字段 (或是 ID 字段值并无实质意义), 例如: 1. 每天例行发出的提醒信息可能并不需要 ID 值, 但是, 当使用者从清单中选择一笔资料时, 系统如何明确地找到被指定的提醒明细呢? 2. 展开MPS 是一个批次作业, 其过程中会有许多的记录临时被产生或是合并! 在每一笔过渡记录产生时, 并不需要具有编码规则的 ID 字段, 但是在整合同料号同时段的生产需求时, 如何能够正确地标记已被合并的数据呢? 归纳以上的难题, 我们可以发现需求的核心就是: 能否为数据库里的所有记录, 赋予一个: (1)简单, (2)独一无二, (3)不会改变的"身份识别"? How does GUID change the world ? GUID 的唯一性完全满足我们的需求, 因此, 我们可以一个 GUID 字段做为代理键, 以取代自然键 (通常为资料表中"ID" 字段, 或是复合字段组成的主要索引), 并化解实务上遭遇的问题: 一. GUID 隔离了自然键以及关联的资料表: 自然键是可以被检视的, 若自然键为ForeignKey 且有对应的关联资料, 为了保持资料的一致性, 自然键值不应被异动或删除! 这样的保护控制大都需要额外的程序代码去处理! 代理键通常并不具字面上的意涵(例如: GUID), 因此, 在检视资料时并不会显现该字段, 所以代理键在产生后, 该值并无机会发生改变! 以代理键替代自然键成为资料表关联的主角, 自然键相对地成为类似"备忘" 性质的字段! 由于代理键被隐藏而不会被变更, 不论自然键如何修改, 根源于代理键所形成的数据关联永远稳定地存在! 二. GUID 替代复合字段的自然主键: 当自然主键是由多个字段组合而成时, 要撰写资料关联的SQL command 就显得有点烦人! 运用 GUID 字段进行关联, 可以轻易地使 SQL 化繁为简! 另外, 当自然键由复合字段组成时, 有更多字段被同时使用于资料关联, 相对地, 需要对字段组合进行更多判断或是限制变更! 这样的控制需要付出偏高的代价, 实务上, 也不尽然可以进行控制, 例如: 因为其它单据明细的增减, 会 "强迫" 部份单据明细自然键里的序号字段递增/ 递减! 若以 GUID 做为代理键担任资料表的关联角色, 单据明细的 "序号" 字段因不涉及关联, 即可依据实务上的需求被变更, 并不需要额外的判断或控制! 三. 无自然键的资料表由 GUID 代理主键的功能: 在无自然主键的资料表建立 GUID 代理键后, 每一笔记录即刻具备可供识别 "身份" 的信息! 即使后来因为需求变更而必须存取该资料表中特定的记录时, GUID 代理键的存在足以满足这类的变动 (类似 "防震" 效果)! The actual usage of GUID 一. 在多对多的关联里替代复合字段以简化对应: 如: 传票分录的立冲 (传票明细多对多立冲), 借还款的冲销 (多对多冲销), ..... 等, 可由 GUID 代理键进行关联, 即可依 GUID 合并后统计冲销金额! 二. 在临时产生的资料中提供精确的识别: 如: 有些复杂报表需要额外统计或运算, 当这些运算不需以程序代码循环处理, 即可以SQL command 直接在该报表的暂存数据表批次运算(其实多数报表均为如此)! 当多人同时检视相同报表 (检视条件并不相同), 即可运用 GUID 做为该暂存资料表中的识别! 每个使用者每次检视报表即为一个 Task (TaskGUID), 报表运算后只回传 TaskGUID 相符的数据! 几种高阶的开发语言 (包括: .NET, VB, Delphi, PowerBuilder, ....) 均已包装或是实现呼叫 Windows API 以产生 GUID 字符串的方法了, 所以这个 GUID 值可以由程序代码自行产生! Microsoft SQLServer 也提供了取得 GUID 字符串的函式 NewID; 还有未公开的 Stored procedure: sp_MSforeachtable, 它可以对数据库中所有的资料表进行统一的操作! 结合 sp_MSforeachtable 与 NewID, 我们可以在几秒钟内对所有资料表统一添加名为 "GUID" 的字段(实例如下), 开始让 GUID 做为资料搜寻或关联的根据! /* 先指定要添加 GUID 字段的数据库后, 直接执行下列语法 */ sp_MSforeachtable @command1='Alter TABLE ? ADD [GUID] VarChar(36) NOT NULL Constraint [?_DF_GUID] DEFAULT (newid()) Constraint [?_IX_GUID] UNIQUE NonClustered' What's matter with GUID? 由于做为索引字段(不论是Clustered Index/ Nonclustered Index) 的GUID 长度为36 Bytes, 相较于其它的自然键或自动编号(整数) 均显得过长, 使得索引页可容纳的信息较少, 必须搜寻较多的索引页次才能查询所需要的结果, 因此, 各个网络讨论区对于 GUID 在效能方面的表现是颇有质疑的! 不过, 数据库系统的效能包括许多因素, 包括: 系统设计理念, 资料表切割, 索引设定, SQL command 撰写, 内存配置, IO 设备, ..... 等 (排行愈前者影响愈大), 效能调整需要全面的协调与改善, GUID 不会是运行效能的瓶颈肇因! 市场上已存在着全面运用 GUID 代理键的商用软件, 被中小企业广泛采用的 Microsoft.SharePoint 系统也是如此! 由于运用 GUID 做为代理键, 许多因为自然键直接关联的难题会因为 GUID 的隔离而被优雅地化解, 原自然键上的复杂控制均可省略, 资料表之间的关联变得单纯, 数据库系统的运作也更稳定! |