Redis面试问题详解2
Redis面试问题详解2
一、分布式锁
分布式锁主要用于解决多服务器之间的并发问题。Redis通过SETNX
命令实现分布式锁,确保同一时间只有一个线程可以获取锁。
1. 基本实现
获取锁
使用SETNX
命令设置锁,并设置一个过期时间,避免死锁。
String lockKey = "lock:" + key;
if (redis.setnx(lockKey, "1", 30) == 1) { // 尝试获取锁,超时30秒// 执行业务逻辑
} else {// 等待锁释放Thread.sleep(100);
}
释放锁
使用DEL
命令释放锁。
redis.del(lockKey);
2. watchdog
(看门狗)机制
watchdog
是Redisson提供的一种自动续期机制,用于防止锁因为超时而被误释放。它通过后台线程定期向Redis发送续期请求。
1、自动续期:
当一个线程获取锁后,watchdog
会启动一个后台线程,定期向Redis发送续期请求。
2、续期频率:
续期频率通常设置为锁超时时间的一半。例如,如果锁的超时时间是30秒,watchdog
会每15秒发送一次续期请求。
3、释放锁:
当业务逻辑执行完成后,watchdog
会自动停止续期,并释放锁。
代码示例
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;public class DistributedLockExample {public static void main(String[] args) {// 配置RedissonConfig config = new Config();config.useSingleServer().setAddress("redis://127.0.0.1:6379");RedissonClient redisson = Redisson.create(config);// 获取锁RLock lock = redisson.getLock("lockKey");try {// 获取锁,自动续期lock.lock();// 执行业务逻辑System.out.println("执行业务逻辑...");Thread.sleep(10000); // 模拟长时间的业务逻辑} finally {// 释放锁lock.unlock();}// 关闭Redissonredisson.shutdown();}
}
实际应用场景
假设我们有一个库存扣减的场景,需要确保同一时间只有一个线程可以扣减库存:
public void deductStock(String productId, int quantity) {String lockKey = "lock:stock:" + productId;RLock lock = redisson.getLock(lockKey);try {lock.lock();// 查询库存int currentStock = database.getStock(productId);if (currentStock >= quantity) {// 扣减库存database.updateStock(productId, currentStock - quantity);System.out.println("库存扣减成功");} else {System.out.println("库存不足");}} finally {lock.unlock();}
}
3. 可重入性
分布式锁支持同一线程的重入,避免死锁。Redisson默认支持可重入锁,允许多次获取和释放同一把锁。
public void nestedLockExample() {String lockKey = "lock:nested";RLock lock = redisson.getLock(lockKey);try {lock.lock();System.out.println("第一次获取锁");// 再次获取锁lock.lock();System.out.println("第二次获取锁");} finally {lock.unlock();System.out.println("第一次释放锁");lock.unlock();System.out.println("第二次释放锁");}
}
二、主从复制
主从复制用于解决Redis的高并发问题,通过主节点负责写操作,从节点负责读操作,提高系统的读写能力。
1. 全量同步
全量同步是指从节点一次性同步主节点的所有数据。
操作步骤
- 从节点向主节点发送
REPLCONF
和offset
。 - 主节点通过
BGSAVE
生成RDB文件并发送给从节点。 - 在RDB生成期间,主节点将操作记录到缓存中。
- 主节点将缓存中的操作发送给从节点执行。
2. 增量同步
增量同步是指主节点将未同步的操作发送给从节点。
操作步骤
- 从节点向主节点发送
REPLCONF
和offset
。 - 主节点根据
offset
判断需要同步的内容。 - 主节点将未同步的操作发送给从节点执行。
3. 代码示例
# 主节点配置
slaveof <主节点IP> <主节点端口>
4. 策略对比
策略 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
全量同步 | 数据一致性高 | 初始同步时间长 | 第一次同步或数据不一致时 |
增量同步 | 同步速度快 | 依赖之前的同步状态 | 数据部分不一致时 |
三、哨兵模式
哨兵模式用于解决Redis的高可用问题。当主节点出现故障时,哨兵会选举一个从节点升级为主节点。
1. 工作原理
- 主观下线:单个哨兵判断主节点下线。
- 客观下线:多个哨兵(超过半数)确认主节点下线。
- 选举新主节点:哨兵通过投票机制选举新的主节点。
2. 代码示例
# 配置哨兵
sentinel monitor <主节点名称> <主节点IP> <主节点端口> <哨兵数量>
sentinel down-after-milliseconds <主节点名称> 5000
sentinel parallel-syncs <主节点名称> 1
sentinel failover-timeout <主节点名称> 60000
四、分片集群
分片集群用于解决高并发写操作和海量数据存储问题。通过将数据分布到多个主节点,提高系统的读写能力。
1. Hash槽
Redis集群有16384个槽,每个Key通过哈希算法映射到一个槽。这种设计确保数据均匀分布,提高集群的扩展性。
2. 分片存储
每个主节点负责一部分槽,数据通过槽分布到不同的节点。这种分片机制允许集群处理更大的数据量和更高的并发请求。
3. 主从监督
主节点通过PING
相互监督,确保高可用性。如果某个主节点出现故障,其他节点可以快速检测并进行故障转移。
五、Redis缓存为什么速度这么快
- 纯内存操作:所有数据存储在内存中,读写速度快。
- 单线程模型:避免了多线程的上下文切换开销。
- IO多路复用:通过
epoll
机制高效处理多个连接。 - 多线程优化:Redis 6.0引入多线程处理命令,提高性能。
1. IO多路复用
IO多路复用通过一个线程监控多个socket
,当某个socket
有请求时,线程会立即处理该请求。Redis使用epoll
模式实现IO多路复用,显著提高了性能。
2. 多线程优化
Redis 6.0引入多线程处理命令,命令的接受和转发改为多线程方式,而核心数据结构操作仍保持单线程。
3. 代码示例
// Redis配置
redis {threads 8io-threads 4io-threads-do-reads yes
}
4. 策略对比
策略 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
单线程模型 | 简化并发控制 | 无法充分利用多核CPU | 通用场景 |
IO多路复用 | 高效处理多个连接 | 实现复杂 | 高并发场景 |
多线程优化 | 提高命令处理速度 | 仅适用于部分命令 | Redis 6.0及以上版本 |
还有部分内容参考之前写的Redis面试问题缓存相关详解-CSDN博客