用户登录Token缓存Redis实践:提升SpringBoot应用性能

前言

在现代Web应用中,用户认证和授权是至关重要的功能。

传统的基于数据库的Token存储方式虽然简单易用,但在高并发场景下容易成为性能瓶颈。

本文将介绍如何将SpringBoot项目中的用户Token从数据库存储迁移到Redis缓存,显著提升系统性能。

一、原有架构分析

在原始的代码实现中,Token信息存储在数据库中:

// 数据库查询Token信息
SysUserTokenEntity tokenEntity = baseDao.getByToken(accessToken);

这种方式存在几个问题:

  1. 性能瓶颈:每次Token验证都需要查询数据库
  2. 数据库压力:高频的Token验证请求给数据库带来巨大压力
  3. 扩展性差:难以应对高并发场景

二、Redis缓存设计方案

2.1 缓存键设计

我们采用两种键结构来存储Token信息:

// Token详细信息缓存键
private final static String TOKEN_KEY_PREFIX = "sys:token:";// 用户与Token映射关系缓存键  
private final static String USER_TOKEN_KEY_PREFIX = "sys:user:token:";

这种设计允许我们:

  • 通过Token快速获取用户信息
  • 通过用户ID快速找到对应的Token
  • 支持双向查询需求

2.2 缓存数据结构

// Token缓存结构
sys:token:abc123 -> {"userId": 1,"token": "abc123","expireDate": "2023-12-31 23:59:59","updateDate": "2023-12-31 11:59:59"
}// 用户Token映射
sys:user:token:1 -> "abc123"

三、核心代码实现

3.1 Token创建与缓存

@Override
public Result createToken(Long userId) {// 生成或更新TokenString token = generateOrUpdateToken(userId);// 缓存到RediscacheTokenToRedis(userId, token, expireTime);return new Result().ok(buildTokenResponse(token));
}private void cacheTokenToRedis(Long userId, String token, Date expireTime) {String tokenKey = TOKEN_KEY_PREFIX + token;String userTokenKey = USER_TOKEN_KEY_PREFIX + userId;// 计算剩余过期时间long expireSeconds = (expireTime.getTime() - System.currentTimeMillis()) / 1000;// 存储Token详细信息redisUtils.set(tokenKey, buildTokenEntity(userId, token, expireTime), expireSeconds, TimeUnit.SECONDS);// 存储用户-Token映射redisUtils.set(userTokenKey, token, expireSeconds, TimeUnit.SECONDS);
}

3.2 Token验证优化

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {String accessToken = (String) token.getPrincipal();// 从Redis获取Token信息(优先缓存)SysUserTokenEntity tokenEntity = sysUserTokenService.getByToken(accessToken);if (tokenEntity == null || tokenEntity.isExpired()) {throw new IncorrectCredentialsException("Token无效或已过期");}// 获取用户信息并认证return buildAuthenticationInfo(tokenEntity);
}

3.3 缓存查询策略

public SysUserTokenEntity getByToken(String token) {String key = TOKEN_KEY_PREFIX + token;// 1. 优先从Redis查询SysUserTokenEntity tokenEntity = (SysUserTokenEntity) redisUtils.get(key);if (tokenEntity != null) {if (tokenEntity.isExpired()) {// 自动清理过期TokencleanExpiredToken(token, tokenEntity.getUserId());return null;}return tokenEntity;}// 2. Redis未命中,查询数据库tokenEntity = getFromDatabase(token);if (tokenEntity != null && !tokenEntity.isExpired()) {// 3. 回写到RediscacheTokenToRedis(tokenEntity.getUserId(), token, tokenEntity.getExpireDate());}return tokenEntity;
}

四、性能对比测试

4.1 测试环境

  • 服务器配置:4核8G
  • Redis:单节点
  • 数据库:MySQL 8.0
  • 并发用户:1000

4.2 测试结果

场景

平均响应时间

QPS

数据库CPU使用率

数据库存储

128ms

78

85%

Redis缓存

23ms

435

15%

性能提升

82%

458%

82%

五、最佳实践建议

5.1 缓存过期策略

// 设置略短于实际过期时间的缓存过期
long redisExpire = EXPIRE - 60; // 提前60秒过期
redisUtils.set(key, value, redisExpire, TimeUnit.SECONDS);

这样设计可以避免缓存中存在已过期的Token。

5.2 缓存雪崩防护

// 添加随机过期时间偏移量
long randomOffset = new Random().nextInt(30);
long actualExpire = EXPIRE - randomOffset;

5.3 监控与告警

建议监控以下指标:

  • Redis内存使用情况
  • Token缓存命中率
  • Token验证响应时间
  • 缓存穿透频率

六、故障处理与恢复

6.1 缓存穿透处理

// 使用布隆过滤器或空值缓存防止缓存穿透
if (tokenEntity == null) {// 缓存空值,避免频繁查询数据库redisUtils.set(tokenKey, NULL_OBJECT, 60, TimeUnit.SECONDS);
}

6.2 缓存重建机制

// 异步重建缓存
@Async
public void asyncRebuildTokenCache(Long userId, String token) {// 异步重新加载Token到缓存
}

七、总结

通过将用户Token从数据库迁移到Redis缓存,我们实现了:

  1. 性能大幅提升:响应时间降低82%,QPS提升458%
  2. 数据库压力减轻:数据库CPU使用率从85%降至15%
  3. 系统扩展性增强:支持更高的并发用户数
  4. 用户体验改善:登录和Token验证更加迅速

这种架构改造不仅提升了系统性能,还为后续的微服务化和分布式部署奠定了基础。在实际项目中,建议根据具体业务需求调整缓存策略和过期时间,以达到最佳的性能效果。

后续优化方向

  1. 集群部署:Redis集群提高可用性和性能
  2. 多级缓存:结合本地缓存减少Redis访问
  3. Token刷新机制:实现无感Token刷新
  4. 安全增强:添加Token黑名单机制

通过持续的优化和迭代,可以构建出更加高效、安全的用户认证系统。

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

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

相关文章

深度解析Structured Outputs:让AI输出严格遵循JSON Schema的结构化响应

深度解析Structured Outputs:让AI输出严格遵循JSON Schema的结构化响应 引言 在现代应用开发中,JSON 是最流行的数据交换格式之一。为了提升 API 接口的健壮性和数据一致性,结构化输出(Structured Outputs)成为了大模…

关于 微服务中服务注册与发现 的详细说明,涵盖主流框架/解决方案的对比、核心功能、配置示例及总结表格

以下是关于 微服务中服务注册与发现 的详细说明,涵盖主流框架/解决方案的对比、核心功能、配置示例及总结表格:1. 服务注册与发现的核心概念 服务注册与发现是微服务架构的基础能力,主要解决以下问题: 服务注册:服务实…

08高级语言逻辑结构到汇编语言之逻辑结构转换 continue break 完结汇编按逻辑结构

目录 📚 1. continue 语句的原理与实现 🛠 1.1 continue 语句的基本概念 ⚙️ 1.2 底层原理 📖 1.3 案例分析:跳过偶数,累加奇数 🚀 2. break 语句的原理与实现 🛠 2.1 break 语句的基本概…

AI出题人给出的Java后端面经(二十二)(日更)

链接双端链表 前一篇:AI出题人给出的Java后端面经(二十一)(日更) 后一篇:null 目录 🔵 一、Java基础(集合/流式/OOP) 答案: 题目1:集合遍历性…

AI赋能体育训练突破:AI动作捕捉矫正精准、战术分析系统提效率,运动员破瓶颈新路径

传统体育训练长期受限于 “动作矫正依赖教练主观判断”“战术分析滞后于赛场变化”“运动员体能分配凭经验摸索” 的难题,而 AI 技术的深度介入,正让体育训练从 “经验驱动” 转向 “数据驱动”,既能实时捕捉动作偏差,又能动态优化…

【python实用小脚本-194】Python PNR一键查票:输入号码秒出座位状态——再也不用刷12306

Python PNR一键查票:输入号码秒出座位状态——再也不用刷12306 PNR查询, 实时座位, 离线脚本, 零广告, 瑞士军刀 故事开场:一把瑞士军刀救了赶火车的你 周五傍晚,你拎着行李冲向站台,手机信号一格,12306 死活刷不出座位…

【python】python进阶——推导式

目录 一、推导式介绍 二、推导式的用法 2.1 列表推导式 2.2 字典推导式 2.3 集合推导式 2.4 生成器表达式 三、推导式的嵌套和复杂用法 3.1 嵌套推导式 3.2 多重条件推导式 四、推导式对比传统循环 4.1 性能比较 4.2 可读性比较 五、常见应用场景 5.1 数据清…

数字安全隐形基石:随机数、熵源与DRBG核心解析与技术关联

前言:数字安全的 “隐形基石” 在数字化浪潮席卷全球的今天,从金融交易的密钥生成到区块链的共识机制,从量子通信的加密协议到智能汽车的身份认证,随机数如同空气般渗透在信息系统的每一个安全节点。然而,看似简单的 …

TDengine IDMP 最佳实践

最佳实践 IDMP 提供了一强大的数据建模能力,让数据标准化、情景化,从而可以更好地利用 AI 技术,从数据中挖掘出业务价值,但数据建模本身是一个很难用 AI 完成的事情。 为最大程度减少建模的成本,TDengine 推荐在数据…

8.20网络编程——sqlite3数据库

文章目录一、思维导图二、学生管理系统1、myhead.h2、代码三、牛客网刷题一、思维导图 二、学生管理系统 1、myhead.h #ifndef __MYHEAD_H__ #define __MYHEAD_H__#include <string.h> #include <stdio.h> #include <stdlib.h> #include <errno.h>#d…

电脑不能访问服务器磁盘,连不上服务器。解决办法:在窗口中输入 regedit 确定即可打开注册表,。。。。0改为1,确认;

打开注册表&#xff1a; 按键盘上的 WinR 键&#xff0c;打开运行窗口&#xff0c;在窗口中输入 regedit 确定即可打开注册表。&#xff08;或者直接在左下角搜索框中搜索“注册表”&#xff09; 依次打开以下目录 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Service…

EP4CE40F23I7N Altera FPGA Cyclone IV E

EP4CE40F23I7N 阿尔特拉 Altera Cyclone IV E 系列的一颗中等密度、低功耗 FPGA&#xff0c;通信接口与工业控制等应用。该器件采用成熟的工艺制程&#xff0c;器件规模约为 39k 左右的逻辑单元&#xff08;Logic Elements&#xff09;&#xff0c;由若干逻辑阵列块&#xff08…

【typenum】 21 类型级别计算最大公约数(Gcd)

一、源码 代码实现了一个在类型级别计算最大公约数&#xff08;GCD&#xff09;的系统 定义&#xff08;type_operators.rs&#xff09; /// A **type operator** that computes the [greatest common divisor][gcd] of Self and Rhs. /// /// [gcd]: https://en.wikipedia.org…

如何为 Visual Studio 2019 安装 WDK

我用nmake编译代码提示错误&#xff1a;fatal error U1052: 未找到文件“\makefile.def”&#xff0c;经过排查发现是代码依赖WDK&#xff0c;所以研究了一下WDK的安装步骤&#xff0c;下面是具体步骤&#xff1a; 请遵循以下步骤来为你的 VS2019 搭建完整的驱动开发环境&…

使用 Apache Flink CDC 3.0 实现 MySQL 到 Elasticsearch 的数据同步

下面我将创建一个完整的 Spring Boot 项目&#xff0c;使用 Flink CDC 3.0 基于 MySQL 的 binlog 实现数据同步到 Elasticsearch。 项目概述 这个项目将&#xff1a; 使用 Flink CDC 连接 MySQL 并读取 binlog处理数据变化&#xff08;插入、更新、删除&#xff09;将数据同步到…

Web网站的运行原理2

请求Web网站的文件-HTTP 可以使用HTTP协议在Web浏览器和Web服务器应用程序之间传输Web网页的文件。 在进行HTTP传输之前&#xff0c;需要先在Web浏览器和Web服务器应用程序之间建立TCP连接。 使用HTTP请求可以要求Web浏览器向Web服务器应用程序传输文件。 传输Web网站的文件-HT…

论文阅读:Do As I Can, Not As I Say: Grounding Language in Robotic Affordances

地址&#xff1a;Do As I Can, Not As I Say: Grounding Language in Robotic Affordances 摘要 大型语言模型&#xff08;LLM&#xff09;能够编码丰富的世界语义知识&#xff0c;这类知识对于机器人执行自然语言表达的高层级、时间扩展指令具有重要价值。然而&#xff0c;语…

Django管理后台结合剪映实现课件视频生成应用

在教学内容的数字化制作中&#xff0c;如何将课件与音频快速转换为视频是一项高频需求。借助管理后台和剪辑工具&#xff0c;可以实现课件内容的下载、转换和草稿生成&#xff0c;大幅减少重复操作。 【AI教育教学考试系统】课件在线剪映视频草稿生成应用这里实现的课件PPT部分…

AI升级社区便民服务:AI办事小程序高效办证+应急系统秒响应,告别跑腿愁住得更安心

朋友&#xff0c;你有没有在社区办过事&#xff1f;想给孩子办入学证明&#xff0c;得先跑居委会开证明&#xff0c;再去街道办事处盖章&#xff0c;来回几趟不说&#xff0c;要是材料没带全&#xff0c;还得重新跑&#xff1b;家里水管爆了&#xff0c;半夜联系物业&#xff0…

el-table-draggable拖拽实现表格内容排序

1、图片2、安装包import ElTableDraggable from "el-table-draggable";3、代码&#xff08;html&#xff09;<el-table-draggable:data"soloTableData"input"dragInputHandlerSolo"><el-table:data"soloTableData"row-key&qu…