本文链接地址:http://blog.chinaunix.net/uid-31396856-id-5792271.html
在数据库设计时,主要就是对实体和关系的设计,实体表现出来就是表,关系表现出来就是外键。主键的简单定义就是表中为每一行数据的唯一标识。其实更准确的说法,每一行数据的唯一标识是候选键(CandidateKey),一个表中可以有很多个候选键,主键是候选键中的一个,主要用于更方便的检索和管理数据。由于主键常常用于检索数据,也用于表之间的关联,所以主键的设计的好坏将会严重影响数据操作的性能。
一、主键的数据类型选择
最常见的主键数据类型是数字类型、固定长度的字符类型和GUID类型。
一般对主键有以下两个要求:
越短越好——越短在一个Page中存储的节点越多,检索速度就越快。
顺序增长——如果每一条插入的数据的主键都比前面的主键大,那么B-Tree上的节点也是顺序增长的,不会造成频繁的B-Tree分割。
总之,主键设计要求字段短是为了查询性能块,顺序增长是为了提高插入速度。查询的时候,数字的查询效率是最高的,比字符类型好。
(1)数字类型:能用短就不用长,但是要结合业务数据的增长量设计。根据数据量决定是用int16还int32或者int64,能用int32的就不需要使用int64。
(2)字符类型:
字符类型基本不满足前面提到的2点要求,字符类型一般不会很短,而且也很可能不是顺序增长的,所以不是特别推荐的主键类型。
如果业务需求要求必须使用字符类型,那么也尽量使用char(XX)而不要使用varchar(XX),因为在RDBMS中,对于定长字符串和变成字符串的数据结构是不一样的,varchar的性能更差。
(3)GUID类型:
guid并不是所有数据库都有对应的数据类型,SQL Server有uniqueidentifier,MySQL没有,orcle可以通过函数sys_guid()获取。GUID类型在是16个字节,不算短,比4个字节的Int32长多了,在使用的时候很消耗CPU。在插入新数据时,GUID一般都是使用NewId()这样的生成随机GUID的方式生成的,所以也不是顺序增长的,在插入速度上不会很快。
但是由于guid相对于数字和字符类型有一定特点,因此在一些特定环境仍然会有使用GUID和字符串来当主键的情况,原因是
(1)相对于数字类型,字符类型更易读易记,在检索关联的数据时,更方便直接。
(2)GUID的优势是全球唯一,也就是说同样的系统,在分布式环境中,那么里面的数据的主键仍然是唯一的,这样有助于数据的集成。典型的例子就是一个系统根据业务需要分区域都部署,每个区域的数据各种录入,互不干扰,然后再把每个区域的数据汇总。
二、使用逻辑主键还是业务主键
前面说到一个表可能有很多个唯一标识的key,那么这么多唯一key中,哪个应该拿来做主键呢?
1、逻辑主键,就是再新建一个独立的字段作为主键,该字段并没有业务含义,只是一个自增列或者流水号,用于唯一标识每一行数据,这是逻辑主键。
2、业务主键,就是选择其中较短较常用的唯一属性作为主键,这是业务主键。
我倾向于是不要使用任何有业务含义的字段作主键,而是使用一个自增的(或者系统生成的)没有实际业务意义的字段作为主键。为什么呢?主要是出于以下考虑:
1、具有业务意义的字段很可能是用户从系统录入的,任何用户的任何输入都有不确定性,只要是用户自己录入的,就有出错的可能性。如果发现录入错误,这个时候再对主键进行修改,可能会涉及到表结构的改变,也可能涉及到大量关联的外键表的修改,这个时候就很麻烦了。
2、具有业务意义的字段虽然在当前是唯一的,是不变的,但是并不能保证随着政策变动、业务调整等原因,导致该业务字段需要修改,以满足新的业务要求,这个时候要修改主键也是很麻烦的事情。比如部门表,我们以部门Code作为主键,但是后来部门变动,Code修改,则系统部门表的主键也得更改。
3、还有一个原因是业务主键在数据录入的时候不一定是明确知道的,有时我们会在不知道业务主键的情况下,就录入其他相关信息,这个时候,如果使用业务主键做数据库的主键,那么数据将无法录入。比如员工表把员工号作为主键,那么员工还没有入职,没有员工号的时候,HR需要先维护一些该预入职员工的信息是不可能的。
三、复合主键和联合主键
(1)复合主键
所谓的复合主键 在一个数据表中通过多个字段作为主键来确定一条记录,那么,多个字段组成的就是复合主键。复合主键联合保证数据的完整性。
(2) 联合主键:就是多个主键联合形成一个主键组合,主键原则上是唯一的,别被唯一值所困扰。 联合主键的意义:当两个数据表形成的是多对多的关系,那么需要通过两个数据表的主键来组成联合主键,就可以确定每个数据表的其中一条记录了。用n个字段(n>=2)来确定一条记录,说明,这n个字段都不是唯一的,但是这n个字段可以分别重复,这么设置的好处,可以很直观的看到某个重复字段的记录条数。
四、主键值的生成
主键值的生成有很多种,每种数据库也不尽然相同,常用几种生成方式:
(1)自增,适用于MySQL、DB2、MS SQL Server,采用数据库生成的主键,用于为long、short、int类型生成唯一标识,完全由数据库管理主键的值。
(2)Sequence对象,DB2、Oracle均支持的序列,用于为long、short或int生成唯一标识,现在SQL Server已支持。主要是在数据库中有一个Sequence对象,通过该对象生成主键。
(3)GUID,这是用于GUID类型的主键,可以使用newid()这种数据库提供的函数,或者使用程序生成Guid并赋值,orcle可以通过函数sys_guid常用的主键生成方式,。
(4)UUID
常见的方式,128位。可以利用数据库也可以利用程序生成,一般来说全球唯一。
(5)max+1
设计主键字段为number类型的,每次录入取得的最大值作为新记录的标示。通过查询表记录的最大值max(*),然后将最大值进行加1,最后将值更新保存。
特点是主键长度可控,移植性较好 ,但是这种情况并发写可能会造成主键冲突,对并发也不太好控制 。
(6)单独建一个存放主键的表.
特点是:实现简单,移植性较好 ,但是需要考虑并发问题,整个系统主键生成都依赖该表,性能影响可能较大。
(7)使用时间戳+随机数
特点是:实现简单,与数据库无关,移植性较好 。但是长度太长,最少也得15位以上,不仅占空间并且建索引的话性能会比较差点。
(8)Hilo值,这是一种使用高低位算法生成的数字值的主键。该值由NHibernate程序内部生成。
使用一个高/低位算法生成的long、short或int类型的标识符,给定一个表和字段作为高位值的来源,默认的表是hibernate_unique_key,默认的字段是next_hi。它将id的产生源分成两部分,DB+内存,然后按照算法结合在一起产生id值,可以在很少的连接次数内产生多条记录,提高效率
(9)Twitter的snowflake算法
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。snowflake算法可以根据自身项目的需要进行一定的修改。比如估算未来的数据中心个数,每个数据中心的机器数以及统一毫秒可以能的并发数来调整在算法中所需要的bit数。
(10)其他程序赋值,完全由程序根据自己的算法生成并赋值。
---The end