当前位置: 首页 > news >正文

项目一系列-第8章 性能优化Redis基础

第8章 性能优化&Redis基础

Redis概述

学习目标

在本章节中,我们将学习如何使用Redis来优化系统性能。具体的学习目标包括:

  • 掌握Redis的环境准备,如服务安装、启动和停止。
  • 理解并描述Redis的常见数据类型及其特点。
  • 熟练掌握操作不同数据类型的常用命令。
  • 学会在Java中操作Redis。
  • 掌握在若依框架中更方便地使用Redis。

Redis简介

Redis是什么?

Redis是一个开源的高性能键值存储系统,常用于缓存解决方案。它不仅支持简单的key-value存储,还提供了丰富的数据结构,适用于多种场景。Redis的特点如下:

  • 基于内存存储:提供极高的读写速度。
  • 支持多种数据结构:如字符串(String)、哈希(Hash)、列表(List)、集合(Set)和有序集合(Sorted Set)。
  • NoSQL数据库:非关系型数据库是对传统关系型数据库的补充。
关系型与非关系型数据库对比
例如
例如
关系型数据库
MySQL
PostgreSQL
Oracle
SQLServer
非关系型数据库
Redis
MongoDB
Memcached
Redis的应用场景

Redis因其高性能和灵活性被广泛应用于互联网技术领域,尤其是处理热点数据(如热门商品、资讯、新闻等),提高系统的可扩展性和用户体验。

Redis环境准备

安装Redis

使用Docker安装Redis

为了简化部署过程,我们推荐使用Docker来安装Redis:

  1. 下载Redis镜像:
    docker pull redis
    
  2. 创建并运行Redis容器:
    docker run -d --name redis \--restart=always \-p 6379:6379 redis \--requirepass "123456"
    
    这里设置了密码123456来保护你的Redis实例。
Docker安装流程图示
下载Redis镜像
创建Redis容器
设置密码
启动Redis服务

Redis客户端图形工具

如果需要在Windows环境下操作Redis,可以使用Redis客户端图形工具。这类工具可以帮助你更直观地管理和监控Redis中的数据。

Redis客户端连接流程图
安装Redis客户端
配置连接参数
连接到Redis服务器
管理数据

通过上述步骤,我们完成了Redis的基础环境搭建。

如果客户端连接Redis失败,可以进入到容器内部操作:

[root@localhost ~]# docker exec -it redis bash
root@081e74f9bedb:/data# redis-cli -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> get name
(nil)

接下来将继续深入探讨Redis的不同数据类型及相应的操作命令。

Spring Data Redis

Redis常见的客户端

前面咱们讲解了Redis的常用命令,这些命令是咱们操作Redis的基础,那么咱们在java程序中应该如何操作Redis呢?这就需要使用Redis的Java客户端,就如同咱们使用JDBC操作MySQL数据库一样。

Redis 的 Java 客户端很多,常用的几种:

  • Jedis
  • Lettuce
  • Spring Data Redis

Spring 对 Redis 客户端进行了整合,提供了 Spring Data Redis,封装了 Jedis,在Spring Boot项目中还提供了对应的Starter,即 spring-boot-starter-data-redis。使用起来非常快速和简单。

咱们重点学习Spring Data Redis

概述

Spring Data Redis 是 Spring 的一部分,在 Spring 应用中通过简单的配置就可以访问 Redis 服务,对 Redis 底层开发包进行了高度封装。在 Spring 项目中,可以使用Spring Data Redis来简化 Redis 操作。

网址:https://spring.io/projects/spring-data-redis

Spring Boot提供了对应的Starter,maven依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

该依赖已经添加到了zzyl-common模块中,可以直接使用

Spring Data Redis中提供了一个高度封装的类:RedisTemplate,对相关api进行了归类封装,将同一类型操作封装为Operation接口,具体分类如下:

  • ValueOperations:string数据操作
  • HashOperations:hash类型的数据操作
  • SetOperations:set类型数据操作
  • ZSetOperations:zset类型数据操作
  • ListOperations:list类型的数据操作

环境搭建

在目前若依提供的环境已经集成了Redis的起步依赖,咱们可以在项目中直接进行单元测试,不过,需要在项目中集成单元测试的起步依赖

在zzyl-admin模块中的pom文件中,新增单元测试起步依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId>
</dependency>

操作常见类型数据

下面咱们可以先使用单元测试,来测试一下常见的对Redis的操作

在zzyl-admin模块下创建测试类,并集成springboot,如下:

package com.zzyl.redis;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;@SpringBootTest
public class RedisTest {@Autowiredprivate RedisTemplate<String, String> redisTemplate;@Testpublic void test() {System.out.println(redisTemplate);}
}
  • 上述代码中直接注入了RedisTemplate,并且指定了泛型为String
    • 如果不指定泛型也可以进行操作,但是存储对象的时候需要进行序列化
    • 一般项目中存储对象都会先转换为json字符串,再进行存储,所以一般会选择使用泛型为String,避免大量的序列化操作
操作字符串数据类型

基于刚才的命令,字符串类型常见命令有:set get setex setnx

/*** 操作字符串类型的数据*/
@Test
public void testString() {// 普通的set方法redisTemplate.opsForValue().set("name", "张三");System.out.println(redisTemplate.opsForValue().get("name"));// 设置带有过期时间的keyredisTemplate.opsForValue().set("token", "123qweasd",20, TimeUnit.SECONDS);System.out.println(redisTemplate.opsForValue().get("token"));// setnx 当指定的键key不存在时,会将key的值设置为value,返回true,否则返回false(不能覆盖原有值)System.out.println(redisTemplate.opsForValue().setIfAbsent("lock", "09876", 5, TimeUnit.MINUTES));System.out.println(redisTemplate.opsForValue().setIfAbsent("lock", "34567", 5, TimeUnit.MINUTES));
}
操作哈希类型数据

基于刚才的命令,哈希类型常见命令有:hset hget hkeys hvals hdel

@Test
public void testHash() {// hash请求   大key 小key  valueredisTemplate.opsForHash().put("user", "name", "张三");redisTemplate.opsForHash().put("user", "age", "30");// 根据大key和小key获取值System.out.println(redisTemplate.opsForHash().get("user", "name"));// 根据大key获取所有的小keySet<Object> keys = redisTemplate.opsForHash().keys("user");System.out.println(keys);// 根据大key获取所有的值List<Object> values = redisTemplate.opsForHash().values("user");System.out.println(values);// 删除小key和值redisTemplate.opsForHash().delete("user", "age");
}
操作列表类型数据

基于刚才的命令,列表类型常见命令有:lpush lrange rpop lpop llen

/*** 操作列表类型的数据*/
@Test
public void testList() {// 插入多个值[a,b,c]redisTemplate.opsForList().leftPushAll("mylist", "a", "b", "c");// 在列表左边插入一个值[d,a,b,c]redisTemplate.opsForList().leftPush("mylist", "d");// 获取列表中的数据System.out.println(redisTemplate.opsForList().range("mylist", 0, -1));// 从左边弹出一个,并获取值,弹出后列表中删除System.out.println(redisTemplate.opsForList().leftPop("mylist"));// 获取列表的长度System.out.println(redisTemplate.opsForList().size("mylist"));
}
操作集合类型数据

基于刚才的命令,集合类型常见命令有:sadd smembers scard sinter sunion

/*** 操作集合类型的数据*/
@Test
public void testSet() {// 添加数据redisTemplate.opsForSet().add("myset1", "a", "b", "c", "d");redisTemplate.opsForSet().add("myset2", "a", "b", "x", "y");// 获取集合中的所有成员Set<String> members = redisTemplate.opsForSet().members("myset1");System.out.println(members);// 获取集合大小long size = redisTemplate.opsForSet().size("myset1");System.out.println(size);// 交集Set<String> intersection = redisTemplate.opsForSet().intersect("myset1", "myset2");System.out.println("交集:" + intersection);// 并集Set<String> union = redisTemplate.opsForSet().union("myset1", "myset2");System.out.println("并集:" + union);
}
操作有序集合类型数据

基于刚才的命令,有序集合类型常见命令有:zadd zrange zincrby zrem

/*** 操作有序集合类型的数据*/
@Test
public void testZset() {// 添加数据redisTemplate.opsForZSet().add("myzset", "a", 1);redisTemplate.opsForZSet().add("myzset", "b", 10);redisTemplate.opsForZSet().add("myzset", "c", 20);// 获取集合中的所有成员Set<String> members = redisTemplate.opsForZSet().range("myzset", 0, -1);System.out.println(members);// 给a成员的分数增加10redisTemplate.opsForZSet().incrementScore("myzset", "a", 10);// 删除a、b两个成员redisTemplate.opsForZSet().remove("myzset", "a", "b");
}
通用命令操作

基于刚才的命令,字符串类型常见命令有:keys exists type del

/*** 通用命令操作*/
@Test
public void testCommon() {// 获取所有keySet<String> keys = redisTemplate.keys("*");System.out.println(keys);// 判断key是否存在Boolean isName = redisTemplate.hasKey("name");System.out.println(isName);// 获取key的类型DataType type = redisTemplate.type("myzset");System.out.println(type.name());// 删除keyredisTemplate.delete("myzset");}

项目集成Redis缓存

在目前的若依框架中,就是使用Redis来作为缓存的,核心配置类如下:

  • 在zzyl-framework模块中的com.zzyl.framework.config.RedisConfig类

    • 作用:配置类,开启了缓存注解、对象序列化和反序列化

      对象序列化是将对象转换为可存储或传输的字节序列的过程,这种序列化后的字节序列可以保存在文件,数据库或通过网络进行传输,将来需要用到对象时可以获取到序列化之后的结果,再反序列化为对象。

      想要实现序列化的类,必须实现Serializable接口

  • 在com.zzyl.common.core.redis.RedisCache

    • 封装了常见的操作Redis的方法,在业务开发中,可以直接使用这个类的方法来操作Redis

哪些数据需要添加放到缓存中

  • 高频访问且变更较少的数据:用户信息(基本资料、权限)、配置信息(系统配置、业务规则等)
  • 数据库查询结果:复杂的数据库查询(多表或量大且需要经常访问)
  • 热门内容:热门文章、热门视频、热门评论等,访问量高的数据
  • 临时数据:临时存储一些数据以便后续使用(如验证码、临时文件路径等),在内存中以便快速访问。
  • 需要快速访问的小数据集:某些情况下,即使数据不大,但如果需要频繁访问,可以考虑将其缓存起来以提高性能。

缓存使用策略

查询数据:先查询缓存,再查询数据库

	@Autowiredprivate RedisTemplate<Object, Object> redisTemplate;	/*** 查询所有护理项目** @return 护理项目列表*/@Overridepublic List<NursingProjectVo> getAll() {// 从缓存中查询所有护理项目List<NursingProjectVo> list = (List<NursingProjectVo>) redisTemplate.opsForValue().get(CacheConstants.NURSING_PROJECT_ALL_KEY);// 如果缓存中查到了,直接返回if (ObjectUtil.isNotEmpty(list)) {return list;}// 如果缓存中没有查到,则从数据库中查询list = nursingProjectMapper.getAll();// 放入缓存中redisTemplate.opsForValue().set(CacheConstants.NURSING_PROJECT_ALL_KEY, list);// 返回结果return list;}

更新数据:先更新数据库,再删除缓存(增删改操作都要有走一步)

    /*** 新增护理项目* * @param nursingProject 护理项目* @return 结果*/@Overridepublic int insertNursingProject(NursingProject nursingProject){boolean flag = save(nursingProject);// 删除缓存deleteCache();return flag ? 1 : 0;}/*** 修改护理项目* * @param nursingProject 护理项目* @return 结果*/@Overridepublic int updateNursingProject(NursingProject nursingProject){boolean flag = updateById(nursingProject);// 删除缓存deleteCache();return flag ? 1 : 0;}/*** 批量删除护理项目* * @param ids 需要删除的护理项目主键* @return 结果*/@Overridepublic int deleteNursingProjectByIds(Long[] ids){boolean flag = removeByIds(Arrays.asList(ids));// 删除缓存deleteCache();return flag ? 1 : 0;}/*** 删除护理项目信息* * @param id 护理项目主键* @return 结果*/@Overridepublic int deleteNursingProjectById(Long id){boolean flag = removeById(id);// 删除缓存deleteCache();return flag ? 1 : 0;}private void deleteCache() {redisTemplate.delete(CacheConstants.NURSING_PROJECT_ALL_KEY);}

作为缓存的key推荐放到CacheConstants常量类中维护

    /*** 护理项目缓存的 key*/public static final String NURSING_PROJECT_ALL_KEY = "nursingProject:all";

项目集成Redis缓存分几步?

1)分析功能模块中哪些接口可以使用缓存

2)引入依赖 spring-boot-starter-data-redis

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId>
</dependency>

在若依框架中已经集成了Redis的起步依赖。

3)编写操作Redis缓存的代码

4)可以再常量类 CacheConstants 中维护用于缓存的key

http://www.dtcms.com/a/356396.html

相关文章:

  • Python OpenCV图像处理与深度学习
  • 30分钟入门实战速成Cursor IDE(2)
  • 30分钟入门实战速成Cursor IDE(1)
  • 微硕WINSOK高性能NP沟道MOS管WSP4067在Type-C双向快充电源管理系统中的应用
  • Vibe Coding、AI IDE/插件
  • Ansible Playbook 实践
  • 随机森林的 “Bootstrap 采样” 与 “特征随机选择”:如何避免过拟合?(附分类 / 回归任务实战)
  • html入门教程
  • Java使用apache.commons.math3的DBSCAN实现自动聚类
  • 【Docker/Redis】服务端高并发分布式结构演进之路
  • 构建免费的音视频转文字工具:支持多语言的语音识别项目
  • OpenTenBase实战:从MySQL迁移到分布式HTAP的那些坑与收获
  • mysql双机热备(主主模式)
  • 桌面GIS软件添加第三方图层
  • 【web3】十分钟了解web3是什么?
  • Java 将HTML文件、HTML字符串转换为图片
  • 交叉编译 手动安装 libzip 库 移植ARM 需要 zlib的
  • R ggplot2学习Nature子刊一张图,换数据即可用!
  • [机器学习]基于K-means聚类算法的鸢尾花数据及分类
  • 把 AI 塞进「智能跳绳」——基于 MEMS 传感器的零样本卡路里估算器
  • 机器学习回顾——线性回归
  • GitHub 宕机自救指南:打造韧性开发体系
  • 基于站点、模式、遥感多源降水数据融合与评估;Python驱动下,从基础处理、机器学习建模到气候态产品生成的全流程解析
  • Consul 操作命令汇总 - Prometheus服务注册
  • 选华为实验工具:eNSP Pro 和社区在线实验哪个更适合?
  • 腾讯位置商业授权微信小程序距离计算
  • OpenAI GPT-realtime 实时语音 API 完整指南:2025年语音AI的革命性突破
  • STM32的RTC模块及其应用场景
  • MTK Linux DRM分析(二十三)- MTK mtk_drm_crtc.c(Part2)
  • 嵌入式Linux驱动开发 - GPIO LED驱动