使用 Redis 生成分布式唯一 ID 的实现与优化

在线工具站
  • 推荐一个程序员在线工具站:程序员常用工具(http://cxytools.com),有时间戳、JSON格式化、文本对比、HASH生成、UUID生成等常用工具,效率加倍嘎嘎好用。
程序员资料站
小报童专栏精选Top100
  • 推荐一个小报童专栏导航站:小报童精选Top100(http://xbt100.top),收录了生财有术项目精选、AI海外赚钱、纯银的产品分析等专栏,陆续会收录更多的专栏,欢迎体验~

在分布式系统中,生成唯一 ID 是一个常见且重要的需求。唯一 ID 的生成需要满足高并发、全局唯一性和高可用等要求。

分布式唯一 ID 的需求

分布式唯一 ID 的生成涉及到以下几个关键需求:

  1. 全局唯一性:生成的 ID 在整个系统中必须是唯一的,避免冲突。
  2. 高可用性:生成 ID 的服务必须具备高可用性,确保在高并发场景下不出现性能瓶颈。
  3. 有序性:在某些场景下,需要生成的 ID 有一定的顺序性,以便进行排序和分析。
  4. 高性能:生成 ID 的过程需要高效,能够支持高并发请求。

使用 Redis 生成分布式唯一 ID 的优势

Redis 作为一种高性能的内存数据库,具有以下优势,使其成为生成分布式唯一 ID 的理想选择:

  1. 高性能:Redis 的操作基于内存,读写速度极快,能够支持高并发请求。
  2. 简单易用:Redis 提供了丰富的命令,可以方便地实现 ID 生成逻辑。
  3. 原子操作:Redis 的原子性操作确保了 ID 生成过程中的一致性,避免并发冲突。

常见的分布式唯一 ID 生成方案

1. 使用 Redis 的自增命令 INCR

Redis 的 INCR 命令可以对指定键的值进行原子性自增操作,非常适合生成递增的唯一 ID。

java 复制代码
// 使用 Redis INCR 命令生成唯一 ID
Jedis jedis = new Jedis("localhost");
long uniqueId = jedis.incr("unique_id_key");
优点
  • 简单易用:只需要调用 Redis 的 INCR 命令即可实现。
  • 原子性:Redis 保证 INCR 操作的原子性,避免并发冲突。
缺点
  • 单点问题:如果 Redis 实例宕机,可能会导致 ID 生成服务不可用。

2. 使用 Redis 的哈希(Hash)结构

通过 Redis 的哈希结构,可以生成多种类型的唯一 ID。例如,使用哈希字段存储不同业务的计数器。

java 复制代码
// 使用 Redis 哈希结构生成唯一 ID
Jedis jedis = new Jedis("localhost");
long orderId = jedis.hincrBy("order_id_key", "order", 1);
long userId = jedis.hincrBy("user_id_key", "user", 1);
优点
  • 分类管理:可以为不同的业务类型生成独立的唯一 ID。
  • 原子性:Redis 保证 HINCRBY 操作的原子性。
缺点
  • 与 INCR 命令类似,仍然存在单点问题。

3. 使用 Redis 和 Snowflake 算法

Snowflake 算法是 Twitter 开源的一种分布式唯一 ID 生成算法。ID 由时间戳、机器 ID 和序列号组成,能够在分布式环境中生成高性能的全局唯一 ID。

可以结合 Redis 和 Snowflake 算法,通过 Redis 管理机器 ID,避免冲突。

java 复制代码
// Snowflake 算法 Java 实现
public class SnowflakeIdWorker {
    private final long twepoch = 1288834974657L;
    private final long workerIdBits = 5L;
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long sequenceBits = 12L;
    private final long workerIdShift = sequenceBits;
    private final long timestampLeftShift = sequenceBits + workerIdBits;
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    private long workerId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;

    public SnowflakeIdWorker(long workerId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException("worker Id can't be greater than " + maxWorkerId + " or less than 0");
        }
        this.workerId = workerId;
    }

    public synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
        }
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        return ((timestamp - twepoch) << timestampLeftShift) |
                (workerId << workerIdShift) |
                sequence;
    }

    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    protected long timeGen() {
        return System.currentTimeMillis();
    }
}

// 使用 Redis 获取机器 ID
Jedis jedis = new Jedis("localhost");
long workerId = jedis.incr("worker_id_key");
SnowflakeIdWorker idWorker = new SnowflakeIdWorker(workerId);
long uniqueId = idWorker.nextId();
优点
  • 高性能:Snowflake 算法能够生成高性能的唯一 ID。
  • 分布式:能够在分布式环境中生成唯一 ID,避免单点问题。
缺点
  • 实现复杂度较高:需要额外的逻辑处理时间戳、机器 ID 和序列号。

优化策略

1. 高可用架构

为了避免 Redis 单点故障,可以使用 Redis 的高可用架构,例如 Redis 主从复制和 Redis 哨兵模式。通过这些机制,可以在 Redis 宕机时自动切换到从节点,保证 ID 生成服务的高可用性。

shell 复制代码
# Redis Sentinel 配置示例
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 10000

2. 缓存策略

在客户端实现缓存策略,减少对 Redis 的频繁访问。例如,可以预先从 Redis 获取一段 ID 范围,缓存在本地,当本地缓存的 ID 用完时再向 Redis 请求新的 ID 范围。

java 复制代码
public class IdGenerator {
    private static final int BATCH_SIZE = 1000;
    private long currentId = 0;
    private long maxId = 0;

    public synchronized long getNextId() {
        if (currentId >= maxId) {
            Jedis jedis = new Jedis("localhost");
            currentId = jedis.incrBy("unique_id_key", BATCH_SIZE);
            maxId = currentId + BATCH_SIZE - 1;
        }
        return currentId++;
    }
}

3. 分段锁

通过分段锁机制,将 ID 生成操作分散到多个 Redis 实例上,减少单个 Redis 实例的压力,提高系统整体的性能和可靠性。

java 复制代码
// 使用分段锁生成唯一 ID
public class SegmentIdGenerator {
    private static final int SEGMENT_COUNT = 10;
    private Jedis[] jedisClients = new Jedis[SEGMENT_COUNT];

    public SegmentIdGenerator() {
        for (int i = 0; i < SEGMENT_COUNT; i++) {
            jedisClients[i] = new Jedis("localhost", 6379 + i);
        }
    }

    public synchronized long getNextId() {
        int segment = (int) (Thread.currentThread().getId() % SEGMENT_COUNT);
        return jedisClients[segment].incr("unique_id_key");
    }
}

实际应用案例

订单系统

在电商平台的订单系统中,需要为每个订单生成唯一的订单 ID。通过 Redis 和 Snowflake 算法,可以在高并发环境下快速生成唯一的订单 ID,并保证订单的顺序性。

用户系统

在用户注册系统中,需要为每个用户生成唯一的用户 ID。通过 Redis 的自增命令,可以简单高效地生成全局唯一的用户 ID。

分布式任务调度

在分布式任务调度系统中,需要为每个任务生成唯一的任务 ID。通过 Redis 和分段锁机制,可以在多节点环境下实现高效的唯一 ID 生成。

总结

通过使用 Redis,可以在分布式系统中高效地生成唯一 ID。结合 Redis 的自增命令、哈希结构和 Snowflake 算法等技术手段,可以满足不同场景下的唯一 ID 生成需求。

相关推荐
笃行35011 小时前
数据库性能优化的五处内核改造,KingbaseES 配了四件诊断工具
数据库
虹科网络安全13 小时前
Redis Radar:在统一视图中掌握 Redis 部署、健康与容量
经验分享·redis·艾体宝·艾体宝it·艾体宝redis
非优秀程序员13 小时前
第02章 杭电的英语老师与一台服务器|《互联网产品恩仇录》
数据库·node.js·代码规范
大大大大晴天14 小时前
每天认识一个组件:Redis
redis
SelectDB18 小时前
SelectDB 是哪个公司的?是国产数据库吗?一篇讲清飞轮科技与 Apache Doris 的关系
数据库
这个DBA有点耶18 小时前
库内计算推理:一条SQL背后的算子融合,和外挂方案差了几次网络往返
数据库·人工智能·sql
这个DBA有点耶20 小时前
函数索引的“完全一致”有多苛刻:写法、字符集、类型,差一处就失效
数据库·mysql·代码规范
星恒随风20 小时前
MySQL 从零入门(三):表的约束详解
linux·服务器·数据库·笔记·mysql
Alluxio21 小时前
Alluxio + OCI:为 AI 工作负载实现亚毫秒访问延迟与可扩展吞吐
运维·数据库·人工智能·分布式·缓存·ai·语言模型