概述

前一篇文章讲述了最流行的分布式ID生成算法snowflake,本篇文章根据美团点评分布式ID生成系统文章,介绍另一种相对更容易理解和编写的分布式ID生成方式。

实现原理

Leaf这个名字是来自德国哲学家、数学家莱布尼茨的一句话:

There are no two identical leaves in the world

"世界上没有两片相同的树叶"

设置数据表主键自增是最简单的方案,缺点也很明显:

  • 强依赖数据库,无法提供高可用

  • ID生成强依赖单台服务,无法横向扩展

很容易想到,如果我的应用每次申请一批id,插入数据时顺序取一个使用,即将耗尽时再去获取一批新的id,如此即可在一定程度上减弱与数据库的关系,同时将单台扩展延伸为获取id的步长。

负责发放ID的服务既可以使用MySQL服务,也可以使用Redis等服务。

基于MySQL实现

首先我们建立一张数据库表

DROP TABLE IF EXISTS `leafsegment`;
CREATE TABLE `leafsegment`  (
  `biz_tag` varchar(255) NULL DEFAULT NULL,
  `max_id` bigint(20) NULL DEFAULT 0,
  `step` int(11) NULL DEFAULT 5000,
  `desc` varchar(255)  NULL DEFAULT NULL,
  `update_time` datetime(0) NULL DEFAULT now()
); -- 添加一条初始化数据
INSERT INTO `leafsegment` VALUES ('test', 0, 5000, '测试', '2018-12-06 23:32:11');

数据库表如下图

biz_tag:业务标记,不同业务使用不同的值,可以最大限度地利用ID

max_id:当前已经被申请走的最大Id

step:每次申请Id的步长

desc:业务内容描述

update_time:最新一次申请时间

应用如何获取一批有效ID呢?

Begin
UPDATE leafsegment SET max_id=max_id+step,update_time=now() WHERE biz_tag='test'
SELECT biz_tag, max_id, step FROM leafsegment WHERE biz_tag='test'
Commit

在一个事务周期内完成max_id的更新,和最新数据的获取,天然解决了资源竞争问题。

而后,我们就可以在应用中将[max_id-step+1,max_id]闭区间的所有值作为ID来使用了。

基于Redis实现

Redis的实现更为简单,基本原理是利用了Redis的IncrBy命令实现原子加N,具体实现流程无须赘述。

代码实现

首先我们定义一个传递Step(步长)和MaxId(最大值)的DTO

    /// <summary>
    /// 数据单元
    /// </summary>
    public class DataVal
    {
        /// <summary>
        /// 当前最大Id
        /// </summary>
        public long MaxId { get; set; } = 1;
        /// <summary>
        /// 当前步长
        /// </summary>
        public int Step { get; set; } = 1000;
    }

这个类仅负责将ID生发器的数据传入核心类LeafSegment中。核心类的具体实现如下代码:

    /// <summary>
    /// 美团的Leaf Segment 方案
    /// </summary>
    public class LeafSegment
    {
        private long _currentStep = long.MaxValue >> 1;
        private readonly Func<DataVal> _idGetAction;
        private readonly ConcurrentQueue<long> _data = new ConcurrentQueue<long>();
        private readonly AutoResetEvent _autoReset = new AutoResetEvent(false);         /// <summary>
        /// 美团的Leaf Segment 方案
        /// </summary>
        /// <param name="idGetAction">Id生成策略</param>
        /// <param name="prefill">是否立即初始化数据</param>
        public LeafSegment(Func<DataVal> idGetAction,bool prefill=false)
        {
            _idGetAction = idGetAction;
            if (prefill)
            {
                FillData();
            }
            Loop();
        }         /// <summary>
        /// 获取下一个Id
        /// </summary>
        /// <returns></returns>
        public long NextId()
        {
            _autoReset.Set();
            if (_data.TryDequeue(out var result))
            {
                return result;
            }             throw new Exception("Resource not enough");
        }         private void Loop()
        {
            (new Thread(_ =>
            {
                while (true)
                {
                    _autoReset.WaitOne();
                    FillData();
                }
            }) {IsBackground = true}).Start();         }         private void FillData()
        {
            //数量小于步长一半时触发拉新
            while (_data.Count < (_currentStep >> 1))
            {
                var tmp = _idGetAction.Invoke();
                _currentStep = tmp.Step;
                for (var i = tmp.MaxId - tmp.Step + 1; i <= tmp.MaxId; i++)
                {
                    _data.Enqueue(i);
                }
            }
        }
    }

此处需要注意的是LeafSegment构造函数的第一个入参IdGetAction是一个返回DataVal的回调函数,因此外部实现中可以在该回调函数中返回所需ID序列;

第二个参数prefill,该参数控制实例化LeafSegment对象时,是否同步调用获取ID区段,如该值为false,将会由启动的线程稍后补充数据。

完整实现、使用Demo以及benchmark测试请参见源代码:https://github.com/sampsonye/nice

基于.NET Standard的分布式自增ID算法--美团点评LeafSegment的更多相关文章

  1. 基于.NET Standard的分布式自增ID算法--Snowflake

    概述 本篇文章主要讲述分布式ID生成算法中最出名的Snowflake算法.搞.NET开发的,数据库主键最常见的就是int类型的自增主键和GUID类型的uniqueidentifier. 那么为何还要引 ...

  2. 基于.NET Standard的分布式自增ID算法--Snowflake代码实现

    概述 上篇文章介绍了3种常见的Id生成算法,本篇主要介绍如何使用C#实现Snowflake. 基础字段 /// <summary> /// 工作节点Id(长度为5位) /// </s ...

  3. 详解Twitter开源分布式自增ID算法snowflake(附演算验证过程)

    详解Twitter开源分布式自增ID算法snowflake,附演算验证过程 2017年01月22日 14:44:40 url: http://blog.csdn.net/li396864285/art ...

  4. Twitter分布式自增ID算法snowflake原理解析

    以JAVA为例 Twitter分布式自增ID算法snowflake,生成的是Long类型的id,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特(0和1). 那么一个 ...

  5. Twitter分布式自增ID算法snowflake原理解析(Long类型)

    Twitter分布式自增ID算法snowflake,生成的是Long类型的id,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特(0和1). 那么一个Long类型的6 ...

  6. 分布式自增ID算法-Snowflake详解

    1.Snowflake简介 互联网快速发展的今天,分布式应用系统已经见怪不怪,在分布式系统中,我们需要各种各样的ID,既然是ID那么必然是要保证全局唯一,除此之外,不同当业务还需要不同的特性,比如像并 ...

  7. Twitter的分布式自增ID算法snowflake

    snowflake 分布式场景下获取自增id git:https://github.com/twitter/snowflake 解读: http://www.cnblogs.com/relucent/ ...

  8. 一秒可生成500万ID的分布式自增ID算法—雪花算法 (Snowflake,Delphi 版)

    概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种 ...

  9. Twitter的分布式自增ID算法snowflake (Java版)

    概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种 ...

随机推荐

  1. shell脚本常用技巧

    shell脚本常用技巧 1.获取随机字符串或数字 ~]#echo $RANDOM | md5sum | cut -c 1-6 ~]#openssl rand -base64 4 | cut -c 1- ...

  2. search文件中的config

    config文件中出现这句话时,代表该部分search应写成文件中封装好的search部分.即: var search = null;     this.search = search;     se ...

  3. linux通配符含义

    linux通配符含义: .    当前目录**** ..   当前目录的上一级目录**** *    通配符,代表任意0个或多个字符***** ?   通配符,代表重复0个或一个0前面的字符 :    ...

  4. python基础学习22----协程

    协程,又称微线程.英文名Coroutine. 协程最大的优势就是协程极高的执行效率.因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就 ...

  5. windows下,怎么轻易拷贝一个文件的完整路径?

      1. 到目录下,复制文件 2. win+R ,打开"运行"输入框 3.ctrl+v

  6. 张高兴的 .NET Core IoT 入门指南:(一)环境配置、Blink、部署

    如何在 Raspberry Pi 的 Raspbian 上构建使用 GPIO 引脚的 IoT 程序?你可能会回答使用 C++ 或 Python 去访问 Raspberry Pi 的引脚.现在,C# 程 ...

  7. 【转】ios tableView那些事(一)创建一个简单的tableView

    工作也有半年多了!几乎每个项目中的会用到tableview这个神奇而好用的控件,在学习和工作中都会看别人的博客!对我有很大的帮助,就如同站在巨人的肩膀上的感觉吧 哈哈!于是决定重新开始写博客,希望能帮 ...

  8. 20145236《网络对抗》进阶实验——Return-to-libc攻击

    20145236<网络对抗>进阶实验--Return-to-libc攻击 基础知识 Return-into-libc攻击方式不具有同时写和执行的行为模式,因为其不需要注入新的恶意代码,取而 ...

  9. CentOS 6.5安装配置NFS服务器

    OS:centos 6.5 服务端:10.1.11.201 客户端:10.1.11.202 10.1.11.203 10.1.11.204 1.服务端的配置 安装必须的yum包: yum -y ins ...

  10. 根据Uri获取文件的绝对路径

    简易版处理(实际并没发现有什么问题) public static String getRealPathFromURI(Context context, Uri contentURI) { String ...