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

在线工具站
  • 推荐一个程序员在线工具站:程序员常用工具http://cxytools.com),有时间戳、JSON格式化、文本对比、HASH生成、UUID生成等常用工具,效率加倍嘎嘎好用。
程序员资料站
小报童专栏精选Top100
  • 推荐一个小报童专栏导航站:小报童精选Top100http://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 生成需求。

相关推荐
掘金-我是哪吒24 分钟前
分布式微服务系统架构第158集:JavaPlus技术文档平台日更-JVM基础知识
jvm·分布式·微服务·架构·系统架构
阿蒙Amon38 分钟前
C#读写文件:多种方式详解
开发语言·数据库·c#
东窗西篱梦1 小时前
Redis集群部署指南:高可用与分布式实践
数据库·redis·分布式
Acrel_Fanny1 小时前
Acrel-1000系列分布式光伏监控系统在湖北荆门一马光彩大市场屋顶光伏发电项目中应用
分布式
xufwind1 小时前
spark standlone 集群离线安装
大数据·分布式·spark
就是有点傻2 小时前
C#如何实现中英文快速切换
数据库·c#
半新半旧2 小时前
Redis集群和 zookeeper 实现分布式锁的优势和劣势
redis·分布式·zookeeper
1024小神2 小时前
hono框架绑定cloudflare的d1数据库操作步骤
数据库
亲爱的非洲野猪2 小时前
Kafka “假死“现象深度解析与解决方案
分布式·kafka
CodeWithMe2 小时前
【Note】《Kafka: The Definitive Guide》第三章: Kafka 生产者深入解析:如何高效写入 Kafka 消息队列
分布式·kafka