高并发系统-分布式唯一ID生成(一)

本文主要总结市面上常见的分布式ID生成方案,并结合自身项目经历对齐总结

定义

分布式 ID 是分布式系统下的 ID,其具有唯一标识。类似系统中常见的用户ID、订单号、券码,都应该具备唯一性。

算法要求

  • 全局唯一:生成的 ID 必须全局唯一
  • 趋势递增:我们应该尽量选择有序的主键来保证索引的性能
  • 好接入:要秉着拿来即用的设计原则,在系统设计和实现上要尽可能的简单
  • 信息安全:如果是连续的 ID,攻击者很容易就猜出下一条记录的 ID,所以有些情况下尽量让 ID 无规则(可选)
  • 含时间戳:含时间戳便于追踪(可选)

可靠性要求

  • 高可用:要保证在99.999%的情况下能够生成一个唯一的分布式 ID
  • 低延迟:生成分布式 ID 的速度一定要快
  • 高QPS:假如一下子来10w个生成分布式 ID 的请求,服务器要能扛得住并且能够一下子生成10w个

生成方案

UUID

包含32个16进制的数字,以连字符分割成五段,格式是8-4-4-4-12

优点:

性能好,本地生成,无网络消耗

缺点:

存储消耗空间大(32 个字符串,128 位)

不安全(基于 MAC 地址生成 UUID 的算法会造成 MAC 地址泄露)

无序(非自增)

没有具体业务含义

需要解决重复 ID 问题(当机器时间不对的情况下,可能导致会产生重复 ID)

无序,不能生成递增的 ID,而且很长,入库性能差(MYSQL)。因为MySQL的 是 B+ 树索引,每插入一条新数据,都会对索引进行改造,因为 UUID 的无序,每次插入数据时 B+ 树的改造就会很大,也就是导致索引分裂

应用场景:

用于分布式日志追踪的traceId生成,可参照:基于LOGBACK实现的分布式日志跟踪

案例

java 复制代码
import java.util.UUID;

public class IdGenerator {
    public IdGenerator() {

    }

    public static String createID() {
        String id = UUID.randomUUID().toString();
        id = id.replace("-","");
        return id;
    }
}

数据库自增主键

直接依赖关系型数据库机制-自增主键来产生唯一ID

优点:

实现起来比较简单、ID 有序递增、存储消耗空间小

缺点:

支持的并发量不大(依赖数据库性能)

存在数据库单点问题(可以使用数据库集群解决,不过增加了复杂度)

ID 没有具体业务含义

每次获取 ID 都要访问一次数据库(增加了对数据库的压力)

应用场景

可以用于TPS不高场景的分布式唯一ID获取

不过需要注意自增键初始值和步长,不能随意更改 主要是auto_increment_incrementauto_increment_offset参数

案例

建表

sql 复制代码
drop TABLE IF EXISTS `sequence_id`;
CREATE TABLE `sequence_id` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT comment '自增ID',
  `stub` varchar(20) NOT NULL DEFAULT '' comment '随机值',
  PRIMARY KEY (`id`),
  UNIQUE KEY `stub` (`stub`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=10 comment '自增序列表';

mybatics xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.toby.dynamic.data.source.db.dao.config.SeqIdMapper">

    <insert id="getNextId">
        <selectKey keyProperty="id" resultType="java.lang.Long" order="AFTER">
            SELECT LAST_INSERT_ID()
        </selectKey>
        INSERT INTO sequence_id (stub) VALUES (#{name})
    </insert>
</mapper>

mapper文件

java 复制代码
public interface SeqIdMapper {

    long getNextId(@Param("name") String name);
}

test用例

java 复制代码
package com.toby.dynamic.data.source.db.dao.config;

import com.toby.dynamic.data.source.start.PointApplication;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = PointApplication.class)
public class SeqIdMapperTest {
    private static final Logger LOGGER = LoggerFactory.getLogger(SeqIdMapperTest.class);
    @Autowired
    private SeqIdMapper seqIdMapper;

    @Test
    public void getNextId() {
        long ret = seqIdMapper.getNextId(String.valueOf(System.currentTimeMillis()));
        LOGGER.info("getNextId ret={}", ret);
        Assert.assertTrue(ret > 0);
    }
}

运行结果

sql 复制代码
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@54be6213] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@322b09da] will not be managed by Spring
==>  Preparing: INSERT INTO sequence_id (stub) VALUES (?) 
==> Parameters: 1703319042518(String)
<==    Updates: 1
==>  Preparing: SELECT LAST_INSERT_ID() 
==> Parameters: 
<==    Columns: LAST_INSERT_ID()
<==        Row: 1
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@54be6213]

具体代码参考:github.com/hongyuwen/H...

未完待续

参考
分布式 ID 详解
分布式ID

相关推荐
PAK向日葵2 小时前
【算法导论】PDD 0817笔试题题解
算法·面试
uzong3 小时前
技术故障复盘模版
后端
GetcharZp4 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程4 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研4 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi5 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国6 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy6 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
喂完待续6 小时前
Apache Hudi:数据湖的实时革命
大数据·数据仓库·分布式·架构·apache·数据库架构
AntBlack6 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt