使用 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 生成需求。

相关推荐
结衣结衣.7 分钟前
完全理解C语言函数
java·linux·c语言·数据库·经验分享·笔记
ItKevin爱java22 分钟前
JDBC中如何处理数据库连接超时和SQL超时?
数据库·sql
2401_8576226623 分钟前
【SQL Server高可用性全解】构建永不宕机的数据库解决方案
数据库·oracle
youhebuke22531 分钟前
SQLAlchemy pool_pre_ping
数据库·oracle·sqlalchemy
白菜!!!43 分钟前
SQL INSERT批量插入方式
数据库·sql·mysql·mybatis
不如小布.44 分钟前
MYSQL篇二:数据库的操作
数据库·mysql·oracle
屿小夏.1 小时前
【SQL】已解决:SQL错误(15048): 数据兼容级别有效值为100、110或120
数据库·sql·oracle
小的~~1 小时前
大数据面试题之数据库(2)
大数据·数据库
醇氧1 小时前
【MySQL】MySQL 9.0悄悄的来了
数据库·mysql
Java追光着1 小时前
谷粒商城笔记-03-分布式基础概念
笔记·分布式·谷粒商城