news 2026/9/12 18:58:30

Java数据库与数据存储:Redis

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java数据库与数据存储:Redis

1. 引言

在当今互联网高并发场景下,数据库的性能瓶颈往往成为系统扩展的最大障碍。Redis 作为一款高性能的内存数据库,凭借其丰富的数据结构、极快的读写速度和灵活的持久化机制,已经成为 Java 后端开发中不可或缺的组件。

本文将系统性地介绍 Redis 的核心知识体系,包括五种基本数据结构、持久化机制、集群部署模式,以及面试中高频出现的缓存穿透、缓存雪崩和缓存击穿三大经典问题。无论你是准备面试还是实际项目开发,这篇文章都能为你提供完整的知识框架。

2. Redis 概述

2.1 什么是 Redis

Redis(Remote Dictionary Server)是一个开源的、基于内存的键值对存储系统,由 Salvatore Sanfilippo 于 2009 年开发。它支持多种数据结构,如字符串、哈希、列表、集合和有序集合,并提供了丰富的操作命令。

2.2 Redis 的核心特性

  • 高性能:基于内存操作,单线程模型避免了并发竞争,读写速度可达 10 万次/秒以上
  • 丰富的数据结构:支持 String、Hash、List、Set、ZSet 五种基本类型及 Bitmap、HyperLogLog、Geo 等高级类型
  • 持久化机制:支持 RDB 快照和 AOF 日志两种持久化方式
  • 高可用:支持主从复制、哨兵模式和集群模式
  • 原子性操作:所有命令都是原子操作,支持事务和 Lua 脚本

2.3 Redis 的应用场景

  • 缓存系统:减轻数据库压力,提升系统响应速度
  • 分布式锁:基于 SETNX 命令实现分布式锁
  • 排行榜:利用 ZSet 实现实时排行榜
  • 计数器:利用 INCR/DECR 实现访问计数、库存扣减
  • 消息队列:利用 List 的 LPUSH/BRPOP 实现简单的消息队列
  • 会话管理:存储用户登录状态和 Session 信息

3. Redis 数据结构详解

3.1 String(字符串)

String 是 Redis 最基本的数据类型,可以存储字符串、整数或浮点数,最大容量为 512MB。

常用命令

SET key value# 设置键值GET key# 获取值INCR key# 值加1DECR key# 值减1SETEX key seconds value# 设置带过期时间的键值MSET key1 value1 key2 value2# 批量设置

Java 使用示例

// 使用 Spring Data Redis@AutowiredprivateStringRedisTemplatestringRedisTemplate;// 设置缓存stringRedisTemplate.opsForValue().set("user:1001","张三",30,TimeUnit.MINUTES);// 获取缓存Stringuser=stringRedisTemplate.opsForValue().get("user:1001");// 计数器操作Longcount=stringRedisTemplate.opsForValue().increment("page:view:count");

应用场景:缓存对象、计数器、分布式锁、Session 管理。

3.2 Hash(哈希)

Hash 是一个键值对集合,适合存储对象类型的数据,可以单独修改某个字段而不影响其他字段。

常用命令

HSET key field value# 设置字段值HGET key field# 获取字段值HGETALL key# 获取所有字段和值HDEL key field# 删除字段HINCRBY key field increment# 字段值增加

Java 使用示例

@AutowiredprivateRedisTemplate<String,Object>redisTemplate;// 存储用户对象HashOperations<String,String,Object>hashOps=redisTemplate.opsForHash();Map<String,Object>userMap=newHashMap<>();userMap.put("name","李四");userMap.put("age",25);userMap.put("email","lisi@example.com");hashOps.putAll("user:1002",userMap);// 获取单个字段Objectname=hashOps.get("user:1002","name");// 获取整个对象Map<String,Object>user=hashOps.entries("user:1002");

应用场景:存储对象信息、购物车、用户资料。

3.3 List(列表)

List 是一个双向链表,支持从两端插入和弹出元素,适合实现队列和栈。

常用命令

LPUSH key value# 从左侧插入RPUSH key value# 从右侧插入LPOP key# 从左侧弹出RPOP key# 从右侧弹出LRANGE key start stop# 获取范围内的元素LLEN key# 获取列表长度

Java 使用示例

// 实现消息队列ListOperations<String,String>listOps=redisTemplate.opsForList();// 生产者:发送消息listOps.rightPush("message:queue","消息1");listOps.rightPush("message:queue","消息2");// 消费者:接收消息(阻塞式)Stringmessage=listOps.leftPop("message:queue",10,TimeUnit.SECONDS);

应用场景:消息队列、最新消息列表、时间线。

3.4 Set(集合)

Set 是无序的字符串集合,元素唯一,支持集合运算(交集、并集、差集)。

常用命令

SADD key member# 添加元素SREM key member# 删除元素SMEMBERS key# 获取所有元素SISMEMBER key member# 判断元素是否存在SINTER key1 key2# 求交集SUNION key1 key2# 求并集

Java 使用示例

SetOperations<String,String>setOps=redisTemplate.opsForSet();// 添加标签setOps.add("user:1001:tags","Java","Redis","Spring");// 判断是否包含某元素BooleanhasTag=setOps.isMember("user:1001:tags","Java");// 求共同关注Set<String>common=setOps.intersect("user:1001:follows","user:1002:follows");

应用场景:标签系统、好友关系、去重操作。

3.5 ZSet(有序集合)

ZSet 在 Set 的基础上增加了分数(score)属性,元素按分数排序,适合实现排行榜。

常用命令

ZADD key score member# 添加元素及分数ZRANGE key start stop# 按分数升序获取ZREVRANGE key start stop# 按分数降序获取ZSCORE key member# 获取元素分数ZINCRBY key increment member# 增加元素分数ZRANK key member# 获取元素排名

Java 使用示例

ZSetOperations<String,String>zSetOps=redisTemplate.opsForZSet();// 添加排行榜数据zSetOps.add("ranking:game","玩家A",100);zSetOps.add("ranking:game","玩家B",85);zSetOps.add("ranking:game","玩家C",95);// 获取前三名Set<String>top3=zSetOps.reverseRange("ranking:game",0,2);// 增加分数zSetOps.incrementScore("ranking:game","玩家B",20);

应用场景:排行榜、延时队列、优先级队列。

4. Redis 持久化机制

4.1 RDB(Redis DataBase)持久化

RDB 是 Redis 默认的持久化方式,通过创建快照(Snapshot)将内存中的数据保存到磁盘。

工作原理

  • 在指定的时间间隔内,将内存中的数据集快照写入磁盘
  • 默认文件名为dump.rdb
  • 通过 fork 子进程完成快照生成,不影响主进程服务

触发条件

# 配置文件 redis.conf 中的默认配置save9001# 900秒内至少1次修改save30010# 300秒内至少10次修改save6010000# 60秒内至少10000次修改

优点

  • 文件紧凑,适合备份和灾难恢复
  • 恢复速度快
  • 对性能影响小

缺点

  • 可能丢失最后一次快照后的数据
  • fork 子进程时可能短暂阻塞

4.2 AOF(Append Only File)持久化

AOF 通过记录每次写操作命令来持久化数据,以追加的方式写入日志文件。

工作原理

  • 将每次写命令追加到appendonly.aof文件
  • 支持三种同步策略:always、everysec、no
  • 支持 AOF 重写机制压缩文件大小

配置方式

# 开启 AOFappendonlyyes# 同步策略appendfsync always# 每次写入都同步,最安全但性能最差appendfsync everysec# 每秒同步一次,推荐appendfsync no# 由操作系统决定同步时机

优点

  • 数据安全性高,最多丢失 1 秒数据
  • 日志文件可读,便于排查问题

缺点

  • 文件体积较大
  • 恢复速度较慢
  • 相同数据集下 AOF 文件比 RDB 大

4.3 混合持久化

Redis 4.0 引入了混合持久化机制,结合 RDB 和 AOF 的优点:

  • 使用 RDB 作为基础快照
  • 在快照之间使用 AOF 记录增量操作
  • 兼顾恢复速度和数据安全性
# 开启混合持久化aof-use-rdb-preambleyes

5. Redis 集群模式

5.1 主从复制模式

主从复制是最基础的集群模式,通过将主节点的数据同步到从节点,实现读写分离和数据备份。

架构特点

  • 一个主节点(Master)负责写操作
  • 多个从节点(Slave)负责读操作
  • 从节点自动同步主节点数据

配置方式

# 在从节点配置文件中指定主节点replicaof192.168.1.1006379# 或使用命令动态设置SLAVEOF192.168.1.1006379

复制原理

  1. 从节点发送 SYNC 命令
  2. 主节点执行 BGSAVE 生成 RDB 快照
  3. 主节点将快照发送给从节点
  4. 从节点加载快照
  5. 主节点将增量命令发送给从节点

优点

  • 实现读写分离,提升读性能
  • 数据冗余备份
  • 配置简单

缺点

  • 主节点故障时无法自动切换
  • 从节点过多时同步压力大

5.2 哨兵模式(Sentinel)

哨兵模式在主从复制基础上增加了自动故障转移能力,实现高可用。

架构特点

  • 哨兵进程监控主从节点的运行状态
  • 主节点故障时自动选举新的主节点
  • 客户端通过哨兵获取当前主节点地址

哨兵配置

# sentinel.confsentinel monitor mymaster192.168.1.10063792sentinel down-after-milliseconds mymaster5000sentinel failover-timeout mymaster15000

故障转移流程

主节点故障

哨兵检测到主节点下线

投票选举新主节点

从节点升级为主节点

其他从节点重新同步

客户端连接新主节点

优点

  • 自动故障转移,高可用
  • 无需人工干预
  • 支持监控和通知

缺点

  • 只有一个主节点,写性能受限
  • 配置相对复杂

5.3 Cluster 集群模式

Cluster 模式是 Redis 3.0 引入的分布式解决方案,支持数据分片和水平扩展。

架构特点

  • 数据自动分片到 16384 个哈希槽
  • 每个节点负责一部分哈希槽
  • 支持多主多从,无中心化架构
  • 支持在线扩容和缩容

哈希槽分配

# 集群节点分配哈希槽redis-cli-p7001cluster addslots0-5460 redis-cli-p7002cluster addslots5461-10922 redis-cli-p7003cluster addslots10923-16383

集群配置示例

# 创建集群(至少3个主节点)redis-cli--clustercreate192.168.1.101:7001192.168.1.102:7002\192.168.1.103:7003 --cluster-replicas1

数据分片原理

客户端请求

计算哈希槽

CRC16(key) % 16384

定位节点

执行操作

优点

  • 支持水平扩展,突破单机内存限制
  • 高可用,支持自动故障转移
  • 无中心化,避免单点瓶颈

缺点

  • 不支持多键操作(跨槽位)
  • 客户端需要支持集群协议
  • 数据迁移期间可能影响性能

6. 缓存穿透、缓存雪崩与缓存击穿

6.1 缓存穿透

问题描述:查询一个不存在的数据,缓存和数据库中都没有,导致每次请求都直接打到数据库。

产生原因

  • 恶意攻击,构造大量不存在的 key
  • 业务逻辑缺陷,查询了不存在的 ID

解决方案

方案一:缓存空值

publicObjectgetData(Stringkey){// 先从缓存查询Objectvalue=redisTemplate.opsForValue().get(key);if(value!=null){returnvalue;}// 缓存未命中,查询数据库ObjectdbValue=queryFromDatabase(key);if(dbValue==null){// 缓存空值,设置较短的过期时间redisTemplate.opsForValue().set(key,"",60,TimeUnit.SECONDS);returnnull;}// 缓存真实数据redisTemplate.opsForValue().set(key,dbValue,30,TimeUnit.MINUTES);returndbValue;}

方案二:布隆过滤器

// 使用 Guava 布隆过滤器BloomFilter<String>bloomFilter=BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()),1000000,// 预计元素数量0.01// 误判率);// 初始化时加载所有存在的 keybloomFilter.put("user:1001");bloomFilter.put("user:1002");publicObjectgetData(Stringkey){// 布隆过滤器判断if(!bloomFilter.mightContain(key)){returnnull;// 一定不存在,直接返回}// 可能存在,继续查询缓存和数据库// ...}

6.2 缓存雪崩

问题描述:大量缓存同时过期或 Redis 服务宕机,导致所有请求直接打到数据库,造成数据库压力骤增甚至崩溃。

产生原因

  • 缓存设置了相同的过期时间
  • Redis 服务宕机
  • 缓存服务器重启

解决方案

方案一:过期时间加随机值

// 设置过期时间时加入随机值,避免同时过期intbaseExpire=30*60;// 基础过期时间30分钟intrandomExpire=newRandom().nextInt(300);// 随机0-300秒redisTemplate.opsForValue().set(key,value,baseExpire+randomExpire,TimeUnit.SECONDS);

方案二:多级缓存

// 一级缓存:本地缓存(Caffeine)Cache<String,Object>localCache=Caffeine.newBuilder().expireAfterWrite(5,TimeUnit.MINUTES).maximumSize(10000).build();// 二级缓存:RedispublicObjectgetData(Stringkey){// 先查本地缓存ObjectlocalValue=localCache.getIfPresent(key);if(localValue!=null){returnlocalValue;}// 再查 RedisObjectredisValue=redisTemplate.opsForValue().get(key);if(redisValue!=null){localCache.put(key,redisValue);returnredisValue;}// 最后查数据库ObjectdbValue=queryFromDatabase(key);if(dbValue!=null){redisTemplate.opsForValue().set(key,dbValue,30,TimeUnit.MINUTES);localCache.put(key,dbValue);}returndbValue;}

方案三:Redis 高可用

  • 使用哨兵模式或集群模式
  • 配置主从复制,实现故障自动转移
  • 使用持久化机制,重启后快速恢复

6.3 缓存击穿

问题描述:某个热点 key 在缓存过期的瞬间,大量并发请求同时访问该 key,导致所有请求都打到数据库。

产生原因

  • 热点数据过期
  • 高并发访问同一数据

解决方案

方案一:互斥锁

publicObjectgetData(Stringkey){// 先从缓存查询Objectvalue=redisTemplate.opsForValue().get(key);if(value!=null){returnvalue;}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 18:58:23

网络摄像机首次播放音视频延时出图像踩坑记录

ZLMediaKit / RTSP / H.264 播放音视频踩坑记录 - SDP、SPS、PPS 完整详解适用场景&#xff1a;RTSP 信令交互、addStreamProxy 拉流、WebRTC、H264 裸流、花屏、首帧黑屏、无法解码&#xff0c;绝大多数问题根源就是 SDP / SPS/PPS。1、SDP 是什么 SDP&#xff1a;Session Des…

作者头像 李华
网站建设 2026/9/12 18:58:05

计算机硬件基础知识点:构建系统级故障推理框架

1. 这不是教科书&#xff0c;是十年装机维修老手的“硬件认知地图”你打开电脑前&#xff0c;有没有想过&#xff1a;为什么插上电源线&#xff0c;主板上的LED灯会亮&#xff1f;为什么换一根内存条&#xff0c;系统就蓝屏了&#xff1f;为什么硬盘灯狂闪时&#xff0c;鼠标却…

作者头像 李华
网站建设 2026/9/12 18:57:53

ISP---RAW数据解析:从PixelFormat、Packed_Buffer到Bayer解码

相机SDK仅向应用层交付void* buffer裸字节流、图像宽高及基础元数据&#xff0c;如何将无结构的字节序列&#xff0c;还原为精准、无失真的二维RAW图像&#xff1f; RAW硬件-软件传输链路遵循固定范式&#xff1a; Sensor → PixelFormat → Transport Buffer → Unpack → 2D …

作者头像 李华
网站建设 2026/9/12 18:54:35

Python情感分析实战:从文本到情绪识别的技术实现

1. 项目概述&#xff1a;Python情感计算实战情感计算作为自然语言处理&#xff08;NLP&#xff09;的重要分支&#xff0c;正在深刻改变人机交互的方式。这个项目将带你用Python构建一个能读懂人类情绪的智能系统——从原始文本到精准的情绪识别&#xff0c;完整实现情感分析的…

作者头像 李华
网站建设 2026/9/12 18:54:09

LlamaIndex 系列【25】检索增强策略:ColBERT(延迟交互检索)

文章目录1. 前言2. ColBERT 是什么&#xff1f;2.1 BERT 模型2.2 ColBERT 检索模型2.3 三种架构对比3. 工作流程3.1 文档离线编码3.2 用户查询编码3.3 MaxSim 匹配3.4 计算得分4. 两种场景4.1 召回4.2 重排序1. 前言 在之前我们介绍了两种编码器&#xff1a; Bi-encoders&…

作者头像 李华
网站建设 2026/9/12 18:53:22

编译原理作业实战:词法分析、语法分析与错误恢复的完整实现

简介&#xff1a;北京邮电大学计算机科学与技术专业大三上学期编译原理课程作业完整资料包&#xff0c;作业得分97分。内容覆盖词法分析与语法分析两大核心模块&#xff0c;包含可直接运行的源代码、实验报告、文档说明及配套PPT和PDF讲义&#xff0c;适合正在学习编译原理、需…

作者头像 李华