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

Redis 面试宝典

目录

  • 第一题:Redis是什么?有哪些数据类型?
  • 第二题:Redis的持久化机制有哪些?RDB和AOF的区别?
  • 第三题:Redis如何实现高可用?主从复制、哨兵、集群的区别?
  • 第四题:Redis的缓存穿透、缓存击穿、缓存雪崩是什么?如何解决?
  • 第五题:Redis的过期策略和内存回收机制?
  • 第六题:Redis的性能优化有哪些方法?
  • 第七题:Redis的事务机制和Lua脚本?
  • 第八题:Redis的常见应用场景和最佳实践?
  • 第九题:缓存技术有哪些分类?为什么Redis成为主流?ZooKeeper可以作为缓存吗?
  • 第十题:Redis适合做分布式锁吗?有哪些更好的替代方案?
  • 第十一题:单线程的Redis为什么那么快?现在还是单线程吗?
  • 第十二题:Redisson是什么?它解决了什么问题?
  • 第十三题:Redis的操作为什么是原子性的?如何保证原子性?
  • 第十四题:Redisson分布式延迟队列是什么?如何实现?
  • 第十五题:Redis的淘汰策略有哪些?涉及了哪些算法?
  • 第十六题:Redis红锁是什么?Redisson分布式锁如何提高可靠性?
  • 第十七题:数据库乐观锁、悲观锁与Redis分布式锁的区别和使用场景?
  • 第十八题:Redis只存10万数据,如何保证都是热点数据?Redis挂了怎么办?会满的情况如何设计处理方案?
  • 第十九题:Redis内存满了怎么办?会挂吗?如何设计处理方案?
  • 第二十题:Redis分布式锁为什么用Lua脚本保证原子性,不用事务?
  • 第二十一题:Redis分布式锁不可用时怎么办?如何设计?
  • 第二十二题:Redis为什么比MySQL快?性能优势分析
  • 第二十三题:Redis会阻塞吗?如果阻塞了怎么排查和处理?
  • 第二十四题:什么是Redis大Key?会出现什么问题?应该如何避免?
  • 第二十五题:Redis如何实现Session共享?

第一题:Redis是什么?有哪些数据类型?

1. Redis简介

问题描述:了解Redis的基本概念、特点和核心优势。

Redis是什么
Redis(Remote Dictionary Server)是一个开源的内存数据结构存储,用作数据库、缓存和消息中间件。

Redis的核心特点

  • 内存存储:数据存储在内存中,读写速度极快
  • 数据结构丰富:支持多种数据结构
  • 持久化:支持RDB和AOF两种持久化方式
  • 高可用:支持主从复制、哨兵、集群
  • 原子操作:所有操作都是原子性的

2. Redis的数据类型

问题描述:Redis支持哪些数据类型,每种类型的特点和应用场景。

基本数据类型

类型说明应用场景示例
String字符串缓存、计数器、分布式锁SET key value
Hash哈希表对象存储、用户信息HSET user:1 name "张三"
List列表消息队列、最新列表LPUSH news "新闻1"
Set集合标签、去重、交集并集SADD tags "java" "redis"
ZSet有序集合排行榜、范围查询ZADD rank 100 "用户1"

高级数据类型

类型说明应用场景
BitMap位图用户签到、在线状态
HyperLogLog基数统计独立访客统计
Geo地理位置附近的人、距离计算
Stream消息队列、事件溯源

3. 数据类型使用示例

String操作

// 基本操作
redisTemplate.opsForValue().set("key", "value");
redisTemplate.opsForValue().get("key");
redisTemplate.opsForValue().increment("counter");

Hash操作

// 存储用户信息
redisTemplate.opsForHash().put("user:1", "name", "张三");
Map<Object, Object> user = redisTemplate.opsForHash().entries("user:1");

List操作

// 消息队列
redisTemplate.opsForList().leftPush("queue", "message1");
redisTemplate.opsForList().rightPop("queue");

Set操作

// 标签系统
redisTemplate.opsForSet().add("tags", "java", "redis", "spring");
redisTemplate.opsForSet().members("tags");

ZSet操作

// 排行榜
redisTemplate.opsForZSet().add("rank", "user1", 100);
redisTemplate.opsForZSet().reverseRange("rank", 0, 9); // 获取前10名

第二题:Redis的持久化机制有哪些?RDB和AOF的区别?

1. Redis持久化机制

问题描述:Redis如何保证数据持久化,RDB和AOF两种机制的区别和选择。

RDB(Redis Database)

  • 全量备份:将内存中的数据快照保存到磁盘
  • 二进制格式:文件紧凑,恢复速度快
  • 自动触发:根据配置自动执行
  • 手动触发:通过SAVE或BGSAVE命令

AOF(Append Only File)

  • 增量备份:记录每个写操作命令
  • 文本格式:可读性好,文件较大
  • 实时写入:每个写操作都会记录
  • 重写机制:定期压缩AOF文件

2. RDB和AOF对比

特性RDBAOF
文件大小
恢复速度
数据安全性可能丢失数据数据更安全
性能影响较小较大
文件格式二进制文本

3. 各自缺点分析

RDB缺点

  • 数据丢失风险:最后一次快照到故障期间的数据会丢失
  • fork开销:BGSAVE需要fork子进程,内存占用翻倍
  • 阻塞风险:SAVE命令会阻塞主线程,影响服务可用性
  • 频率限制:无法做到秒级数据恢复,只能恢复到快照时间点

AOF缺点

  • 文件体积大:记录所有写操作,文件比RDB大很多
  • 恢复速度慢:需要重放所有写操作,恢复时间较长
  • 性能开销:每次写操作都要记录,影响写入性能
  • 文件损坏风险:AOF文件损坏可能导致数据无法恢复

4. 配置示例

RDB配置

# redis.conf
save 900 1      # 900秒内至少1个key变化
save 300 10     # 300秒内至少10个key变化
save 60 10000   # 60秒内至少10000个key变化dbfilename dump.rdb
dir /var/lib/redis

AOF配置

# redis.conf
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec  # 每秒同步一次# AOF重写配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

5. 持久化策略选择

生产环境建议

  • 数据安全要求高:使用AOF
  • 性能要求高:使用RDB
  • 兼顾两者:同时开启RDB和AOF

最佳实践

# 同时开启RDB和AOF
save 900 1
save 300 10
save 60 10000appendonly yes
appendfsync everysec

第三题:Redis如何实现高可用?主从复制、哨兵、集群的区别?

1. 主从复制(Master-Slave)

问题描述:Redis如何实现高可用,主从复制、哨兵、集群三种模式的区别和选择。

工作原理

  • 主节点:负责写操作,数据同步到从节点
  • 从节点:负责读操作,从主节点同步数据
  • 异步复制:主节点异步将数据同步到从节点

配置示例

# 主节点配置
# redis-master.conf
port 6379
bind 0.0.0.0# 从节点配置
# redis-slave.conf
port 6380
bind 0.0.0.0
replicaof 127.0.0.1 6379

Java配置

@Configuration
public class RedisSentinelConfig {@Beanpublic LettuceConnectionFactory redisConnectionFactory() {// 1. 创建哨兵配置对象RedisSentinelConfiguration config = new RedisSentinelConfiguration();// 2. 配置主节点名称config.master("mymaster");// 3. 添加哨兵节点地址config.sentinel("127.0.0.1", 26379);// 4. 创建连接工厂return new LettuceConnectionFactory(config);}
}

2. 哨兵模式(Sentinel)

工作原理

  • 监控:监控主从节点状态
  • 通知:通知客户端节点状态变化
  • 自动故障转移:主节点故障时自动选举新主节点

配置示例

# sentinel.conf
port 26379
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1

3. 集群模式(Cluster)

工作原理

  • 分片存储:数据分散存储在多个节点
  • 无中心化:每个节点都是平等的
  • 自动故障转移:节点故障时自动切换

配置示例

# redis-cluster.conf
port 7000
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-node-timeout 5000
appendonly yes

4. 三种模式对比

特性主从复制哨兵模式集群模式
高可用手动切换自动切换自动切换
扩展性读扩展读扩展读写扩展
数据一致性最终一致最终一致最终一致
复杂度
适用场景读写分离高可用大规模数据

第四题:Redis的缓存穿透、缓存击穿、缓存雪崩是什么?如何解决?

1. 缓存穿透

问题描述:查询不存在的数据,缓存和数据库都没有,导致请求直接打到数据库,造成数据库压力过大。

解决方案

  1. 布隆过滤器:在缓存前加一层布隆过滤器,过滤掉不存在的数据请求
  2. 缓存空值:对于查询结果为空的数据,也缓存起来,设置较短的过期时间
  3. 参数校验:在应用层对请求参数进行校验,过滤掉明显不合法的请求

方案实现描述
布隆过滤器通过多个哈希函数将元素映射到位数组,实现快速判断元素是否存在:

  • 初始化:设置预期插入数量和误判率
  • 添加元素:通过哈希函数映射到位数组
  • 查询元素:检查所有对应位是否都为1
  • 特点:可能误判但不会漏判

代码实现

@Component
public class BloomFilterService {private BloomFilter<String> bloomFilter;@PostConstructpublic void init() {// 1. 初始化布隆过滤器:设置预期插入数量100万,误判率1%bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), 1000000, 0.01);}public boolean mightContain(String key) {// 2. 查询元素:检查元素是否可能存在(可能误判但不漏判)return bloomFilter.mightContain(key);}public void put(String key) {// 3. 添加元素:将元素通过哈希函数映射到位数组bloomFilter.put(key);}
}
// 2. 缓存空值
@Service
public class UserService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;public User getUserById(Long id) {String key = "user:" + id;User user = (User) redisTemplate.opsForValue().get(key);if (user != null) {return user;}// 查询数据库user = userMapper.selectById(id);if (user != null) {redisTemplate.opsForValue().set(key, user, Duration.ofMinutes(30));} else {// 缓存空值,设置较短过期时间redisTemplate.opsForValue().set(key, new User(), Duration.ofMinutes(5));}return user;}
}

2. 缓存击穿

问题描述:热点数据过期,大量请求同时访问数据库,造成数据库瞬间压力过大。

解决方案

  1. 互斥锁:使用分布式锁,只允许一个线程去查询数据库,其他线程等待
  2. 永不过期:热点数据不设置过期时间,通过异步任务定期更新
  3. 缓存预热:系统启动时提前加载热点数据到缓存

方案实现描述
互斥锁方案通过分布式锁确保只有一个线程能查询数据库:

  • 双重检查:获取锁后再次检查缓存,避免重复查询
  • 锁超时:设置锁的过期时间,防止死锁
  • 原子释放:使用Lua脚本确保只有锁持有者能释放锁
  • 重试机制:锁获取失败时可以选择重试或直接返回

代码实现

public Object getHotData(String key) {// 1. 第一次检查缓存Object data = redisTemplate.opsForValue().get(key);if (data != null) return data;// 2. 尝试获取分布式锁String lockKey = "lock:" + key;String lockValue = UUID.randomUUID().toString();Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, Duration.ofSeconds(10));if (lock) {try {// 3. 双重检查缓存(避免重复查询)data = redisTemplate.opsForValue().get(key);if (data != null) return data;// 4. 查询数据库并更新缓存data = loadDataFromDB(key);redisTemplate.opsForValue().set(key, data, Duration.ofMinutes(30));return data;} finally {// 5. 原子释放锁(使用Lua脚本确保只有锁持有者能释放)String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"return redis.call('del', KEYS[1]) else return 0 end";redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(lockKey), lockValue);}}return null;
}
// 2. 永不过期方案
public void setHotData(String key, Object data) {redisTemplate.opsForValue().set(key, data); // 不设置过期时间// 通过异步任务定期更新缓存
}// 3. 缓存预热方案
@PostConstruct
public void warmupCache() {List<String> hotKeys = getHotKeys();for (String key : hotKeys) {if (!redisTemplate.hasKey(key)) {Object data = loadDataFromDB(key);if (data != null) {redisTemplate.opsForValue().set(key, data, Duration.ofMinutes(30));}}}
}

3. 缓存雪崩

问题描述:大量缓存同时过期,导致请求直接打到数据库,造成数据库压力过大甚至崩溃。

解决方案

  1. 随机过期时间:给缓存设置随机的过期时间,避免同时过期
  2. 多级缓存:使用本地缓存+Redis缓存的多级架构
  3. 缓存预热:系统启动时提前加载数据到缓存
  4. 熔断降级:当缓存失效时,使用熔断机制保护数据库

方案实现描述
随机过期时间通过给缓存设置不同的过期时间来避免同时失效:

  • 随机范围:在基础过期时间基础上增加随机值(如30-40分钟)
  • 分散失效:不同缓存实例的过期时间不同,避免集中失效
  • 降低冲击:将原本的瞬间大流量分散到不同时间点

代码实现

// 随机过期时间实现
public void setCache(String key, Object value) {int randomExpire = 1800 + (int)(Math.random() * 600); // 30-40分钟redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(randomExpire));
}

方案实现描述
多级缓存通过本地缓存+Redis缓存+数据库的三级架构提高性能:

  • 本地缓存:使用ConcurrentHashMap存储热点数据,访问速度最快
  • Redis缓存:分布式缓存,所有应用实例共享
  • 数据库:数据持久化存储
  • 查询策略:按顺序查询,命中即返回,未命中则查询下一级

代码实现

public class MultiLevelCacheService {private final Map<String, Object> localCache = new ConcurrentHashMap<>();public Object getData(String key) {Object data = localCache.get(key);if (data != null) return data;data = redisTemplate.opsForValue().get(key);if (data != null) {localCache.put(key, data);return data;}data = loadDataFromDB(key);if (data != null) {redisTemplate.opsForValue().set(key, data, Duration.ofMinutes(30));localCache.put(key, data);}return data;}
}

第五题:Redis的过期策略和内存回收机制?

1. 过期策略

问题描述:Redis如何删除过期的键值对,保证内存的有效利用。

三种过期策略对比

策略说明优点缺点
定时删除设置过期时间时创建定时器内存及时释放CPU开销大
惰性删除访问时检查是否过期CPU开销小内存可能浪费
定期删除定期扫描过期key平衡CPU和内存可能删除不及时

Redis采用的策略
Redis采用惰性删除 + 定期删除的组合策略:

  • 惰性删除:每次访问键时检查过期时间,过期则删除
  • 定期删除:每100ms随机抽取20个key检查,删除过期的键

2. 内存回收机制

问题描述:当Redis内存达到上限时,如何选择要删除的键来释放内存。

内存回收流程

  1. 检查内存使用量
  2. 超过maxmemory时触发淘汰
  3. 根据淘汰策略删除key
  4. 释放内存空间

8种淘汰策略

  • noeviction:不淘汰,内存满时报错
  • allkeys-lru:所有键中LRU淘汰(推荐)
  • volatile-lru:设置了过期时间的键中LRU淘汰
  • allkeys-lfu:所有键中LFU淘汰
  • volatile-lfu:设置了过期时间的键中LFU淘汰
  • allkeys-random:所有键中随机淘汰
  • volatile-random:设置了过期时间的键中随机淘汰
  • volatile-ttl:设置了过期时间的键中TTL最小的淘汰

配置示例

# redis.conf
maxmemory 2gb
maxmemory-policy allkeys-lru
maxmemory-samples 5

3. 内存优化建议

减少内存使用

// 使用合适的数据类型
redisTemplate.opsForHash().putAll("user:1", userMap);
redisTemplate.opsForValue().set("key", "value", Duration.ofMinutes(30));// 3. 使用压缩
redisTemplate.opsForValue().set("key", compressedValue);

第六题:Redis的性能优化有哪些方法?

1. 连接优化

问题描述:如何优化Redis连接,提高并发处理能力。

优化策略

  1. 连接池配置:合理配置连接池参数,避免频繁创建连接
  2. 连接复用:使用连接池复用连接,减少连接开销
  3. 超时设置:设置合理的连接超时时间

方案实现描述
连接池优化通过合理配置参数提高Redis连接性能:

  • 连接数配置:设置合适的最大连接数和空闲连接数
  • 超时设置:配置连接超时时间,避免长时间等待
  • 连接验证:确保连接可用性

代码实现

@Configuration
public class RedisConfig {@Beanpublic LettuceConnectionFactory redisConnectionFactory() {// 1. 配置连接池参数GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();poolConfig.setMaxTotal(20);        // 最大连接数:避免连接不足或过多poolConfig.setMaxIdle(10);         // 最大空闲连接:保持空闲连接减少创建开销poolConfig.setMinIdle(5);          // 最小空闲连接:确保有足够的空闲连接poolConfig.setMaxWaitMillis(3000); // 连接超时:避免长时间等待// 2. 构建客户端配置LettucePoolingClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder().poolConfig(poolConfig).build();// 3. 配置Redis服务器信息RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();config.setHostName("localhost");config.setPort(6379);// 4. 创建连接工厂return new LettuceConnectionFactory(config, clientConfig);}
}

2. 命令优化

问题描述:如何优化Redis命令执行,提高操作效率。

优化策略

  1. 批量操作:使用Pipeline批量执行命令,减少网络往返
  2. 命令选择:选择合适的数据结构和命令
  3. 避免阻塞命令:避免使用KEYS、FLUSHALL等阻塞命令

方案实现描述
批量操作通过Pipeline技术减少网络往返次数:

  • Pipeline技术:将多个命令打包发送
  • 批量操作:使用executePipelined和multiGet
  • 性能提升:减少网络延迟,提高吞吐量

代码实现

public void batchSet(Map<String, Object> data) {// 1. 使用Pipeline批量写入:减少网络往返次数redisTemplate.executePipelined((RedisCallback<Object>) connection -> {// 2. 遍历数据并序列化for (Map.Entry<String, Object> entry : data.entrySet()) {connection.set(entry.getKey().getBytes(), redisTemplate.getValueSerializer().serialize(entry.getValue()));}return null;});
}public List<Object> batchGet(List<String> keys) {// 3. 批量读取:一次性获取多个键值对return redisTemplate.opsForValue().multiGet(keys);
}

3. 数据结构优化

选择合适的数据类型

// 1. 计数器场景:使用String类型的INCR命令
redisTemplate.opsForValue().increment("counter"); // 2. 对象存储场景:使用Hash类型存储结构化数据
Map<String, Object> user = new HashMap<>();
user.put("name", "张三");
user.put("age", 25);
redisTemplate.opsForHash().putAll("user:1", user); // 3. 消息队列场景:使用List类型的LPUSH/RPOP
redisTemplate.opsForList().leftPush("queue", "message"); // 4. 标签去重场景:使用Set类型存储唯一元素
redisTemplate.opsForSet().add("tags", "java", "redis"); // 5. 排行榜场景:使用ZSet类型存储有序数据
redisTemplate.opsForZSet().add("rank", "user1", 100);

4. 网络优化

减少网络往返

// 使用Pipeline减少网络往返
public List<Object> pipelineOperations() {// 1. 使用Pipeline批量操作:减少网络往返次数return redisTemplate.executePipelined((RedisCallback<Object>) connection -> {// 2. 批量设置键值对:一次性发送多个命令for (int i = 0; i < 1000; i++) {connection.set(("key" + i).getBytes(), ("value" + i).getBytes());}return null;});
}

第七题:Redis的事务机制和Lua脚本?

1. Redis事务

问题描述:Redis如何保证多个命令的原子性执行,以及事务的ACID特性。

事务特性

  • 原子性:事务中的命令要么全部执行,要么全部不执行
  • 隔离性:事务执行过程中不会被其他命令打断
  • 一致性:事务执行前后数据保持一致
  • 持久性:事务执行结果会持久化到磁盘

事务命令

MULTI          # 开始事务
SET key1 value1
SET key2 value2
EXEC           # 执行事务
DISCARD        # 取消事务
WATCH key      # 监视key
UNWATCH        # 取消监视

方案实现描述
Redis事务通过MULTI/EXEC命令实现批量操作的原子性:

  • 事务开始:使用MULTI命令开始事务
  • 命令入队:事务中的命令会被放入队列,不立即执行
  • 事务执行:使用EXEC命令执行队列中的所有命令
  • 原子性保证:所有命令要么全部执行成功,要么全部失败

代码实现

public void executeTransaction() {// 1. 使用SessionCallback执行事务redisTemplate.execute((SessionCallback<Object>) operations -> {// 2. 开始事务:MULTI命令operations.multi();// 3. 命令入队:添加到事务队列,不立即执行operations.opsForValue().set("key1", "value1");operations.opsForValue().set("key2", "value2");// 4. 执行事务:EXEC命令,原子性执行所有命令return operations.exec();});
}

2. Lua脚本

问题描述:如何使用Lua脚本实现复杂的业务逻辑,保证原子性。

Lua脚本优势

  • 原子性:脚本执行过程中不会被其他命令打断
  • 减少网络往返:多个命令合并为一个脚本
  • 复杂逻辑:支持条件判断、循环等复杂逻辑

方案实现描述
Lua脚本在Redis中执行,提供原子性和复杂逻辑支持:

  • 原子性执行:脚本执行过程中不会被其他命令打断
  • 减少网络往返:多个Redis命令合并为一个脚本执行
  • 复杂逻辑:支持条件判断、循环等复杂业务逻辑
  • 参数传递:通过KEYS和ARGV数组传递参数

代码实现

-- 限流脚本:实现分布式限流功能
-- 1. 获取参数:限流键、限制次数、过期时间
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local expire = tonumber(ARGV[2])-- 2. 获取当前计数
local current = redis.call('GET', key)
if current == false then-- 3. 首次访问:设置初始值为1并设置过期时间redis.call('SET', key, 1)redis.call('EXPIRE', key, expire)return 1
elselocal count = tonumber(current)if count < limit then-- 4. 未超限:递增计数redis.call('INCR', key)return count + 1else-- 5. 已超限:返回-1表示限流return -1end
end

Java调用Lua脚本

@Service
public class LuaScriptService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;private final DefaultRedisScript<Long> rateLimitScript;public LuaScriptService() {rateLimitScript = new DefaultRedisScript<>();rateLimitScript.setScriptText("local key = KEYS[1]\n" +"local limit = tonumber(ARGV[1])\n" +"local expire = tonumber(ARGV[2])\n" +"local current = redis.call('GET', key)\n" +"if current == false then\n" +"    redis.call('SET', key, 1)\n" +"    redis.call('EXPIRE', key, expire)\n" +"    return 1\n" +"else\n" +"    local count = tonumber(current)\n" +"    if count < limit then\n" +"        redis.call('INCR', key)\n" +"        return count + 1\n" +"    else\n" +"        return -1\n" +"    end\n" +"end");rateLimitScript.setResultType(Long.class);}public boolean isAllowed(String key, int limit, int expire) {Long result = redisTemplate.execute(rateLimitScript, Arrays.asList(key), String.valueOf(limit), String.valueOf(expire));return result != null && result > 0;}
}

第八题:Redis的常见应用场景和最佳实践?

1. 常见应用场景

问题描述:Redis在实际项目中的主要应用场景有哪些,如何正确使用。

主要应用场景

  1. 缓存:提高数据访问速度,减轻数据库压力
  2. 分布式锁:保证分布式环境下的数据一致性
  3. 消息队列:实现异步消息处理
  4. 计数器:实现访问统计、点赞等功能
  5. 会话存储:实现分布式Session管理

代码实现

// 1. 缓存场景实现:提高数据访问速度
public User getUserById(Long id) {// 1.1 构建缓存键String key = "user:" + id;// 1.2 先从缓存获取数据User user = (User) redisTemplate.opsForValue().get(key);if (user == null) {// 1.3 缓存未命中,查询数据库user = userMapper.selectById(id);if (user != null) {// 1.4 将数据写入缓存,设置过期时间redisTemplate.opsForValue().set(key, user, Duration.ofMinutes(30));}}return user;
}// 2. 消息队列实现:异步消息处理
// 2.1 生产者:推送消息到队列
redisTemplate.opsForList().rightPush("queue", message);// 2.2 消费者:从队列获取消息
Object message = redisTemplate.opsForList().leftPop("queue");// 计数器
redisTemplate.opsForValue().increment("counter");

2. 最佳实践

问题描述:使用Redis时应该遵循哪些最佳实践,避免常见问题。

最佳实践要点

  1. 命名规范:使用有意义的键名,便于管理和维护
  2. 过期时间设置:根据业务需求设置合理的过期时间
  3. 错误处理:妥善处理Redis连接异常和操作失败
  4. 监控告警:监控Redis性能指标,及时发现问题

代码实现

// 命名规范示例
"user:123:profile"     // 用户信息
"order:456:status"     // 订单状态// 过期时间设置
redisTemplate.opsForValue().set("hot_data", data, Duration.ofMinutes(30));
redisTemplate.opsForValue().set("session", session, Duration.ofDays(7));

错误处理

public Object get(String key) {try {return redisTemplate.opsForValue().get(key);} catch (Exception e) {log.error("Redis操作失败: {}", e.getMessage());return null;}
}

监控和告警

@Scheduled(fixedRate = 60000)
public void monitorRedis() {try {redisTemplate.opsForValue().get("health_check");Properties info = redisTemplate.getConnectionFactory().getConnection().info("memory");String usedMemory = info.getProperty("used_memory");sendMetrics(usedMemory);} catch (Exception e) {sendAlert("Redis监控异常: " + e.getMessage());}
}

第九题:缓存技术有哪些分类?为什么Redis成为主流?ZooKeeper可以作为缓存吗?

1. 缓存技术分类

问题描述:了解缓存技术的不同分类方式,为选择合适的缓存方案提供依据。

按存储位置分类

  • 本地缓存:内存缓存(HashMap)、磁盘缓存、CPU缓存
  • 分布式缓存:Redis、Memcached、Hazelcast

按缓存策略分类

  • 写策略:Write-Through、Write-Behind、Write-Around
  • 读策略:Cache-Aside、Read-Through、Refresh-Ahead

按数据一致性分类

  • 强一致性缓存:缓存与数据库数据完全一致
  • 最终一致性缓存:允许短暂的数据不一致

2. 主流缓存技术对比

缓存技术数据结构持久化集群支持适用场景
Redis丰富(9种)支持RDB/AOF原生支持通用缓存、会话存储、消息队列
Memcached仅key-value不支持需要客户端实现简单缓存、会话存储
Hazelcast丰富支持原生支持Java应用、分布式计算
Caffeine丰富不支持不支持高性能本地缓存

3. 为什么Redis成为主流?

数据结构丰富:支持9种数据结构满足不同场景需求

redisTemplate.opsForValue().set("key", "value");           // String:缓存、计数器
redisTemplate.opsForHash().put("user:1", "name", "张三");   // Hash:对象存储
redisTemplate.opsForList().leftPush("queue", "message");    // List:消息队列
redisTemplate.opsForSet().add("tags", "java", "redis");     // Set:去重、标签
redisTemplate.opsForZSet().add("rank", "user1", 100);       // ZSet:排行榜

性能优异:内存存储,读写速度极快,单机QPS可达10万+,支持持久化(RDB/AOF)

高可用架构:主从复制、哨兵模式、集群模式

replicaof 127.0.0.1 6379
sentinel monitor mymaster 127.0.0.1 6379 2
cluster-enabled yes

功能强大:发布订阅、Lua脚本、事务支持

redisTemplate.convertAndSend("channel", "message");
redisTemplate.execute((SessionCallback<Object>) operations -> { operations.multi(); operations.opsForValue().set("key1", "value1"); return operations.exec(); 
});

应用场景广泛:缓存、分布式锁、消息队列、计数器

// 缓存场景
String key = "user:" + id; 
User user = (User) redisTemplate.opsForValue().get(key);
if (user == null) { user = userMapper.selectById(id); if (user != null) redisTemplate.opsForValue().set(key, user, Duration.ofMinutes(30)); 
}// 分布式锁
return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value, Duration.ofSeconds(expireTime)));// 消息队列
redisTemplate.opsForList().rightPush("queue", message); 
Object message = redisTemplate.opsForList().leftPop("queue");// 计数器
redisTemplate.opsForValue().increment("page:view:home");

4. ZooKeeper可以作为缓存吗?

技术可行性:ZooKeeper可以存储数据,但性能远低于Redis

if (zooKeeper.exists(path, false) == null) {zooKeeper.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
} else {zooKeeper.setData(path, data.getBytes(), -1);
}

为什么不推荐

特性RedisZooKeeper
QPS100,000+1,000-5,000
延迟<1ms10-50ms
存储限制无限制单节点4MB
设计目的数据存储协调服务

ZooKeeper的正确用途

// 配置管理
zooKeeper.setData("/config/database/url", "jdbc:mysql://localhost:3306/db".getBytes(), -1);// 服务发现
zooKeeper.create("/services/user-service/instance-1", "192.168.1.100:8080".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);// 分布式锁
String lockNode = zooKeeper.create("/locks/resource/lock-", "locked".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);// 集群管理
zooKeeper.create("/cluster/master", "node-1".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);

5. 正确的缓存选择

多级缓存架构

// 1. 先查本地缓存
Object data = localCache.getIfPresent(key);
if (data != null) return data;// 2. 再查Redis缓存
data = redisTemplate.opsForValue().get(key);
if (data != null) {localCache.put(key, data);return data;
}// 3. 查数据库
data = loadDataFromDB(key);
if (data != null) {redisTemplate.opsForValue().set(key, data, Duration.ofMinutes(30));localCache.put(key, data);
}

选择建议

  • 本地缓存:Caffeine、Guava Cache(高性能、无网络开销)
  • 分布式缓存:Redis、Memcached(数据共享、高可用)
  • 协调服务:ZooKeeper(配置管理、服务发现、分布式锁)

第十题:Redis适合做分布式锁吗?有哪些更好的替代方案?

1. Redis分布式锁适用性分析

问题描述:Redis是否适合做分布式锁,存在哪些问题。

Redis分布式锁的适用性

  • 适合场景:高性能要求、最终一致性可接受、简单业务逻辑
  • 不适合场景:强一致性要求、金融交易、关键业务数据

主要问题

  1. 主从切换问题:主从切换时锁信息丢失
  2. 时钟依赖问题:依赖系统时钟,时钟跳跃影响锁有效性
  3. 网络分区问题:网络分区可能导致锁状态不一致
  4. 单点故障风险:单Redis实例故障导致锁服务不可用

性能对比

分布式锁方案获取锁延迟可用性一致性
Redis1-5ms最终一致
ZooKeeper10-50ms强一致
etcd5-20ms强一致
数据库10-100ms强一致

3. 更好的替代方案

ZooKeeper分布式锁

// 创建临时顺序节点
String lockNode = zooKeeper.create(lockPath + "/lock-", "locked".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);// 获取所有锁节点
List<String> locks = zooKeeper.getChildren(lockPath, false);
Collections.sort(locks);// 检查是否是最小节点
String currentNode = lockNode.substring(lockNode.lastIndexOf("/") + 1);
return currentNode.equals(locks.get(0));// 释放锁
zooKeeper.delete(lockNode, -1);

etcd分布式锁

// 创建租约
LeaseGrantResponse lease = kvClient.leaseGrant(ttl);
leaseId = lease.getID();// 尝试获取锁
TxnResponse txn = kvClient.txn().If(new Cmp(ByteSequence.from(key, StandardCharsets.UTF_8), Cmp.Op.EQUAL, CmpTarget.createRevision(0))).Then(Op.put(ByteSequence.from(key, StandardCharsets.UTF_8), ByteSequence.from("locked", StandardCharsets.UTF_8), PutOption.newBuilder().withLeaseId(leaseId).build())).commit();return txn.isSucceeded();

数据库分布式锁

String sql = "INSERT INTO distributed_lock (lock_key, lock_value, expire_time, create_time) " +"VALUES (?, ?, ?, ?) " +"ON DUPLICATE KEY UPDATE " +"lock_value = CASE WHEN expire_time < NOW() THEN VALUES(lock_value) ELSE lock_value END, " +"expire_time = CASE WHEN expire_time < NOW() THEN VALUES(expire_time) ELSE expire_time END";int rows = jdbcTemplate.update(sql, lockKey, lockValue, LocalDateTime.now().plusSeconds(expireSeconds), LocalDateTime.now());
return rows > 0;

4. 方案选择建议

选择标准

标准RedisZooKeeperetcd数据库
性能⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
一致性⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
可用性⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
易用性⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

场景选择

// Redis适用场景:高性能、最终一致性可接受
String lockKey = "seckill:lock:" + productId;
return redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", Duration.ofMillis(100));// ZooKeeper适用场景:强一致性、公平锁
String lockPath = "/locks/transaction/" + orderId;
return zkLock.tryLock(lockPath);

混合方案

// 混合锁策略:Redis高性能 + ZooKeeper强一致性
if (tryRedisLock(key)) {if (zkLock.tryLock(key)) {return true; // 双重锁获取成功} else {unlockRedisLock(key); // ZooKeeper锁失败,释放Redis锁return false;}
}
return false;

5. 最佳实践

锁监控

// 记录锁获取
lockAcquireCounter.increment(Tags.of("type", lockType, "success", String.valueOf(success))
);// 记录锁释放
lockReleaseCounter.increment(Tags.of("type", lockType));
lockHoldTimer.record(holdTime, TimeUnit.MILLISECONDS);

选择建议

  • 高性能场景:Redis分布式锁(秒杀、限流)
  • 强一致性场景:ZooKeeper分布式锁(金融交易、订单处理)
  • 平衡场景:etcd分布式锁
  • 简单场景:数据库分布式锁
  • 复杂场景:混合方案(Redis + ZooKeeper)

第十一题:单线程的Redis为什么那么快?现在还是单线程吗?

1. Redis单线程为什么那么快?

问题描述:Redis采用单线程模型,为什么性能依然很高,有哪些技术优势。

核心原因分析:1.内存存储(读写速度极快) 2.高效的数据结构(跳跃表、压缩列表等) 3.I/O多路复用(epoll技术处理大量并发) 4.避免上下文切换(单线程模型)

代码验证

// 内存访问性能:纳秒级响应
redisTemplate.opsForValue().set("key", "value"); // 1. 内存操作,极快响应// 高效数据结构:针对性能优化
String sds = new String("Redis SDS");           // 2. 动态字符串:预分配空间
SkipList<String> skipList = new SkipList<>();   // 3. 跳跃表:O(logN)查找
ZipList zipList = new ZipList();                // 4. 压缩列表:内存连续
IntSet intSet = new IntSet();                   // 5. 整数集合:自动编码

I/O多路复用

// 3. Redis使用epoll/kqueue等I/O多路复用技术实现
// 传统阻塞I/O:每个连接一个线程(性能差)
ServerSocket serverSocket = new ServerSocket(6379);
while (true) {Socket clientSocket = serverSocket.accept(); // 1. 阻塞等待连接new Thread(() -> handleClient(clientSocket)).start(); // 2. 每个连接一个线程
}// Redis I/O多路复用:一个线程处理多个连接(高性能)
Selector selector = Selector.open(); // 3. 创建选择器
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false); // 4. 设置为非阻塞
serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 5. 注册监听事件while (true) {int readyChannels = selector.select(); // 6. 等待事件发生(非阻塞)if (readyChannels == 0) continue;for (SelectionKey key : selector.selectedKeys()) { // 7. 处理就绪事件if (key.isAcceptable()) {SocketChannel clientChannel = serverChannel.accept(); // 8. 接受新连接clientChannel.register(selector, SelectionKey.OP_READ); // 9. 注册读事件} else if (key.isReadable()) {handleReadEvent((SocketChannel) key.channel()); // 10. 处理读事件}}
}

避免上下文切换

// 多线程上下文切换成本(性能差)
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {executor.submit(() -> {synchronized (this) { // 1. 锁竞争导致上下文切换processCommand("SET key" + Thread.currentThread().getId() + " value"); // 2. 保存恢复状态开销}});
}// Redis单线程优势(性能好)
while (true) {String command = commandQueue.poll(); // 3. 直接获取命令,无线程切换if (command != null) {processCommand(command); // 4. 串行处理,无锁竞争}
}

2. Redis现在还是单线程吗?

总体回答:Redis的线程模型经历了演进过程

  • Redis 6.0之前:完全单线程模型,主线程处理所有命令,只有后台线程处理持久化等任务
  • Redis 6.0之后:引入多线程I/O,但命令执行仍然是单线程的
  • Redis 7.0:进一步优化多线程I/O性能,但核心命令处理依然是单线程

核心要点:Redis的命令执行始终是单线程的,多线程只用于网络I/O处理,这样既保证了数据一致性,又提升了网络性能。

Redis 6.0之前:单线程模型

// Redis 6.0之前单线程模型核心实现
Map<String, Object> memory = new ConcurrentHashMap<>(); // 1. 内存存储
BlockingQueue<String> commandQueue = new LinkedBlockingQueue<>(); // 2. 命令队列// 主线程:命令处理(单线程保证原子性)
while (true) {String command = commandQueue.take(); // 3. 接收命令String[] parts = command.split(" "); // 4. 解析命令Object result = executeCommand(parts[0], parts); // 5. 执行命令sendResponse(result); // 6. 返回结果
}// 后台线程:持久化和过期键处理
new Thread(() -> saveRDB(), "rdb-thread").start(); // 7. RDB持久化
new Thread(() -> flushAOF(), "aof-thread").start(); // 8. AOF持久化
new Thread(() -> deleteExpiredKeys(), "expire-thread").start(); // 9. 过期键删除

Redis 6.0之后:多线程I/O

// Redis 6.0多线程I/O核心实现
ExecutorService ioThreadPool = Executors.newFixedThreadPool(4); // 1. 创建I/O线程池
BlockingQueue<Command> commandQueue = new LinkedBlockingQueue<>(); // 2. 命令队列
BlockingQueue<Response> responseQueue = new LinkedBlockingQueue<>(); // 3. 响应队列// 主线程:命令执行(单线程保证原子性)
while (true) {Command cmd = commandQueue.take(); // 4. 获取命令Object result = executeCommand(cmd); // 5. 执行命令responseQueue.offer(new Response(cmd.getClientId(), result)); // 6. 放入响应队列
}// I/O线程:网络处理(多线程提升性能)
ioThreadPool.submit(() -> {while (true) {Response response = responseQueue.take(); // 7. 获取响应sendResponseToClient(response); // 8. 发送给客户端}
});

Redis 7.0:多线程优化

// Redis 7.0多线程I/O进一步优化
ExecutorService ioThreadPool = Executors.newFixedThreadPool(8); // 1. 增加I/O线程数
BlockingQueue<Command> commandQueue = new LinkedBlockingQueue<>(); // 2. 优化命令队列
BlockingQueue<Response> responseQueue = new LinkedBlockingQueue<>(); // 3. 优化响应队列// 主线程:命令执行(依然单线程保证原子性)
while (true) {Command cmd = commandQueue.take(); // 4. 获取命令Object result = executeCommand(cmd); // 5. 执行命令responseQueue.offer(new Response(cmd.getClientId(), result)); // 6. 放入响应队列
}// I/O线程池:网络处理(更多线程提升性能)
ioThreadPool.submit(() -> {while (true) {Response response = responseQueue.take(); // 7. 获取响应sendResponseToClient(response); // 8. 发送给客户端}
});// 内存管理优化:改进的内存分配和回收
MemoryManager memoryManager = new MemoryManager(); // 9. 优化内存管理
memoryManager.optimizeAllocation(); // 10. 内存分配优化// 持久化性能改进:异步持久化
AsyncPersistence persistence = new AsyncPersistence(); // 11. 异步持久化
persistence.asyncSave(); // 12. 异步保存数据

3. 性能对比分析

单线程 vs 多线程性能

指标单线程多线程I/O说明
CPU利用率多线程能更好利用多核CPU
内存使用中等多线程需要更多内存
延迟极低单线程延迟更稳定
吞吐量中等多线程吞吐量更高
复杂度单线程实现简单

实际性能测试

**性能测试对比**(测试场景:100万次SET操作):- **Redis 5.0 (单线程)**:QPS约8万,延迟0.1-0.5ms,CPU使用率25%
- **Redis 6.0 (多线程I/O)**:QPS约12万,延迟0.1-0.8ms,CPU使用率60%
- **Redis 7.0 (优化多线程)**:QPS约15万,延迟0.1-0.6ms,CPU使用率70%

4. 为什么保持命令执行单线程?

数据一致性
单线程模型天然保证了数据一致性:

  • 避免竞态条件:多个线程同时INCR counter可能导致结果错误,单线程避免了这个问题
  • 串行执行:所有命令按顺序执行,保证操作的原子性
  • 事务ACID特性:单线程环境下的ACID特性更容易保证

避免锁竞争
单线程模型避免了多线程的锁竞争问题:

  • 无需锁保护:单线程环境下不存在并发访问,无需synchronized等锁机制
  • 无竞争开销:避免了锁竞争导致的性能损失和死锁风险
  • 性能更高:没有锁的开销,执行效率更高
  • 实现更简单:代码逻辑更清晰,调试更容易

内存管理简单
单线程模型在内存管理方面具有显著优势:

  • 无需内存屏障:不需要考虑多核CPU之间的内存同步
  • 无缓存一致性协议:避免了复杂的缓存一致性维护
  • 内存分配高效:避免了多线程竞争导致的内存分配延迟
  • 垃圾回收简单:单线程环境下GC算法更简单高效

5. 多线程I/O的实现原理

总体回答:Redis 6.0+多线程I/O的实现机制

Redis 6.0引入多线程I/O是为了解决网络I/O瓶颈问题,但命令执行仍然保持单线程

  • 主线程职责:命令解析、执行、响应生成
  • I/O线程职责:网络数据读写、客户端连接管理
  • 线程间通信:通过队列实现主线程与I/O线程的协作
  • 性能提升:网络I/O性能提升2-4倍,但命令执行性能不变

核心设计原则:网络I/O多线程化,命令执行单线程化,既保证数据一致性又提升网络性能。

I/O多路复用 + 多线程

// Redis多线程I/O实现
// 主线程:负责命令执行,保证原子性
Thread mainThread = new Thread(() -> {while (true) {Command cmd = commandQueue.take();    // 从队列获取命令Object result = executeCommand(cmd);  // 执行命令(单线程保证原子性)responseQueue.put(result);            // 将结果放入响应队列}
});// I/O线程池:负责网络I/O处理,提升并发性能
ExecutorService ioThreadPool = Executors.newFixedThreadPool(4);
ioThreadPool.submit(() -> handleNetworkIO()); // 处理网络读写事件

配置优化
Redis多线程I/O的关键配置参数说明:

# Redis配置文件 redis.conf# 1. I/O线程数量配置
io-threads 4                    # 设置I/O线程数量,建议为CPU核心数
io-threads-do-reads yes         # 启用I/O线程处理读操作# 2. 网络连接优化
tcp-backlog 511                 # TCP连接队列长度,提高并发连接能力
tcp-keepalive 300               # TCP keepalive时间,保持连接活跃# 3. 内存和缓冲区优化
tcp-user-timeout 30000          # TCP用户超时时间
repl-backlog-size 1mb           # 复制积压缓冲区大小
client-output-buffer-limit normal 0 0 0  # 客户端输出缓冲区限制# 4. 性能调优参数
hz 10                           # 后台任务执行频率
maxmemory-policy allkeys-lru    # 内存淘汰策略

参数详解

  • io-threads:I/O线程数量,通常设为CPU核心数,过多会导致上下文切换开销
  • io-threads-do-reads:启用I/O线程处理读操作,提升网络读取性能
  • tcp-backlog:TCP连接队列长度,影响并发连接处理能力
  • tcp-keepalive:TCP保活机制,防止连接超时断开
  • hz:后台任务执行频率,影响过期键删除、持久化等任务频率

6. 性能优化建议

单线程优化
针对Redis单线程模型的性能优化策略:

  • Pipeline批量操作:将多个命令打包发送,减少网络往返次数
  • 随机过期时间:避免大量key同时过期造成的性能抖动
  • 合理的数据结构选择:根据使用场景选择最适合的数据类型
  • 避免大key操作:避免单次操作处理过大的数据量
  • 连接池优化:合理配置连接池参数,减少连接创建开销

多线程优化
针对Redis 6.0+多线程I/O的优化策略:

  • I/O线程数量调优:通常设置为CPU核心数,避免过多线程导致上下文切换开销
  • 连接池配置优化:合理设置最大连接数和空闲连接数,平衡性能和资源消耗
  • 批量操作优化:利用multiSet等批量操作减少网络往返
  • 网络参数调优:调整tcp-backlog、tcp-keepalive等网络参数提升并发能力
  • 监控和调优:监控I/O线程使用率,根据实际负载调整线程数量

7. 未来发展趋势

Redis未来发展方向
Redis技术发展的主要趋势:

1. 更多多线程优化

  • 命令执行多线程:Redis正在实验命令执行的多线程化,但会保持数据一致性
  • 更好的负载均衡:优化多线程间的任务分配和负载均衡机制

2. 硬件加速

  • DPDK网络加速:使用DPDK等网络加速技术提升网络处理性能
  • NUMA优化:更好地利用NUMA架构优化内存访问性能

3. 新特性增强

  • 更好的持久化:优化RDB和AOF持久化机制,提升数据可靠性
  • 更强的集群功能:增强Redis Cluster的功能和稳定性

4. 云原生支持

  • 容器化支持:更好地支持Docker、Kubernetes等容器化部署
  • 微服务集成:与微服务架构更好地集成,支持服务发现、配置管理等

选择建议
根据不同的使用场景选择合适的Redis版本:

1. 高并发场景
选择Redis 6.0+,启用多线程I/O,可以显著提升网络处理能力,适合高并发的Web应用

2. 低延迟场景
选择Redis 5.0,单线程延迟更稳定,适合对延迟要求极高的金融交易等场景

3. 生产环境
选择Redis 7.0,性能最优,稳定性最好,适合生产环境部署

4. 学习测试
选择最新版本,体验新特性,了解Redis技术发展趋势

5. 特殊需求

  • 内存敏感:选择Redis 6.0,内存使用更优化
  • 功能丰富:选择最新版本,支持更多数据类型和命令
  • 兼容性:选择Redis 5.0,与老版本客户端兼容性最好

第十二题:Redisson是什么?它解决了什么问题?

1. Redisson简介

问题描述:Redisson是什么,它解决了原生Redis客户端的哪些问题。

Redisson是什么:Redis的Java客户端,提供丰富的分布式服务,基于Netty的异步非阻塞客户端。

主要特性:1.丰富的分布式数据结构(锁、集合、对象) 2.高可用支持(集群、哨兵、主从) 3.自动连接管理(重连、连接池) 4.序列化支持(多种序列化方式)

代码示例

// 1. 基于Netty的异步非阻塞客户端
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379").setConnectionPoolSize(10).setConnectionMinimumIdleSize(5);RedissonClient redisson = Redisson.create(config);// 2. 丰富的分布式数据结构
RLock lock = redisson.getLock("myLock");
RSet<String> set = redisson.getSet("mySet");
RList<String> list = redisson.getList("myList");
RMap<String, String> map = redisson.getMap("myMap");// 3. 开箱即用的分布式服务
RLock distributedLock = redisson.getLock("serviceLock");
RSemaphore semaphore = redisson.getSemaphore("serviceSemaphore");
RAtomicLong counter = redisson.getAtomicLong("serviceCounter");// 4. 高可用架构支持
config.useClusterServers().addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001");config.useSentinelServers().setMasterName("mymaster").addSentinelAddress("redis://127.0.0.1:26379");config.useMasterSlaveServers().setMasterAddress("redis://127.0.0.1:6379").addSlaveAddress("redis://127.0.0.1:6380");

2. Redisson解决的问题

原生Redis客户端的问题:1.连接管理复杂(手动生命周期管理、连接池配置、异常处理) 2.序列化处理复杂(手动序列化/反序列化、类型转换、方式不统一) 3.分布式锁实现复杂(手动锁逻辑、超时处理、死锁风险) 4.集群支持有限(分片逻辑、故障转移、负载均衡) 5.开发效率低(代码重复、维护成本高、学习成本高)

Redisson的解决方案:1.自动连接管理(连接池管理、自动重连、健康检查) 2.自动序列化(多种序列化方式、类型安全、透明序列化) 3.开箱即用的分布式锁(自动锁超时、自动续期、死锁检测) 4.完整的集群支持(自动分片、故障转移、负载均衡) 5.丰富的分布式对象(分布式集合、分布式对象、分布式服务)

3. Redisson分布式锁实现

可重入锁

RLock lock = redissonClient.getLock("myLock");
boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);
if (isLocked) { doBusinessLogic(); }
if (lock.isHeldByCurrentThread()) { lock.unlock(); }

公平锁

RLock fairLock = redissonClient.getFairLock("fairLock");
fairLock.lock();
doBusinessLogic();
fairLock.unlock();

读写锁

RReadWriteLock rwlock = redissonClient.getReadWriteLock("rwlock");
RLock readLock = rwlock.readLock(); readLock.lock(); readData(); readLock.unlock();
RLock writeLock = rwlock.writeLock(); writeLock.lock(); writeData(); writeLock.unlock();

信号量

RSemaphore semaphore = redissonClient.getSemaphore("semaphore");
semaphore.acquire();
doBusinessLogic();
semaphore.release();

4. Redisson分布式集合

Redisson提供了丰富的分布式数据结构,包括:

  • 分布式Map:支持原子操作、批量操作
  • 分布式Set:支持集合运算(交集、并集、差集)
  • 分布式List:支持队列操作(FIFO、LIFO)
  • 分布式Queue:支持阻塞队列、延迟队列

5. Redisson分布式服务

分布式计数器

RAtomicLong counter = redissonClient.getAtomicLong("counter");
counter.set(0);
long value = counter.incrementAndGet();
long newValue = counter.addAndGet(10);
boolean success = counter.compareAndSet(10, 20);

分布式限流器

RRateLimiter rateLimiter = redissonClient.getRateLimiter("rateLimiter");
rateLimiter.trySetRate(RateType.OVERALL, 10, 1, RateIntervalUnit.SECONDS);
boolean acquired = rateLimiter.tryAcquire();
if (acquired) { doBusinessLogic(); } else { throw new RateLimitExceededException(); }

分布式调度器

RScheduledExecutorService executor = redissonClient.getExecutorService("scheduler");
executor.schedule(() -> System.out.println("延迟执行任务"), 10, TimeUnit.SECONDS);
executor.scheduleAtFixedRate(() -> System.out.println("定时执行任务"), 0, 5, TimeUnit.SECONDS);

6. Redisson配置和优化

Redisson支持多种配置方式:

  • 单节点配置:适用于单机Redis部署
  • 集群配置:适用于Redis集群部署
  • 哨兵配置:适用于Redis哨兵模式
  • 主从配置:适用于Redis主从模式

配置要点

  • 连接池大小:根据并发量设置合适的连接池大小
  • 超时设置:设置合理的连接、读写超时时间
  • 密码认证:配置Redis密码认证
  • 数据库选择:选择使用的Redis数据库编号

性能优化
Redisson性能优化的关键策略:

1. 连接池优化

  • 合理设置连接池大小:根据并发量和服务器性能设置合适的连接池大小
  • 设置合适的超时时间:避免连接超时导致的性能问题
  • 连接池监控:监控连接池使用情况,及时调整参数

2. 序列化优化

  • 使用高效的序列化方式:选择性能更好的序列化算法(如Kryo、Protobuf)
  • 避免序列化大对象:减少序列化数据的大小,提升传输效率
  • 序列化缓存:对重复序列化的对象进行缓存

3. 批量操作优化

  • 使用RBatch进行批量操作:将多个操作打包发送,减少网络往返次数
  • 批量锁操作:对多个资源同时加锁,减少锁竞争
  • 异步操作:使用异步操作提升并发性能

4. 监控和调优

  • 监控连接数:实时监控连接池使用情况,避免连接不足
  • 监控锁竞争情况:监控锁的获取和释放频率,优化锁策略
  • 调整超时参数:根据实际业务场景调整各种超时参数
  • 性能指标监控:监控QPS、延迟、错误率等关键指标

7. Redisson vs 原生Redis客户端

功能对比

特性Redisson原生Redis客户端
分布式锁开箱即用需要自己实现
分布式集合支持不支持
连接管理自动手动
序列化自动手动
集群支持完整基础
性能
学习成本

使用场景选择

选择Redisson的场景

  • 需要分布式锁:业务中需要分布式锁控制并发访问
  • 需要分布式集合:需要分布式Map、Set、List等数据结构
  • 需要分布式服务:需要分布式计数器、限流器、调度器等
  • 需要高可用支持:需要Redis集群、哨兵等高可用架构
  • 希望简化开发:希望减少样板代码,提高开发效率

选择原生Redis客户端的场景

  • 只需要基本操作:只需要基本的Redis操作,不需要分布式功能
  • 对性能要求极高:需要极致的性能优化,愿意承担开发复杂度
  • 需要完全控制:需要完全控制连接管理和序列化方式
  • 项目已使用其他客户端:项目已经使用Jedis、Lettuce等客户端

8. 最佳实践

锁使用最佳实践

  • 总是使用try-finally释放锁:确保锁在任何情况下都能正确释放
  • 设置合理的超时时间:避免死锁,设置合理的等待和持有时间
  • 避免锁嵌套:避免复杂的锁嵌套,防止死锁风险
  • 监控锁竞争情况:监控锁的获取和释放频率,优化锁策略
  • 使用合适的锁类型:根据业务需求选择合适的锁类型(可重入锁、公平锁、读写锁)

性能优化最佳实践

  • 使用连接池:合理配置连接池大小,避免连接不足
  • 合理设置超时时间:设置合理的连接、读写超时时间
  • 使用批量操作:使用RBatch进行批量操作,减少网络往返
  • 避免大对象序列化:避免序列化过大的对象,提升传输效率
  • 监控和调优:持续监控性能指标,及时调整配置参数

第十三题:Redis的操作为什么是原子性的?如何保证原子性?

1. Redis原子性的原因

问题描述:Redis的操作为什么是原子性的,如何保证原子性。

原子性保证机制

  1. 单线程模型:Redis 6.0之前采用单线程处理所有命令
  2. 命令级原子性:每个命令的执行都是原子的
  3. 内存操作原子性:内存读写操作本身是原子的
  4. 网络I/O原子性:网络传输保证数据完整性

方案实现描述
Redis原子性通过单线程模型和事件循环机制保证:

  • 单线程处理:Redis 6.0之前采用单线程处理所有命令
  • 事件循环:命令接收->解析->执行->返回的串行处理
  • 无并发竞争:避免了多线程环境下的竞态条件
  • 内存操作原子性:CPU级别的内存操作本身就是原子的

代码实现

// 1. 单线程命令处理流程:串行执行保证原子性
String command = receiveCommand();
Object parsed = parseCommand(command);
Object result = executeCommand(parsed);
sendResponse(result);// 2. 命令级别的原子性:每个命令都是原子操作
redisTemplate.opsForValue().set("key", "value");      // 原子操作:SET命令
redisTemplate.opsForValue().increment("counter");      // 原子操作:INCR命令
redisTemplate.opsForList().leftPush("list", "item");   // 原子操作:LPUSH命令
redisTemplate.opsForSet().add("set", "member");        // 原子操作:SADD命令
redisTemplate.opsForValue().multiSet(Map.of("key1", "value1", "key2", "value2"));  // 原子操作:MSET命令

2. Redis如何保证原子性

单线程事件循环

// Redis事件循环:单线程处理所有事件
while (true) {processFileEvents();    // 处理文件事件:网络I/O事件处理processTimeEvents();    // 处理时间事件:定时任务事件处理// 命令执行串行化:每个命令完整执行后才处理下一个命令
}

3. 原子性保证机制

内存操作原子性
Redis的内存操作具有原子性保证:

  • 内存分配原子性:内存分配操作不会被打断,确保数据完整性
  • 数据写入原子性:数据写入操作是原子的,要么完全成功要么完全失败
  • 内存管理原子性:内存的分配、回收、访问都是原子操作
  • 数据结构操作原子性:对Hash、Set、List等数据结构的操作都是原子的

网络I/O原子性
Redis的网络I/O操作具有原子性保证:

  • 命令接收原子性:完整接收一个命令后才开始处理,避免命令被截断
  • 命令执行原子性:单个命令的执行过程是原子的,不会被其他操作打断
  • 响应发送原子性:命令执行结果的发送是原子的,确保客户端收到完整响应
  • 事务操作原子性:MULTI/EXEC事务中的所有命令要么全部执行成功,要么全部回滚

4. 原子性示例

基本命令原子性

// 1. 字符串操作
redisTemplate.opsForValue().set("key", "value");     // 原子
redisTemplate.opsForValue().append("key", "suffix"); // 原子
redisTemplate.opsForValue().increment("counter");    // 原子// 2. 列表操作
redisTemplate.opsForList().leftPush("list", "item"); // 原子
redisTemplate.opsForList().rightPop("list");         // 原子// 3. 集合操作
redisTemplate.opsForSet().add("set", "member");      // 原子
redisTemplate.opsForSet().remove("set", "member");   // 原子// 4. 哈希操作
redisTemplate.opsForHash().put("hash", "field", "value"); // 原子
redisTemplate.opsForHash().delete("hash", "field");       // 原子

复杂命令原子性

// 1. 批量操作
Map<String, String> data = Map.of("key1", "value1", "key2", "value2");
redisTemplate.opsForValue().multiSet(data);  // 原子:要么全部成功,要么全部失败// 2. 条件操作
redisTemplate.opsForValue().setIfAbsent("key", "value");  // 原子:检查并设置
redisTemplate.opsForValue().setIfPresent("key", "newValue"); // 原子:检查并更新// 3. 范围操作
redisTemplate.opsForList().trim("list", 0, 10);  // 原子:截取列表
redisTemplate.opsForZSet().removeRangeByScore("zset", 0, 100); // 原子:按分数删除

5. 原子性限制

多命令非原子性

// 问题:多个命令不是原子的
String value1 = redisTemplate.opsForValue().get("key1");
String value2 = redisTemplate.opsForValue().get("key2");
redisTemplate.opsForValue().set("result", value1 + value2);// 问题:在get和set之间,其他客户端可能修改了key1或key2
// 解决方案:使用Lua脚本或事务

事务的原子性

// 1. 使用MULTI/EXEC
redisTemplate.execute((SessionCallback<Object>) operations -> {operations.multi();operations.opsForValue().set("key1", "value1");operations.opsForValue().set("key2", "value2");return operations.exec();
});// 2. 使用Lua脚本
String script = "local key1 = redis.call('GET', KEYS[1]) " +"local key2 = redis.call('GET', KEYS[2]) " +"redis.call('SET', KEYS[3], key1 .. key2) " +"return 'OK'";redisTemplate.execute(new DefaultRedisScript<>(script, String.class),Arrays.asList("key1", "key2", "result"));

6. 原子性保证总结

Redis原子性保证机制总结

1. 单线程模型

  • 命令串行执行:所有命令在单线程中串行执行,避免了并发竞争
  • 无并发竞争:单线程模型天然避免了多线程的并发访问问题
  • 天然原子性:单个命令的执行天然具有原子性

2. 内存操作

  • 单个内存操作是原子的:每个内存读写操作都是原子的
  • 数据结构操作是原子的:对Hash、Set、List等数据结构的操作都是原子的

3. 网络I/O

  • 命令接收是原子的:完整接收一个命令后才开始处理
  • 结果返回是原子的:命令执行结果的发送是原子的

4. 事务支持

  • MULTI/EXEC事务:支持多命令事务,要么全部成功要么全部失败
  • Lua脚本事务:Lua脚本中的多个命令也是原子的

5. 限制和注意事项

  • 多命令不是原子的:多个独立命令之间不是原子的
  • 需要事务或Lua脚本保证:跨命令的原子性需要事务或Lua脚本来保证

第十四题:Redisson分布式延迟队列是什么?如何实现?

1. 分布式延迟队列简介

问题描述:什么是分布式延迟队列,Redisson如何实现延迟队列功能。

延迟队列概念
延迟队列是一种支持延迟执行的消息队列,消息在指定时间后才会被消费,常用于定时任务、订单超时处理等场景。

实现原理

  1. 基于ZSet:使用Redis的有序集合存储消息
  2. 时间戳排序:Score为执行时间戳,Member为消息内容
  3. 定时扫描:定期扫描到期的消息进行消费

延迟队列核心概念
延迟队列是一种支持延迟执行的消息队列,具有以下特点:

  • 延迟执行:消息在指定时间后才会被消费
  • 分布式环境:支持分布式部署,多节点协同工作
  • 高可用:基于Redis的高可用特性
  • 消息持久化:消息存储在Redis中,支持持久化

应用场景

  • 订单超时取消:电商订单30分钟内未支付自动取消
  • 定时任务调度:定时执行某些业务逻辑
  • 延迟通知:延迟发送通知消息
  • 重试机制:失败后延迟重试

Redisson延迟队列特点

  • 自动管理:自动处理消息的延迟和到期
  • 高可靠性:基于Redis的持久化特性
  • 分布式支持:支持多节点部署
  • 简单易用:提供简洁的API接口

代码实现

// 1. 创建延迟队列
RQueue<String> queue = redissonClient.getQueue("delayed_queue");
RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(queue);// 2. 添加延迟消息
delayedQueue.offer("订单超时处理", 30, TimeUnit.SECONDS);
delayedQueue.offer("定时任务执行", 60, TimeUnit.SECONDS);// 3. 消费消息
String message = queue.poll(1, TimeUnit.SECONDS);
if (message != null) {handleMessage(message);
}

2. Redisson延迟队列实现原理

基于ZSet的实现

// 1. 计算执行时间:当前时间 + 延迟时间
long executeTime = System.currentTimeMillis() + 30000; // 30秒后执行
String message = "{\"orderId\": \"12345\", \"action\": \"cancel\"}";// 2. 添加延迟消息到ZSet:Score为执行时间戳,Member为消息内容
redisTemplate.opsForZSet().add("delayed_queue:order", message, executeTime);// 3. 扫描到期消息:获取当前时间之前的消息
Set<String> expiredMessages = redisTemplate.opsForZSet().rangeByScore("delayed_queue:order", 0, System.currentTimeMillis());// 4. 处理并删除消息:处理到期消息并从ZSet中移除
for (String expiredMessage : expiredMessages) {processMessage(expiredMessage);redisTemplate.opsForZSet().remove("delayed_queue:order", expiredMessage);
}

核心数据结构

Redisson延迟队列使用多种Redis数据结构协同工作:

数据结构说明

  • ZSet(有序集合):存储延迟消息,按时间戳排序
  • List(列表):存储到期消息和处理队列
  • Hash(哈希):存储统计信息和元数据

数据流转过程

  1. 消息入队:延迟消息存入ZSet,按执行时间排序
  2. 定时扫描:定期扫描ZSet,将到期消息移到List
  3. 消息消费:从List中取出消息进行处理
  4. 异常处理:处理失败的消息存入死信队列

代码实现

// 延迟队列数据结构实现
// 1. 延迟队列ZSet - 存储延迟消息
RScoredSortedSet<String> delayedSet = redissonClient.getScoredSortedSet("delayed_queue:order_timeout_queue");// 添加延迟消息:30分钟后执行
long executeTime = System.currentTimeMillis() + 30 * 60 * 1000;
delayedSet.add(executeTime, "订单ID:12345,超时处理");
delayedSet.add(executeTime + 60000, "订单ID:12346,超时处理");// 2. 处理队列List - 存储到期消息
RList<String> processingQueue = redissonClient.getList("processing_queue:order_timeout_queue");// 3. 死信队列List - 存储处理失败的消息
RList<String> deadLetterQueue = redissonClient.getList("dead_letter_queue:order_timeout_queue");// 4. 统计信息Hash - 存储队列统计
RMap<String, Integer> stats = redissonClient.getMap("queue_stats:order_timeout_queue");
stats.put("total", 100);
stats.put("processed", 85);
stats.put("failed", 5);
stats.put("pending", 10);// 定时扫描:将到期消息移到处理队列
long currentTime = System.currentTimeMillis();
Collection<String> expiredMessages = delayedSet.valueRange(0, true, currentTime, true);
for (String message : expiredMessages) {delayedSet.remove(message);processingQueue.add(message);
}// 处理消息
String message = processingQueue.poll();
if (message != null) {try {processMessage(message);stats.addAndGet("processed", 1);stats.addAndGet("pending", -1);} catch (Exception e) {deadLetterQueue.add(message);stats.addAndGet("failed", 1);stats.addAndGet("pending", -1);}
}

3. Redisson延迟队列使用

基本使用

// 1. 获取延迟队列
RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(redissonClient.getQueue("delayed_queue")
);// 2. 添加延迟消息
delayedQueue.offer("message1", 10, TimeUnit.SECONDS);
delayedQueue.offer("message2", 30, TimeUnit.SECONDS);// 3. 获取队列
RQueue<String> queue = redissonClient.getQueue("delayed_queue");// 4. 消费消息
String message = queue.poll();
if (message != null) {processMessage(message);
}

高级使用

// 1. 自定义消息对象
RDelayedQueue<OrderMessage> delayedQueue = redissonClient.getDelayedQueue(redissonClient.getQueue("order_delayed_queue")
);// 2. 添加复杂消息
OrderMessage orderMessage = new OrderMessage("12345", "cancel");
delayedQueue.offer(orderMessage, 30, TimeUnit.MINUTES);// 3. 批量添加消息
List<OrderMessage> messages = Arrays.asList(new OrderMessage("12346", "cancel"),new OrderMessage("12347", "cancel")
);for (OrderMessage msg : messages) {delayedQueue.offer(msg, 30, TimeUnit.MINUTES);
}

4. 延迟队列应用场景

订单超时取消

方案逻辑说明
订单超时取消是电商系统的核心功能,通过延迟队列实现:

  1. 创建订单时:添加30分钟的延迟取消消息
  2. 支付成功时:取消对应的延迟消息(避免误取消)
  3. 延迟到期时:检查订单状态,如果未支付则自动取消
  4. 异常处理:记录取消日志,通知用户

实现要点

  • 使用订单ID作为消息标识,便于后续取消
  • 支付成功后需要取消延迟消息,避免误取消
  • 取消前需要二次确认订单状态

代码实现

// 1. 创建订单时添加延迟取消消息
RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(redissonClient.getQueue("order_cancel_queue")
);
delayedQueue.offer(orderId, 30, TimeUnit.MINUTES);// 2. 消费取消消息
RQueue<String> queue = redissonClient.getQueue("order_cancel_queue");
String orderId = queue.poll(1, TimeUnit.SECONDS);
if (orderId != null) {handleOrderCancel(orderId);
}

定时任务调度

方案逻辑说明
定时任务调度系统通过延迟队列实现灵活的定时执行:

  1. 任务注册:将任务和延迟时间添加到延迟队列
  2. 任务调度:延迟队列自动在指定时间将任务移到处理队列
  3. 任务执行:消费者从处理队列取出任务并执行
  4. 任务监控:记录任务执行状态和结果

实现要点

  • 支持动态添加和取消定时任务
  • 任务执行状态跟踪和异常处理
  • 支持任务优先级和重试机制

代码实现

// 1. 添加定时任务
RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(redissonClient.getQueue("task_queue")
);
delayedQueue.offer(taskId, delaySeconds, TimeUnit.SECONDS);// 2. 消费任务消息
RQueue<String> queue = redissonClient.getQueue("task_queue");
String taskId = queue.poll(1, TimeUnit.SECONDS);
if (taskId != null) {executeTask(taskId);
}

延迟通知

方案逻辑说明
延迟通知系统用于在指定时间后发送通知给用户:

  1. 通知注册:将通知内容和延迟时间添加到延迟队列
  2. 通知调度:延迟队列在指定时间将通知移到处理队列
  3. 通知发送:消费者从处理队列取出通知并发送
  4. 发送状态跟踪:记录通知发送状态和用户反馈

实现要点

  • 支持多种通知类型(短信、邮件、推送等)
  • 支持通知优先级和批量发送
  • 支持用户偏好设置和免打扰时间

代码实现

// 1. 发送延迟通知
RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(redissonClient.getQueue("notification_queue")
);
String notification = userId + ":" + message;
delayedQueue.offer(notification, delayMinutes, TimeUnit.MINUTES);// 2. 消费通知消息
RQueue<String> queue = redissonClient.getQueue("notification_queue");
String notification = queue.poll(1, TimeUnit.SECONDS);
if (notification != null) {String[] parts = notification.split(":");String userId = parts[0];String message = parts[1];sendNotification(userId, message);
}

5. 延迟队列优化

性能优化

// 1. 批量处理消息
RQueue<String> queue = redissonClient.getQueue("delayed_queue");
List<String> messages = new ArrayList<>();
for (int i = 0; i < 100; i++) {String message = queue.poll();if (message != null) {messages.add(message);} else {break;}
}// 2. 连接池优化
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379").setConnectionPoolSize(20).setConnectionMinimumIdleSize(5);

可靠性优化

// 1. 消息重试机制
RQueue<String> queue = redissonClient.getQueue("delayed_queue");
RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(queue);String message = queue.poll();
if (message != null) {try {processMessage(message);} catch (Exception e) {// 重试:5秒后重新处理delayedQueue.offer(message, 5, TimeUnit.SECONDS);}
}// 2. 死信队列处理
RQueue<String> deadLetterQueue = redissonClient.getQueue("dead_letter_queue");
String failedMessage = deadLetterQueue.poll();
if (failedMessage != null) {// 处理死信消息
}

6. 延迟队列最佳实践

配置最佳实践

Redis配置优化

  • 设置合适的内存限制:maxmemory 2gb
  • 配置淘汰策略:maxmemory-policy allkeys-lru
  • 启用持久化:save 900 1

连接池配置

Config config = new Config();
config.useSingleServer().setConnectionPoolSize(20).setConnectionMinimumIdleSize(5).setConnectTimeout(3000);

使用最佳实践

消息设计原则

  • 消息体尽量小,避免大对象序列化
  • 包含必要的元数据(时间戳、重试次数等)
  • 支持JSON序列化,便于调试

错误处理策略

  • 实现指数退避重试机制
  • 记录详细的错误日志
  • 配置死信队列处理失败消息

性能优化要点

  • 批量处理消息,减少网络开销
  • 异步处理,提高吞吐量
  • 合理设置延迟时间,避免过于频繁的调度

监控告警机制

  • 监控队列长度和处理速度
  • 设置告警阈值(队列长度>1000)
  • 定期检查死信队列和处理失败率

7. 延迟队列对比

与其他方案对比

方案优点缺点适用场景
Redisson延迟队列简单易用、高性能、高可用依赖Redis、内存占用高并发、简单场景
RabbitMQ延迟插件功能强大、可靠性高配置复杂、性能一般复杂业务、高可靠性
Kafka延迟队列高吞吐量、可扩展实现复杂、延迟精度低大数据量、高吞吐
数据库定时任务简单可靠、数据持久化性能低、扩展性差低频、简单场景

选择建议

1. 高并发场景

  • 选择:Redisson延迟队列
  • 原因:性能高、实现简单、基于Redis内存存储
  • 适用:秒杀、限流、订单超时等高频场景

2. 高可靠性场景

  • 选择:RabbitMQ延迟插件
  • 原因:可靠性高、功能完善、支持消息持久化
  • 适用:金融交易、重要通知等对可靠性要求高的场景

3. 大数据量场景

  • 选择:Kafka延迟队列
  • 原因:高吞吐量、可扩展、支持分区
  • 适用:日志处理、数据分析等大数据量场景

4. 简单场景

  • 选择:数据库定时任务
  • 原因:实现简单、维护方便、无需额外组件
  • 适用:小型项目、低频任务等简单场景

第十五题:Redis的淘汰策略有哪些?涉及了哪些算法?

1. Redis淘汰策略概述

问题描述:Redis内存满时如何选择要删除的键,各种淘汰策略的特点和适用场景。

淘汰策略概念:当Redis内存达到maxmemory限制时,需要选择哪些key进行删除来释放内存空间。

触发条件:1. 内存使用达到maxmemory设置;2. 新写入数据时内存不足;3. 执行某些命令时内存不足

配置方式

# Redis配置文件
maxmemory 2gb
maxmemory-policy allkeys-lru
// Java代码配置
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379").setMaxMemory(2L * 1024 * 1024 * 1024)  // 2GB.setMaxMemoryPolicy("allkeys-lru");

策略分类按作用范围allkeys-*对所有key生效,volatile-*只对设置了过期时间的key生效;按算法LRU最近最少使用,LFU最少频率使用,Random随机选择,TTL按过期时间选择;特殊策略noeviction不淘汰任何key,内存满时直接返回错误

2. 具体淘汰策略详解

noeviction策略策略说明:不淘汰任何key,当内存不足时直接返回错误;适用场景:对数据完整性要求极高,有外部监控和清理机制;优缺点:✅数据不会丢失,行为可预测;❌可能导致写入失败,需要外部监控

LRU策略策略说明:LRU最近最少使用算法,使用近似LRU算法,随机采样5个key;两种类型allkeys-lru从所有key中选择,volatile-lru从设置了过期时间的key中选择;适用场景:访问模式相对均匀,热点数据不明显,对性能要求较高

LFU策略策略说明:LFU最少频率使用算法,维护每个key的访问频率,使用衰减机制避免频率无限增长;两种类型allkeys-lfu从所有key中选择,volatile-lfu从设置了过期时间的key中选择;适用场景:有明显的热点数据,访问模式不均匀,需要保护热点数据

Random策略策略说明:随机选择key进行删除;两种类型allkeys-random从所有key中随机选择,volatile-random从设置了过期时间的key中随机选择;适用场景:访问模式完全随机,对淘汰策略要求不高;优缺点:✅实现简单,性能开销小;❌可能删除重要数据,淘汰效果不可预测

TTL策略策略说明:TTL按过期时间选择,只对设置了过期时间的key生效,选择剩余过期时间最短的key删除;策略类型volatile-ttl从设置了过期时间的key中选择即将过期的key删除;适用场景:大部分key都设置了过期时间,希望优先删除即将过期的key;优缺点:✅优先删除即将过期的数据,减少内存浪费;❌只对设置了过期时间的key生效,可能删除仍在使用中的数据

3. 淘汰算法实现原理

LRU算法实现双向链表+HashMap:使用双向链表维护访问顺序,HashMap提供O(1)访问;访问时移动:访问key时将其移动到链表头部;淘汰尾部:内存满时淘汰链表尾部节点;近似LRU:Redis使用随机采样5个key,选择最近最少使用的

LFU算法实现频率计数+有序集合:使用HashMap记录key频率,LinkedHashSet按频率分组;访问时递增:访问key时频率+1,移动到对应频率组;衰减机制:定期降低频率防止计数器溢出;概率递增:频率越高,递增概率越低;淘汰最低频率:选择频率最低的key进行淘汰

4. 淘汰策略配置和监控

配置示例基本配置maxmemory 2gbmaxmemory-policy allkeys-lru不同场景配置:缓存场景allkeys-lru保护热点数据,会话存储volatile-lru优先淘汰过期会话,计数器allkeys-lfu保护高频访问的计数器,临时数据volatile-ttl优先淘汰即将过期的数据;动态配置CONFIG SET maxmemory 4gbCONFIG SET maxmemory-policy allkeys-lfu

监控指标内存使用指标used_memory当前使用内存,used_memory_peak内存使用峰值,maxmemory最大内存限制;淘汰统计指标evicted_keys总淘汰key数量,evicted_keys_per_second每秒淘汰key数量;命中率指标keyspace_hits命中次数,keyspace_misses未命中次数,hit_rate命中率计算;监控命令INFO memoryINFO statsMONITOR

5. 淘汰策略选择指南

场景选择缓存场景:推荐allkeys-lru,缓存数据可以重新加载,优先保留最近使用的数据;会话存储:推荐volatile-lru,会话数据有生命周期,优先保留最近使用的会话;计数器场景:推荐allkeys-lfu,计数器访问频率差异大,保护热点计数器;临时数据:推荐volatile-ttl,临时数据有明确的生命周期,优先删除即将过期的;重要数据:推荐noeviction,重要数据不能丢失,需要外部监控和清理

性能对比

策略内存开销CPU开销准确性适用场景
noeviction重要数据
allkeys-lru通用缓存
allkeys-lfu热点数据
allkeys-random随机访问
volatile-lru会话存储
volatile-lfu临时热点
volatile-ttl临时数据
volatile-random临时随机

6. 淘汰策略优化与最佳实践

配置优化:合理设置maxmemory,留出系统内存空间;根据业务场景选择淘汰策略,平衡性能和准确性;启用内存监控,设置告警阈值

数据结构优化:使用合适的数据类型,避免大key;设置合理的过期时间,减少内存碎片;使用压缩和批量操作提高效率

监控与维护:定期检查内存使用情况和淘汰频率;分析内存增长趋势和淘汰原因;备份重要配置,准备故障处理方案


第十六题:Redis红锁是什么?Redisson分布式锁如何提高可靠性?

问题描述:什么是Redis红锁,它解决了什么问题,如何提高分布式锁的可靠性。

红锁概念:红锁(RedLock)是基于多个Redis实例的分布式锁算法,通过多个独立的Redis实例来提高锁的可靠性。

解决的问题:1. 单Redis实例的可靠性问题:单点故障导致锁丢失;2. 主从切换导致的锁丢失:主从切换时锁信息丢失;3. 网络分区问题:网络分区导致锁状态不一致

代码实现

// 1. 获取红锁:在多个Redis实例上获取锁
public boolean tryLock(String lockKey, long waitTime, long leaseTime) {List<RLock> locks = new ArrayList<>();int successCount = 0;long startTime = System.currentTimeMillis();// 1.1 在所有Redis实例上尝试获取锁,控制总时间for (RedissonClient client : redissonClients) {long remainingTime = waitTime - (System.currentTimeMillis() - startTime);if (remainingTime <= 0) break;RLock lock = client.getLock(lockKey);locks.add(lock);if (lock.tryLock(remainingTime, leaseTime, TimeUnit.MILLISECONDS)) {successCount++;}}// 1.2 检查是否达到多数成功if (successCount >= majorityThreshold) {return true;} else {// 1.3 未达到多数,释放已获取的锁releaseLocks(locks);return false;}
}// 2. 释放红锁:释放所有已获取的锁
public void unlock(List<RLock> locks) {for (RLock lock : locks) {if (lock.isHeldByCurrentThread()) {lock.unlock();}}
}// 3. 红锁可靠性验证:检查锁是否仍然有效
public boolean validateLock(List<RLock> locks) {int validLocks = 0;for (RLock lock : locks) {if (lock.isHeldByCurrentThread() && lock.remainTimeToLive() > 0) {validLocks++;}}return validLocks >= majorityThreshold;
}

红锁解决的问题

// 1. 解决单点故障问题
// 单Redis实例问题:实例宕机导致锁丢失
String singleRedisUrl = "redis://127.0.0.1:6379";
RedissonClient singleClient = Redisson.create(createConfig(singleRedisUrl));// 红锁解决方案:多个独立实例
List<String> multiRedisUrls = Arrays.asList("redis://127.0.0.1:6379","redis://127.0.0.1:6380", "redis://127.0.0.1:6381"
);
RedLockImplementation redLock = new RedLockImplementation(multiRedisUrls);// 2. 解决主从切换问题
// 主从切换问题:切换时锁信息丢失
Config masterSlaveConfig = new Config();
masterSlaveConfig.useMasterSlaveServers().setMasterAddress("redis://127.0.0.1:6379").setSlaveAddresses("redis://127.0.0.1:6380", "redis://127.0.0.1:6381");// 红锁解决方案:独立实例,无主从关系// 3. 解决网络分区问题
// 网络分区问题:分区导致锁状态不一致
// 单Redis实例:网络分区可能导致锁状态错误
// 红锁解决方案:多数投票机制// 单Redis实例网络分区问题
boolean lockA = redisTemplate.opsForValue().setIfAbsent("lock", "clientA", Duration.ofSeconds(30));
// 网络分区发生,锁可能已过期但客户端A不知道
boolean lockB = redisTemplate.opsForValue().setIfAbsent("lock", "clientB", Duration.ofSeconds(30));
// 结果:两个客户端都认为持有锁,导致数据不一致// 红锁解决网络分区问题
List<RedissonClient> clients = Arrays.asList(client1, client2, client3, client4, client5);
int successCount = 0;
for (RedissonClient client : clients) {RLock lock = client.getLock("redlock");if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {successCount++;}
}
// 多数投票:需要至少3个实例成功(5个中的多数)
if (successCount >= 3) {// 获取红锁成功,即使部分实例网络分区也不影响
}

2. Redisson分布式锁可靠性分析

Redisson锁的可靠性问题单Redis实例问题:实例宕机、主从切换、网络分区导致锁丢失;锁超时问题:业务超时、锁提前释放、续期失败;时钟问题:时钟跳跃、不同步影响过期时间;网络问题:延迟、分区影响锁操作

Redisson锁的可靠性保证自动续期机制:后台线程定期续期,避免锁提前过期,业务执行期间锁不丢失;可重入锁支持:同一线程可多次获取锁,支持嵌套调用;公平锁支持:按请求顺序获取锁,避免锁饥饿;锁超时保护:设置合理超时时间,自动释放过期锁

lock.lock(30, TimeUnit.SECONDS); // 设置30秒超时
RLock fairLock = redissonClient.getFairLock("fair_lock");

3. 提高Redisson锁可靠性的方法

使用红锁提高可靠性红锁配置:创建多个独立的Redis客户端实例,使用多数投票机制提高可靠性;红锁优势:单个Redis实例故障不影响整体锁,多数投票机制保证锁的正确性,降低单点故障风险

RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
redLock.tryLock(10, 30, TimeUnit.SECONDS);

配置优化提高可靠性连接池配置:设置合理的连接池大小和超时时间,配置重试次数和重试间隔;锁配置优化:设置合理的锁超时时间,启用自动续期,根据业务场景调整重试策略;监控配置:启用慢查询日志,监控锁竞争情况,设置告警阈值

config.useSingleServer().setConnectionPoolSize(20).setRetryAttempts(3);

业务层可靠性保证幂等性设计:使用业务ID确保操作幂等性,检查业务状态,避免重复执行;重试机制:实现指数退避重试策略,设置最大重试次数;超时处理:使用CompletableFuture实现异步超时控制,避免长时间阻塞

String lockKey = "business:" + businessId;
lock.tryLock(10, 30, TimeUnit.SECONDS);

4. 红锁的局限性和改进

红锁的局限性性能问题:需要访问多个Redis实例,网络延迟增加,获取锁时间变长,资源消耗增加;复杂性增加:需要维护多个Redis实例,配置和管理复杂,故障排查困难,运维成本提高;时钟依赖:仍然依赖系统时钟,时钟跳跃问题依然存在,需要时钟同步;网络分区问题:网络分区时可能无法获取锁,导致服务不可用,需要网络恢复才能正常工作

红锁的改进方案使用更可靠的存储:ZooKeeper强一致性适合分布式协调,etcd高可用支持分布式锁,数据库利用事务特性保证锁的可靠性;混合方案:Redis + ZooKeeper提供性能和可靠性,Redis + 数据库双重保障,多级锁机制不同级别使用不同策略;业务层保证:幂等性设计确保重复操作无副作用,重试机制处理锁获取失败,超时处理避免长时间等待;监控和告警:锁竞争监控、锁超时告警、性能监控

5. 分布式锁选择建议与最佳实践

不同场景的选择高性能场景:Redis单实例锁,性能最高延迟最低,风险单点故障;高可靠性场景:Redis红锁,可靠性较高性能较好,风险复杂性增加;强一致性场景:ZooKeeper锁,强一致性保证,风险性能较低;平衡场景:etcd锁,性能和可靠性平衡,风险学习成本;简单场景:数据库锁,实现简单可靠性高,风险性能较低

可靠性对比

锁类型可靠性性能一致性复杂度适用场景
Redis单实例最终高性能
Redis红锁最终平衡
ZooKeeper强一致
etcd平衡
数据库简单

最佳实践

  • Redisson锁:使用有意义的锁名称避免锁嵌套,设置合理超时时间避免死锁,总是使用try-finally确保锁释放,减少锁持有时间使用合适锁类型,监控锁竞争情况和锁超时,单元测试集成测试压力测试验证锁正确性
  • 红锁:至少3个Redis实例相互独立,配置合理超时时间确保网络稳定性,使用内网连接优化网络延迟,监控实例状态实现自动故障转移,并行获取锁优化锁超时时间,监控锁获取成功率和锁超时情况

第十七题:数据库乐观锁、悲观锁与Redis分布式锁的区别和使用场景?

1. 锁机制对比

基本概念对比

悲观锁:假设会发生冲突,提前加锁。特点:阻塞等待,安全性高。实现:数据库行锁、表锁。适用:写操作频繁,冲突概率高的场景

乐观锁:假设不会冲突,提交时检查。特点:非阻塞,性能好。实现:版本号、CAS操作。适用:读操作频繁,冲突概率低的场景

分布式锁:跨进程、跨机器的锁机制。特点:解决分布式环境下的并发问题。实现:Redis、ZooKeeper、数据库。适用:分布式系统,需要跨节点协调的场景

实现方式对比

数据库悲观锁

SELECT * FROM table WHERE id = 1 FOR UPDATE;

数据库乐观锁

UPDATE table SET value = new_value, version = version + 1 
WHERE id = 1 AND version = old_version;

Redis分布式锁

SET key value NX EX timeout

2. 锁机制特点对比

锁类型优点缺点适用场景
悲观锁数据安全性高、实现简单、强一致性保证性能较低、并发度低、可能产生死锁写操作频繁、数据一致性要求高、并发量不大
乐观锁性能高无阻塞、并发度高、不会产生死锁实现复杂、可能出现ABA问题、冲突时需要重试读操作频繁、冲突概率低、高并发场景
Redis分布式锁性能高基于内存、支持高并发、实现相对简单单点故障风险、主从切换问题、时钟依赖问题分布式系统、高并发场景、对性能要求高

5. 使用场景对比

场景选择指南

锁类型典型应用场景
数据库悲观锁银行转账、库存扣减、订单处理、数据一致性要求极高
数据库乐观锁用户信息更新、文章点赞、计数器更新、高并发读多写少
Redis分布式锁分布式任务调度、缓存更新、分布式限流、跨服务资源控制

性能对比

锁类型性能并发度一致性复杂度适用场景
数据库悲观锁强一致
数据库乐观锁最终高并发
Redis分布式锁最终分布式

第十八题:Redis只存10万数据,如何保证都是热点数据?Redis挂了怎么办?会满的情况如何设计处理方案?

1. 保证热点数据的策略

数据预热策略:系统启动时预先加载热点数据,避免冷启动问题

@PostConstruct
public void warmupOnStartup() {List<User> hotUsers = userService.getHotUsers(1000);for (User user : hotUsers) {redisTemplate.opsForValue().set("user:" + user.getId(), user, Duration.ofHours(1));}
}

智能缓存策略:基于访问频率和业务重要性动态调整缓存时间

public void cacheByFrequency(String key, Object value) {String frequencyKey = "freq:" + key;redisTemplate.opsForValue().increment(frequencyKey);Long frequency = redisTemplate.opsForValue().get(frequencyKey);Duration cacheTime = frequency > 100 ? Duration.ofHours(2) : Duration.ofMinutes(30);redisTemplate.opsForValue().set(key, value, cacheTime);
}

LRU优化策略:配置LRU策略,数据分层存储,定期清理冷数据

// 配置:maxmemory-policy allkeys-lru
redisTemplate.opsForValue().set("hot:user:1", userData, Duration.ofDays(7));
redisTemplate.opsForValue().set("warm:user:2", userData, Duration.ofHours(2));
redisTemplate.opsForValue().set("cold:user:3", userData, Duration.ofMinutes(30));

2. Redis故障处理

Redis挂了的影响缓存失效:所有缓存数据丢失,请求直接打到数据库,数据库压力激增;分布式锁失效:锁状态丢失,可能出现并发问题,业务逻辑异常;会话丢失:用户登录状态丢失,需要重新登录,用户体验下降;计数器丢失:访问统计丢失,业务指标异常,监控数据不准确

故障处理策略降级策略:Redis异常时自动降级到数据库,保证服务可用性;熔断机制:连续失败超过阈值时开启熔断,避免雪崩效应;多级缓存:本地缓存+Redis+数据库的多级缓存架构,提高容错能力

// 降级策略示例
public User getUserById(Long id) {try {User user = (User) redisTemplate.opsForValue().get("user:" + id);if (user != null) return user;} catch (Exception e) {log.error("Redis访问异常", e);}return userService.getUserById(id); // 降级到数据库
}

高可用架构主从复制:配置主从复制提高可用性,replicaof 127.0.0.1 6379哨兵模式:自动故障转移,sentinel monitor mymaster 127.0.0.1 6379 2集群模式:数据分片提高性能,cluster-enabled yes读写分离:写操作使用主节点,读操作使用从节点,从节点异常时降级到主节点

// 读写分离示例
public void write(String key, Object value) {masterRedis.opsForValue().set(key, value); // 写操作使用主节点
}public Object read(String key) {try {return slaveRedis.opsForValue().get(key); // 读操作使用从节点} catch (Exception e) {return masterRedis.opsForValue().get(key); // 降级到主节点}
}

3. Redis会满的处理方案设计

容量规划与监控内存需求分析:分析业务数据量、增长速度、访问模式;容量预留:预留20-30%的内存空间,应对突发情况;监控告警:设置内存使用率告警,提前发现问题

数据分层存储热点数据识别:通过监控识别热点数据,优先保留;自动分层:根据访问频率自动调整数据存储位置;生命周期管理:设置合理的过期时间,定期清理无用数据

集群扩容分片策略:根据key的hash值或业务逻辑分片;负载均衡:确保数据均匀分布,避免热点问题;故障转移:主从复制,自动故障转移

智能淘汰策略策略选择:根据业务特点选择LRU、LFU、TTL等策略;参数调优:调整淘汰策略参数,平衡性能和准确性;监控优化:监控淘汰效果,持续优化策略

4. 监控和告警

Redis监控关键指标监控:内存使用率、连接数、命中率等核心指标;健康检查:定期检查Redis服务状态;告警机制:设置阈值,异常时及时告警

// 关键指标监控
public void monitorKeyMetrics() {Long usedMemory = redisTemplate.getConnectionFactory().getConnection().info("memory").getUsedMemory();Long connectedClients = redisTemplate.getConnectionFactory().getConnection().info("clients").getConnectedClients();double hitRate = (double) hits / (hits + misses);sendMetrics("redis.memory.used", usedMemory);sendMetrics("redis.clients.connected", connectedClients);sendMetrics("redis.hit.rate", hitRate);
}// 健康检查
public boolean isHealthy() {try {redisTemplate.opsForValue().set("health:check", "ok", Duration.ofSeconds(10));String result = (String) redisTemplate.opsForValue().get("health:check");return "ok".equals(result);} catch (Exception e) {return false;}
}// 告警机制
public void checkAndAlert() {if (getMemoryUsage() > 0.8) {sendAlert("Redis内存使用率过高: " + getMemoryUsage());}if (getHitRate() < 0.8) {sendAlert("Redis命中率过低: " + getHitRate());}
}

第十九题:Redis内存满了怎么办?会挂吗?如何设计处理方案?

1. 问题分析

问题描述:Redis内存满时的处理方案和设计思路。

Redis会满的场景数据量增长:业务数据持续增长,超过Redis内存限制;内存泄漏:程序bug导致内存无法释放;配置不当:maxmemory设置过小,无法满足业务需求;热点数据集中:大量热点数据集中在单个Redis实例

内存满的影响写入失败:新数据无法写入Redis,SET命令返回错误;淘汰策略生效:自动删除数据释放内存,可能误删重要数据;性能下降:内存分配失败,频繁淘汰操作增加CPU开销;服务不稳定:可能出现OOM,服务重启,数据丢失风险

Redis不会立即挂掉淘汰策略保护:根据配置的淘汰策略删除数据释放内存;写入保护:内存满时拒绝写入,返回错误信息保护现有数据;监控告警:内存使用率告警,及时处理问题避免服务中断

2. 处理方案

方案一:紧急扩容
设计思路:立即增加Redis实例内存或启动新实例
实现要点:动态调整maxmemory配置,启动新Redis实例分担负载
适用场景:内存使用率突然激增,需要快速恢复服务

方案二:数据清理
设计思路:清理无用数据,释放内存空间
实现要点:删除过期数据、临时数据、大key,清理内存碎片
适用场景:内存使用率持续较高,有可清理的数据

方案三:数据分层存储
设计思路:根据数据重要性分层存储,热点数据存Redis
实现要点:热点数据存Redis,温数据Redis+数据库,冷数据仅数据库
适用场景:数据量大,需要长期优化内存使用

方案四:集群扩容
设计思路:使用Redis集群,数据分片存储
实现要点:水平扩展Redis节点,数据分散存储,提高容量
适用场景:单机内存限制,需要大规模扩容

3. 核心代码实现

紧急扩容实现

// 1. 动态调整maxmemory
CONFIG SET maxmemory 4gb  // 从2gb扩容到4gb// 2. 启动新Redis实例
RedisInstance newInstance = new RedisInstance("redis://127.0.0.1:6380");
newInstance.start();

数据清理实现

// 1. 清理无用数据
redisTemplate.delete(redisTemplate.keys("temp:*")); // 删除临时数据
redisTemplate.delete(redisTemplate.keys("cache:*")); // 删除缓存数据// 2. 清理大key
Set<String> bigKeys = findBigKeys(redisTemplate);
for (String key : bigKeys) {redisTemplate.delete(key); // 删除大key
}

数据分层存储实现

public Object getData(String key) {Object data = redisTemplate.opsForValue().get(key); // 1. 先查Redisif (data != null) return data;data = loadFromDatabase(key); // 2. 再查数据库if (data != null) {redisTemplate.opsForValue().set(key, data, Duration.ofMinutes(30)); // 3. 写入Redis}return data;
}

集群扩容实现

// 1. 配置Redis集群
config.useClusterServers().addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001").addNodeAddress("redis://127.0.0.1:7002", "redis://127.0.0.1:7003");// 2. 数据分片存储
String shardKey = "shard:" + (key.hashCode() % 4);
redisTemplate.opsForValue().set(shardKey, value);

4. 监控和预防

监控方案实时监控:每分钟检查内存使用率,超过80%告警;趋势分析:分析内存增长趋势,预测使用情况,提前扩容;自动扩容:内存使用率超过85%时触发自动扩容

预防措施合理设置maxmemory:留出系统内存空间,考虑Redis内存开销;选择合适的淘汰策略:根据业务场景选择,平衡性能和准确性;数据生命周期管理:设置合理的过期时间,定期清理无用数据;监控和告警:实时监控内存使用,设置告警阈值

5. 最佳实践

设计原则预防为主:通过监控和预警避免内存满;快速响应:内存满时快速扩容或清理;数据分层:根据重要性分层存储;集群部署:使用集群提高容量和可用性

选择建议紧急情况:立即扩容,快速恢复服务;长期优化:数据分层存储,合理设置过期时间;大规模场景:使用Redis集群,水平扩展;成本敏感:优化数据结构,减少内存占用


第二十题:Redis分布式锁为什么用Lua脚本保证原子性,不用事务?

1. Redis事务的局限性

问题描述:为什么Redis分布式锁使用Lua脚本而不是事务来保证原子性。

Redis事务的问题命令入队:MULTI/EXEC只是将命令放入队列,不立即执行;无回滚机制:Redis事务不支持回滚,即使某个命令失败;无条件判断:无法在事务中根据条件决定是否执行后续命令;竞态条件:在GET和DEL之间,其他客户端可能修改了锁

方案实现描述:Redis事务无法实现分布式锁需要的条件判断和原子性:事务执行机制:命令先入队,后批量执行,无法进行条件判断;竞态条件:多个命令之间存在时间间隔,可能被其他操作干扰;逻辑限制:不支持IF-THEN-ELSE等条件语句

代码实现

// Redis事务的问题:无法保证原子性
redisTemplate.execute((SessionCallback<Object>) operations -> {operations.multi();                    // 1. 开始事务operations.opsForValue().get("lock");  // 2. 获取锁值(入队)operations.opsForValue().del("lock");  // 3. 删除锁(入队)return operations.exec();              // 4. 执行事务(可能失败)
});

2. Lua脚本的原子性优势

Lua脚本的优势原子执行:整个脚本作为一个命令执行,不会被其他命令打断;条件判断:可以在脚本中进行条件判断和逻辑控制;无竞态条件:脚本执行过程中不会被其他客户端干扰;网络优化:减少网络往返次数,提高性能

方案实现描述:Lua脚本在Redis中执行,提供真正的原子性和条件判断:原子性保证:脚本执行过程中不会被其他命令打断;条件逻辑:支持IF-THEN-ELSE等条件判断;复杂操作:可以在一个脚本中执行多个相关操作;性能优化:减少网络往返,提高执行效率

代码实现

// Lua脚本保证原子性:检查并删除锁
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"return redis.call('del', KEYS[1]) else return 0 end";Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList("lock"), "clientId"
);

3. 具体对比分析

事务 vs Lua脚本对比

特性Redis事务Lua脚本
原子性批量执行,非真正原子真正原子性
条件判断不支持支持IF-THEN-ELSE
竞态条件存在不存在
回滚机制不支持支持
性能一般更好

方案实现描述:分布式锁需要条件判断和原子性,Redis事务无法满足需求:锁释放场景:需要先检查锁是否属于自己,再决定是否删除;锁续期场景:需要先验证锁的有效性,再延长过期时间;复杂逻辑:需要根据锁的状态执行不同的操作

代码实现

// 分布式锁释放:事务方式(有问题)
public void releaseLockWithTransaction(String lockKey, String clientId) {redisTemplate.execute((SessionCallback<Object>) operations -> {operations.multi();String lockValue = (String) operations.opsForValue().get(lockKey);if (clientId.equals(lockValue)) {  // ❌ 条件判断无效operations.opsForValue().del(lockKey);}return operations.exec();});
}// 分布式锁释放:Lua脚本方式(正确)
public void releaseLockWithLua(String lockKey, String clientId) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"return redis.call('del', KEYS[1]) else return 0 end";Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(lockKey), clientId);
}

4. 使用场景选择

使用Lua脚本的场景:✅ 分布式锁的获取和释放;✅ 需要条件判断的原子操作;✅ 复杂的业务逻辑;✅ 需要减少网络往返的操作

使用事务的场景:✅ 简单的批量操作;✅ 不需要条件判断的场景;✅ 性能要求不高的场景

方案实现描述:根据业务需求选择合适的原子性保证方式:分布式锁:必须使用Lua脚本,需要条件判断和原子性;批量操作:可以使用事务,提高执行效率;复杂逻辑:使用Lua脚本,支持条件判断和循环;简单操作:使用事务,减少网络开销

代码实现

// 适合用事务:简单批量操作
redisTemplate.execute((SessionCallback<Object>) operations -> {operations.multi();operations.opsForValue().set("key1", "value1");operations.opsForValue().set("key2", "value2");return operations.exec();
});// 适合用Lua脚本:复杂条件判断
String script = "local count = redis.call('get', KEYS[1]) " +"if tonumber(count) < tonumber(ARGV[1]) then " +"    return redis.call('incr', KEYS[1]) " +"else return -1 end";

第二十一题:Redis分布式锁不可用时怎么办?如何设计?

1. 问题分析

问题描述:Redis分布式锁不可用时的处理方案和设计思路。

Redis不可用的场景单点故障:Redis实例宕机;网络分区:网络中断导致无法连接Redis;主从切换:主从切换过程中的短暂不可用;内存满:Redis内存满导致写入失败

影响分析锁获取失败:无法获取新的分布式锁;锁释放失败:无法释放已持有的锁;锁状态丢失:Redis重启后锁信息丢失;业务中断:关键业务无法执行

2. 处理方案

方案一:降级策略
设计思路:Redis不可用时,降级到本地锁或数据库锁
实现要点:优先尝试Redis锁,失败时降级到本地锁,保证业务连续性
适用场景:对一致性要求不高,可以接受本地锁限制的场景

方案二:多Redis实例(红锁)
设计思路:使用多个独立的Redis实例,多数投票机制
实现要点:在多个Redis实例上获取锁,达到多数成功才算获取成功
适用场景:对可靠性要求高,可以接受性能损失的场景

方案三:混合锁策略
设计思路:Redis + ZooKeeper/数据库的双重保障
实现要点:先获取Redis锁,再获取ZooKeeper锁,双重保障
适用场景:对可靠性和性能都有要求的场景

方案四:业务层保障
设计思路:通过业务逻辑设计来降低对分布式锁的依赖
实现要点:使用幂等性、乐观锁、重试机制等业务层保障
适用场景:可以通过业务设计避免分布式锁的场景

3. 核心代码实现

降级策略实现

public boolean tryLock(String lockKey, long timeout, TimeUnit unit) {try {return tryRedisLock(lockKey, timeout, unit); // 1. 优先尝试Redis锁} catch (Exception e) {return tryLocalLock(timeout, unit); // 2. 降级到本地锁}
}

红锁实现

public boolean tryLock(String lockKey, long waitTime, long leaseTime) {int successCount = 0;for (RedissonClient client : redissonClients) { // 1. 多个Redis实例if (client.getLock(lockKey).tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS)) {successCount++; // 2. 统计成功数}}return successCount >= majorityThreshold; // 3. 多数投票
}

混合锁实现

public boolean tryLock(String lockKey, long timeout, TimeUnit unit) {if (tryRedisLock(lockKey, timeout, unit)) { // 1. Redis锁if (tryZooKeeperLock(lockKey, timeout, unit)) { // 2. ZooKeeper锁return true; // 3. 双重锁成功} else {releaseRedisLock(lockKey); // 4. 释放Redis锁}}return false;
}

4. 监控和告警

监控方案健康检查:定期检查Redis连接状态;连接数监控:监控Redis连接数是否过高;内存监控:监控Redis内存使用率;告警机制:异常时及时发送告警

实现要点:每30秒检查一次Redis健康状态,连接数超过1000时告警,内存使用率超过80%时告警

5. 最佳实践

设计原则多层防护:Redis + 本地锁 + 业务层保障;快速降级:Redis不可用时快速切换到备用方案;监控告警:实时监控Redis状态,及时发现问题;业务设计:通过业务逻辑减少对分布式锁的依赖

选择建议高性能场景:使用降级策略,Redis不可用时降级到本地锁;高可靠性场景:使用红锁或多Redis实例;平衡场景:使用混合锁策略,Redis + ZooKeeper;业务优化:通过幂等性和乐观锁减少锁依赖

第二十二题:Redis为什么比MySQL快?性能优势分析

1. 问题分析

问题描述:Redis相比MySQL的性能优势原因分析。

性能对比Redis:内存存储,微秒级响应,单线程模型,简单数据结构;MySQL:磁盘存储,毫秒级响应,多线程模型,复杂关系型结构

核心差异存储介质:内存 vs 磁盘;数据模型:键值对 vs 关系型;并发模型:单线程 vs 多线程;数据结构:简单 vs 复杂

2. 性能优势分析

方案一:存储介质优势
设计思路:内存访问速度远快于磁盘访问
实现要点:内存读写速度是磁盘的10万倍,无需磁盘I/O,数据直接在内存中操作
性能提升:内存访问延迟0.1微秒,磁盘访问延迟10毫秒,性能提升10万倍

方案二:数据模型简化
设计思路:键值对模型比关系型模型更简单高效
实现要点:无需复杂的表结构、索引、JOIN操作,直接通过key访问value
性能提升:避免SQL解析、查询优化、索引查找等复杂操作,访问路径更短

方案三:单线程模型
设计思路:单线程避免线程切换和锁竞争开销
实现要点:无需线程同步、锁机制、上下文切换,CPU利用率更高
性能提升:避免多线程开销,减少系统调用,提高CPU缓存命中率

方案四:数据结构优化
设计思路:专门优化的数据结构,针对特定场景设计
实现要点:String、Hash、List、Set、ZSet等数据结构,针对不同场景优化
性能提升:时间复杂度O(1)的查找,避免复杂的B+树遍历

3. 技术原理分析

内存管理连续内存分配:数据存储在连续内存中,访问效率高;内存池技术:预分配内存池,减少内存分配开销;内存压缩:数据压缩存储,提高内存利用率

网络优化二进制协议:RESP协议比HTTP协议更高效;连接复用:长连接减少连接建立开销;批量操作:Pipeline批量执行命令,减少网络往返

算法优化哈希表:O(1)时间复杂度的查找;跳表:有序集合的高效实现;压缩列表:小数据的高效存储;快速列表:链表的优化实现

系统调用优化零拷贝:避免数据在用户态和内核态之间拷贝;事件驱动:I/O多路复用,单线程处理多个连接;异步处理:非阻塞I/O,提高并发能力

4. 性能瓶颈对比

Redis瓶颈内存限制:受限于可用内存大小;单线程限制:CPU密集型操作可能成为瓶颈;持久化开销:RDB和AOF持久化影响性能

MySQL瓶颈磁盘I/O:随机读写性能差;锁竞争:行锁、表锁影响并发性能;查询复杂度:复杂SQL查询性能差;索引维护:索引更新开销大

性能对比总结读取性能:Redis比MySQL快100-1000倍;写入性能:Redis比MySQL快10-100倍;并发性能:Redis单线程但无锁竞争,MySQL多线程但有锁开销;内存使用:Redis内存使用更高效,MySQL需要额外内存缓存

5. 最佳实践

设计原则选择合适的存储:热点数据用Redis,冷数据用MySQL;数据分层:根据访问频率分层存储;缓存策略:合理使用缓存减少数据库压力;性能监控:监控Redis和MySQL性能指标

选择建议高并发读取:使用Redis缓存热点数据;复杂查询:使用MySQL处理复杂业务逻辑;实时性要求高:使用Redis存储实时数据;数据一致性要求高:使用MySQL保证ACID特性

总结:Redis比MySQL快的主要原因是内存存储、数据模型简化、单线程模型和数据结构优化,但两者各有优势,应该根据具体场景选择合适的存储方案。

第二十三题:Redis会阻塞吗?如果阻塞了怎么排查和处理?

1. 问题分析

问题描述:Redis阻塞的原因分析、排查方法和处理方案。

Redis阻塞的场景慢查询:执行时间超过配置阈值的命令;大key操作:操作大key导致长时间阻塞;内存满:内存不足触发淘汰策略,影响性能;持久化阻塞:RDB或AOF持久化过程中的阻塞;网络问题:网络延迟或连接数过多

影响分析响应延迟:客户端请求响应时间增加;连接超时:客户端连接超时,请求失败;服务不可用:严重阻塞可能导致服务完全不可用;数据丢失:阻塞期间可能丢失数据或连接

2. 阻塞原因分析

方案一:慢查询阻塞
设计思路:识别和优化执行时间过长的Redis命令
实现要点:监控命令执行时间,设置slowlog阈值,分析慢查询日志,优化查询语句
排查方法:使用SLOWLOG命令查看慢查询,分析执行时间长的命令,优化业务逻辑

方案二:大key操作阻塞
设计思路:识别和处理占用内存过大的key
实现要点:扫描Redis中的大key,分析大key的访问模式,优化数据结构或拆分大key
排查方法:使用MEMORY USAGE命令分析key内存占用,使用SCAN命令扫描大key

方案三:内存压力阻塞
设计思路:解决内存不足导致的性能问题
实现要点:监控内存使用率,优化淘汰策略,清理无用数据,扩容内存
排查方法:使用INFO memory命令查看内存使用情况,分析内存增长趋势

方案四:持久化阻塞
设计思路:优化RDB和AOF持久化过程,减少阻塞时间
实现要点:调整持久化配置,使用BGSAVE避免阻塞,优化AOF重写策略
排查方法:监控持久化过程,分析fork子进程的性能影响

3. 排查方法

监控工具Redis-cli:使用INFO、SLOWLOG、MEMORY等命令监控;Redis监控工具:使用RedisInsight、Redis Commander等图形化工具;系统监控:使用top、htop、iostat等系统工具监控资源使用

关键命令INFO命令:查看Redis运行状态、内存使用、连接数等信息;SLOWLOG命令:查看慢查询日志,分析性能瓶颈;MEMORY命令:分析内存使用情况,识别大key;CLIENT LIST命令:查看客户端连接状态

日志分析Redis日志:分析Redis运行日志,查找错误和警告信息;系统日志:分析系统日志,查找资源不足或网络问题;应用日志:分析应用程序日志,查找Redis相关错误

4. 处理方案

紧急处理重启Redis:严重阻塞时重启Redis服务,快速恢复服务;清理连接:使用CLIENT KILL命令清理异常连接;调整配置:临时调整maxmemory、timeout等配置参数

优化处理慢查询优化:优化业务逻辑,避免复杂查询;大key处理:拆分大key,优化数据结构;内存优化:清理无用数据,调整淘汰策略;持久化优化:调整持久化配置,减少阻塞时间

预防措施监控告警:建立完善的监控告警机制,及时发现阻塞问题;性能测试:定期进行性能测试,识别潜在的性能瓶颈;容量规划:合理规划Redis容量,避免资源不足

5. 最佳实践

设计原则预防为主:通过监控和优化避免阻塞问题;快速响应:阻塞时快速定位和解决问题;持续优化:持续监控和优化Redis性能;架构设计:采用合理的架构设计,提高系统稳定性

选择建议慢查询阻塞:优化业务逻辑,避免复杂查询;大key阻塞:拆分大key,优化数据结构;内存阻塞:扩容内存,优化淘汰策略;持久化阻塞:调整持久化配置,使用异步持久化

总结:Redis阻塞问题需要从慢查询、大key、内存压力、持久化等多个维度进行分析和处理,通过完善的监控、快速的排查和有效的优化,确保Redis服务的稳定运行。


第二十四题:什么是Redis大Key?会出现什么问题?应该如何避免?

1. 问题分析

问题描述:Redis大Key的定义、问题分析和避免方案。

大Key定义内存占用大:单个key占用的内存超过1MB或10MB;元素数量多:List、Set、ZSet等集合类型元素数量超过10000个;字符串长度长:String类型value长度超过100KB;Hash字段多:Hash类型字段数量超过1000个

大Key产生原因业务设计不当:将大量相关数据存储在一个key中;数据积累:业务数据不断积累,没有及时清理;批量操作:批量写入数据时没有合理分片;缓存策略:缓存策略设计不合理,导致数据集中

2. 问题分析

方案一:性能问题
设计思路:大Key操作会导致Redis性能下降
问题表现:操作大Key时响应时间长,影响其他请求处理;网络传输时间长,占用带宽资源;内存分配时间长,影响内存管理效率
影响范围:影响Redis整体性能,可能导致服务响应缓慢

方案二:内存问题
设计思路:大Key占用大量内存,影响系统稳定性
问题表现:内存使用率过高,可能触发淘汰策略;内存分配失败,导致写入失败;内存碎片增加,降低内存利用率
影响范围:可能导致Redis内存不足,影响数据存储

方案三:阻塞问题
设计思路:大Key操作会阻塞Redis主线程
问题表现:删除大Key时阻塞时间长,影响其他操作;序列化/反序列化大Key耗时,影响响应时间;网络传输阻塞,影响并发处理
影响范围:严重时可能导致Redis服务不可用

方案四:数据一致性问题
设计思路:大Key操作失败可能导致数据不一致
问题表现:大Key操作超时,可能导致部分数据更新失败;网络中断时大Key传输失败,数据丢失;内存不足时大Key写入失败,数据不完整
影响范围:可能导致业务数据不一致,影响系统可靠性

3. 检测方法

监控工具Redis-cli:使用MEMORY USAGE命令检测单个key内存占用;Redis监控工具:使用RedisInsight等工具可视化分析;自定义脚本:编写脚本定期扫描大Key

检测命令MEMORY USAGE:检测指定key的内存占用;SCAN命令:扫描所有key,分析内存使用;INFO memory:查看整体内存使用情况;SLOWLOG:分析慢查询,识别大Key操作

检测策略定期扫描:定期扫描Redis中的大Key,建立大Key清单;实时监控:监控key操作耗时,识别潜在大Key;阈值告警:设置大Key阈值,超过阈值时告警

4. 避免方案

方案一:数据分片
设计思路:将大Key拆分成多个小Key
实现要点:根据业务逻辑将数据分片,使用hash算法分散存储,保持数据访问的均匀性
适用场景:数据量大且可以按业务逻辑分片的场景

方案二:数据压缩
设计思路:压缩存储数据,减少内存占用
实现要点:使用压缩算法压缩数据,在存储和读取时进行压缩和解压缩
适用场景:数据重复性高,压缩效果好的场景

方案三:数据分层
设计思路:根据数据访问频率分层存储
实现要点:热点数据存Redis,冷数据存数据库,实现数据自动分层
适用场景:数据有明显的冷热区分,访问模式相对固定的场景

方案四:数据结构优化
设计思路:选择合适的数据结构存储数据
实现要点:根据业务需求选择最优数据结构,避免使用不必要的大集合
适用场景:数据结构选择不当,可以优化的场景

5. 处理方案

紧急处理删除大Key:使用UNLINK命令异步删除大Key;数据迁移:将大Key数据迁移到其他存储;服务重启:严重时重启Redis服务

优化处理数据分片:将大Key拆分成多个小Key;数据压缩:压缩存储数据;数据分层:实现数据分层存储;结构优化:优化数据结构选择

预防措施设计规范:建立key设计规范,避免大Key产生;监控告警:建立大Key监控告警机制;定期清理:定期清理无用数据;容量规划:合理规划Redis容量

6. 最佳实践

设计原则预防为主:通过设计规范避免大Key产生;快速检测:建立快速检测机制,及时发现大Key;有效处理:采用合适的处理方法,解决大Key问题;持续优化:持续优化数据结构,提高系统性能

选择建议数据分片:数据量大且可分片时使用;数据压缩:数据重复性高时使用;数据分层:数据有冷热区分时使用;结构优化:数据结构选择不当时使用

总结:Redis大Key问题需要从检测、分析、处理、预防等多个维度进行管理,通过合理的设计和有效的处理,确保Redis系统的高性能和稳定性。


第二十五题:Redis如何实现Session共享?

1. Session共享问题

问题描述:传统Session在分布式环境下的问题,如何使用Redis实现Session共享。

传统Session的问题

  1. 单机Session问题:用户请求只能访问固定服务器
  2. 集群环境问题:多台服务器Session不共享
  3. 扩展性问题:难以水平扩展
  4. 负载均衡问题:无法实现真正的负载均衡

Redis Session优势

  1. 集中存储:所有Session数据存储在Redis中
  2. 共享访问:任何服务器都可以访问Session数据
  3. 高可用:Redis集群保证高可用性
  4. 性能优异:内存存储,访问速度快

Redis Session共享的优势

  • 集中存储:所有服务器共享Session数据
  • 高性能:基于内存,读写速度快
  • 高可用:支持主从复制和集群模式
  • 灵活配置:可设置过期时间,支持多种数据结构

2. Spring Session实现

Spring Session配置自动配置:使用@EnableRedisHttpSession注解自动配置;连接工厂:配置Redis连接工厂;过期时间:设置Session过期时间

@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
public class RedisSessionConfig {@Beanpublic LettuceConnectionFactory connectionFactory() {return new LettuceConnectionFactory(new RedisStandaloneConfiguration("127.0.0.1", 6379));}
}

Session使用示例登录设置:设置用户ID和用户名到Session;获取信息:从Session获取用户信息;退出清理:清除Session数据

@GetMapping("/login")
public String login(HttpSession session) {session.setAttribute("userId", "12345");session.setAttribute("username", "张三");return "登录成功";
}@GetMapping("/userinfo")
public Map<String, Object> getUserInfo(HttpSession session) {Map<String, Object> userInfo = new HashMap<>();userInfo.put("userId", session.getAttribute("userId"));userInfo.put("username", session.getAttribute("username"));return userInfo;
}

3. 手动实现Session共享

Session管理器属性设置:使用Hash结构存储Session属性,设置过期时间;属性获取:从Hash中获取Session属性;Session失效:删除整个Session数据

// 1. 设置Session属性
redisTemplate.opsForHash().put(sessionKey, key, value);
redisTemplate.expire(sessionKey, Duration.ofSeconds(SESSION_TIMEOUT));// 2. 获取Session属性
return redisTemplate.opsForHash().get(sessionKey, key);// 3. 删除Session
redisTemplate.delete(sessionKey);

Session拦截器请求拦截:在请求处理前获取Session ID并刷新过期时间;Cookie解析:从Cookie中获取JSESSIONID;Session管理:将Session ID设置到请求属性中

// 1. 获取Session ID并刷新
String sessionId = getSessionId(request);
if (sessionId != null) {sessionManager.refreshSession(sessionId);request.setAttribute("sessionId", sessionId);
}// 2. 从Cookie获取JSESSIONID
for (Cookie cookie : cookies) {if ("JSESSIONID".equals(cookie.getName())) {return cookie.getValue();}
}

4. Session数据结构设计

Session数据结构Hash结构:使用Hash存储Session,Key为session:sessionId,Field为属性名,Value为属性值;数据示例:session:abc123包含userId和username等属性

// Session数据结构示例
// session:abc123 -> {
//   "userId": "12345",
//   "username": "张三"
// }

Session序列化JSON序列化:使用GenericJackson2JsonRedisSerializer进行JSON序列化;连接工厂:配置Redis连接工厂

// 配置Redis模板和序列化器
template.setConnectionFactory(connectionFactory());
template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());

5. Session安全处理

Session安全策略安全Session ID:使用SecureRandom生成32字节随机Session ID;IP验证:验证Session IP地址防止会话劫持;安全存储:存储客户端IP用于验证

// 1. 生成安全Session ID
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[32];
random.nextBytes(bytes);
return Base64.getEncoder().encodeToString(bytes);// 2. 验证Session IP
String storedIp = sessionManager.getSessionAttribute(sessionId, "ip");
String currentIp = getClientIp(request);
return currentIp.equals(storedIp);

Session监控活跃Session监控:每分钟统计活跃Session数量;过期监控:监控Session TTL,即将过期时告警

// 1. 监控活跃Session数量
Set<String> sessionKeys = redisTemplate.keys("session:*");
int activeSessionCount = sessionKeys.size();
sendMetrics("session.active.count", activeSessionCount);// 2. 监控Session过期
Long ttl = redisTemplate.getExpire(key);
if (ttl != null && ttl < 300) {log.warn("Session即将过期: {}, TTL: {}", key, ttl);
}

6. 性能优化

Session性能优化连接池配置:设置最大连接数20,最大空闲连接10,最小空闲连接5;批量操作:使用multiGet批量获取属性,使用putAll批量设置属性

// 1. 连接池配置
poolConfig.setMaxTotal(20);
poolConfig.setMaxIdle(10);
poolConfig.setMinIdle(5);// 2. 批量获取Session属性
List<Object> attributes = redisTemplate.opsForHash().multiGet("session:abc123", Arrays.asList("userId", "username"));// 3. 批量设置Session属性
Map<String, Object> sessionData = new HashMap<>();
sessionData.put("userId", "12345");
sessionData.put("username", "张三");
redisTemplate.opsForHash().putAll("session:abc123", sessionData);

7. 最佳实践

Session最佳实践设计原则:只存储必要的用户信息,避免存储大量数据;安全考虑:使用HTTPS传输,设置安全的Cookie属性;性能优化:使用连接池,批量操作,本地缓存;监控告警:监控Session数量,监控Session过期

配置示例完整配置:启用Redis HttpSession,设置过期时间1800秒;连接工厂:配置连接池参数;Cookie序列化:设置安全的Cookie属性

// 1. 启用Redis HttpSession
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)// 2. 配置连接池
poolConfig.setMaxTotal(20);
poolConfig.setMaxIdle(10);
poolConfig.setMinIdle(5);// 3. 配置安全Cookie
serializer.setCookieName("JSESSIONID");
serializer.setUseHttpOnlyCookie(true);
serializer.setUseSecureCookie(true);

关键知识点总结

  1. 数据类型:String、Hash、List、Set、ZSet等基本类型,BitMap、HyperLogLog等高级类型
  2. 持久化:RDB全量备份,AOF增量备份,生产环境建议同时开启
  3. 高可用:主从复制、哨兵模式、集群模式,根据需求选择
  4. 缓存问题:穿透、击穿、雪崩的解决方案
  5. 分布式锁:Redis适合高性能场景,ZooKeeper适合强一致性场景
  6. 性能优化:连接池、批量操作、Pipeline、合适的数据类型
  7. 事务和Lua:Redis事务的局限性,Lua脚本的优势
  8. 缓存技术对比:Redis成为主流的原因,ZooKeeper不适合做缓存
  9. 淘汰策略:8种策略,根据场景选择合适的策略
  10. Session共享:Spring Session实现,支持集群部署
  11. Lua脚本 vs 事务:分布式锁必须用Lua脚本保证原子性,事务无法满足条件判断需求
  12. 分布式锁可靠性:Redis不可用时的降级策略、红锁、混合锁、业务层保障
  13. 内存管理:内存满时的处理方案、紧急扩容、数据清理、分层存储、集群扩容
  14. 性能优势:Redis比MySQL快的原因、存储介质、数据模型、并发模型、数据结构优化
  15. 阻塞排查:Redis阻塞原因分析、排查方法、处理方案、监控工具
  16. 大Key管理:大Key定义、问题分析、检测方法、避免方案、处理策略
  17. 最佳实践:命名规范、过期时间、错误处理、监控告警
http://www.dtcms.com/a/407633.html

相关文章:

  • 【LeetCode_21】合并两个有序链表
  • 大庆建设工程交易中心网站提供网站建设管理
  • VSCode编译器测试yolo环境配置
  • 网站建设类国外企业招聘网站
  • tp做网站房地产培训网站建设
  • php网站做分享到朋友圈网站设置搜索框是什么知识点
  • 免费psd图片素材网站ui设计培训大概多少钱
  • 《C++程序设计》笔记p6
  • 安徽同济建设集团网站公司搭建网站模板
  • 【读书笔记】《大国大成》
  • C++笔记(基础)引用 inline内联函数
  • 焦作网站建设公司哪家好dz整站网站建设
  • 建站快车品牌北京网站建设兴田德润放心
  • cuda编程笔记(22)-- NVIDIA Triton Inference Server
  • 怎么知道网站是否被百度收录软件开发工具有哪些
  • 伦理治理进入程序化攻坚阶段
  • 经典网站赏析永久使用免费虚拟主机
  • 【跟我学YOLO】YOLO26:YOLO Vision 2025 最新发布的端到端视觉 AI 新突破
  • 什么网站百度收录好最新国家大事时政新闻
  • 怎么做网站手机版辅助设计软件有哪些
  • Model Context Protocol (MCP)详解与Spring Boot集成实战
  • 珠海h5模板建站网站建设考试卷a卷
  • 豆包Seedream 4.0创意玩法大赏:开启AI绘画新纪元
  • 算法基础篇(5)前缀和
  • 手机网站宽度多少合适网站开发行业代码
  • 了解一下Ubuntu上搭建的ROS环境
  • 博客网站搭建网站建设需要资质么
  • 泰安市景区建设网站阿里巴巴企业网站怎么做
  • 网站采用什么字体wordpress get_pages()
  • 禁用内核模块,是否需要执行脚本 $ sudo update-initramfs -u $ sudo update-grub ?