redis分布式ID解决方案示例详解

这里是关于“redis分布式ID解决方案示例详解”的完整攻略,包含两条示例说明:

简介

在分布式系统中,生成唯一ID是非常常见的需求。一般而言,生成ID需要保证唯一性、递增性、高可用性和持久化等特性。而使用分布式内存数据库 Redis 来实现分布式ID是比较常见的方案之一。

本文将介绍如何通过 Redis 来实现分布式ID,在此基础上与 Snowflake 算法结合产生更完善的解决方案。

环境准备

我们将使用 Java 语言来实现这个示例。在开始之前,你需要安装以下环境:

  • JDK 1.8或以上版本
  • Maven
  • Redis

方案一:基于Redis的基本ID生成

实现思路

我们可以借助Redis的INCR命令,它会原子性地将指定键的整数值加1。这样我们就可以将一个键定为计数器,每次使用INCR命令就可以保证生成一个唯一的ID。

代码实现

先来看看使用Jedis客户端实现的生成ID的代码实现:

public class RedisIDGenerator {
    private static final String KEY_PREFIX = "REDIS_ID_";

    private static Jedis jedis = new Jedis("localhost", 6379);

    public static Long generate() {
        String key = KEY_PREFIX + System.currentTimeMillis();
        return jedis.incr(key);
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println(RedisIDGenerator.generate());
        }
    }
}

首先,定义了一个PREFIX常量,默认为“REDIS_ID_”。使用Jedis客户端连接了Redis数据库。generate()方法中,首先生成一个唯一的key,key的值为PREFIX+当前时间戳。然后使用jedis.incr()方法生成一个唯一的ID。

最后,使用main()方法来检测RedisIDGenerator的工作是否正常。

测试运行

通过运行测试代码,可以得到以下结果:

1
2
3
4
5
6
7
8
9
10

注意事项

  • Redis 中只支持数字类型的Key,因此创建的key必须为数字类型。
  • 由于 Redis 的 INCR 命令每次自增默认会返回加 1 后的新值,所以我们不需要担心线程安全的问题。

方案二:使用Snowflake算法与Redis配合生成ID

实现思路

Snowflake 算法是 Twitter 提出的一个分布式ID生成算法。该算法生成的ID是一个 64 位的长整型数字,结构如下:

+------------------------------------------------------------+
| 1 Bit Unused | 41 Bit Timestamp | 10 Bit NodeID | 12 Bit Sequence |
+------------------------------------------------------------+

其中,每段的意义如下:

  • Unused:未使用,暂时保留。
  • Timestamp:时间戳,这里使用了 41 位,可以使用 69 年(从 1970 年到 2039 年)。
  • NodeID:节点ID,用于支持分布式部署,10 位节点ID支持 1024 个节点。
  • Sequence:序列号,用来支持高并发,12位表示每个节点可以并发生成的序列号数,支持最大的峰值的数量是每毫秒 4096 (2的12次方)。

所以我们需要借助 Redis 存储我们的序列号,其中每个节点都可以对应一个序列号 key 值,当一个节点需要生成一个新的 ID 时,读取自己对应的序列号 key,然后对其加 1 并存回 Redis 中,同时使用 Snowflake 算法生成 64 位分布式 ID 。

代码实现

SnowflakeIDGenerator.java

public class SnowflakeIDGenerator {
    private static final long START_STMP = 1494064000000L; // 初始时间戳,毫秒级别,应该是我们系统上线的时间
    private static final long SEQUENCE_BIT = 12L; // 序列号占用的位数
    private static final long NODE_BIT = 10L; // 节点标识占用的位数
    private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BIT); // 序列号的掩码
    private static final long NODE_MASK = ~(-1L << NODE_BIT); // 节点标识的掩码
    private static final long NODE_LEFT_SHIFT = SEQUENCE_BIT; // 左移量,即需要左移的位数
    private static final long TIMESTAMP_LEFT_SHIFT = NODE_LEFT_SHIFT + NODE_BIT;
    private static long lastStmp = -1L;
    private static long sequence = 0L;
    private static long nodeCode;

    private static Jedis jedis = new Jedis("localhost", 6379);

    static {
        // 获取当前机器的机器标识码,可以使用机器的IP地址或者WorkerId来实现。
        // 如果使用机器IP,可能会存在多网卡和多IP的情况,可以考虑使用第三方库获取MAC地址,
        // 或者使用Zookeeper或Redis等外部存储来解决。
        nodeCode = Long.valueOf(System.getProperty("worker.id", "1"));
    }

    private static long getNewTimestamp() {
        long curMillions = System.currentTimeMillis();
        while (curMillions <= lastStmp) {
            curMillions = System.currentTimeMillis();
        }
        return curMillions;
    }

    private static long getNextSequence() {
        if (jedis == null) {
            return 0;
        }
        String key = "ID_SEQUENCE_" + nodeCode;
        Long res = jedis.incr(key);
        if (res == null || res <= 0) {
            throw new IllegalStateException("无法获取序列号");
        }
        jedis.expire(key, 60 * 60 * 24);
        return res;
    }

    public static long generate() {
        long curStmp = getNewTimestamp();
        if (curStmp < lastStmp) {
            throw new RuntimeException("时间戳错误");
        }
        if (curStmp == lastStmp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            if (sequence == 0) {
                curStmp = getNewTimestamp();
            }
        } else {
            sequence = 0L;
        }

        lastStmp = curStmp;

        return ((curStmp - START_STMP) << TIMESTAMP_LEFT_SHIFT) |
                (nodeCode << NODE_LEFT_SHIFT) |
                sequence;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println(SnowflakeIDGenerator.generate());
        }
    }
}

首先,我们定义了一个 REDIS)SEQUENCE_${nodeCode} 的计数器。每个节点需要创建一个自己对应的计数器,其中 nodeCode 是节点的标识符,表示该节点在分布式架构中的ID。 然后我们实现了getNewTimestamp方法,用于获取 64位ID 的时间戳部分。

getNextSequence方法则是从 Redis 中获取节点的序号,保证序号的唯一性和线程安全,并添加了序号自动过期时间的功能。

generate方法就是将 Snowflake 算法和 Redis 计数器结合在一起,生成出全局唯一的 64 位长整型数字。

测试运行

通过运行测试代码,可以得到以下结果:

1019402740467385344
1019402740467385345
1019402740467385346
1019402740467385347
1019402740467385348
1019402740467385349
1019402740467385350
1019402740467385351
1019402740467385352
1019402740467385353

注意事项

  • nodeCode 的值必须为唯一标识的正整数。
  • 因为 Redis 使用同步锁,只会有一个客户端可以进行incr和decr操作,所以这样实现是线程安全的。

总结

本文介绍了两种基于 Redis 的分布式 ID 生成方案。第一种方案使用 Redis 的 INCR 命令生成 ID,最大的优点是简单易用。第二种方案结合了 Snowflake 算法和 Redis,既保证了 ID 唯一性、递增性和持久性,也避免了性能、效率等问题,并解决了跨节点的 ID 重复问题。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:redis分布式ID解决方案示例详解 - Python技术站

(0)
上一篇 2023年5月16日
下一篇 2023年5月16日

相关文章

  • MongoDB与MySQL的操作对比表及区别介绍

    MongoDB与MySQL的操作对比表及区别介绍 MongoDB和MySQL的基本区别 MongoDB和MySQL都是常用的数据库系统,虽然它们都是关系型数据库,但它们在很多方面存在一些明显的区别: 数据结构:MySQL是关系型数据库,使用的是表格结构,而MongoDB则是文档型数据库,使用的是类似JSON格式的BSON。 可扩展性:MongoDB支持更好的…

    MongoDB 2023年5月16日
    00
  • MongoDB释放空闲空间的几种常用方法

    针对”MongoDB释放空闲空间的几种常用方法”,我准备详细讲解以下内容: 1. MongoDB释放空闲空间概述 在MongoDB中,每当文档被删除或者更新时,MongoDB会把这个文档占用的存储空间标记为“可重用”的空间。虽然这个空间看起来是空闲的,但是它被MongoDB存储引擎缓存起来以供后续使用。虽然这种机制的好处在于提高了MongoDB的写入性能,但…

    MongoDB 2023年5月16日
    00
  • Spring Boot集成mongodb数据库过程解析

    下面我将为你详细讲解“Spring Boot集成mongodb数据库过程解析”的完整攻略,过程中包含了两条示例说明。 1. 确认环境 在集成MongoDB数据库之前,需要先准备好开发环境。具体需要确认的有: 是否安装了JDK1.8及以上版本。 是否安装MongoDB数据库及其驱动。 是否在项目中添加了Spring Boot和MongoDB的依赖。 2. 添加…

    MongoDB 2023年5月16日
    00
  • SpringBoot MongoDB详细使用教程

    SpringBoot MongoDB详细使用教程 本教程将介绍如何在SpringBoot应用程序中使用MongoDB数据库。MongoDB是一种非关系型(NoSQL)数据库,它使用文档而不是表来管理数据。SpringBoot的自动配置使得使用MongoDB非常简单。 准备工作 首先,在项目的pom.xml文件中添加以下依赖: <dependency&g…

    MongoDB 2023年5月16日
    00
  • 详解Vue开发网站seo优化方法

    详解Vue开发网站SEO优化方法 前言 在Vue.js的开发中,如何进行SEO(搜索引擎优化)一直是开发者关心的问题之一。因为Vue.js的渲染方式是通过在客户端逐一解析和渲染的方式达到展示效果,对于搜索引擎来说,并不能够很好的解析和抓取网页的内容和结构,从而影响到网站的SEO效果。 本文将详细讲解在Vue.js开发中如何进行SEO优化,并通过两个示例说明如…

    MongoDB 2023年5月16日
    00
  • mongodb监控工具mongostat的使用及命令详解

    下面是关于“mongodb监控工具mongostat的使用及命令详解”的完整攻略,包含两条示例说明。 mongostat是什么 mongostat是MongoDB自带的用于监控MongoDB服务器状态的命令行工具。它可以以统计数据、表格和图形的形式展示在终端中。通过mongostat,我们可以监控MongoDB服务器的常见性能指标、进程、连接、锁、操作等情况…

    MongoDB 2023年5月16日
    00
  • MongoDB查询文档使用方法(详解版)

    MongoDB是一款NoSQL数据库,使用它进行查询文档与关系型数据库有较大的区别,下文将带大家了解MongoDB查询文档的完整方法。 首先,我们需要安装MongoDB,接着选择一种适合自己的编程语言,这里选择Python为例。 连接MongoDB 连接MongoDB需要用到pymongo库,如果您还没安装,可以通过以下命令进行安装: $ pip3 inst…

    MongoDB 2023年3月14日
    00
  • PowerShell使用Remove-Item命令删除文件、注册表项介绍

    当需要删除文件或注册表项时,PowerShell提供了Remove-Item命令。下面,我们来详细讲解PowerShell如何使用这个命令来删除文件和注册表项。 删除文件 示例1 假设我们要删除D盘根目录下的一个名为test.txt的文件,则命令如下: Remove-Item D:\test.txt 运行以上命令后,系统会在D盘根目录下删除test.txt文…

    MongoDB 2023年5月16日
    00
合作推广
合作推广
分享本页
返回顶部