Java进阶全套教程(七)—— Redis超详细实战详解

Java进阶全套教程(七)------ Redis超详细实战详解

一、Redis核心前置:为什么需要NoSQL数据库

1.1 单机MySQL架构的演进与瓶颈

互联网早期(90年代-2010年初期),Web业务流量小、页面以静态展示为主、用户交互极少,单机MySQL+单机Tomcat的架构完全可以承载所有业务需求,架构简单、维护成本极低。

随着互联网用户爆发式增长、动态交互业务(评论、点赞、下单、社交)普及,单机架构陆续暴露一系列性能瓶颈,行业通过多层架构迭代优化,逐步催生了NoSQL数据库的应用场景:

迭代阶段1:Tomcat与数据库资源竞争

初始架构中,Web服务(Tomcat)和MySQL数据库部署在同一台服务器,CPU、内存、磁盘IO资源相互抢占。高并发场景下,Tomcat处理请求占用大量资源,导致MySQL查询、写入响应缓慢,直接造成页面卡顿、接口超时。

解决方案:服务与数据库物理分离部署,Tomcat应用服务器、MySQL数据库服务器独立独占硬件资源,彻底解决资源抢占问题,分别提升应用响应和数据库读写性能。

迭代阶段2:单机数据库并发读写瓶颈

服务分离后,单Tomcat的性能瓶颈被解决,但随着并发请求量持续攀升,所有读写请求最终全部穿透到单机MySQL。数据库的连接数、磁盘IO、锁竞争成为新的性能瓶颈,无法支撑高并发业务场景。

解决方案:引入多级缓存架构,拦截绝大多数数据库请求。采用Memcached作为本地进程缓存,Redis作为分布式缓存,将热点数据预存至内存,90%以上的查询请求直接从缓存获取,大幅降低数据库访问压力。

迭代阶段3:单机Tomcat并发压力瓶颈

缓存拦截大量请求后,流量压力全部集中在单机Tomcat上,单应用服务器的线程数、CPU处理能力有限,高并发下线程阻塞、请求排队,接口响应速度持续变慢,出现服务卡顿、熔断风险。

解决方案:引入Nginx反向代理实现负载均衡,多台服务器部署Tomcat集群,Nginx通过轮询、权重、IP哈希等策略,将用户请求均匀分发至各个应用节点,横向提升系统并发承载能力。

迭代阶段4:单机MySQL读写压力过载

应用集群扩容后,系统整体并发量大幅提升,大量读写请求穿透缓存直达数据库。单机MySQL无法同时承载高频写入和海量查询,读请求阻塞写请求,数据库性能急剧下降。

解决方案:MySQL主从复制+读写分离架构,Master主库专注处理写入操作,多个Slave从库分担查询读操作,有效拆分读写压力,提升数据库整体吞吐量和可扩展性。

迭代阶段5:多业务数据库资源竞争

随着业务模块不断迭代(用户、订单、商品、支付、社交),所有业务数据存储在同一MySQL实例中,高流量业务会抢占低流量业务的数据库资源,导致不同业务相互干扰,出现业务稳定性问题。

解决方案:按业务垂直分库,将用户库、订单库、商品库、支付库拆分独立部署,隔离业务资源竞争,针对高并发业务单独扩容数据库节点,保障核心业务稳定性。

1.2 NoSQL数据库诞生的核心原因

传统关系型数据库(MySQL、Oracle)擅长处理结构化、低并发、强事务的数据场景,但互联网Web2.0时代,数据形态和业务场景发生颠覆性变化,暴露出诸多短板:

  1. 数据量级激增:用户行为日志、社交动态、地理位置、设备信息等数据成倍增长,海量数据存储与查询成为难题;

  2. 数据类型多样化:除了传统二维表结构化数据,大量非结构化数据(图片、视频、文档、HTML文本)、半结构化数据(JSON、XML)诞生,关系型数据库适配性极差;

  3. 高并发读写需求:电商秒杀、短视频、社交点赞等场景,每秒数万次读写请求,关系型数据库磁盘读写模式无法支撑;

  4. 低容错高扩展需求:互联网业务需要快速迭代、横向扩容,关系型数据库固定表结构、扩容复杂的特性无法适配。

NoSQL(Not Only SQL)数据库应运而生,它并非替代SQL,而是弥补关系型数据库的短板,专注解决海量数据、高并发、多数据类型、高扩展的业务场景。

二、NoSQL核心概念与四大分类

2.1 结构化与非结构化数据对比

2.1.1 结构化数据

严格遵循固定格式、长度规范,以二维数据表形式存储,拥有固定字段和数据类型,是关系型数据库的核心存储数据。典型代表:用户信息、订单数据、商品基础信息。

特点:格式统一、支持复杂SQL查询、事务一致性强、更新维护便捷,但灵活性差、海量数据读写性能弱。

2.1.2 非结构化数据

无固定数据结构、无预定义模型,无法通过二维表逻辑展示。典型代表:图片、视频、音频、Word文档、日志文件、网页源码。

特点:数据形态灵活、种类繁多、数据量大,不支持SQL复杂查询,适合通过NoSQL数据库存储管理。

2.1.3 半结构化数据

介于结构化与非结构化之间,拥有自定义结构,无固定字段约束,可灵活拓展字段,典型代表:JSON、XML格式数据,是当下互联网业务最常用的数据格式。

2.2 NoSQL四大主流分类及适用场景

2.2.1 KV键值型NoSQL(代表:Redis、Memcached)

最基础、应用最广泛的NoSQL类型,以**键值对(Key-Value)**为核心存储结构,Key唯一标识数据,Value支持多种数据格式。

核心特点:数据基于内存存储、读写速度极致高效、查询时间复杂度O(1)、支持数据持久化、部署简单。Redis基准测试TPS可达10万+,是高并发缓存场景的首选。

适用场景:热点数据缓存、分布式锁、计数器、限流、会话存储、排行榜、实时统计数据。

2.2.2 列式NoSQL(代表:HBase、Cassandra)

区别于传统行式存储,采用按列存储的模式,是大数据时代核心存储技术。行式存储会读取整行所有字段数据,而列式存储仅读取查询所需列,极大减少磁盘IO。

核心特点:海量数据存储、横向扩展性极强、查询效率高、容错性好,适合超大规模数据归档与统计分析。

适用场景:大数据日志存储、海量用户行为分析、离线数据统计、物联网时序数据存储。

2.2.3 文档型NoSQL(代表:MongoDB)

JSON/XML文档为最小存储单元,每个文档可自定义字段、灵活拓展结构,无需提前定义表结构,完美适配半结构化数据。

核心特点:结构灵活、字段可动态增减、支持丰富的查询语法、适配复杂业务数据。

适用场景:商品详情、用户评论、博客文章、内容资讯、动态拓展字段的业务数据。

2.2.4 搜索型NoSQL(代表:ElasticSearch)

专门解决关系型数据库全文检索短板,MySQL的Like模糊查询效率极低、无法支持分词检索、高亮匹配、聚合统计,搜索型数据库通过倒排索引实现秒级全文检索。

核心特点:全文检索能力强、支持分词、模糊匹配、聚合查询、海量数据检索高效。

适用场景:商品搜索、文章检索、日志检索、APP内容模糊查询、站内搜索系统。

2.3 关系型数据库与NoSQL数据库深度对比

2.3.1 关系型数据库(MySQL/Oracle)优缺点

核心优点

  1. 结构规范:统一二维表结构,数据规整、易于维护;

  2. 查询强大:标准SQL语法,支持多表联查、子查询、聚合查询等复杂操作;

  3. 事务可靠:完美支持ACID事务特性,保证数据一致性,适合支付、订单等核心业务;

  4. 生态成熟:运维工具完善、社区活跃、兼容性强。

核心缺点

  1. 读写性能弱:基于磁盘存储,海量数据高并发读写效率低;

  2. 灵活性差:表结构固定,字段修改、拓展成本高;

  3. 横向扩容复杂:集群部署、分片扩容难度大、成本高。

2.3.2 NoSQL数据库优缺点

核心优点

  1. 格式灵活:支持KV、文档、列式等多种存储格式,适配各类数据形态;

  2. 性能极致:支持内存存储,读写速度远超关系型数据库;

  3. 扩展性强:天然支持分布式集群,横向扩容无瓶颈;

  4. 成本低廉:全部开源、部署简单、无需昂贵硬件设备。

核心缺点

  1. 无标准SQL:查询语法不统一,学习和开发成本较高;

  2. 事务支持弱:绝大多数NoSQL不支持完整ACID事务,数据一致性较差;

  3. 复杂查询薄弱:不支持多表联查、复杂聚合,业务适配场景有限。

三、企业级SQL+NoSQL混合架构实战

当下互联网企业不会单一使用SQL或NoSQL,而是采用混合架构,根据数据特性匹配最优存储方案,以淘宝商品存储架构为例:

3.1 商品分层存储方案

  1. 商品基础结构化数据 :商品名称、价格、规格、厂商、库存、商家信息,存储于MySQL,保障数据一致性和事务性;

  2. 商品详情、评论、图文描述 :非结构化、字段动态拓展数据,存储于MongoDB,适配灵活的数据结构;

  3. 商品图片、视频资源 :超大静态资源,存储于分布式文件系统(阿里云OSS、HDFS、TFS);

  4. 商品关键词、检索数据 :支持全文模糊搜索,同步至ElasticSearch,实现高效商品检索;

  5. 商品热点数据、瞬时流量数据 :热门商品、实时销量、浏览量、秒杀数据,缓存至Redis,支撑高并发访问。

3.2 混合架构解决的核心问题

互联网业务存在数据类型多样、数据源繁杂、业务迭代快的特点,单一数据库无法适配全场景。SQL+NoSQL混合架构,结合了关系型数据库的事务可靠性和NoSQL的高性能、高灵活性,配合统一数据服务平台,实现数据解耦、业务无感知迭代。

四、Redis核心定义与基础特性

4.1 Redis官方定义

Redis是一款ANSI C语言开发、开源、基于内存、支持持久化、多数据结构、网络分布式的键值对NoSQL数据库,遵守BSD开源协议,支持跨平台、多语言API对接,是目前企业最主流的分布式缓存中间件。

4.2 Redis核心核心特性

  1. 极致高性能:纯内存操作,无磁盘IO开销,单节点QPS可达10万+,响应延迟毫秒级;

  2. 丰富数据结构:支持8种以上数据类型,适配各类业务场景;

  3. 数据持久化:支持RDB、AOF双持久化机制,防止内存数据丢失;

  4. 分布式扩展:支持主从、哨兵、Cluster集群三种架构,无上限横向扩容;

  5. 功能丰富:支持事务、发布订阅、流水线、过期淘汰、限流、分布式锁等高级功能;

  6. 多语言兼容:完美适配Java、Python、Go、PHP等主流开发语言。

4.3 主流企业应用Redis案例

国内一线互联网企业均以Redis作为核心缓存中间件:阿里巴巴、京东、美团、百度、微博、字节跳动、Github等,广泛应用于缓存、限流、分布式锁、实时统计、消息队列等场景。

五、Redis环境搭建(Linux+Docker双方案)

5.1 Linux源码编译安装Redis(6.2.4稳定版)

5.1.1 环境准备

Redis基于C语言开发,编译需要GCC编译环境,首先安装依赖:

Plain 复制代码
# 安装gcc编译环境
yum install -y gcc gcc-c++ make

# 验证安装是否成功
gcc --version

5.1.2 解压编译安装

Plain 复制代码
# 上传redis-6.2.4.tar.gz至服务器,解压压缩包
tar -zxvf redis-6.2.4.tar.gz

# 进入解压目录
cd redis-6.2.4

# 编译源码
make

# 安装至指定目录 /usr/local/bin
make install

5.1.3 核心工具说明

  • redis-server:Redis服务启动程序

  • redis-cli:Redis客户端连接工具

  • redis-benchmark:官方性能压测工具

  • redis-check-rdb:RDB持久化文件修复工具

  • redis-check-aof:AOF持久化文件修复工具

  • redis-sentinel:哨兵模式启动工具

5.1.4 服务启动与配置

Plain 复制代码
# 前台启动(测试使用,关闭终端服务停止)
redis-server

# 后台启动配置:修改redis.conf
vim redis.conf
# 将daemonize no改为yes,开启守护进程
daemonize yes

# 指定配置文件后台启动
redis-server redis.conf

# 客户端连接服务
redis-cli

# 检测服务连通性,返回PONG即为正常
127.0.0.1:6379> ping

5.2 Docker快速安装Redis(生产推荐)

Docker安装无需编译,环境隔离、部署便捷、一键启停,是企业测试、生产环境主流部署方式。

Plain 复制代码
# 拉取最新稳定版Redis镜像
docker pull redis:latest

# 启动Redis容器,端口映射、后台运行、命名
docker run -itd --name redis-local -p 6379:6379 --restart always redis:latest

# 查看容器运行状态
docker ps | grep redis-local

# 进入容器内部
docker exec -it redis-local /bin/bash

# 查看Redis版本
redis-server --version

# 客户端连接测试
redis-cli
127.0.0.1:6379> ping

5.3 Redis基础核心常识

5.3.1 默认数据库规则

Redis默认提供16个独立数据库 (编号0-15),数据库之间数据完全隔离、互不干扰。可通过redis.conf中databases参数修改数据库数量,生产环境建议不同业务使用不同数据库隔离数据。

常用数据库操作命令:

Plain 复制代码
# 切换至1号数据库
select 1

# 清空当前数据库所有数据
flushdb

# 清空所有16个数据库数据
flushall

5.3.2 单线程+IO多路复用核心原理

Redis核心设计:单线程处理所有命令请求 ,配合IO多路复用技术实现高并发。

核心原因:Redis瓶颈不在CPU,而在网络IO和内存带宽。单线程避免了多线程锁竞争、线程切换的开销,实现简单且性能稳定。

IO多路复用通俗原理:单个线程统一监听多个客户端网络连接,当连接有请求事件时,统一处理,无需为每个连接创建独立线程,极大提升系统吞吐量,支撑上万并发连接。

5.3.3 默认端口6379由来

Redis默认端口6379为开发者自定义,无技术逻辑,来源于字符谐音,是行业经典冷知识。

六、Redis通用Key操作命令(企业常用)

Redis所有数据均以Key-Value形式存储,通用Key命令可适配所有数据类型,是日常开发高频操作。

6.1 核心Key命令详解

Plain 复制代码
// 1. 查看库中Key(生产禁止keys *,阻塞线程,使用scan替代)
scan 0 match user* count 10

// 2. 判断Key是否存在,返回存在的Key数量
exists user:1001 user:1002

// 3. 查看Key对应的数据类型
type user:1001

// 4. 删除单个/多个Key,返回删除成功数量
del user:1001 user:1002

// 5. 设置Key过期时间(秒级)
expire user:1003 3600

// 6. 查看Key剩余过期时间
// -2=Key不存在,-1=永久有效,正数=剩余秒数
ttl user:1003

// 7. 移除Key过期时间,设为永久有效
persist user:1003

6.2 生产规范

严禁线上使用keys *命令,该命令会遍历所有Key,单线程阻塞,导致所有业务请求超时,生产环境统一使用scan渐进式遍历。

七、Redis八大核心数据类型实战

7.1 String字符串类型(最常用)

7.1.1 类型特性

Redis最基础的数据类型,一个Key对应一个String Value,二进制安全,可存储文本、数字、序列化对象、图片二进制数据,单Value最大支持512MB。

7.1.2 高频命令实战

Plain 复制代码
# 基础赋值取值
set order:10001:status 1
get order:10001:status

# 追加内容
append order:10001:desc "已支付"

# 获取字符串长度
strlen order:10001:desc

# 带过期时间赋值(热点数据缓存专用)
setex user:1001:info 1800 "{name:张三,age:25}"

# 不存在则赋值(分布式锁核心命令)
setnx lock:pay 1

# 批量赋值取值
mset goods:1001:price 99 goods:1002:price 199
mget goods:1001:price goods:1002:price

# 数字自增自减(计数器核心)
incr user:1001:view
incrby user:1001:score 5
decr user:1001:view
decrby user:1001:score 2

# 区间截取
getrange user:1001:name 0 2

# 先取值再赋值
getset user:1001:name 李四

7.1.3 企业实战场景

用户信息缓存、商品价格缓存、计数器(浏览量、点赞数)、分布式锁、验证码存储、限流计数。

7.2 List列表类型

7.2.1 类型特性

字符串有序列表,底层基于双向链表实现,头尾操作O(1)极致高效,中间索引操作性能较差,最大支持40亿+元素,元素允许重复。

7.2.2 高频命令实战

Plain 复制代码
# 左侧插入(头部)、右侧插入(尾部)
lpush msg:list "消息1" "消息2"
rpush msg:list "消息3" "消息4"

# 查看列表所有元素(0第一个,-1最后一个)
lrange msg:list 0 -1

# 弹出头部/尾部元素
lpop msg:list
rpop msg:list

# 获取指定索引元素
lindex msg:list 0

# 获取列表长度
llen msg:list

# 删除指定数量指定元素
lrem msg:list 2 "消息1"

# 修改指定索引元素
lset msg:list 0 "最新消息"

7.2.3 企业实战场景

简单消息队列、最新消息列表、时序数据存储、排行榜基础、操作日志记录。

7.3 Set集合类型

7.3.1 类型特性

无序、不可重复的字符串集合,底层基于Hash表实现,增删查O(1)效率,支持集合交集、并集、差集运算。

7.3.2 高频命令实战

Plain 复制代码
# 添加集合元素(自动去重)
sadd user:1001:follow 1002 1003 1004 1002

# 查看所有元素
smembers user:1001:follow

# 判断元素是否存在
sismember user:1001:follow 1002

# 获取集合元素个数
scard user:1001:follow

# 删除指定元素
srem user:1001:follow 1004

# 随机删除一个元素
spop user:1001:follow

# 随机获取指定数量元素(不删除)
srandmember user:1001:follow 2

# 集合运算
sinter user:1001:follow user:1002:follow  # 交集(共同关注)
sunion user:1001:follow user:1002:follow  # 并集(所有关注)
sdiff user:1001:follow user:1002:follow  # 差集(独有关注)

7.3.3 企业实战场景

用户关注/粉丝列表、好友匹配、黑白名单、随机抽奖、去重统计。

7.4 Hash哈希类型

7.4.1 类型特性

键值对集合(Key->Field->Value),适合存储对象数据,相比String序列化存储,可单独修改对象某个字段,无需整体更新,节省网络IO。

7.4.2 高频命令实战

Plain 复制代码
# 单个字段赋值
hset user:1001 name 张三 age 25 gender 男

# 单个字段取值
hget user:1001 name

# 批量赋值
hmset user:1001 phone 13800138000 address 北京市

# 判断字段是否存在
hexists user:1001 age

# 获取所有字段/所有值
hkeys user:1001
hvals user:1001

# 字段数值自增
hincrby user:1001 age 1

# 删除指定字段
hdel user:1001 address

# 字段不存在则赋值
hsetnx user:1001 email 123@qq.com

7.4.3 企业实战场景

用户信息、购物车数据、商品详情、设备信息等结构化对象数据缓存。

7.5 Zset有序集合类型

7.5.1 类型特性

有序不重复集合,每个元素绑定一个score分数,Redis根据分数自动排序,分数可重复、元素唯一,是排行榜场景专属数据类型。

7.5.2 高频命令实战

Plain 复制代码
# 添加元素与分数
zadd rank:goods:sales 1000 商品1 2000 商品2 1500 商品3

# 正序查询(从小到大)、带分数
zrange rank:goods:sales 0 -1 withscores

# 倒序查询(从大到小)
zrevrange rank:goods:sales 0 -1 withscores

# 分数区间查询
zrangebyscore rank:goods:sales 1000 2000 withscores

# 元素分数自增
zincrby rank:goods:sales 500 商品1

# 删除指定元素
zrem rank:goods:sales 商品3

# 统计区间元素个数
zcount rank:goods:sales 1000 2000

# 查询元素排名
zrank rank:goods:sales 商品2

7.5.3 企业实战场景

商品销量排行榜、用户积分排行榜、热搜榜单、延时队列、限流排序。

7.6 Bitmaps位图类型

7.6.1 类型特性

基于二进制位(0/1)存储数据,极致节省内存,1个字节可存储8个状态数据,适合二值状态统计场景。

7.6.2 高频命令实战

Plain 复制代码
# 设置指定偏移量位值(0=未打卡,1=已打卡)
setbit user:1001:sign 1 1
setbit user:1001:sign 2 0
setbit user:1001:sign 3 1

# 获取指定偏移量状态
getbit user:1001:sign 1

# 统计所有为1的位数(打卡天数)
bitcount user:1001:sign

# 位图运算(交集、并集)
bitop or user:sign:all user:1001:sign user:1002:sign

7.6.3 企业实战场景

用户签到统计、活跃用户统计、在线状态检测、布隆过滤器底层实现。

7.7 Geospatial地理空间类型

7.7.1 类型特性

专门存储经纬度坐标数据,支持地理位置距离计算、范围查询,适配LBS地理位置业务。

7.7.2 高频命令实战

Plain 复制代码
# 添加地理位置(经度、纬度、名称)
geoadd lbs:shop 116.405 39.904 北京门店
geoadd lbs:shop 121.472 31.231 上海门店

# 获取位置经纬度
geopos lbs:shop 北京门店

# 计算两地距离(默认米,支持km/mi/ft)
geodist lbs:shop 北京门店 上海门店 km

# 范围查询(查询指定经纬度1000km内门店)
georadius lbs:shop 110 30 1000 km

7.7.3 企业实战场景

附近的门店、附近的好友、同城推荐、地理位置测距。

7.8 HyperLogLog基数统计类型

7.8.1 类型特性

专门用于基数去重统计,极小内存占用(12KB)可统计上亿级数据,存在极低误差(0.81%),适合非精准海量去重统计。

7.8.2 高频命令实战

Plain 复制代码
# 添加访问用户ID
pfadd page:index:uv 1001 1002 1003 1001 1002

# 统计独立访客数(自动去重)
pfcount page:index:uv

# 合并多个统计集合
pfmerge page:total:uv page:index:uv page:goods:uv

7.8.3 企业实战场景

网站UV统计、页面独立访客、IP访问量统计、文章阅读量去重统计。

八、Java整合Redis实战(Jedis+SpringDataRedis)

8.1 Jedis原生整合(基础客户端)

8.1.1 Maven依赖

Plain 复制代码
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.8.0</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

8.1.2 Jedis连接池工具类(生产可用)

Plain 复制代码
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Jedis连接池工具类,避免频繁创建销毁连接
 */
public class JedisUtil {
    // 单例连接池
    private static JedisPool jedisPool;

    static {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(50); // 最大连接数
        poolConfig.setMaxIdle(10); // 最大空闲连接
        poolConfig.setMinIdle(5); // 最小空闲连接
        poolConfig.setMaxWait(-1); // 最大等待时间
        // 初始化连接池
        jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379, 3000);
    }

    // 获取Redis连接
    public static Jedis getJedis() {
        return jedisPool.getResource();
    }

    // 关闭连接,归还连接池
    public static void close(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }
}

8.1.3 八大数据类型Java实战测试

Plain 复制代码
import org.junit.Test;
import redis.clients.jedis.Jedis;
import java.util.List;
import java.util.Set;

public class JedisRedisTest {

    @Test
    public void testString() {
        Jedis jedis = JedisUtil.getJedis();
        // String操作
        jedis.set("user:name:1001", "张三");
        String name = jedis.get("user:name:1001");
        System.out.println("用户姓名:" + name);
        jedis.incr("user:view:1001");
        JedisUtil.close(jedis);
    }

    @Test
    public void testList() {
        Jedis jedis = JedisUtil.getJedis();
        jedis.lpush("order:list", "10001", "10002", "10003");
        List<String> orderList = jedis.lrange("order:list", 0, -1);
        System.out.println("订单列表:" + orderList);
        JedisUtil.close(jedis);
    }

    @Test
    public void testHash() {
        Jedis jedis = JedisUtil.getJedis();
        jedis.hset("user:1001", "age", "25");
        jedis.hset("user:1001", "gender", "男");
        String age = jedis.hget("user:1001", "age");
        System.out.println("用户年龄:" + age);
        JedisUtil.close(jedis);
    }
}

8.2 SpringBoot整合SpringDataRedis(企业主流)

8.2.1 Maven核心依赖

Plain 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.48</version>
</dependency>

8.2.2 application.yml配置文件

Plain 复制代码
spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: 
    database: 0
    timeout: 30000ms
    lettuce:
      pool:
        max-active: 20
        max-idle: 10
        min-idle: 5
        max-wait: -1ms

8.2.3 自定义序列化配置(解决乱码问题)

Plain 复制代码
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);

        // String类型Key序列化
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);

        // JSON类型Value序列化
        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();
        redisTemplate.setValueSerializer(jsonSerializer);
        redisTemplate.setHashValueSerializer(jsonSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

8.2.4 通用Redis工具类(封装全类型操作)

Plain 复制代码
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // ==================== String类型操作 ====================
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    public void setExpire(String key, Object value, long time) {
        redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
    }

    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    // ==================== Hash类型操作 ====================
    public void hSet(String key, String field, Object value) {
        redisTemplate.opsForHash().put(key, field, value);
    }

    public Object hGet(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    // ==================== List类型操作 ====================
    public void lPush(String key, Object value) {
        redisTemplate.opsForList().leftPush(key, value);
    }

    public List<Object> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    // ==================== Set类型操作 ====================
    public void sAdd(String key, Object... values) {
        redisTemplate.opsForSet().add(key, values);
    }

    public Set<Object> sMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    // ==================== 通用操作 ====================
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    public void del(String... keys) {
        if (keys != null && keys.length > 0) {
            redisTemplate.delete(CollectionUtils.arrayToList(keys));
        }
    }
}

九、Redis高级功能实战

9.1 流水线Pipeline(批量操作优化)

9.1.1 核心原理

普通Redis命令:N条命令 = N次网络IO + N次命令执行;

Pipeline流水线:N条命令 = 1次网络IO + N次命令执行,大幅减少网络传输开销,批量插入性能提升10倍以上。

9.1.2 Java Pipeline实战代码

Plain 复制代码
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;

public class RedisPipelineTest {
    // 批量插入1000条数据,流水线优化
    public static void main(String[] args) {
        Jedis jedis = JedisUtil.getJedis();
        long start = System.currentTimeMillis();
        
        // 开启流水线
        Pipeline pipeline = jedis.pipelined();
        for (int i = 1; i <= 1000; i++) {
            pipeline.set("batch:key:" + i, "value:" + i);
            pipeline.expire("batch:key:" + i, 3600);
        }
        // 一次性执行所有命令
        pipeline.sync();
        
        long end = System.currentTimeMillis();
        System.out.println("流水线批量插入耗时:" + (end - start) + "ms");
        JedisUtil.close(jedis);
    }
}

9.2 慢查询日志(性能调优核心)

9.2.1 功能作用

记录执行超时的Redis命令,用于定位慢操作、排查性能瓶颈,是Redis性能调优的核心工具。

9.2.2 核心配置参数

  • slowlog-log-slower-than:慢查询阈值(单位微秒),默认10000微秒(10毫秒),超过阈值的命令被记录;

  • slowlog-max-len:慢查询日志最大存储条数,默认128条,超出后淘汰旧日志。

9.2.3 动态配置与查询命令

Plain 复制代码
# 查看慢查询配置
config get slow*

# 修改阈值为1毫秒(高并发生产推荐)
config set slowlog-log-slower-than 1000

# 增大日志存储条数
config set slowlog-max-len 1000

# 查看最近3条慢查询日志
slowlog get 3

# 查看慢查询日志总数
slowlog len

9.2.4 生产调优建议

高并发场景建议将慢查询阈值调整为1毫秒,日志条数调整为1000以上,便于全面排查慢命令,避免Redis单线程阻塞导致的性能卡顿。

十、Redis数据持久化(核心重点)

Redis默认数据存储在内存中,内存数据断电即失、重启清空 ,为了防止服务宕机、服务器重启导致数据丢失,Redis提供了两套成熟的持久化机制:RDB快照持久化AOF日志持久化,企业生产环境普遍采用「RDB+AOF混合持久化」方案,兼顾性能与数据安全。

10.1 RDB持久化(快照持久化)

10.1.1 核心原理

RDB(Redis Database)是Redis默认开启的持久化方式。核心原理是:在指定时间间隔内,将内存中完整的所有数据以二进制快照形式写入磁盘rdb文件,相当于给内存数据拍一张"全景照片"。

当Redis重启时,会自动读取rdb文件,一次性加载所有快照数据,快速恢复内存数据。RDB存储的是数据结果,而非操作命令。

10.1.2 RDB触发机制(自动+手动)

1、自动触发(配置文件规则)

redis.conf默认内置持久化策略,满足任一条件自动生成RDB快照:

Plain 复制代码
# 900秒(15分钟)内,至少1个key发生修改,触发持久化
save 900 1
# 300秒(5分钟)内,至少10个key发生修改,触发持久化
save 300 10
# 60秒内,至少10000个key发生修改,触发持久化
save 60 10000

核心逻辑:低频变动长间隔持久化,高频变动短间隔持久化,平衡性能与数据完整性。

2、手动触发(生产调试常用)
  • save命令 :同步持久化,主线程执行,持久化期间阻塞所有读写请求,大数据量场景会造成服务卡顿,生产禁止使用

  • bgsave命令:异步持久化,Redis会fork一个子进程,由子进程完成快照写入,主线程正常处理业务请求,不阻塞服务,是生产唯一可用的手动触发方式。

3、特殊触发场景

执行shutdown正常关闭Redis、执行flushall/flushdb清空数据时,会自动触发RDB持久化。

10.1.3 RDB核心配置参数

Plain 复制代码
# RDB快照文件名
dbfilename dump.rdb
# RDB文件存储路径(默认当前目录)
dir ./
# 持久化出错时,是否停止写入操作(生产开启,保证数据安全)
stop-writes-on-bgsave-error yes
# 是否开启RDB文件压缩(默认开启,节省磁盘,轻微消耗CPU)
rdbcompression yes
# 是否开启文件校验和检测(防止文件损坏)
rdbchecksum yes

10.1.4 RDB优缺点详解

核心优点

  1. 文件体积小、恢复速度快:存储二进制快照文件,无冗余日志,重启加载速度远快于AOF,适合全量数据备份;

  2. 性能损耗极低:依托子进程异步持久化,主线程不阻塞,不影响业务读写;

  3. 适合冷备份、容灾迁移:单文件存储,传输、备份、迁移极其便捷。

致命缺点

  1. 存在数据丢失风险:RDB是间隔性快照,两次快照之间的增量数据不会落地磁盘。若服务突然宕机,会丢失上一次快照到宕机前的所有数据;

  2. fork子进程消耗资源:大数据量场景下,fork进程会拷贝内存页表,短暂占用CPU和内存资源;

  3. 无法实现秒级数据恢复:最小数据丢失窗口为数秒到数分钟,无法满足极致数据一致性场景。

10.2 AOF持久化(日志持久化)

10.2.1 核心原理

AOF(Append Only File)是Redis增量日志持久化机制,默认关闭 。核心原理:记录Redis执行的每一条写操作命令,以日志形式追加写入aof文件,类似于MySQL的binlog日志。

Redis重启时,会逐条重放AOF文件中的所有写命令,还原完整内存数据。AOF存储的是操作过程,而非数据结果。

10.2.2 AOF核心配置与三种刷盘策略

AOF核心在于刷盘策略,决定命令何时从内存缓冲区写入磁盘,直接影响性能和数据安全:

Plain 复制代码
# 开启AOF持久化(默认no关闭)
appendonly yes
# AOF日志文件名
appendfilename appendonly.aof

# 三种刷盘策略(核心重点)
# 1、appendfsync always:每执行一条写命令,立即刷盘
# 数据零丢失,性能极差,高并发场景TPS暴跌,几乎不用
appendfsync always

# 2、appendfsync everysec:每秒刷盘一次(默认推荐)
# 每秒统一将缓冲区数据刷入磁盘,性能均衡
# 宕机最多丢失1秒数据,企业主流方案
appendfsync everysec

# 3、appendfsync no:交由操作系统自主刷盘
# 性能最高,数据丢失不可控,丢失数据量最大,生产禁用
appendfsync no

10.2.3 AOF重写机制(解决日志膨胀问题)

1、重写背景

AOF会持续记录所有写命令,长期运行会导致日志文件无限膨胀,大量冗余命令(如多次修改同一个key、重复自增)堆积,占用磁盘空间、拖慢重启恢复速度。

2、重写核心原理

Redis会触发AOF重写,不读取旧日志,直接遍历当前内存数据,生成最简命令替换原有冗余日志。例如:100次incr自增命令,重写后直接合并为1条set最终结果命令,极大压缩文件体积。

3、重写触发方式
  • 自动重写:满足配置阈值自动触发,默认文件体积超过上次重写后100%、且文件大于64MB触发;

  • 手动重写 :执行bgrewriteaof命令手动触发异步重写。

10.2.4 AOF优缺点详解

核心优点

  1. 数据安全性极高:默认每秒刷盘,最多丢失1秒数据,远优于RDB;

  2. 日志持久、容错性强:以命令追加形式写入,文件不易损坏,即使局部损坏也可修复;

  3. 支持增量恢复:可精准恢复任意时间段的数据。

核心缺点

  1. 文件体积大:同等数据量下,AOF文件远大于RDB快照,磁盘占用更高;

  2. 恢复速度慢:重启需要逐条重放命令,数据量大时恢复耗时远高于RDB;

  3. 性能略低于RDB:持续日志写入、定期重写会轻微消耗CPU和磁盘IO。

10.3 RDB与AOF全方位对比(面试必背)

对比维度 RDB快照持久化 AOF日志持久化
存储内容 内存数据二进制快照(结果) 所有写操作命令(过程)
持久化频率 间隔性持久化 实时/秒级增量持久化
数据丢失 丢失两次快照间数据,丢失量大 最多丢失1秒数据,安全性高
文件体积 小、压缩率高 大、存在冗余日志
恢复速度 极快 较慢
性能损耗 极低 轻微损耗
默认状态 开启 关闭
相关推荐
皮卡狮几秒前
Linux静态库和动态库详解
linux
撩得Android一次心动13 分钟前
Linux编程笔记4【个人用】
linux·笔记·学习
苦瓜小生41 分钟前
AI名词大扫盲!最全的面试ai名词集合
人工智能·面试
龙虾PRO1 小时前
能源与股票量化核心差异,AI全链路落地实操手册
java·面试·职场和发展
Marst Code1 小时前
Python 3.9 已停止维护!从 3.9 到 3.14 全版本深度对比,生产环境该选哪个?
开发语言·python
llwszx1 小时前
【Java/Go后端手撸原生Agent(第九篇):Plan-and-Execute规划模式——从“走一步看一步“到“先谋后动“】
java·python·golang·agent开发·plan模式·规划执行模式
七牛云行业应用1 小时前
Ollama 本地部署 DeepSeek 完全指南:macOS / Windows / Linux 三端安装 + GPU 配置 + API 调用
linux·windows·macos
ZJH__GO1 小时前
网络编程v2--多客户端互通
java·运维·服务器·开发语言·计算机网络
uzong1 小时前
把一面面试总结做成一个 Skill:重复的事,就别每次都重写
后端·面试