基于.NET Standard的分布式自增ID算法--美团点评LeafSegment
概述
前一篇文章讲述了最流行的分布式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的更多相关文章
- 基于.NET Standard的分布式自增ID算法--Snowflake
概述 本篇文章主要讲述分布式ID生成算法中最出名的Snowflake算法.搞.NET开发的,数据库主键最常见的就是int类型的自增主键和GUID类型的uniqueidentifier. 那么为何还要引 ...
- 基于.NET Standard的分布式自增ID算法--Snowflake代码实现
概述 上篇文章介绍了3种常见的Id生成算法,本篇主要介绍如何使用C#实现Snowflake. 基础字段 /// <summary> /// 工作节点Id(长度为5位) /// </s ...
- 详解Twitter开源分布式自增ID算法snowflake(附演算验证过程)
详解Twitter开源分布式自增ID算法snowflake,附演算验证过程 2017年01月22日 14:44:40 url: http://blog.csdn.net/li396864285/art ...
- Twitter分布式自增ID算法snowflake原理解析
以JAVA为例 Twitter分布式自增ID算法snowflake,生成的是Long类型的id,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特(0和1). 那么一个 ...
- Twitter分布式自增ID算法snowflake原理解析(Long类型)
Twitter分布式自增ID算法snowflake,生成的是Long类型的id,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特(0和1). 那么一个Long类型的6 ...
- 分布式自增ID算法-Snowflake详解
1.Snowflake简介 互联网快速发展的今天,分布式应用系统已经见怪不怪,在分布式系统中,我们需要各种各样的ID,既然是ID那么必然是要保证全局唯一,除此之外,不同当业务还需要不同的特性,比如像并 ...
- Twitter的分布式自增ID算法snowflake
snowflake 分布式场景下获取自增id git:https://github.com/twitter/snowflake 解读: http://www.cnblogs.com/relucent/ ...
- 一秒可生成500万ID的分布式自增ID算法—雪花算法 (Snowflake,Delphi 版)
概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种 ...
- Twitter的分布式自增ID算法snowflake (Java版)
概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种 ...
随机推荐
- w3school 基础学习
http://www.w3school.com.cn/ http://www.w3school.com.cn/sql/sql_quickref.asp
- quarz时间配置
Cron表达式的格式:秒 分 时 日 月 周 年(可选). 字段名 允许的值 允许的特殊字符 秒 ...
- python容错
#try: except: else: #为什么叫容错呢,先说说错误,这里说的错误并不是因为马虎或者什么原因在脚本中留下的bug,这个不能容掉,所谓容掉就是略过这个错误,要在测试时候发现并修正,需要容 ...
- C语言short int
因为C语言中short int占2个字节,有16个二进制位,共可表示2^16种状态.因为它用来表示有符号数,而0也要占用一个状态.所以,16位的原码可以表示的数是-32767~+32767,它的0可以 ...
- Linux 下Shell的学习3-service编程
1. vim /etc/init.d/nginx 2. chmod 755 /etc/init.d/nginx 3. service nginx status #!/bin/bash # nginx ...
- 有关于分布式缓存Hazelcast
如果在内网段中部署或者启动缓存服务.不能存在相同的组名称.如同使用dubbo一样,会导致无法连接到缓存节点
- BZOJ4556:[TJOI\HEOI2016]字符串(后缀数组,主席树,二分,ST表)
Description 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为n的字符串s,和m个问题.佳媛姐姐必须正确回答这m个问题,才能打开箱 ...
- Effective MySQL之SQL语句最优化——读书笔记之一
第一章,DBA5分钟速成 本章知识点如下: 寻找运行慢SQL的语句: show full processlist查看所有正在执行的进程及执行的语句耗时: 命令后面\G可以让命令按行显示(默认是按列). ...
- Docker技术入门与实战 第二版-学习笔记-9-Docker Compose 项目-2-Compose 命令说明
Compose 命令说明 1)命令对象与格式 对于 Compose 来说,大部分命令的对象既可以是项目本身,也可以指定为项目中 的服务或者容器.如果没有特别的说明,命令对象将是项目,这意味着项目中所有 ...
- VMware安装VMware tool是 遇到The path "" is not a valid path to the 3.10.0-693.el7.x86_64 kernel headers.
The path "" is not a valid path to the 3.10.0-693.el7.x86_64 kernel headers.问题是找不到内核头文件,需要 ...