去年做了一个产品,会经常导入导出大量的外部数据,这些数据的ID有的是GUID类型,有的是字符串,也有的是自增。GUID类型没有顺序,结果要排序得借助其它业务字段,整体查询效率比较低;字符串ID本来是用来转换GUID的或者数字ID的,结果有些字符串ID不符合规范,常常有特殊数据需要处理;自增主键ID的数据导入合并经常有冲突。

为了避免GUID主键的“索引页分裂”问题,提高查询效率,同时为了解决分布式环境下的数据导入合并问题,强烈需要一种分布式的,有序的ID生成方案。我参考了雪花ID(Twitter-Snowflake,64位自增ID算法)实现方案,设计一个更容易肉眼观察数值连续有序的分布式ID方案。

跟雪花ID方案一样,都是使用时间数据做为生成ID的基础,不同的在于对数据的具体处理方式。另外,为了确保每台机器ID的不同,可以配置指定此ID,在应用程序配置文件中如下配置:

<!--分布式ID标识,3位整数,范围101-999 大小-->
<add key="SOD_MachineID" value="101"/>

如果不配置分布式ID,默认将根据当前机器IP随机生成3位分布式机器ID。

该算法的实现比雪花算法简单不少,详细的不多说,先直接看代码:

        /// <summary>
/// 获取一个新的有序GUID整数
/// </summary>
/// <param name="dt">当前时间</param>
/// <param name="haveMs">是否包含毫秒,如果不包含,将使用3位随机数代替</param>
/// <returns></returns>
protected internal static long InnerNewSequenceGUID(DateTime dt, bool haveMs)
{
//线程安全的自增并且不超过最大值10000
int countNum = System.Threading.Interlocked.Increment(ref SeqNum);
if (countNum >= )
{
while (Interlocked.Exchange(ref signal, ) != )//加自旋锁
{
//黑魔法
}
//进入临界区
if (SeqNum >= )
{
SeqNum = ;
//达到1万个数后,延迟10毫秒,重新取系统时间,避免重复
Thread.Sleep();
dt = DateTime.Now;
}
countNum = System.Threading.Interlocked.Increment(ref SeqNum);
//离开临界区
Interlocked.Exchange(ref signal, ); //释放锁
} //日期以 2017.3.1日为基准,计算当前日期距离基准日期相差的天数,可以使用20年。
//日期部分使用4位数字表示
int days = (int)dt.Subtract(baseDate).TotalDays;
//时间部分表示一天中所有的秒数,最大为 86400秒,共5位
//日期时间总位数= 4(日期)+5(时间)+3(毫秒)=12
int times = dt.Second + dt.Minute * + dt.Hour * ;
//long 类型最大值 9223 3720 3685 4775 807
//可用随机位数= 19-12=7
long datePart = ((long)days + ) * * * * ;
long timePart = (long)times * * ;
long msPart = ;
if (haveMs)
{
msPart = (long)dt.Millisecond ;
}
else {
msPart = new Random().Next(, );
}
long dateTiePart = (datePart + timePart + msPart*) * ; int mid = MachineID * ;
//得到总数= 4(日期)+5(时间)+3(毫秒)+7(GUID)
long seq = dateTiePart + mid; return seq + countNum; ;
}

注意:上面使用了一个模拟的自旋锁,用来在末尾的顺序号超过1万的时候归零重新计算,并且睡眠10毫秒从而根本上杜绝重复ID。

每秒不重复ID生成数:

从上面的程序代码中,得知 ID总数= 4位(日期)+5位(时间)+3位(毫秒)+7位(GUID)。
其中,7位(GUID)中,除去前3位的分布式机器ID,剩余4位有序数字,可以表示1万个数字。
所以,该方面每毫秒最大可以生成1万个不重复的ID数,每秒最大可以生成1千万个不重复ID。
当然这是理论大小,实际上受到当前机器的计算能力限制。

该方法进行了再次封装,用于在不同情况下分别使用:

      /// <summary>
/// 生成一个新的在秒级别有序的长整形“GUID”,在一秒内,数据比较随机,线程安全,
/// 但不如NewUniqueSequenceGUID 方法结果更有序(不包含毫秒部分)
/// </summary>
/// <returns></returns>
public static long NewSequenceGUID()
{
return UniqueSequenceGUID.InnerNewSequenceGUID(DateTime.Now,false);
} /// <summary>
/// 生成一个唯一的更加有序的GUID形式的长整数,在一秒内,一千万个不重复ID,线程安全。可用于严格有序增长的ID
/// </summary>
/// <returns></returns>
public static long NewUniqueSequenceGUID()
{
return UniqueId.NewID();
} /// <summary>
/// 当前机器ID,可以作为分布式ID,如果需要指定此ID,请在应用程序配置文件配置 SOD_MachineID 的值,范围大于100,小于1000.
/// </summary>
/// <returns></returns>
public static int CurrentMachineID()
{
return UniqueSequenceGUID.GetCurrentMachineID();
}

最后,像下面这样使用即可:

        Console.WriteLine("当前机器的分布式ID:{0}",CommonUtil.CurrentMachineID());
Console.WriteLine("测试分布式ID:秒级有序");
for (int i= ; i < ; i++)
{
Console.Write(CommonUtil.NewSequenceGUID());
Console.Write(",");
}
Console.WriteLine();
Console.WriteLine("测试分布式ID:唯一且有序");
for (int i = ; i < ; i++)
{
Console.Write(CommonUtil.NewUniqueSequenceGUID());
Console.Write(",");
}
Console.WriteLine();

下面是生成的ID数字示例:

当前机器的分布式ID:832
测试分布式ID:秒级有序
1460532991258320201,1460532991258320202,1460532991258320203,1460532991258320204,1460532991258320205,
1460532991258320206,1460532991258320207,1460532991258320208,1460532991258320209,1460532991258320210,
1460532991258320211,1460532991258320212,1460532991258320213,1460532991258320214,1460532991258320215,
1460532991258320216,1460532991258320217,1460532991258320218,1460532991258320219,1460532991258320220,
1460532991258320221,1460532994488320222,1460532994488320223,1460532994488320224,1460532994488320225,
1460532994488320226,1460532994488320227,1460532994488320228,1460532994488320229,1460532994488320230,
1460532994488320231,1460532994488320232,1460532994488320233,1460532994488320234,1460532994488320235,
1460532994488320236,1460532994488320237,1460532994488320238,1460532994488320239,1460532994488320240,
1460532994488320241,1460532994488320242,1460532994488320243,1460532994488320244,1460532994488320245,
1460532994488320246,1460532994488320247,1460532994488320248,1460532993018320249,1460532993018320250,
测试分布式ID:唯一且有序
1460532997708320251,1460532997708320252,1460532997718320253,1460532997718320254,1460532997718320255,
1460532997728320256,1460532997728320257,1460532997728320258,1460532997738320259,1460532997738320260,
1460532997788320261,1460532997788320262,1460532997788320263,1460532997838320264,1460532997838320265,
1460532997838320266,1460532997838320267,1460532997858320268,1460532997858320269,1460532997858320270,
1460532997858320271,1460532997868320272,1460532997868320273,1460532997868320274,1460532997878320275,
1460532997878320276,1460532997878320277,1460532997878320278,1460532997888320279,1460532997888320280,
1460532997888320281,1460532997888320282,1460532997898320283,1460532997908320284,1460532997918320285,
1460532997918320286,1460532997918320287,1460532997918320288,1460532997928320289,1460532997928320290,
1460532997928320291,1460532997928320292,1460532997938320293,1460532997938320294,1460532997948320295,
1460532997948320296,1460532997948320297,1460532998028320298,1460532998028320299,1460532998068320300,
 

注:本文生成ID的方法已经在产品中大量使用,运行情况良好。

要使用本程序,你可以Nuget 下载SOD的程序包(支持.NET 2.0项目),然后像本文示例这样使用即可:

Install-Package PDF.NET.SOD.Core

获取SOD的源码,请Fork我们的Github:

https://github.com/znlgis/sod

源码位置在 https://github.com/znlgis/sod/tree/master/src/SOD 目录下。

有疑问,请加QQ群154224970 咨询,感谢大家支持SOD框架!

每秒生成一千万个【可视有序】分布式ID的简单方案的更多相关文章

  1. 美团技术分享:深度解密美团的分布式ID生成算法

    本文来自美团技术团队“照东”的分享,原题<Leaf——美团点评分布式ID生成系统>,收录时有勘误.修订并重新排版,感谢原作者的分享. 1.引言 鉴于IM系统中聊天消息ID生成算法和生成策略 ...

  2. 美团分布式ID生成框架Leaf源码分析及优化改进

    本文主要是对美团的分布式ID框架Leaf的原理进行介绍,针对Leaf原项目中的一些issue,对Leaf项目进行功能增强,问题修复及优化改进,改进后的项目地址在这里: Leaf项目改进计划 https ...

  3. 分布式ID生成方法-趋势有序的全局唯一ID

    一.需求缘起 几乎所有的业务系统,都有生成一个记录标识的需求,例如: (1)消息标识:message-id (2)订单标识:order-id (3)帖子标识:tiezi-id 这个记录标识往往就是数据 ...

  4. 细聊分布式ID生成方法

    细聊分布式ID生成方法 https://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=403837240&idx=1&sn=ae9 ...

  5. 高并发之 - 全局有序唯一id Snowflake 应用实战

    前言 本篇主要介绍高并发算法Snowflake是怎么应用到实战项目中的. 对于怎么理解Snowflake算法,大家可以从网上搜索‘Snowflake’,大量资源可供查看,这里就不一一详诉,这里主要介绍 ...

  6. 【58沈剑架构系列】细聊分布式ID生成方法

    一.需求缘起 几乎所有的业务系统,都有生成一个记录标识的需求,例如: (1)消息标识:message-id (2)订单标识:order-id (3)帖子标识:tiezi-id 这个记录标识往往就是数据 ...

  7. 分布式ID生成方案

    系统唯一ID是设计一个系统的时候常常会遇到的问题,也常常为这个问题而纠结. 生成ID的方法有很多,适应不同的场景.需求以及性能要求.所以有些比较复杂的系统会有多个ID生成的策略. 0. 分布式ID要求 ...

  8. 160302、细聊分布式ID生成方法

    一.需求缘起 几乎所有的业务系统,都有生成一个记录标识的需求,例如: (1)消息标识:message-id (2)订单标识:order-id (3)帖子标识:tiezi-id 这个记录标识往往就是数据 ...

  9. 搞懂分布式技术12:分布式ID生成方案

    搞懂分布式技术12:分布式ID生成方案 ## 转自: 58沈剑 架构师之路 2017-06-25 一.需求缘起 几乎所有的业务系统,都有生成一个唯一记录标识的需求,例如: 消息标识:message-i ...

随机推荐

  1. 1.6 Why only in China?

    Android plug-in is in full swing in China, why is it silent in foreign countries? The applications o ...

  2. Python爬虫7-Cookie & Session

    GitHub代码练习地址:1.手动利用cookie访问网页:https://github.com/Neo-ML/PythonPractice/blob/master/SpiderPrac10_cook ...

  3. Node.js(day4)

    一.一些小问题 1.文件操作路径和模块读取路径的问题 我们使用fs核心模块系统进行文件操作时一般这样书写路径 fs.readFile('./views/index.html');//读取views目录 ...

  4. [Swift]LeetCode228. 汇总区间 | Summary Ranges

    Given a sorted integer array without duplicates, return the summary of its ranges. Example 1: Input: ...

  5. [Swift]LeetCode938. 二叉搜索树的范围和 | Range Sum of BST

    Given the root node of a binary search tree, return the sum of values of all nodes with value betwee ...

  6. 白话说java gc垃圾回收

    gc是java区别于其他好几门语言(c/c++)的一个代表功能(当然也有很多可以自动管理内存的语言,如所有的脚本语言,你根本不知道内存管理这回事)! 当然,之所以要把c/c++和java相比,是因为j ...

  7. 关于CGI 和 PHP-FPM需要弄清的

    https://blog.csdn.net/gao_yu_long/article/details/79390510 补充一点: 如果php-cgi -b 127.0.0.1:6999,则6999端口 ...

  8. Python爬虫入门教程 5-100 27270图片爬取

    27270图片----获取待爬取页面 今天继续爬取一个网站,http://www.27270.com/ent/meinvtupian/ 这个网站具备反爬,so我们下载的代码有些地方处理的也不是很到位, ...

  9. jar文件和aar文件的区别

    1.   *.jar,JAR 文件就是 JavaArchive File,顾名思意,它的应用是与 Java 息息相关的,是 Java 的一种文档格式.只包含了class文件与清单文件 ,不包含资源文件 ...

  10. CentOs~程序部署那些事

    永久更新中…… 主要说一下在centos里,在安装程序和监控程序时,用到的一些常用的命令,希望可以帮到大家! 远程安装程序包:yum install 程序包名 下载程序包:wget 程序包地址 解压t ...