高并发系统-分布式唯一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

相关推荐
一只爱打拳的程序猿12 分钟前
【Spring】更加简单的将对象存入Spring中并使用
java·后端·spring
阿伟*rui13 分钟前
认识微服务,微服务的拆分,服务治理(nacos注册中心,远程调用)
微服务·架构·firefox
ZHOU西口1 小时前
微服务实战系列之玩转Docker(十八)
分布式·docker·云原生·架构·数据安全·etcd·rbac
时差9531 小时前
【面试题】Hive 查询:如何查找用户连续三天登录的记录
大数据·数据库·hive·sql·面试·database
假装我不帅2 小时前
asp.net framework从webform开始创建mvc项目
后端·asp.net·mvc
神仙别闹2 小时前
基于ASP.NET+SQL Server实现简单小说网站(包括PC版本和移动版本)
后端·asp.net
计算机-秋大田2 小时前
基于Spring Boot的船舶监造系统的设计与实现,LW+源码+讲解
java·论文阅读·spring boot·后端·vue
货拉拉技术3 小时前
货拉拉-实时对账系统(算盘平台)
后端
deephub3 小时前
Tokenformer:基于参数标记化的高效可扩展Transformer架构
人工智能·python·深度学习·架构·transformer