2.3 基于算法实现 【转载】

  这里介绍下Twitter的Snowflake算法——snowflake,它把时间戳,工作机器id,序列号组合在一起,以保证在分布式系统中唯一性和自增性。

  snowflake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞,在同一毫秒内最多可以生成 1024 X 4096 = 4194304个全局唯一ID。

  优点:不依赖数据库,完全内存操作速度快

  缺点:不同服务器需要保证系统时钟一致

  snowflake的C#版本的简单实现:

    public class SnowflakeIdWorker
{
/// <summary>
/// 开始时间截
/// 1288834974657 是(Thu, 04 Nov 2010 01:42:54 GMT) 这一时刻到1970-01-01 00:00:00时刻所经过的毫秒数。
/// 当前时刻减去1288834974657 的值刚好在2^41 里,因此占41位。
/// 所以这个数是为了让时间戳占41位才特地算出来的。
/// </summary>
public const long Twepoch = 1288834974657L; /// <summary>
/// 工作节点Id占用5位
/// </summary>
const int WorkerIdBits = 5; /// <summary>
/// 数据中心Id占用5位
/// </summary>
const int DatacenterIdBits = 5; /// <summary>
/// 序列号占用12位
/// </summary>
const int SequenceBits = 12; /// <summary>
/// 支持的最大机器Id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
/// </summary>
const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits); /// <summary>
/// 支持的最大数据中心Id,结果是31
/// </summary>
const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits); /// <summary>
/// 机器ID向左移12位
/// </summary>
private const int WorkerIdShift = SequenceBits; /// <summary>
/// 数据标识id向左移17位(12+5)
/// </summary>
private const int DatacenterIdShift = SequenceBits + WorkerIdBits; /// <summary>
/// 时间截向左移22位(5+5+12)
/// </summary>
public const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits; /// <summary>
/// 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
/// </summary>
private const long SequenceMask = -1L ^ (-1L << SequenceBits); /// <summary>
/// 毫秒内序列(0~4095)
/// </summary>
private long _sequence = 0L; /// <summary>
/// 上次生成Id的时间截
/// </summary>
private long _lastTimestamp = -1L; /// <summary>
/// 工作节点Id
/// </summary>
public long WorkerId { get; protected set; } /// <summary>
/// 数据中心Id
/// </summary>
public long DatacenterId { get; protected set; } /// <summary>
/// 构造器
/// </summary>
/// <param name="workerId">工作ID (0~31)</param>
/// <param name="datacenterId">数据中心ID (0~31)</param>
public SnowflakeIdWorker(long workerId, long datacenterId)
{
WorkerId = workerId;
DatacenterId = datacenterId; if (workerId > MaxWorkerId || workerId < 0)
{
throw new ArgumentException(String.Format("worker Id can't be greater than {0} or less than 0", MaxWorkerId));
}
if (datacenterId > MaxDatacenterId || datacenterId < 0)
{
throw new ArgumentException(String.Format("datacenter Id can't be greater than {0} or less than 0", MaxDatacenterId));
}
} private static readonly object _lockObj = new Object(); /// <summary>
/// 获得下一个ID (该方法是线程安全的)
/// </summary>
/// <returns></returns>
public virtual long NextId()
{
lock (_lockObj)
{
//获取当前时间戳
var timestamp = TimeGen(); //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
if (timestamp < _lastTimestamp)
{
throw new InvalidOperationException(String.Format(
"Clock moved backwards. Refusing to generate id for {0} milliseconds", _lastTimestamp - timestamp));
} //如果是同一时间生成的,则进行毫秒内序列
if (_lastTimestamp == timestamp)
{
_sequence = (_sequence + 1) & SequenceMask;
//毫秒内序列溢出
if (_sequence == 0)
{
//阻塞到下一个毫秒,获得新的时间戳
timestamp = TilNextMillis(_lastTimestamp);
}
} //时间戳改变,毫秒内序列重置
else
{
_sequence = 0;
} //上次生成ID的时间截
_lastTimestamp = timestamp; //移位并通过或运算拼到一起组成64位的ID
return ((timestamp - Twepoch) << TimestampLeftShift) |
(DatacenterId << DatacenterIdShift) |
(WorkerId << WorkerIdShift) | _sequence;
}
} /// <summary>
/// 生成当前时间戳
/// </summary>
/// <returns>毫秒</returns>
private static long GetTimestamp()
{
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
} /// <summary>
/// 生成当前时间戳
/// </summary>
/// <returns>毫秒</returns>
protected virtual long TimeGen()
{
return GetTimestamp();
} /// <summary>
/// 阻塞到下一个毫秒,直到获得新的时间戳
/// </summary>
/// <param name="lastTimestamp">上次生成Id的时间截</param>
/// <returns></returns>
protected virtual long TilNextMillis(long lastTimestamp)
{
var timestamp = TimeGen();
while (timestamp <= lastTimestamp)
{
timestamp = TimeGen();
}
return timestamp;
}
}

  测试:

    [TestClass]
public class SnowflakeTest
{
[TestMethod]
public void MainTest()
{
SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
for (int i = 0; i < 1000; i++)
{
Trace.WriteLine(string.Format("{0}-{1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffffff"), idWorker.NextId()));
}
}
}

  结果:

  总之,GUID能满足大部分需要,但如果想要我们的程序精益求精,也可以考虑使用本文提到的方法,感谢阅读。

雪花算法,生成分布式唯一ID的更多相关文章

  1. 雪花算法生成全局唯一ID

    系统中某些场景少不了全局唯一ID的使用,来保证数据的唯一性.除了通过数据库自带的自增id来保证 id 的唯一性,通常为了保证的数据的可移植性会选择通过程序生成全局唯一 id.百度了不少php相关的生成 ...

  2. 基于雪花算法生成分布式ID(Java版)

    SnowFlake算法原理介绍 在分布式系统中会将一个业务的系统部署到多台服务器上,用户随机访问其中一台,而之所以引入分布式系统就是为了让整个系统能够承载更大的访问量.诸如订单号这些我们需要它是全局唯 ...

  3. 雪花算法生成分布式ID

    分布式主键ID生成方案 分布式主键ID的生成方案有以下几种: 数据库自增主键 缺点: 导入旧数据时,可能会ID重复,导致导入失败 分布式架构,多个Mysql实例可能会导致ID重复 UUID 缺点: 占 ...

  4. 分布式唯一id:snowflake算法思考

    匠心零度 转载请注明原创出处,谢谢! 缘起 为什么会突然谈到分布式唯一id呢?原因是最近在准备使用RocketMQ,看看官网介绍: 一句话,消息可能会重复,所以消费端需要做幂等.为什么消息会重复后续R ...

  5. 分布式唯一ID生成算法-雪花算法

    在我们的工作中,数据库某些表的字段会用到唯一的,趋势递增的订单编号,我们将介绍两种方法,一种是传统的采用随机数生成的方式,另外一种是采用当前比较流行的“分布式唯一ID生成算法-雪花算法”来实现. 一. ...

  6. 分布式唯一ID生成方案选型!详细解析雪花算法Snowflake

    分布式唯一ID 使用RocketMQ时,需要使用到分布式唯一ID 消息可能会发生重复,所以要在消费端做幂等性,为了达到业务的幂等性,生产者必须要有一个唯一ID, 需要满足以下条件: 同一业务场景要全局 ...

  7. 分布式唯一ID生成方案是什么样的?(转)

    一.前言 分布式系统中我们会对一些数据量大的业务进行分拆,如:用户表,订单表.因为数据量巨大一张表无法承接,就会对其进行分库分表. 但一旦涉及到分库分表,就会引申出分布式系统中唯一主键ID的生成问题, ...

  8. 【系统设计】分布式唯一ID生成方案总结

    目录 分布式系统中唯一ID生成方案 1. 唯一ID简介 2. 全局ID常见生成方案 2.1 UUID生成 2.2 数据库生成 2.3 Redis生成 2.4 利用zookeeper生成 2.5 雪花算 ...

  9. 分布式唯一ID的生成方案

    分布式ID的特性 全局唯一 不能出现重复的ID,这是最基本的要求. 递增 有利于关系数据库索引性能. 高可用 既然是服务于分布式系统,为多个服务提供ID服务,访问压力一定很大,所以需要保证高可用. 信 ...

随机推荐

  1. GoCN每日新闻(2019-10-10)

    GoCN每日新闻(2019-10-10) GoCN每日新闻(2019-10-10) 1. Go语言源码阅读之bytes.Buffer http://www.pengrl.com/p/60618/ 2. ...

  2. ICEM——对msh文件或者cas文件重新划分边界

    原视频下载地址:https://pan.baidu.com/s/1jIoKSuy 密码: m3uv

  3. SDN第五次上机实验

    1.浏览RYU官网学习RYU控制器的安装和RYU开发入门教程,提交你对于教程代码的理解. 1.通过源码安装RYU控制器 sudo apt-get install python3-pip git clo ...

  4. #C++初学记录(动态规划 被3整除的子序列)

    原题:牛客网 动态规划dynamic programming 的入门级题目 题目描述 : 给你一个长度为50的数字串,问你有多少个子序列构成的数字可以被3整除 答案对1e9+7取模 输入描述: 输入一 ...

  5. 使用CAS实现单点登录功能

    目录 单点登录 简介 CAS服务器部署 上传tomcat服务器压缩到文件夹/usr/local/cas目录下,解压,修改tomcat文件夹名为tomcat 修改tomcat配置文件的端口号 关闭tom ...

  6. Thinking In SE

    各种编程范式的区别 并发模型 并行架构: 位级(bit-level)并行 指令级(instruction-level)并行 数据级(data)并行 数据级并行 任务级(task-level)并行 -- ...

  7. cocos creator策略类手游开发教程

    目录 大小6.83GB,Mp4格式,语言:中文 扫码时备注或说明中留下邮箱 付款后如未回复请至https://shop135452397.taobao.com/ 联系店主

  8. FFmpeg av_seek_frame规律详解

    本帖最后由 TangMonk 于 2016-7-27 10:26 编辑 1 av_seek_frame对视频进行跳转规律 1.1 flags参数 #define AVSEEK_FLAG_BACKWAR ...

  9. Hive Authorization

    https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Authorization https://www.cloudera.c ...

  10. 【curl】certificate is bad 问题解决

    参考:https://blog.scottlowe.org/2018/08/20/troubleshooting-tls-certificates/ 我最近正在写一篇博客文章,内容涉及使用TLS证书进 ...