【SpringBoot篇】基于Redis实现生成全局唯一ID的方法

news2025/1/24 17:45:10

文章目录

  • 🍔生成全局唯一ID
  • 🌹为什么要生成全局唯一id
  • 🌺生成全局id的方法
  • ✨代码实现

在这里插入图片描述

🍔生成全局唯一ID

是一种在分布式系统下用来生成全局唯一id的工具
请添加图片描述

在项目中生成全局唯一ID有很多好处,其中包括:

  • 数据库主键:在数据库中,唯一ID可以作为主键,确保每条记录的唯一性,便于快速检索和更新数据。
  • 分布式系统:在分布式系统中,生成全局唯一ID可以避免不同节点生成相同的ID,确保整个系统的数据一致性。
  • 日志追踪:在日志系统中,给每条日志分配唯一ID可以方便进行日志的追踪和分析。
  • 安全性:某些场景下,需要对数据进行加密或者数据权限控制,唯一ID可以作为安全机制的一部分。
  • 缓存键值:在缓存系统中,使用唯一ID作为键值可以避免不同数据之间的冲突。
  • 数据分片:在分布式存储系统中,唯一ID可以作为数据分片的标识,便于数据的存储和查询。

总之,生成全局唯一ID有助于提高系统的可用性、数据的完整性和安全性,同时也方便数据的管理和分析。因此,在许多项目中都会需要生成全局唯一ID来满足系统的需求。

🌹为什么要生成全局唯一id

生成全局唯一ID的主要目的是确保系统中的实体(如对象、记录、消息等)具有唯一性标识。以下是一些常见的原因:

  • 数据唯一性:全局唯一ID可以确保在系统中每个实体都有一个独一无二的标识符,避免数据冲突和重复。
  • 数据库索引:全局唯一ID通常用作数据库表的主键或索引,以提高数据查询和检索的效率。
  • 分布式系统:在分布式系统中,各个节点可能同时生成ID,为了避免ID的冲突,需要使用全局唯一ID算法确保整个系统中的ID唯一性。
  • 数据跟踪与关联:通过给实体分配唯一ID,可以轻松追踪和关联数据,例如日志记录、事务管理、审计等。
  • 安全性和权限控制:全局唯一ID可以用于确保数据的安全性和权限控制,限制对特定实体的访问和操作。
  • 缓存与缓存失效:在缓存系统中,使用全局唯一ID作为缓存键,可以确保不同实体之间的键不会冲突,并且在缓存失效时能够正确地重新加载数据。

总结来说,生成全局唯一ID有助于确保数据的唯一性、提高系统的可用性和性能,并支持数据跟踪、安全性和权限控制等功能。这在许多系统和应用中都是一个重要的需求。

🌺生成全局id的方法

请添加图片描述

✨代码实现

ID生成器的算法如下
在这里插入图片描述

我们要先生成时间戳,在生成序列号,然后进行拼接

package com.hmdp.utils;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

@Component
public class RedisIdWorker {
    /**
     * 开始时间戳
     */
    private static final long BEGIN_TIMESTAMP = 1640995200L;
    /**
     * 序列号的位数
     */
    private static final int COUNT_BITS = 32;

    private StringRedisTemplate stringRedisTemplate;

    public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    public long nextId(String keyPrefix) {
        // 1.生成时间戳
        LocalDateTime now = LocalDateTime.now();
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timestamp = nowSecond - BEGIN_TIMESTAMP;

        // 2.生成序列号
        // 2.1.获取当前日期,精确到天
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        // 2.2.自增长
        long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);

        // 3.拼接并返回
        return timestamp << COUNT_BITS | count;
    }
}

这段代码的 timestamp << COUNT_BITS | count;是怎么算出序列号的
在这里插入图片描述

在这段代码中,timestamp << COUNT_BITS | count 是通过位运算来生成最终的ID值。

首先,timestamp 是时间戳,代表了从开始时间戳到当前时间的秒数差。COUNT_BITS 是序列号的位数,这里是32位。

位运算符 << 是左移操作符,将 timestamp 的二进制表示向左移动 COUNT_BITS 位,就是将时间戳占据高位。这样做是为了给序列号腾出足够的空间

然后,使用位运算符 | 进行按位或操作,将左移后的时间戳与序列号 count 进行按位或操作,合并它们的二进制表示。

最终得到的结果就是一个64位的ID,其中高位是时间戳部分,低位是序列号部分。

编写代码进行测试
在这里插入图片描述

package com.hmdp;

import com.hmdp.entity.Shop;
import com.hmdp.service.impl.ShopServiceImpl;
import com.hmdp.utils.CacheClient;
import com.hmdp.utils.RedisIdWorker;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.StringRedisTemplate;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.hmdp.utils.RedisConstants.CACHE_SHOP_KEY;
import static com.hmdp.utils.RedisConstants.SHOP_GEO_KEY;

@SpringBootTest
class HmDianPingApplicationTests {

    @Resource
    private CacheClient cacheClient;

    @Resource
    private ShopServiceImpl shopService;

    @Resource
    private RedisIdWorker redisIdWorker;

		private ExecutorService es = Executors.newFixedThreadPool(500);

    @Test
    void testIdWorker() throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(300);

        Runnable task = () -> {
            for (int i = 0; i < 100; i++) {
                long id = redisIdWorker.nextId("order");
                System.out.println("id = " + id);
            }
            latch.countDown();
        };
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 300; i++) {
            es.submit(task);
        }
        latch.await(); //等待上面的结束
        long end = System.currentTimeMillis();
        System.out.println("time = " + (end - begin));
    }

    @Test
    void testSaveShop() throws InterruptedException {
        Shop shop = shopService.getById(1L);
        cacheClient.setWithLogicalExpire(CACHE_SHOP_KEY + 1L, shop, 10L, TimeUnit.SECONDS);
    }
}

在技术的道路上,我们不断探索、不断前行,不断面对挑战、不断突破自我。科技的发展改变着世界,而我们作为技术人员,也在这个过程中书写着自己的篇章。让我们携手并进,共同努力,开创美好的未来!愿我们在科技的征途上不断奋进,创造出更加美好、更加智能的明天!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1329977.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在Next.js和React中搭建Cesium项目

在Next.js和React中搭建Cesium项目&#xff0c;需要确保Cesium能够与服务端渲染(SSR)兼容&#xff0c;因为Next.js默认是SSR的。Cesium是一个基于WebGL的地理信息可视化库&#xff0c;通常用于在网页中展示三维地球或地图。下面是一个基本的步骤&#xff0c;用于在Next.js项目中…

信号与线性系统翻转课堂笔记8——周期信号的频谱

信号与线性系统翻转课堂笔记8——周期性信号的频谱 The Flipped Classroom8 of Signals and Linear Systems 对应教材&#xff1a;《信号与线性系统分析&#xff08;第五版&#xff09;》高等教育出版社&#xff0c;吴大正著 一、要点 &#xff08;1&#xff0c;重点&#…

【github】github设置项目为私有

点击setting change to private 无脑下一步

【Proteus/8086】swjtu西南交大微机与接口技术实验:计时器

实验内容: 计时器基本功能: 1)CPU必须用8086 2)计时器最小计时单位为秒。 3)以00:00格式显示计时,前2位表示分钟,后2位表示秒。 4)计时器是正计时方式 5)有暂停、继续计时功能 6&#xff09;有复位计时功能 7&#xff09;每次按下暂停键&#xff0c;能显示计时间隔时间 参考…

uniapp中使用封装步骤条组件

针对步骤条封装完终于清清楚楚啦 先看效果&#xff1a; 附上代码&#xff1a;使用可直接复用&#xff1a;数据是写在了当前组件中&#xff0c;如有必须&#xff0c;可以使用其中的props传值stepInfos传递相应的数据&#xff0c;根据steps步数就可以控制走到哪一步啦 <temp…

双向数据绑定是什么

一、什么是双向绑定 我们先从单向绑定切入单向绑定非常简单&#xff0c;就是把Model绑定到View&#xff0c;当我们用JavaScript代码更新Model时&#xff0c;View就会自动更新双向绑定就很容易联想到了&#xff0c;在单向绑定的基础上&#xff0c;用户更新了View&#xff0c;Mo…

在 Windows 中关闭指定端口的方法

方法一&#xff1a;使用命令行&#xff08;Command Prompt&#xff09; 查找端口占用情况 打开命令提示符&#xff08;Command Prompt&#xff09;并输入以下命令来查找占用指定端口的进程&#xff1a; netstat -aon|findstr "<port_number>" 这里的 <p…

【笔记】Spring的循环依赖

Spring的循环依赖 ObjectFactory:函数式接口&#xff0c;可以将lambda表达式作为参数放在方法的实参种&#xff0c;在方法执行的时候&#xff0c;并不会实际的调用当前lambda表达式&#xff0c;只有在调用getObject方法的时候才回去调用lambda表达式 为什么spring要用三级缓存…

一周工作问题总结(2023.12.18-2023.12.22)

一周工作问题总结 1. 接口调用频率2. 汉字在数据库中占用字节问题3. Map在循环中修改自己的key与value4. Group BY5.递归同步数据6.代码移动Idea飘红 1. 接口调用频率 供应商给的接口可以每秒调用5-10次&#xff0c;那么我为了保险每秒调用5次&#xff0c;为了防止过度调用接口…

SpringBoot+SSM项目实战 苍穹外卖(7)(Spring Cache)

继续上一节的内容&#xff0c;本节实现缓存菜品、缓存套餐、添加购物车、查看购物车和清空购物车功能。 目录 缓存菜品缓存套餐(基于Spring Cache)EnableCaching、Cacheable、CachePut和CacheEvictSpring Cache实现缓存套餐 添加购物车查看购物车清空购物车 缓存菜品 用户端小…

nodejs+vue+ElementUi医院预约挂号系统3e3g0

本医院预约挂号系统有管理员&#xff0c;医生和用户。该系统将采用B/S结构模式&#xff0c;使用Vue和ElementUI框架搭建前端页面&#xff0c;后端使用Nodejs来搭建服务器&#xff0c;并使用MySQL&#xff0c;通过axios完成前后端的交互 管理员功能有个人中心&#xff0c;用户管…

使用 Elasticsearch 检测抄袭 (一)

作者&#xff1a;Priscilla Parodi 抄袭可以是直接的&#xff0c;涉及复制部分或全部内容&#xff0c;也可以是释义的&#xff0c;即通过更改一些单词或短语来重新表述作者的作品。 灵感和释义之间是有区别的。 即使你得出类似的结论&#xff0c;也可以阅读内容&#xff0c;获得…

java练习题之多态练习

1&#xff1a;关于多态描述错误的是(D) A. 父类型的引用指向不同的子类对象 B. 用引用调用方法&#xff0c;只能调用引用中声明的方法 C. 如果子类覆盖了父类中方法&#xff0c;则调用子类覆盖后的方法 D. 子类对象类型会随着引用类型的改变而改变 2&#xff1a;class Supe…

【ONE·MySQL || 基础介绍】

总言 主要内容&#xff1a;MySQL在Centos 7下的安装&#xff08;主要学习相关指令语句&#xff0c;理解安装操作是在做什么&#xff09;、对MySQL数据库有一个基础认识。 文章目录 总言0、MySQL的安装与卸载&#xff08;Centos 7&#xff09;0.1、MySQL的卸载0.1.1、卸载不必要…

C练习题13答案

单项选择题(本大题共20小题,每小题2分,共40分。在每小题给出的四个备选项中,选出一个正确的答案,并将所选项前的字母填写在答题纸的相应位置上。) 1.结构化程序由三种基本结构组成、三种基本结构组成的算法是(A) A.可以完成任何复杂的任务 B. 只能完成部分复杂的任务 C. 只能完…

没通知就降公积金的脉脉,面试考什么?

写在前面 刚过去的周末&#xff0c;又一家神奇公司映入眼帘&#xff1a; 怎么说呢&#xff1f;自家的瓜出现在自家&#xff0c;至少说明脉脉没有跟微博似的限流。 自家做职场社交&#xff0c;就用自家帖子做全员通知&#xff0c;脉脉你就这么“降本”的吗&#xff1f;&#x1f…

【OCR识别】PaddleHub实现验证码识别

文章目录 前言无脑安装使用PaddleHub寻找预训练模型库完整代码效果图 前言 前面有篇文章介绍了 【网站验证码识别】 &#xff0c;但是其是利用 tesseract 工具的命令行来实现图片内容的识别。 这几天我突然想起&#xff0c;大学时参加百度 AI 比赛用过其 PaddleHub 框架&…

计算机网络 应用层上 | 域名解析系统DNS 文件传输协议FTP,NFS 万维网URL HTTP HTML

文章目录 1 域名系统DNS1.1 域名vsIP&#xff1f;1.2 域名结构1.3 域名到IP的解析过程域名服务器类型 2 文件传送协议2.1 FTP 文件传输协议2.2 NFS 协议2.3 简单文件传送协议 TFTP 3 万维网WWW3.1 统一资源定位符URL3.2 超文本传送协议HTTP3.2.1 HTTP工作流程3.2.2 HTTP报文结构…

flask之文件管理网页(上传,下载,搜索,登录,注册) -- 翔山 第一版

前面说要做一个可以注册&#xff0c;登录&#xff0c;搜索&#xff0c;上传下载的网页&#xff0c;初版来了 第一版主代码 from flask import request, Flask, render_template, redirect, url_for, send_from_directory import bcrypt import ossavePath os.path.join(os.ge…

17个常用经典数据可视化图表与冷门图表

数据可视化是创建信息图形表示的过程。随着可视化技术的飞速发展&#xff0c;可以利用强大的可视化工具选择合适的数据可视化图表来展示数据。以下专业人士都应该知道的一些最重要的数据可视化图表。 常见数据可视化图表 饼图 饼图是最常见和最基本的数据可视化图表之一。饼图…