分布式项目使用Redis实现数据库对象自增主键ID

hello。大家好,我是灰小猿,一个超会写bug的程序猿!

在分布式项目中,数据表的主键ID一般可能存在于UUID或自增ID这两种形式,UUID好理解而且实现起来也最容易,但是缺点就是数据表中的主键ID是32位的字符串,在大数据查询等情况下性能会相对比较差,所以在需求允许的情况下,我们通常会优先考虑使用自增ID来代替UUID使用。

在分布式项目中如果你的数据表的主键ID是自增ID,那么常见的生成对象主键ID的方式有:

  • 雪花算法

    • 优点:实现简单

    • 缺点:生成ID较长、生成ID不连续,会造成ID浪费

  • 采用框架自带的ID生成器,如MybatisPlus的@AutoID

    • 优点:依赖框架,实现简单

    • 缺点:无法在插入对象前获取到对象的主键ID

  • 采用Redis缓存生成主键ID等

    • 优点:生成的ID连续,数据在表中的可读性好

    • 缺点:借助Redis缓存,实现相比前两种较复杂

通过雪花算法生成对象主键ID的方式我们在之前的文章中已经介绍过了,这篇文章我们主要介绍如何通过Redis来实现生成对象自增ID的方法。

1、缓存实现原理

通过Redis实现对象自增ID的方式,主要是通过Redis的INCRINCRBY 命令来对键值进行递增操作,从而实现计数器的功能,主要原因是Redis 是单线程模型,所有命令都是原子操作,不会发生竞态条件,

在使用时要留意以下特点与注意事项

  1. 原子性INCRINCRBYINCRBYFLOAT 命令都是原子性的,这意味着如果多个客户端同时执行这些命令,Redis 会保证每个命令的执行不会发生竞态条件。

  2. 数据类型要求:这些命令要求操作的值是整数(INCRINCRBY)或浮点数(INCRBYFLOAT)。如果键对应的值不是数值类型,Redis 会返回错误。

  3. 性能:Redis 是单线程的,所有命令都是原子操作,因此在高并发环境下执行这些命令时,性能表现非常好。

  4. 键不存在的情况:如果执行 INCRINCRBY 时,键不存在,Redis 会自动创建这个键并初始化其值为 0,然后进行递增。

2、Redis工具类实现ID自动生成

了解到通过Redis实现的原理之后,就是如何设计缓存以保证每张表的数据之间不会相互影响,

以user表为例,对应的model为UserModel,那么我们就可以将model名称作为key,当前已有的最大的ID作为value来进行存储,因为每一张表对应的model名称都是不一样的,所以这里一定不会出现key重复的情况,

下面是一些在Redis中生成和获取对象自增ID的工具方法,方便新建对象时直接调用

@Component("redisUtils")
@RequiredArgsConstructor
@Slf4j
public class RedisUtils implements InitializingBean {private final StringRedisTemplate stringRedisTemplate;private static RedisUtils redisUtils;@Overridepublic void afterPropertiesSet() {redisUtils = this;}/*** 获取自增ID*/public static <T> Integer getIncr(Class<T> tClass) {try {String key = tClass.getName();Long increment = redisUtils.stringRedisTemplate.opsForValue().increment(key, 1);return Math.toIntExact(increment);} catch (Exception e) {e.printStackTrace();throw new RedisSystemException(e.getMessage(), e);}}/*** 获取自增ID,指定递增长度,用于批量创建对象时,只请求一次Redis* 如批量创建五个对象,delta等于5*/public static <T> Integer getIncr(Class<T> tClass, int delta) {try {String key = tClass.getName();Long increment = redisUtils.stringRedisTemplate.opsForValue().increment(key, delta);return Math.toIntExact(increment);} catch (Exception e) {e.printStackTrace();throw new RedisSystemException(e.getMessage(), e);}}/*** 缓存预热,用于项目启动时初始化Redis中各个表的最大ID*/public static <T> long incrPreheat(Class<T> tClass, Long maxValue) {if (maxValue == null) {maxValue = 0L;}if (maxValue < 0) {//异常抛出,预热的初始值不能小于0}Integer incr = getIncr(tClass);if (maxValue <= incr) {return incr;}return getIncr(tClass, (int) (maxValue - incr));}
}

解释:InitializingBean是Spring提供的拓展性接口,InitializingBean接口为bean提供了属性初始化后的处理方法,它只有一个afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。

3、缓存预热

缓存预热是指在项目启动时,将每一张表当前最大的主键ID预先加载到Redis中,这样在后面使用的时候,就可以直接从Redis中获取下一次ID即可,不需要再去访问数据库查询最大ID,缓存预热一般会建立在Redis专门的初始化类中,以便在启动项目时可以运行该类,具体如下:

/*** 缓存预热** @author hxy*/
@Component
@DependsOn("redisUtils")
@RequiredArgsConstructor
public class RedisInit {private final UserMapper userMapper;/*** 获取每一张表中当前的最大ID,然后预热到redis中*/@PostConstructpublic void init() {RedisUtils.incrPreheat(UserModel.class, userMapper.maxId());}
}

解释:@DependsOn注解可以定义在类和方法上,意思是我这个组件要依赖于另一个组件,也就是说被依赖的组件会比该组件先注册到IOC容器中。

解释:@PostConstruct注解

在Java中,@PostConstruct注解,通常用于标记一个方法,它表示该方法在类实例化之后(通过构造函数创建对象之后)立即执行。

加上@PostConstruct注解的方法会在对象的所有依赖项都已经注入完成之后执行。通过使用@PostConstruct注解,我们可以确保在对象完全创建和初始化之后才执行这些操作。这个注解通常用在依赖注入(Dependency Injection)的框架中,例如Spring。

@PostConstruct 注解可以用在任何类的方法上,但它最常用于标记在 Spring Framework 中的 Bean 类中的初始化方法。

4、model层封装调用

在实例化一个model对象的时候,要将model的主键ID进行赋值,这个时候就要从Redis中获取到当前对象应该对应的主键ID,我们可以在model类中构建一个newInstance方法或有参构造,来专门的生成需要赋值主键ID的对象,在方法中调用redis工具类的getIncr方法,获取到最新最小未使用的主键ID并赋值给新建的model即可。

@Getter
@Setter
public class UserModel {private Integer id;private String name;private Integer age;private String tel;public UserModel() {}/*** 提供一个能够生成自增ID的有参构造,需要生成自增ID时调用*/public UserModel(boolean autoId) {if (autoId) {this.id = RedisUtils.getIncr(UserModel.class);}}
}

如果你是需要批量创建对象并且给对象赋值主键ID的情况,可以按照如下方式使用,这样可以只调用一次Redis,避免重复调用Redis影响性能。

@Service
@RequiredArgsConstructor
public class UserServiceImpl {private final UserMapper userMapper;public void batchAddUser(){int addNumber = 10;//提前生成自定跨度的ID,比如之前最大ID是6,获取后得到的incr是16Integer incr = RedisUtils.getIncr(UserModel.class, addNumber);List<UserModel> userModels = new ArrayList<>();for (int i = 0; i < addNumber; i++) {UserModel userModel = new UserModel();//每一个主键ID依次递减使用userModel.setId(incr--);userModels.add(userModel);}userMapper.insertBatch(userModels);}
}

至此,通过Redis来获取对象自增ID的方法已经完成,如果在使用过程中有其他场景需求,可以对redisUtils中的方法进行拓展即可。

我是灰小猿,我们下期见!

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

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

相关文章

C++系统教程008-运算符与表达式

1.运算符与表达式 基本数据类型知道后&#xff0c;就是操作数据。要操作数据&#xff0c;就必须使用运算符和表达式。接下来就是C运算符和表达式的相关知识点&#xff0c; 赋值运算算术运算关系运算逻辑运算逗号运算位运算移位运算sizeof运算数据类型自动转换和强制转换 1.1…

LCR 006. 两数之和 II - 输入有序数组

一.题目&#xff1a; LCR 006. 两数之和 II - 输入有序数组 - 力扣&#xff08;LeetCode&#xff09; 二.我的原始解法-暴力解法超时&#xff1a; class Solution: def twoSum(self, numbers: List[int], target: int) -> List[int]: # 暴力解法 result [] for i in rang…

提供html2canvas+jsPDF将HTML页面以A4纸方式导出为PDF后,内容分页时存在截断的解决思路

前言 最近公司有个系统要做一个质量报告导出为PDF的需求&#xff0c;这个报表的内容是固定格式&#xff0c;但是不固定内容多少的&#xff0c;网上找了很多资料&#xff0c;没有很好的解决我的问题&#xff0c;pdfmakde、还有html2CanvasjsPDF以及Puppeteer无头浏览器的方案都不…

UPLOAD LABS | UPLOAD LABS 靶场初识

关注这个靶场的其它相关笔记&#xff1a;UPLOAD LABS —— 靶场笔记合集-CSDN博客 0x01&#xff1a;UPLOAD LABS 靶场简介 UPLOAD LABS 靶场是一个专门用于学习文件上传漏洞攻击和防御的靶场。它提供了一系列文件上传漏洞的实验环境&#xff0c;用于帮助用户了解文件上传漏洞的…

探索Python词云库WordCloud的奥秘

文章目录 探索Python词云库WordCloud的奥秘1. 背景介绍&#xff1a;为何选择WordCloud&#xff1f;2. WordCloud库简介3. 安装WordCloud库4. 简单函数使用方法5. 应用场景示例6. 常见Bug及解决方案7. 总结 探索Python词云库WordCloud的奥秘 1. 背景介绍&#xff1a;为何选择Wo…

2024年9月中国电子学会青少年软件编程(Python)等级考试试卷(六级)答案 + 解析

一、单选题 1、下面代码运行后出现的图像是&#xff1f;&#xff08; &#xff09; import matplotlib.pyplot as plt import numpy as np x np.array([A, B, C, D]) y np.array([30, 25, 15, 35]) plt.bar(x, y) plt.show() A. B. C. D. 正确答案&#xff1a;A 答案…

gethostname():域名解析

网络信息检索函数 gethostname()获取主机名getpeername()获取与套接口相连的远程协议地址getsockname()获取本地套接口协议地址gethostbyname()根据主机名取得主机信息,endhostent()gethostbyaddr()根据主机地址取得主机信息getprotobyname()根据协议名取的主机…

构建Ceph分布式文件共享系统:手动部署指南

#作者:西门吹雪 文章目录 micro-Services-TutorialCeph分布式文件共享方案部署Ceph集群使用CephCeph在kubernetes集群中的使用 micro-Services-Tutorial 微服务最早由Martin Fowler与James Lewis于2014年共同提出&#xff0c;微服务架构风格是一种使用一套小服务来开发单个应…

React Native 调试指南

写在前面 在 React Native 开发中&#xff0c;调试是不可避免的一部分。无论你是新手还是有经验的开发者&#xff0c;了解如何有效地调试你的应用程序都非常重要。本文将介绍 React Native 的调试方法&#xff0c;包括使用开发者工具、调试原生代码、调试发行版本以及其他调试…

深度学习与持续学习:人工智能的未来与研究方向

文章目录 1. 持续学习与深度学习1.1 深度学习的局限1.2 持续学习的定义 2. 目标与心智2.1 奖励假说2.2 心智的构成 3. 对研究方法的建议3.1 日常写作记录3.2 中立对待流行趋势 1. 持续学习与深度学习 1.1 深度学习的局限 深度学习注重“瞬时学习”&#xff0c;如ChatGPT虽在语…

数据分析——读取

读取(以ysck.txt文件为例)

渗透测试笔记—window基础

声明&#xff1a; 学习视频来自B站up主 【泷羽sec】有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&am…

【Axure高保真原型】天气模板

今天和大家分享天气模板的原型模板&#xff0c;里面包括晴天、多云、阴天、小雨、大雨、暴雨、强雷阵雨、小雪、中雪、大雪、暴雪、雨夹雪、微风、强风、狂风、龙卷风、轻雾、大雾等&#xff0c;后续也可以自行添加。 这个模板是用中继器制作的&#xff0c;所以使用也很方便&a…

java内存管理介绍

1. 堆&#xff08;Heap&#xff09;&#xff1a; • 这是Java对象存储的主要区域&#xff0c;类似于一个大仓库&#xff0c;用于存放所有动态分配的对象实例。堆内存由JVM自动管理&#xff0c;包括对象的分配和回收。 2. 栈&#xff08;Stack&#xff09;&#xff1a; • 每个线…

neo4j图数据库community-5.50创建多个数据库————————————————

1.找到neo4J中的conf文件&#xff0c;我的路径是&#xff1a;D:\Program Files\neo4j-community-5.5.0-windows\neo4j-community-5.5.0\conf 这里找自己的安装路径&#xff0c; 2.用管理员模式打开conf文件&#xff0c;右键管理员&#xff0c;记事本或者not 3.选中的一行新建一…

《Unity Shader 入门精要》高级纹理

立方体纹理 图形学中&#xff0c;立方体纹理&#xff08;Cubemap&#xff09;是环境映射&#xff08;Environment Mapping&#xff09;的一种实现方法。环境映射可以模拟物体周围的环境&#xff0c;而使用了环境映射的物体可以看起来像镀了层金属一样反射出周围的环境。 对立…

【逐行注释】自适应观测协方差R的AUKF(自适应无迹卡尔曼滤波,MATLAB语言编写),附下载链接

文章目录 自适应R的UKF逐行注释的说明运行结果部分代码各模块解释 自适应R的UKF 自适应无迹卡尔曼滤波&#xff08;Adaptive Unscented Kalman Filter&#xff0c;AUKF&#xff09;是一种用于状态估计的滤波算法。它是基于无迹卡尔曼滤波&#xff08;Unscented Kalman Filter&…

11.26 深度学习-激活函数

# 激活函数的作用是在隐藏层引入非线性&#xff0c;使得神经网络能够学习和表示复杂的函数关系&#xff0c;使网络具备**非线性能力**&#xff0c;增强其表达能力。 # 没有激活函数的话 每次神经元做的都是线性变换 (矩阵相乘)换为图像就是一直在旋转 放大 缩小 这样就始终相…

Scala习题

姓名&#xff0c;语文&#xff0c;数学&#xff0c;英语 张伟&#xff0c;87&#xff0c;92&#xff0c;88 李娜&#xff0c;90&#xff0c;85&#xff0c;95 王强&#xff0c;78&#xff0c;90&#xff0c;82 赵敏&#xff0c;92&#xff0c;88&#xff0c;91 孙涛&#xff0c…

【rustdesk】客户端和服务端的安装和部署(自建服务器,docker,远程控制开源软件rustdesk)

【rustdesk】客户端和服务端的安装和部署&#xff08;自建服务器&#xff0c;docker&#xff09; 一、官方部署教程 https://rustdesk.com/docs/zh-cn/client/mac/ 官方服务端下载地址 https://github.com/rustdesk/rustdesk-server/releases 我用的docker感觉非常方便&am…