SQL Server数据库高级进阶之分布式唯一ID生成实战演练
一、背景需求
当我们需要在多个数据库间进行数据的复制自动增长型字段可能造成数据合并时的主键冲突。设想一个数据库中的Order表向另一个库中的Order表复制数据库时,OrderID到底该不该自动增长呢?
数据库自增长ID和无序的UUID方案的不足之处:
1)、采用数据库自增序列:数据迁移合并等比较麻烦。
2)、UUID随机数:采用无意义字符串,没有排序UUID使用字符串形式存储,数据量大时查询效率比较低。(主要是索引查询销量不是最高的)
如果非要使用非自主增长列作为主键的话(分布式系统分库分表中),推使用有序UUID和有序的整长的Rowid(雪花算法snowflake和MongoDB之ObjectId)。
参考资料:为什么要使用自增ID作为主键 - Mr-blue - 博客园
https://www.cnblogs.com/lanqi/p/10185172.html
二、SQL Server分布式唯一ID生成实战演练
唯一ID可以标识数据的唯一性,在分布式系统中生成唯一ID的方案有很多,常见的方式大概有以下三种:
2.1、依赖数据库,使用SQL SERVER无序UUID和有序UUID。
1)、无序UUID:
SELECT newid() --生成36位的GUID
SELECT REPLACE(newid(), '-', '') -- 生成32 位的GUID
2)、有序UUID:
SQLServer 2005已经解决了这个问题,使用的是NEWSEQUENTIALID()
create table jobs
(
id UNIQUEIDENTIFIER ROWGUIDCOL PRIMARY KEY NOT NULL
CONSTRAINT [DF_jobs_id] DEFAULT (NEWSEQUENTIALID()),
account varchar(64) not null,
password varchar(64) not null
)
go
insert jobs (account,password) values ('tudou','123')
insert jobs (account,password) values ('ntudou','123')
insert jobs (account,password) values ('atudou','123')
insert jobs (account,password) values ('btudou','123')
insert jobs (account,password) values ('ctudou','123')
select * from jobs
参考资料:
SQL Server 的 主键 解决方案 NEWID() , 自增ID - 王占波 - 博客园 https://www.cnblogs.com/wangzhanbo/articles/8807125.html
2.2、无序随机UUID和有序UUID。
1)、无序UUID:
string guid = Guid.NewGuid().ToString();
string guid = Guid.NewGuid().ToString("N");
缺点:索引性能差。
2)、有序UUID:
https://www.cnblogs.com/lovewl2/p/10334987.html
https://www.cnblogs.com/shiningrise/p/5690016.html
三、.NET Core分布式唯一ID常见的几种生成方式
唯一ID划分需要根据单体应用还是分布式应用来进行区分。特别是在分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。
1、基于时间戳+随机数方式来生成唯一ID
基于时间戳:DateTime.Now.ToString("yyyyMMddHHmmssfffffff")—这种情况很容易出现重复的编号。
基于时间戳+随机数:DateTime.Now.ToString("yyyyMMddHHmmssfffffff")+Random随机数。
这种方式比较适合针对单体应用并发不高的业务系统,生成方式并不是严格意义上的唯一ID。
2、C#仿造Snowflake雪花算法设计
有这么一种说法,自然界中并不存在两片完全一样的雪花的。每一片雪花都拥有自己漂亮独特的形状、独一无二。雪花算法也表示生成的ID如雪花般独一无二。而twitter的snowflake解决了这种需求。
snowflake是twitter开源的分布式ID生成算法,其核心思想是:一个long型的ID,使用其中41bit作为毫秒数,10bit作为机器编号,12bit作为毫秒内序列号。这个算法单机每秒内理论上最多可以生成1000*(2^12),也就是400W的ID,完全能满足业务的需求。关于雪花算法的组成部分:
雪花算法会生成一个64位的二进制数据,为一个Long型。(转换成字符串后长度最多19位) ,其基本结构:
![](https://upload-images.jianshu.io/upload_images/1676984-e6454dc3e335dee6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
第一位:为未使用
第二部分:41位为毫秒级时间(41位的长度可以使用69年)
第三部分:5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点)
第四部分:最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)
snowflake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和workerId作区分),并且效率较高。经测试snowflake每秒能够产生26万个ID。
C# 分布式自增ID算法snowflake(雪花算法) - 五维思考 - 博客园
https://www.cnblogs.com/zhaoshujie/p/12010052.html
3、C#仿造mongodb的分布式主键ObjectId设计
MongoDB中_id(ObjectId)组成的12个字节按照如下方式生成
![](https://upload-images.jianshu.io/upload_images/1676984-63f90c6397ed0798.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
前四位是时间戳,可以提供秒级别的唯一性。
接下来三位是所在主机的唯一标识符,通常是机器主机名的散列值。
接下来两位是产生 ObjectId 的 PID,确保同一台机器上并发产生的 ObjectId 是唯一的。
前九位保证了同一秒钟不同机器的不同进程产生的 ObjectId 时唯一的。
最后三位是自增计数器,确保相同进程同一秒钟产生的 ObjectId 是唯一的。
作者:阿笨
【官方QQ一群:跟着阿笨一起玩NET(已满)】:422315558
【官方QQ二群:跟着阿笨一起玩C#(已满)】:574187616
【官方QQ三群:跟着阿笨一起玩ASP.NET(已满)】:967920586
【官方QQ四群:Asp.Net Core跨平台技术开发(可加入)】:829227829
【官方QQ五群:.NET Core跨平台开发技术(可加入)】:647639415
【网易云课堂】:https://study.163.com/provider/2544628/index.htm?share=2&shareId=2544628
【腾讯课堂】:https://abennet.ke.qq.com
【51CTO学院】:https://edu.51cto.com/sd/66c64
【微信公众号】:微信搜索:跟着阿笨一起玩NET