百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

分布式ID生成服务-idalloc『DaemonCoder』

liebian365 2025-01-02 17:39 15 浏览 0 评论

idalloc 是我个人开发的开源分布式ID生成框架,经历过生产环境上万QPS的验证,现在推荐给大家:

https://github.com/daemon-coder/idalloc

1. idalloc是什么

idalloc 是一个Go语言开发的框架,他提供了一个生成趋势递增id的server。

2. 为什么需要一个分布式ID生成服务?

在大多数场景下,我们可以用MySQL的自增主键生成id即可,但是有些特殊的场景是需要在数据库外部生成id:

  • 写数据库之前就要用id去做一些业务处理。
  • 数据存储于多种数据库中,例如文件上传时,文件元信息存MySQL,文件内容采用对象存储,可以外部生成一个id把两部分数据做统一。
  • 跨多个系统共用的id,如分布式事务id。
  • 在一些高并发的场景下,MySQL受限写速度的问题,不适用于高并发场景下的id生成。

3. 业界各种ID生成方案的对比

  • UUID
  • 优点:本地生成,性能高。
  • 缺点:存在MAC地址泄漏风险,集群环境中不能保证唯一性,ID长度较长且无序,占用较多存储空间。
  • MySQL自增主键或计数表
  • 优点:实现简单。
  • 缺点:写入性能有限,不适合高并发场景。
  • Redis的INCR命令
  • 优点:性能相对MySQL方案要高很多。
  • 缺点:存在数据丢失风险,需额外出方案保证数据的持久化。
  • Snowflake
  • 优点:本地生成,性能高。
  • 缺点:强依赖系统时间,一旦系统时间回调,会带来灾难性的影响,过于依赖不可控的系统时间不是好的设计;还需要为每个实例维护一份唯一编号。
  • 美团Leaf
  • 优点:与idalloc思路不谋而合,整体方案大同小异
  • 缺点:用Java实现,较为笨重。
  • idalloc
  • 优点
    • 趋势递增:生成的ID类似MySQL的自增主键,具有趋势递增特性。
    • 高性能:支持高并发,批量生成、提前预生成ID。
    • 高可用性:ID存储基于MySQL和Redis组合,允许短时间的数据库宕机而不影响业务运行;同时预留了服务宕机后的备用号段(降级随机数的方案,可以理解为低配版UUID)。
    • 可扩展性强:支持多个业务线共用,满足大规模业务需求。
    • 轻量化:基于Go语言实现,资源占用少,轻负载时只需要10+MB左右的内存。
    • 灵活性高:提供丰富的配置选项,可以根据不同场景进行调优。
  • 缺点
  • 生成的id只能保证趋势递增,不能保证严格递增。
  • 在系统重启时,会出现id段空洞的现象,浪费部分id。
  • 最多生成2^63个id,引入时需要考虑是否足够。

4. idalloc的实现方案

4.1. 整体架构

idalloc的架构结合了Redis和MySQL进行ID的存储与恢复,同时包含了一个进程内ID池和异步请求通道。通过这些机制保证了ID的快速生成与高效同步。下图展示了idalloc的整体架构:

4.2. 批量申请

idalloc通过Redis的INCR命令每次批量申请1万个ID(该值可配置),从而减少高并发场景下对Redis的频繁请求,提升性能。

4.3. 预申请机制

为了进一步优化性能,idalloc实现了预申请机制。当现有ID池中的ID快要耗尽时,异步申请线程会立即触发下一轮的ID申请,并在交付时阻塞。

优点

  • 避免了ID池耗尽时阻塞Redis请求的问题,进一步提升了性能。

缺点

  • 如果进程重启,预申请的ID号段会作废,但这对业务没有影响,属于可接受的缺陷。

4.4. 数据库更新的原子性保障

在Redis同步MySQL或从MySQL恢复Redis的数据时,idalloc通过保证操作的原子性来避免并发冲突。具体的存储格式如下:

MySQL表结构

CREATE TABLE IF NOT EXISTS `tbl_alloc_info` (
    `service_name`        VARCHAR(64)     NOT NULL PRIMARY KEY,
    `last_alloc_value`    BIGINT UNSIGNED NOT NULL DEFAULT '0',
    `data_version`        BIGINT UNSIGNED NOT NULL DEFAULT '0'
) ENGINE = InnoDB CHARACTER SET = utf8mb4;

Redis存储格式

  • :idalloc:alloc_info_$service_name
  • 类型:Hash
  • 字段:lastAllocValue, dataVersion

Redis同步到MySQL:

更新MySQL时,会判断添加版本筛选条件:将要更新的data_version>数据库里的data_version,从而保证并发更新也不会出现旧数据覆盖新数据的情况。

MySQL恢复到Redis:

通过Lua脚本执行以下命令:判断Redis中的data_version是否大于mysql中的,是则更新Redis。从而原子性在保证了数据只会更新为更新的版本。

5. 使用示例

package main

import (
        "github.com/daemon-coder/idalloc/app/server"
        "github.com/daemon-coder/idalloc/definition"
        "github.com/daemon-coder/idalloc/infrastructure/iris_infra/middleware"
        _ "github.com/go-sql-driver/mysql"
        db "myapp/infrastructure/db_infra"
        redis "myapp/infrastructure/redis_infra"
)

func main() {
        // TraceIDHeaderKey时调用idalloc服务时,传递的traceid的请求头,默认为:X-Trace-Id
        middleware.TraceIDHeaderKey = "X-Request-Id"
        idallocServer := server.NewServer(&definition.Config{
                AppName:       "idalloc",
                Redis:         redis.GetClient(),  // Redis连接
                DB:            db.GetDB(),         // MySQL连接
                ServerPort:    8080,
                UsePprof:      true,
                UsePrometheus: true,
                LogLevel:      "INFO",
                RateLimit: definition.RateLimit{
                        Enable: true,
                        Qps:    10000,
                },
                SyncRedisAndDBChanSize:     10000,  // 同步Redis和DB的任务队列大小
                SyncRedisAndDBThreadNum:    10,     // 同步Redis和DB的线程数
                RedisBatchAllocNum:         10000,  // Redis每次分配ID的数量
                WriteDBEveryNVersion:       10,     // Redis更新多少次,才会同步一次到MySQL
                RecoverRedisEveryNVersion:  100,    // Redis更新多少次,才会判断是否从MySQL中恢复到Redis
        })
        idallocServer.Run()
}

相关推荐

C语言自学课程大纲(c语言入门自学资料)

一、自学C语言,很多人不知道应该如何学习,从哪儿学习,学习又分为几个阶段,总是学着学着就很迷茫???分享C语言的学习路线图,跟着路线图学吧,天天看。...

「linux」定时器方案:红黑树、最小堆和时间轮的原理

一、网络事件和时间事件对于服务端来说,驱动服务端逻辑的事件主要有两个,一个是网络事件,另一个是时间事件;...

程序员怎么会不知道 C10K 问题呢?

昨天的文章中提到了C10K问题,结果好些程序员跑过来问,啥是C10K,我写了这么多年程序,我怎么不知道呢?我说,那你听说过前腿儿猪肉吗?今天简单说说C10K的问题。关于这个问题,Ruby...

朝荐开源 - glib(朝廷百科)

glib是一套通用的实用程序库,它为C语言提供了许多有用的数据结构、工具函数和抽象层,旨在简化C语言的跨平台开发,并提高代码的可重用性和效率。glib是GTK+和GNOME桌面环...

libevent总结(事件处理框架)(libevent libev)

libevent的事件处理框架是一个反应堆模型,而反应堆模型的核心就是io复用,拿epoll来说反应堆模型有两个核心数据结构,一个是epoll维护的内核事件表,一个是保存激活事件的事件队列当然,值得注...

日荐开源 - LibEvent(aldente官网网址)

libevent...

快递单号一键查询,高效追踪包裹物流,省时省力!

在繁忙的现代生活中,快递已成为我们日常生活中不可或缺的一部分。然而,面对众多的快递单号,如何快速、准确地查询包裹的物流信息成为了一个难题。现在,我们为您带来了一款快递单号一键查询工具,让您的物流追踪变...

导入不同快递公司下的单号批量查快递动态,一键解决物流查询难题

看着满屏快递单号陷入沉思?同事小王已经用《快递批量查询高手》一键导入多家快递,批量查询快递信息并统计了…而你还在中通、圆通、申通官网来回切换到鼠标冒烟?是时候亮出这个让快递公司接口“集体颤抖”的...

一键解锁快递查询高效能:批量查询快递,智能排序延误单号

当你的客服团队还在用5个浏览器轮番刷新物流页面时,隔壁仓库的王叔已经用快递批量查询高手把多个个滞留件变成会说话的预警红点!这篇教程将揭秘物流圈的「神器」,让「未更新快递」自动排队到你面前认罪。1.在软...

一站式快递单号查询平台,修改单号刷新快递信息的快递查询教程

一站式快递单号查询平台,支持导入单号查询时修改快递单号,高效刷新快递信息的快递查询教程随着电子商务的繁荣发展,快递业务量不断增长,无论是电商卖家还是普通消费者,对快递信息的查询和管理需求都日益增强。为...

高效快递单号查询,批量查询快递信息,多种查看方式满足你的需求

最近有很多朋友在问,如何查快递,怎么根据条件查看单号呢?不知道如何操作的宝贝们,下面请随小编一起来试试,希望能给大家带来帮助。需要哪些工具?安装一个快递批量查询高手快递单号若干怎么快速查询?步骤1:运...

物流查询达人必备!一键批量查询快递单号,根据发出时间筛选单号

嘿,各位快递查询达人们,是不是经常为海量的快递单号查询而头疼不已?想要一款能够在线批量查询快递动态,还能根据发出物流时间一键筛选所需快递单号信息的神器吗?来来来,让我给你们揭秘一款快递批量查询高手软件...

快递查询神器,多单号导入,筛选保存一键完成

当面对如山的快递单号,你是否曾感到手足无措?每一个单号都需要你逐一输入、查询,再逐个根据时间差进行筛选,这样的工作无疑是对耐心与精力的双重考验。但别担心,今天,我们将为你揭示一款物流行业的秘密武器——...

快递单号查询神器:一键复制粘贴,轻松批量追踪同公司快递

嘿,小伙伴们!还在为手动输入快递单号查询物流信息而烦恼吗?是不是觉得每次都要一个个输入单号,既费时又费力?别急,今天我要给大家介绍一款神奇的软件——快递批量查询高手!这款软件就像你的私人快递助手一样,...

快递单号查询入口自动批量查询快递动态并根据派件员字段排序单号

想象一下,面对堆积如山的快递单号,你不再需要一个个手动输入查询,而是轻轻一点,就能瞬间掌握所有快递的物流动态,甚至还能根据派件员智能排序,让管理变得井井有条。这不再是遥不可及的梦想,快递批量查询高手软...

取消回复欢迎 发表评论: