口语八股:Redis 面试实战指南——基础篇、持久化篇

📌 一、Redis基础篇

1.1 什么是Redis?为什么要用Redis?

✅ 正确回答思路:

面试官您好,我从三个方面来回答这个问题:

首先说Redis是什么 : Redis是一个基于内存的NoSQL数据库,全称是RE mote DI ctionary Server(远程字典服务器)。它的数据都存在内存中,所以读写速度非常快,经常被用作缓存、消息队列、分布式锁等场景。

然后说为什么要用Redis,我结合实际项目来说:

  1. 性能极高: Redis所有数据都在内存中,读写速度比MySQL这种基于磁盘的数据库快几个数量级。MySQL单机QPS(每秒查询数)一般在几千,而Redis单机轻松上10万+。

    在我之前的电商项目中,商品详情页每天有几百万的访问量,如果每次都查MySQL肯定扛不住。我们把商品信息缓存到Redis后,响应时间从几百毫秒降到了几毫秒,数据库压力也大大降低。

  2. 数据类型丰富: Redis不像Memcached只支持简单的key-value,它支持String、Hash、List、Set、ZSet等多种数据结构,还有Bitmap、HyperLogLog、GEO、Stream等高级类型。

    比如我们做排行榜功能,用ZSet(有序集合)就特别方便,不需要自己写排序逻辑。

  3. 支持持久化: 虽然Redis是内存数据库,但它支持RDB和AOF两种持久化方式,可以把数据保存到磁盘,重启后恢复数据,不像Memcached重启就丢了。

  4. 支持集群和高可用: Redis支持主从复制、哨兵模式、Cluster集群,可以实现高可用和水平扩展。

最后总结实际应用: 在我的项目中,Redis主要用在这几个地方:

  • 缓存: 缓存热点数据,减轻数据库压力
  • Session共享: 分布式系统中存储用户Session
  • 分布式锁: 用SETNX实现分布式锁,解决并发问题
  • 消息队列: 用List或Stream实现简单的消息队列
  • 计数器: 用INCR实现文章阅读数、点赞数等

💡 加分项: 可以补充说"当然Redis也不是万能的,它的缺点是成本较高(内存贵),数据量太大时需要考虑持久化和内存淘汰策略。所以要根据业务场景合理使用Redis。"


1.2 Redis为什么这么快?

✅ 正确回答思路:

Redis快的原因我总结了五个方面:

1. 纯内存操作 这是最核心的原因!内存的读写速度比磁盘快几个数量级。磁盘IO是毫秒级的,而内存是纳秒级的,差了几百万倍。所以只要数据在内存里,读写自然就快。

2. 单线程模型,避免上下文切换 Redis的核心工作线程是单线程的(注意:Redis 6.0后引入了多线程,但只是用来处理网络IO,核心的命令执行还是单线程)。

单线程有什么好处?

  • 不需要考虑线程安全问题,不用加锁
  • 避免了线程切换和锁竞争的开销
  • 代码实现简单,易于维护

有人可能会问:单线程怎么能处理高并发?这就要靠第三点。

3. IO多路复用 Redis使用了IO多路复用技术(在Linux下用epoll,其他系统用select/kqueue),可以让单个线程同时监听成千上万个客户端连接。

简单来说就是:

  • 传统模式下,一个线程只能处理一个连接,要处理1000个连接就需要1000个线程,开销巨大
  • IO多路复用让一个线程可以同时监听1000个连接,哪个连接有数据就处理哪个,效率很高

4. 高效的数据结构 Redis内部使用了高度优化的数据结构:

  • SDS(Simple Dynamic String): 比C语言原生字符串更高效
  • 跳表(SkipList): 实现有序集合,查询效率O(logN)
  • 压缩列表(ZipList/ListPack): 节省内存
  • 哈希表: 快速的键值查找

而且Redis会根据数据量的大小自动选择最优的底层实现,在性能和内存之间做平衡。

5. 简单高效的协议 Redis使用RESP(REdis Serialization Protocol)协议,这个协议非常简单,解析速度快,序列化和反序列化的开销很小。

实际数据对比: 根据官方测试,Redis单机QPS可以达到:

  • GET/SET操作: 10万+ QPS
  • List的LPUSH/LPOP: 8万+ QPS
  • ZSet的ZADD: 8万+ QPS

而MySQL单机一般只有几千QPS,差距非常明显。

💡 记忆口诀: "内存快,单线程,多路复用,数据结构优,协议简单"


1.3 Redis是单线程的吗?Redis 6.0为什么要引入多线程?

✅ 正确回答思路:

这个问题要分版本来说,而且要澄清"单线程"的具体含义:

Redis 6.0之前:

严格来说,Redis并不是完全的单线程:

  • 命令执行是单线程: 这是核心,所有的Redis命令(GET、SET、INCR等)都是单线程执行的
  • 其他任务是多线程: 比如持久化(RDB/AOF)、异步删除(UNLINK)、集群数据同步等,这些都是用后台线程处理的

所以当我们说"Redis是单线程"时,准确的说法是"Redis的命令执行是单线程的"。

单线程的好处:

  1. 避免了线程切换的开销
  2. 不需要考虑锁的问题,代码简单
  3. 所有命令都是串行执行,天然支持ACID中的原子性

Redis 6.0引入多线程:

但是单线程也有瓶颈!随着硬件性能的提升(多核CPU、万兆网卡),单线程逐渐成为瓶颈。尤其是网络IO处理,成为了性能瓶颈。

所以Redis 6.0引入了多线程,但只是用来处理网络IO(接收请求、发送响应),核心的命令执行还是单线程!

具体来说:

复制代码
客户端请求到来
  ↓
多线程处理网络IO(读取数据)  ← 这里是多线程
  ↓
单线程执行命令(SET、GET等)  ← 这里还是单线程
  ↓
多线程处理网络IO(发送响应)  ← 这里是多线程

为什么这样设计?

  1. 网络IO可以并行: 读取和发送数据包可以多线程处理,互不干扰
  2. 命令执行必须串行: 保证数据一致性,避免并发冲突

是否要开启多线程?

Redis 6.0默认是关闭多线程的,需要手动配置:

conf 复制代码
# redis.conf
io-threads-do-reads yes  # 开启多线程
io-threads 4  # 设置IO线程数,建议不超过CPU核心数

什么时候开启?

  • 单机Redis QPS达到10万+,网络IO成为瓶颈时
  • 机器是多核CPU,比如8核16核
  • 有大量的网络IO操作

我的项目经验: 我们项目的Redis QPS一般在5万左右,用的还是单线程模式,性能就够用了。只有在大促期间,QPS突破10万时,才考虑开启多线程,能提升20%-30%的性能。

💡 总结:

  • Redis 6.0之前,命令执行是单线程,但其他任务(持久化等)是多线程
  • Redis 6.0引入了多线程网络IO,但命令执行还是单线程
  • 多线程主要解决网络IO瓶颈,不改变Redis的核心模型

1.4 Redis支持哪些数据类型?分别有什么应用场景?

✅ 正确回答思路:

Redis的数据类型我分两部分来说:五种基础类型和四种高级类型。

一、五种基础数据类型

1. String(字符串)

  • 特点: 最基础的类型,二进制安全,可以存储任何数据,最大512MB

  • 常用命令: SET、GET、INCR、DECR、MGET、MSET

  • 应用场景

    :

    java 复制代码
    // ① 缓存对象(序列化成JSON)redisTemplate.opsForValue().set("user:1001", JSON.toJSONString(user));// ② 计数器(阅读数、点赞数)redisTemplate.opsForValue().increment("article:1001:views");// ③ 分布式锁Boolean result = redisTemplate.opsForValue()    .setIfAbsent("lock:order:1001", "1", 10, TimeUnit.SECONDS);// ④ Session共享redisTemplate.opsForValue().set("session:" + sessionId, userInfo, 30, TimeUnit.MINUTES);

2. Hash(哈希)

  • 特点: 键值对集合,适合存储对象

  • 常用命令: HSET、HGET、HMGET、HGETALL、HINCRBY

  • 应用场景

    :

    java 复制代码
    // 存储用户信息(比String更节省空间)Map<String, String> userMap = new HashMap<>();userMap.put("name", "张三");userMap.put("age", "25");userMap.put("city", "北京");redisTemplate.opsForHash().putAll("user:1001", userMap);// 购物车// key: cart:userId// field: productId// value: 数量redisTemplate.opsForHash().increment("cart:1001", "product:2001", 1);

对比String存储对象:

  • String: 把整个对象序列化成JSON,修改某个字段要把整个对象读出来、修改、再写回去
  • Hash: 可以直接修改某个字段,更高效,也更省内存

3. List(列表)

  • 特点: 有序、可重复,底层是双向链表(Redis 3.2后改为quicklist)

  • 常用命令: LPUSH、RPUSH、LPOP、RPOP、LRANGE、BLPOP

  • 应用场景

    :

    java 复制代码
    // ① 消息队列(简单场景)// 生产者redisTemplate.opsForList().rightPush("queue:order", orderJson);// 消费者String order = redisTemplate.opsForList().leftPop("queue:order");// ② 最新动态列表(朋友圈、微博)redisTemplate.opsForList().leftPush("timeline:1001", postJson);List<String> posts = redisTemplate.opsForList().range("timeline:1001", 0, 9); // 取最新10条// ③ 阻塞队列(BLPOP)String order = redisTemplate.opsForList().leftPop("queue:order", 10, TimeUnit.SECONDS);

4. Set(集合)

  • 特点: 无序、不重复,底层是哈希表

  • 常用命令: SADD、SMEMBERS、SISMEMBER、SINTER、SUNION、SDIFF

  • 应用场景

    :

    java 复制代码
    // ① 去重(用户签到、文章标签)redisTemplate.opsForSet().add("tags:article:1001", "Java", "Redis", "MySQL");// ② 共同关注(交集)Set<String> common = redisTemplate.opsForSet().intersect("following:1001", "following:1002");// ③ 抽奖(随机弹出)String winner = redisTemplate.opsForSet().pop("lottery:2025");// ④ 点赞(判断是否点过赞)Boolean liked = redisTemplate.opsForSet().isMember("liked:article:1001", "user:1001");

5. ZSet(有序集合)

  • 特点: 有序、不重复,每个元素关联一个分数(score),按分数排序

  • 常用命令: ZADD、ZRANGE、ZREVRANGE、ZRANK、ZINCRBY

  • 应用场景

    :

    java 复制代码
    // ① 排行榜(最常用!)// 添加分数redisTemplate.opsForZSet().incrementScore("rank:game", "player:1001", 100);// 获取TOP 10Set<ZSetOperations.TypedTuple<String>> top10 =     redisTemplate.opsForZSet().reverseRangeWithScores("rank:game", 0, 9);// 获取某个玩家的排名Long rank = redisTemplate.opsForZSet().reverseRank("rank:game", "player:1001");// ② 延时队列(按时间戳排序)redisTemplate.opsForZSet().add("delay:queue", taskJson, System.currentTimeMillis() + 3600000);

二、四种高级数据类型

1. Bitmap(位图) - Redis 2.2+

  • 特点: 用bit位来存储0/1状态,非常节省空间

  • 应用场景

    :

    java 复制代码
    // 用户签到(一年365天只需要46字节!)// 1月1日签到redisTemplate.opsForValue().setBit("sign:user:1001:2025", 0, true);// 1月2日签到redisTemplate.opsForValue().setBit("sign:user:1001:2025", 1, true);// 统计1月份签到了几天Long count = (Long) redisTemplate.execute(    (RedisCallback<Long>) con -> con.bitCount("sign:user:1001:2025".getBytes(), 0, 30));// 在线用户统计(10亿用户只需要119MB!)redisTemplate.opsForValue().setBit("online:users", userId, true);

2. HyperLogLog - Redis 2.8+

  • 特点: 用于基数统计(去重计数),误差率0.81%,但极省内存(12KB)

  • 应用场景

    :

    java 复制代码
    // 统计UV(独立访客数)redisTemplate.opsForHyperLogLog().add("uv:page:home:20250213", "user:1001", "user:1002");// 获取UVLong uv = redisTemplate.opsForHyperLogLog().size("uv:page:home:20250213");// 适合场景:数据量大、允许小误差// 比如统计网站每天的UV,几百万用户,用HyperLogLog只需12KB,用Set可能需要几十MB

3. GEO(地理位置) - Redis 3.2+

  • 特点: 存储地理位置坐标,支持计算距离、范围查询

  • 应用场景

    :

    java 复制代码
    // 附近的人/店铺// 添加位置redisTemplate.opsForGeo().add("restaurants",     new Point(116.404, 39.915), "restaurant:1001");// 查找附近5公里内的餐厅Circle circle = new Circle(new Point(116.404, 39.915),     new Distance(5, Metrics.KILOMETERS));GeoResults<GeoLocation<String>> results =     redisTemplate.opsForGeo().radius("restaurants", circle);// 计算两地距离Distance distance = redisTemplate.opsForGeo().distance(    "restaurants", "restaurant:1001", "restaurant:1002", Metrics.KILOMETERS);

4. Stream(流) - Redis 5.0+

  • 特点: 消息队列,支持消费组,比List实现的队列更强大

  • 应用场景

    :

    java 复制代码
    // 发送消息redisTemplate.opsForStream().add("order:stream",     Collections.singletonMap("orderId", "1001"));// 消费消息(支持消费组,支持ACK)List<MapRecord<String, Object, Object>> messages =     redisTemplate.opsForStream().read(Consumer.from("group1", "consumer1"),         StreamReadOptions.empty().count(10),         StreamOffset.create("order:stream", ReadOffset.lastConsumed()));

总结对比表:

数据类型 特点 典型应用
String 最基础,最通用 缓存、计数器、分布式锁、Session
Hash 键值对集合 存储对象、购物车
List 有序可重复 消息队列、最新列表、Timeline
Set 无序不重复 去重、共同关注、抽奖、点赞
ZSet 有序不重复 排行榜、延时队列
Bitmap 位级操作 签到、在线状态、布隆过滤器
HyperLogLog 基数统计 UV统计
GEO 地理位置 附近的人、LBS
Stream 消息流 高级消息队列

💡 面试技巧 : 不要只说"String可以存字符串",要说具体的业务场景!比如"在我的项目中,用String存储用户Session,用ZSet实现游戏排行榜"。这样面试官会觉得你真正用过Redis。


📌 二、持久化篇

2.1 Redis的持久化机制有哪些?RDB和AOF的区别?

✅ 正确回答思路:

Redis虽然是内存数据库,但为了防止数据丢失,提供了两种持久化机制:RDB和AOF。我从原理、优缺点、实际使用三方面来说:

一、RDB(Redis Database)

1. 原理: RDB就是定期把内存中的数据以快照(snapshot)的形式保存到磁盘上,生成一个dump.rdb文件。

触发时机:

conf 复制代码
# redis.conf配置
save 900 1      # 900秒内至少1个key被修改,就保存
save 300 10     # 300秒内至少10个key被修改,就保存
save 60 10000   # 60秒内至少10000个key被修改,就保存

也可以手动触发:

  • SAVE: 同步保存,会阻塞Redis,生产环境别用!
  • BGSAVE: 后台保存,fork一个子进程去做,不阻塞主进程

2. 工作流程:

复制代码
1. Redis主进程fork一个子进程
2. 子进程把内存数据写入临时RDB文件
3. 写完后,用临时文件替换旧的dump.rdb
4. 子进程退出

重点: fork使用了**写时复制(COW, Copy-On-Write)**技术,fork时不会立即复制所有数据,只有在数据被修改时才复制,所以fork很快。

3. 优点:

  • 恢复速度快: RDB是二进制文件,加载速度快,适合大数据量的恢复
  • 性能好: fork子进程去做,不影响主进程处理请求
  • 文件小: 经过压缩,文件体积小,适合备份和灾难恢复

4. 缺点:

  • 数据丢失风险: 如果Redis挂了,最后一次快照之后的数据会丢失。比如设置5分钟保存一次,挂了可能丢5分钟的数据
  • fork耗时: 数据量大时,fork会比较慢,可能影响性能

二、AOF(Append Only File)

1. 原理: AOF是把每一个写命令(SET、DEL等)都追加到aof文件里,相当于记录操作日志。恢复时重新执行一遍这些命令就行了。

配置:

conf 复制代码
appendonly yes  # 开启AOF
appendfilename "appendonly.aof"  # AOF文件名

2. 同步策略(重要!):

有三种策略,决定了数据安全性:

conf 复制代码
# appendfsync always      # 每个命令都立即同步到磁盘,最安全但最慢
appendfsync everysec    # 每秒同步一次(默认,推荐)
# appendfsync no          # 由操作系统决定何时同步,最快但可能丢数据
  • always: 最安全,几乎不丢数据,但性能差
  • everysec: 折中方案,最多丢1秒数据,性能也还行(推荐)
  • no: 最快,但可能丢几十秒数据

3. AOF重写:

问题: AOF文件会越来越大,比如对同一个key执行了100次SET,AOF就记录了100条命令,但其实只需要最后一条。

解决: AOF重写(Rewrite),把冗余命令合并:

conf 复制代码
auto-aof-rewrite-percentage 100  # AOF文件比上次重写后大100%时,自动重写
auto-aof-rewrite-min-size 64mb   # AOF文件至少要64MB才重写

重写流程:

复制代码
1. fork子进程
2. 子进程根据内存数据生成新的AOF文件
3. 主进程继续处理请求,新的写命令写到AOF重写缓冲区
4. 子进程写完后,主进程把缓冲区的命令追加到新AOF文件
5. 用新文件替换旧文件

4. 优点:

  • 数据更安全: everysec策略最多丢1秒数据,比RDB安全多了
  • 可读性强: AOF是文本文件,可以直接看到命令,出问题方便排查
  • 支持修复: AOF文件损坏了,可以用redis-check-aof工具修复

5. 缺点:

  • 文件大: 相同数据,AOF文件比RDB大
  • 恢复慢: 需要重新执行命令,恢复比RDB慢
  • 性能稍差: 写操作需要追加到文件,有一定开销

三、RDB vs AOF 对比表

对比项 RDB AOF
文件大小 小(二进制压缩) 大(文本文件)
恢复速度
数据安全性 差(可能丢几分钟) 好(最多丢1秒)
性能影响 稍大
可读性 不可读 可读

四、混合持久化(Redis 4.0+)

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

conf 复制代码
aof-use-rdb-preamble yes  # 开启混合持久化

原理: AOF重写时,把RDB格式的数据写到AOF文件开头,后面再追加增量的AOF命令。

复制代码
[RDB格式的全量数据] + [AOF格式的增量命令]

优点:

  • 加载速度快(前面是RDB)
  • 数据丢失少(后面是AOF)

五、实际项目选择

我的项目中,一般这样配置:

conf 复制代码
# 同时开启RDB和AOF
# RDB用于快速恢复和备份
save 900 1
save 300 10
save 60 10000

# AOF用于数据安全
appendonly yes
appendfsync everysec
aof-use-rdb-preamble yes  # 开启混合持久化

不同场景的选择:

  • 缓存场景: 只用RDB或者不持久化,丢了就从数据库加载
  • 数据重要: 用AOF或者RDB+AOF混合,保证数据安全
  • 高性能要求: 用RDB,但要接受可能丢数据
  • 折中方案: 混合持久化(推荐)

💡 记忆口诀:

  • RDB: 快照备份,快速恢复,可能丢数据
  • AOF: 日志追加,安全可靠,文件较大
  • 混合: 两者结合,各取所长

2.2 如果Redis挂了,如何快速恢复数据?

✅ 正确回答思路:

这个问题考察的是持久化和高可用,我从两方面回答:

一、单机Redis的数据恢复

1. 基于RDB恢复

bash 复制代码
# 1. 停止Redis
redis-cli shutdown

# 2. 把备份的dump.rdb复制到Redis数据目录
cp /backup/dump.rdb /var/lib/redis/dump.rdb

# 3. 启动Redis,自动加载RDB文件
redis-server

优点 : 恢复快,几秒到几分钟 缺点: 会丢失最后一次快照之后的数据

2. 基于AOF恢复

bash 复制代码
# 1. 停止Redis
redis-cli shutdown

# 2. 把备份的appendonly.aof复制到数据目录
cp /backup/appendonly.aof /var/lib/redis/appendonly.aof

# 3. 如果AOF文件损坏,先修复
redis-check-aof --fix appendonly.aof

# 4. 启动Redis,自动加载AOF文件
redis-server

优点 : 数据更完整,最多丢1秒 缺点: 恢复慢,需要重新执行命令

3. 如果同时有RDB和AOF

Redis启动时的加载顺序:

复制代码
1. 检查是否有AOF文件
2. 有AOF: 加载AOF(AOF优先级更高,数据更完整)
3. 没有AOF: 加载RDB

二、高可用架构下的快速恢复

在生产环境,不能依赖单机恢复,太慢了!应该用高可用架构:

1. 主从复制 + 哨兵模式

复制代码
Master(主节点)
  ↓ 复制
Slave1  Slave2  Slave3(从节点)
  ↓
Sentinel1  Sentinel2  Sentinel3(哨兵,监控)

工作流程:

复制代码
1. Master挂了,哨兵检测到(主观下线→客观下线)
2. 哨兵投票选举一个Slave提升为新Master
3. 其他Slave自动切换到新Master
4. 应用无感知,自动故障转移

恢复时间: 几秒到几十秒(取决于哨兵的down-after-milliseconds配置)

我的项目配置:

conf 复制代码
# sentinel.conf
sentinel monitor mymaster 192.168.1.100 6379 2  # 2个哨兵认为下线才算下线
sentinel down-after-milliseconds mymaster 5000  # 5秒检测不到就认为下线
sentinel failover-timeout mymaster 60000        # 故障转移超时时间

2. Redis Cluster集群

复制代码
Master1  Master2  Master3
   ↓       ↓        ↓
Slave1  Slave2   Slave3

工作流程:

复制代码
1. 某个Master挂了
2. 它的Slave自动提升为Master
3. 集群自动重新分配槽位
4. 应用无感知

恢复时间: 几秒(cluster-node-timeout配置,默认15秒)

三、实际项目的最佳实践

我们的生产环境是这样做的:

1. 架构:

  • 主从 + 哨兵模式(3主3从3哨兵)
  • 数据重要程度高的业务用Cluster

2. 持久化配置:

conf 复制代码
# Master: 开启AOF,保证数据安全
appendonly yes
appendfsync everysec
aof-use-rdb-preamble yes

# Slave: 只开启RDB,减轻负担
save 900 1

3. 备份策略:

  • 每天凌晨2点,用crontab定时备份RDB文件到远程服务器
  • 保留最近7天的备份
  • 重要数据做异地备份

4. 监控告警:

  • 用Prometheus监控Redis,主节点挂了立即告警
  • 监控内存使用率、命中率、持久化状态

5. 演练:

  • 每个季度做一次故障演练,手动kill掉Master,测试恢复时间
  • 我们测试下来,从Master挂掉到新Master上线,平均10秒左右

四、真实故障案例

我们去年双11期间,主库机器硬盘故障,挂了:

处理流程:

复制代码
1. 哨兵立即检测到,8秒后切换到从库
2. 业务几乎无感知,只有少量请求超时
3. 第二天更换硬盘,把修复好的节点作为新的从库加入

经验教训:

  • 高可用架构太重要了,单机就是定时炸弹
  • 持久化也要做,但高可用更重要
  • 定期演练,不演练等于没准备

💡 总结:

  • 单机: 靠持久化恢复,慢,不推荐生产使用
  • 高可用: 主从+哨兵,或者Cluster,秒级恢复
  • 最佳实践: 高可用架构 + 持久化 + 备份 + 监控 + 演练
相关推荐
学到头秃的suhian4 小时前
Redis的Java客户端
java·数据库·redis
yueyin1234564 小时前
在Django中安装、配置、使用CKEditor5,并将CKEditor5录入的文章展现出来,实现一个简单博客网站的功能
数据库·django·sqlite
人间打气筒(Ada)4 小时前
SQL Server 之创建和管理数据表
运维·服务器·数据库·windows·sql语句·sql server·windows server
eWidget4 小时前
核心系统迁移实战:如何保障从 Oracle 到国产架构的平滑过渡?
数据库·oracle·架构·kingbase·数据库平替用金仓·金仓数据库
Dovis(誓平步青云)4 小时前
《MySQL 事务深度解析:从 ACID 到实战,守住数据一致性的最后防线》
数据库·mysql·flink·etcd·功能详解
霖霖总总14 小时前
[小技巧69]为什么总说MySQL单表“别超 2000 万行”?一篇讲透 InnoDB 存储极限
数据库·mysql
常利兵14 小时前
吃透Java操作符高阶:位操作符+赋值操作符全解析(Java&C区别+实战技巧+面试考点)
java·c语言·面试
安科士andxe14 小时前
实操指南|安科士1.25G CWDM SFP光模块选型、部署与运维全攻略
运维·数据库·5g
Java爱好狂.15 小时前
RDB&AOF持久化原理解析
java·数据库·redis·后端开发·java编程·java程序员·java八股文