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时代,数据形态和业务场景发生颠覆性变化,暴露出诸多短板:
-
数据量级激增:用户行为日志、社交动态、地理位置、设备信息等数据成倍增长,海量数据存储与查询成为难题;
-
数据类型多样化:除了传统二维表结构化数据,大量非结构化数据(图片、视频、文档、HTML文本)、半结构化数据(JSON、XML)诞生,关系型数据库适配性极差;
-
高并发读写需求:电商秒杀、短视频、社交点赞等场景,每秒数万次读写请求,关系型数据库磁盘读写模式无法支撑;
-
低容错高扩展需求:互联网业务需要快速迭代、横向扩容,关系型数据库固定表结构、扩容复杂的特性无法适配。
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)优缺点
核心优点:
-
结构规范:统一二维表结构,数据规整、易于维护;
-
查询强大:标准SQL语法,支持多表联查、子查询、聚合查询等复杂操作;
-
事务可靠:完美支持ACID事务特性,保证数据一致性,适合支付、订单等核心业务;
-
生态成熟:运维工具完善、社区活跃、兼容性强。
核心缺点:
-
读写性能弱:基于磁盘存储,海量数据高并发读写效率低;
-
灵活性差:表结构固定,字段修改、拓展成本高;
-
横向扩容复杂:集群部署、分片扩容难度大、成本高。
2.3.2 NoSQL数据库优缺点
核心优点:
-
格式灵活:支持KV、文档、列式等多种存储格式,适配各类数据形态;
-
性能极致:支持内存存储,读写速度远超关系型数据库;
-
扩展性强:天然支持分布式集群,横向扩容无瓶颈;
-
成本低廉:全部开源、部署简单、无需昂贵硬件设备。
核心缺点:
-
无标准SQL:查询语法不统一,学习和开发成本较高;
-
事务支持弱:绝大多数NoSQL不支持完整ACID事务,数据一致性较差;
-
复杂查询薄弱:不支持多表联查、复杂聚合,业务适配场景有限。
三、企业级SQL+NoSQL混合架构实战
当下互联网企业不会单一使用SQL或NoSQL,而是采用混合架构,根据数据特性匹配最优存储方案,以淘宝商品存储架构为例:
3.1 商品分层存储方案
-
商品基础结构化数据 :商品名称、价格、规格、厂商、库存、商家信息,存储于MySQL,保障数据一致性和事务性;
-
商品详情、评论、图文描述 :非结构化、字段动态拓展数据,存储于MongoDB,适配灵活的数据结构;
-
商品图片、视频资源 :超大静态资源,存储于分布式文件系统(阿里云OSS、HDFS、TFS);
-
商品关键词、检索数据 :支持全文模糊搜索,同步至ElasticSearch,实现高效商品检索;
-
商品热点数据、瞬时流量数据 :热门商品、实时销量、浏览量、秒杀数据,缓存至Redis,支撑高并发访问。
3.2 混合架构解决的核心问题
互联网业务存在数据类型多样、数据源繁杂、业务迭代快的特点,单一数据库无法适配全场景。SQL+NoSQL混合架构,结合了关系型数据库的事务可靠性和NoSQL的高性能、高灵活性,配合统一数据服务平台,实现数据解耦、业务无感知迭代。
四、Redis核心定义与基础特性
4.1 Redis官方定义
Redis是一款ANSI C语言开发、开源、基于内存、支持持久化、多数据结构、网络分布式的键值对NoSQL数据库,遵守BSD开源协议,支持跨平台、多语言API对接,是目前企业最主流的分布式缓存中间件。
4.2 Redis核心核心特性
-
极致高性能:纯内存操作,无磁盘IO开销,单节点QPS可达10万+,响应延迟毫秒级;
-
丰富数据结构:支持8种以上数据类型,适配各类业务场景;
-
数据持久化:支持RDB、AOF双持久化机制,防止内存数据丢失;
-
分布式扩展:支持主从、哨兵、Cluster集群三种架构,无上限横向扩容;
-
功能丰富:支持事务、发布订阅、流水线、过期淘汰、限流、分布式锁等高级功能;
-
多语言兼容:完美适配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优缺点详解
核心优点:
-
文件体积小、恢复速度快:存储二进制快照文件,无冗余日志,重启加载速度远快于AOF,适合全量数据备份;
-
性能损耗极低:依托子进程异步持久化,主线程不阻塞,不影响业务读写;
-
适合冷备份、容灾迁移:单文件存储,传输、备份、迁移极其便捷。
致命缺点:
-
存在数据丢失风险:RDB是间隔性快照,两次快照之间的增量数据不会落地磁盘。若服务突然宕机,会丢失上一次快照到宕机前的所有数据;
-
fork子进程消耗资源:大数据量场景下,fork进程会拷贝内存页表,短暂占用CPU和内存资源;
-
无法实现秒级数据恢复:最小数据丢失窗口为数秒到数分钟,无法满足极致数据一致性场景。
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秒数据,远优于RDB;
-
日志持久、容错性强:以命令追加形式写入,文件不易损坏,即使局部损坏也可修复;
-
支持增量恢复:可精准恢复任意时间段的数据。
核心缺点:
-
文件体积大:同等数据量下,AOF文件远大于RDB快照,磁盘占用更高;
-
恢复速度慢:重启需要逐条重放命令,数据量大时恢复耗时远高于RDB;
-
性能略低于RDB:持续日志写入、定期重写会轻微消耗CPU和磁盘IO。
10.3 RDB与AOF全方位对比(面试必背)
| 对比维度 | RDB快照持久化 | AOF日志持久化 |
|---|---|---|
| 存储内容 | 内存数据二进制快照(结果) | 所有写操作命令(过程) |
| 持久化频率 | 间隔性持久化 | 实时/秒级增量持久化 |
| 数据丢失 | 丢失两次快照间数据,丢失量大 | 最多丢失1秒数据,安全性高 |
| 文件体积 | 小、压缩率高 | 大、存在冗余日志 |
| 恢复速度 | 极快 | 较慢 |
| 性能损耗 | 极低 | 轻微损耗 |
| 默认状态 | 开启 | 关闭 |