一、主从节点失效问题及解决方案
1. 主从失效问题本质
ClientA Master Slave ClientB SET lock_key clientA_id EX 30 (加锁成功) 同步失败(网络问题) Master宕机 选举为新Master SET lock_key clientB_id EX 30 (加锁成功) ClientA Master Slave ClientB
2. 解决方案对比
方案 | 原理 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
min-replicas-to-write | 要求写入N个从节点才算成功 | 保证数据同步性 | 增加写入延迟 | 对一致性要求高的场景 |
Redlock算法 | 需要半数以上节点写入成功 | 更高的可靠性 | 性能损耗大 | 极端一致性要求的场景 |
混合持久化 | AOF+RDB混合持久化策略 | 平衡性能与可靠性 | 仍有数据丢失窗口 | 通用场景 |
二、分布式锁优化方案
1. 分段锁设计
比如商品a的id为product_1001_bike,库存有1000个,普通思路是对这个key加锁,key是product_1001_bike,每次请求只能有一个请求同时操作库存。
可以拆分key为product_1001_bike_1、product_1001_bike_2...product_1001_bike_10,每个key的库存是100,每个key就是一把锁,那么这个时候高并发下,可以最多10个请求同时操作库存。
每次选哪个锁呢?每个key放到一个数组里,随机选,或者轮询
java
// 传统锁
RLock lock = redisson.getLock("product_1001");
// 分段锁优化(10个分段)
String[] segmentKeys = {
"product_1001_seg1",
"product_1001_seg2",
...
"product_1001_seg10"
};
// 随机选择分段
Random rand = new Random();
RLock segmentLock = redisson.getLock(segmentKeys[rand.nextInt(10)]);
2. 读写锁优化
java
// 写操作(独占锁)
RReadWriteLock rwLock = redisson.getReadWriteLock("product_lock");
rwLock.writeLock().lock();
try {
// 更新操作
} finally {
rwLock.writeLock().unlock();
}
// 读操作(共享锁)
rwLock.readLock().lock();
try {
// 查询操作
} finally {
rwLock.readLock().unlock();
}
三、缓存问题综合解决方案
1. 缓存问题矩阵
问题类型 | 现象 | 解决方案 | 补充策略 |
---|---|---|---|
缓存击穿 | 热点key失效瞬间高并发,大量请求打到数据库 | 互斥锁重建 | 永不过期+后台更新 |
缓存穿透 | 透(与缓存击穿作区分),查询不存在的数据,即缓存和数据库都没有, 重复请求可能还会消耗缓存和数据库性能 | 布隆过滤器 缓存空对象 | 空对象短过期时间 |
缓存雪崩 | 大量key同时失效,大量请求打到数据库和后端, 逐渐导致数据库崩溃,后端服务崩溃,甚至整个服务不可用 | 随机过期时间 多级缓存 | 熔断降级机制 |
2. 热点key重建优化流程
是 否 获取成功 获取失败 请求到达 缓存是否存在? 返回缓存数据 尝试获取分布式锁 查询DB并重建缓存 短暂等待后重试 释放锁
3. 热数据,读延期
每天都有访问的key,就是热数据,可以做读延期:
java
// 有数据直接延期 防止key过期访问数据库
String productStr = redisUtil.get(productCacheKey);
if (!StringUtils.isEmpty(productStr)) {
product = JSON.parse0bject(productStr, Product.class);
//读延期
redisUtil.expire(productCacheKey, PRODUCT_CACHE_TIMEOUT, TimeUnit.SECONDS);
return product;
}
4. 多级缓存架构
客户端请求
↓
CDN缓存 → 命中则返回
↓
Nginx本地缓存 → 命中则返回
↓
Redis集群缓存 → 命中则返回
↓
DB查询 + 回填缓存
四、开发规范与性能优化
1. Key设计规范
- 命名规则 :
业务:子业务:ID
(如trade:order:123
) - 长度控制:不超过44字节(Redis优化阈值)
- 特殊字符:禁止包含空格、换行符等
2. BigKey规避策略
数据类型 | 危险阈值 | 优化方案 |
---|---|---|
String | >10KB | 压缩/分片 |
Hash/Set | >1000元素 | 拆分为多个key |
List/ZSet | >5000元素 | 分页存储 |
3. 缓存双写一致性方案对比
方案 | 一致性 | 复杂度 | 性能影响 |
---|---|---|---|
分布式锁 | 强一致 | 高 | 较大 |
延迟双删 | 最终一致 | 中 | 中等 |
监听binlog | 最终一致 | 高 | 小 |
设置过期时间 | 弱一致 | 低 | 无 |
五、高级场景解决方案
1. 突发流量处理
java
// 使用Redisson的tryLock优化热点重建
RLock hotLock = redisson.getLock("hot:"+productId);
if (hotLock.tryLock(0, 30, TimeUnit.SECONDS)) {
try {
// 双重检查
String cache = redis.get(productKey);
if (cache == null) {
Product p = db.get(productId);
redis.setex(productKey, 3600, p);
}
} finally {
hotLock.unlock();
}
} else {
// 快速失败或降级处理
return fallbackProduct;
}
2. 多级缓存实现示例
java
public Product getProduct(Long id) {
// 1. 检查本地缓存
Product product = localCache.get(id);
if (product != null) return product;
// 2. 检查Redis缓存
String redisKey = "product:" + id;
product = redisTemplate.opsForValue().get(redisKey);
if (product != null) {
localCache.put(id, product); // 回填本地缓存
return product;
}
// 3. 数据库查询
return getProductFromDBWithLock(id);
}
六、监控与应急方案
1. 关键监控指标
- 缓存命中率 :
keyspace_hits/(keyspace_hits+keyspace_misses)
- 大Key扫描 :定期执行
redis-cli --bigkeys
- 慢查询 :配置
slowlog-log-slower-than 10ms
2. 应急预案
- 热点Key:提前预热+本地缓存
- 缓存雪崩:差异化过期时间+熔断降级
- 集群故障:手动切换+只读模式