数据库主键分案
一.数据库自带的自增主键
在传统单体数据库中,并且并发量不高的情况下,可以使用数据库自带的自增主键。但是它不能保持连续递增,只保证单调递增,也就是说自增主键值可能是:1,2,3,5 没有自增主键值4,这可能是因为事务回滚。
优点:
简单,代码方便,性能可以接受。数字ID天然排序,对分页或者需要排序的结果很有帮助。
缺点:
分布式数据库的分库分表的时候会有麻烦,假设有两台mysql节点,主键自增值节点1是:1 3 5,节点2是: 2,4,6。但确定好集群节点后,难于扩展。
单体数据库在高并发场景下,每个事务都要去申请主键,数据库如果无法及时处理,自增主键就会成为瓶颈,此时自增主键已经不能解决问题了,往往需要在应用系统上做些优化。
二.Reids生成全局ID
因为Redis是单线的,天生保证原子性,可以使用Redis的原子操作 INCR和INCRBY来实现。对于高并发获取redis的自增ID,可以使用list列表结构来实现队列存储,比如一次生成10w(1,2,3..,100000)个id值存储到list,新增每次需要自增id时,取队列一个。用完了队列中的自增ID,再一次生成100001~200000个。
优点:
不依赖于数据库,灵活方便,且性能优于数据库。数字ID天然排序,对分页或者需要排序的结果很有帮助。如果是订单号生成,可以采用日期+自增ID。
缺点:
需要编码和配置的工作量比较大,需要联网保证redis可用。
注意:
使用自增问题可能带来“尾部热点”,因为在分布式数据库中,都使用了分片,如果是自增,那写入的数据往往集中在一个节点的range范围内,其它节点空闲着。
如果使用Hash分片,写入的数据会被分散到多个range中。主流产品的默认方案是保持 Range 分片,放弃自增主键,转而用随机主键来代替。
随机主键方案如:UUID,内置Radom ID,外置雪花算法。
如果是使用mycat的代理服务器,内置Radom ID。
三.UUID
优点:
简单,代码方便, 生成ID性能非常好,基本不会有性能问题,全球唯一,对分布式数据库或者在遇见数据迁移,系统数据合并,或者数据库变更等情况下,可以从容应对。
缺点:
没有排序,无法保证趋势递增,UUID使用字符串存储,查询的效率比较低,存储空间比较大,写入影响性能(因为要维护索引),如果是海量数据库,就需要考虑存储量的问题,传输数据量大。键值长度过长,达到了 128 位,因此存储和计算的代价都会增加。
uuid做主键不具备业务意义。
其它:
Guid.NewGuid().ToString()=> 36个字符(带连字符的)
输出:12345678-1234-1234-1234-123456789abc
Guid.NewGuid().ToString("N")=> 32个字符(仅数字)
输出:12345678123412341234123456789abc
为了支持排序功能,表中添一个创建时间字段。
四、基于时间戳+随机数方式来生成唯一ID
基于时间戳:DateTime.Now.ToString("yyyyMMddHHmmssfffffff")—这种情况很容易出现重复的编号。
基于时间戳+随机数:DateTime.Now.ToString("yyyyMMddHHmmssfffffff")+Random随机数。
这种方式比较适合针对单体应用并发不高的业务系统,生成方式并不是严格意义上的唯一ID。
五.雪花算法
Twitter的snowflake(雪花)算法,跟UUID一样不用联网。snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:
高位随机+毫秒数+机器码(数据中心+机器id)+10位的流水号码。
优点:
可以用snowflake来代替UUID,弥补 UUID 存在的不足,因为它不仅算法简单易实现,也满足 ID 所需要的全局唯一性,单调递增性,还包含一定的业务上的意义。
缺点:
最大的缺点就是它依赖于系统的时间戳,一旦系统时间不准,就有可能生成重复的 ID。所以如果我们发现系统时钟不准,就可以让发号器暂时拒绝发号,直到时钟准确为止。
实现方案:
1)嵌入到业务代码里,也就是分布在业务服务器中
这种方案的好处是业务代码在使用的时候不需要跨网络调用,性能上会好一些。但是就需要更多的机器 ID 位数来支持更多的业务服务器。另外,由于业务服务器的数量很多,我们很难保证机器 ID 的唯一性,所以就需要引入 ZooKeeper 等分布式一致性组件来保证每次机器重启时都能获得唯一的机器 ID。
2)作为独立的发号器服务部署
业务在使用发号器的时候就需要多一次的网络调用,但是内网的调用对于性能的损耗有限,却可以减少机器 ID 的位数,如果发号器以主备方式部署,同时运行的只有一个发号器,那么机器 ID 可以省略,这样可以留更多的位数给最后的自增信息位。即使需要机器 ID,因为发号器部署实例数有限,那么就可以把机器 ID 写在发号器的配置文件里,这样即可以保证机器 ID 唯一性,也无需引入第三方组件了。
微博和美图都是使用独立服务的方式来部署发号器的,性能上单实例单 CPU 可以达到两万每秒。
Github地址: https://github.com/twitter-archive/snowflake
snowflake生产的ID是一个18位的long型数字。转换为字符串长度为18位.
https://github.com/RobThree/IdGen .net 使用