在 .NET6.0 中实现 Leaf-segment 分布式 ID 生成系统

背景

        分布式 ID (Distributed ID)是指在分布式系统中,为了保证全局唯一性而生成的一类 ID。在分布式系统中,由于存在多个节点同时生成 ID 的情况,因此需要采用一定的算法来保证生成的ID的唯一性和连续性。

        常见的分布式 ID 生成算法有 UUID、Snowflake、Twitter 的雪花算法等。这些算法的实现方式各不相同,但通常都需要依赖于一些分布式协调工具(如 Zookeeper、Etcd 等)来实现节点之间的协调和同步。分布式 ID 在分布式系统中具有重要的作用,可以用于唯一标识分布式系统中的各种实体,如订单、用户等。同时,分布式 ID 的生成速度也对系统的性能有一定的影响,因此需要选择合适的算法和实现方式来满足系统的需求。

Leaf-segment 是美团点评开源的一种分布式 ID 生成方案,是 Leaf 系统的一部分。与 Leaf 的Snowflake 算法不同,Leaf-segment 采用了基于数据库的分段算法,可以在不依赖于特定硬件和时钟同步的情况下生成全局唯一的 ID。

具体来说,Leaf-segment 将 ID 的生成分为两个步骤:

  1. 申请 ID 段:首先,客户端向 Leaf-segment 服务端申请一个 ID 段,包含起始 ID 和结束 ID。

  2. 生成 ID:客户端在本地缓存中维护当前 ID 段的起始 ID 和结束 ID,每次生成 ID 时,先检查本地缓存中的 ID 是否用尽,如果用尽,则重新向 Leaf-segment 服务端申请一个新的 ID 段。

Leaf-segment 的优点包括:

  1. 独立性:Leaf-segment 不依赖于特定硬件和时钟同步,可以在任何环境下生成全局唯一的  ID。

  2. 可扩展性:Leaf-segment 采用了分段算法,可以根据实际需求动态调整 ID 段的大小,支持高并发场景下的 ID 生成。

  3. 可以自定义 max_id(号段) 的大小,非常方便业务从原有的 ID 方式上迁移过来。

        总之,Leaf-segment 是一种非常优秀的分布式 ID 生成方案,可以帮助开发人员快速解决分布式 ID 生成的问题,提高系统的性能和可用性。

数据库表设计(以 MySQL 为例)

  • max_id:当前业务号段的最大值,用于计算下一个号段。
  • step:每次分配的号段长度,初始是 0~1000 的号段,当这个号段用完时,max_id 会从 1000 被更新成 2000(max_id = max_id + step),该值的大小决定更新 max_id 的频率。
  • time:更新号段的时间。
CREATE TABLE IF NOT EXISTS leaf_segment
(
    `max_id` BIGINT NOT NULL,
    `step`   INT    NOT NULL,
    `time`   TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB;

  •  创建表之后,添加一条初始化数据。
INSERT INTO leaf_segment(`max_id`, `step`) SELECT 0, 1000 FROM DUAL WHERE NOT EXISTS(SELECT * FROM leaf_segment);
  •  更新号段的语句。
Begin
UPDATE leaf_segment SET max_id = max_id + step
SELECT max_id, step FROM leaf_segment
Commit

创建实体类

/// <summary>
/// 存储 Leaf 生成 Id 的号段。
/// </summary>
internal class LeafSegment
{
    /// <summary>
    /// 被分配的号段的最大值。
    /// </summary>
    internal long MaxId { get; set; }

    /// <summary>
    /// 每次分配的号段长度。
    /// 初始是 0~1000 的号段,当这个号段用完时,MaxId 会从 1000 被更新成 2000(MaxId = MaxId + Step)。
    /// 该值的大小决定更新 MaxId 的频率。
    /// </summary>
    internal int Step { get; set; }

    /// <summary>
    /// 更新时间。
    /// </summary>
    internal System.DateTime Time { get; set; }
}

生成 ID

long IIdGenerator.Generation(out long previouId)
{
    if (!_init)
    {
        LogUtil.Error(LogTag, "LeafIdGenerator 未初始化成功。");

        throw new SystemException("LeafIdGenerator 未初始化成功。");
    }

    while (true)
    {
        _segmentBuffer.Lock.EnterReadLock();

        try
        {
            var segment = _segmentBuffer.Current;

            // 检查下号段是否只剩下 10%。
            if (!_segmentBuffer.IsNextReady && segment.Surplus < 0.1 * segment.Step && _segmentBuffer.AtomicBoolean.CompareAndSet(false, true))
            {
                _segmentBuffer.Task = Task.Run(OpenSegmentBuffer);
            }

            var result = Interlocked.Increment(ref segment._value);

            // 如果生成的号段未超出就直接返回。
            if (result < segment.MaxId)
            {
                previouId = Interlocked.Exchange(ref _previouId, result);

                return result;
            }
        }
        catch
        {
            throw;
        }
        finally
        {
            // 释放读锁。
            _segmentBuffer.Lock.ExitReadLock();
        }

        //LogUtil.Debug(LogTag, $"等待异步更新。");

        _segmentBuffer.Task?.Wait();

        _segmentBuffer.Lock.EnterWriteLock();

        try
        {
            var segment = _segmentBuffer.Current;

            var result = Interlocked.Increment(ref segment._value);

            // 如果生成的号段未超出就直接返回。
            if (result < segment.MaxId)
            {
                previouId = Interlocked.Exchange(ref _previouId, result);

                return result;
            }

            if (_segmentBuffer.IsNextReady)
            {
                // 切换位置。
                _segmentBuffer.SwitchPos();
                _segmentBuffer.IsNextReady = false;

                //LogUtil.Debug(LogTag, $"切换号段完成。");
            }
            else
            {
                //LogUtil.Debug(LogTag, $"继续。");
                continue;
            }
        }
        catch
        {
            throw;
        }
        finally
        {
            _segmentBuffer.Lock.ExitWriteLock();
        }
    }
}

总结

        以上就是在 .net 中实现 Leaf——美团点评分布式 ID 生成系统的介绍,具体源码可以从我的 GitHub 中拉取,如有任何问题请通过微信公众号私信我。

微信公众号:KeisoftCN

源码地址:https://github.com/macroecho/netLeaf

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值