一、Redis数据类型 1.1 五种基本数据类型 类型 命令 使用场景 String SET/GET 简单缓存、计数器、分布式锁 Hash HSET/HGET 对象缓存、购物车 List LPUSH/LPOP 队列、消息队列、粉丝列表 Set SADD/SMEMBERS 标签、抽奖、去重 Sorted Set ZADD/ZRANGE 排行榜、延迟队列
1.2 String详解 // 基本操作 redis. set ( "key" , "value" ) ; String value= redis. get ( "key" ) ; // 计数器 redis. incr ( "view:article:1" ) ; // 原子递增 redis. decr ( "stock:product:100" ) ; // 原子递减 // 设置过期时间 redis. setex ( "captcha:13800138000" , 300 , "1234" ) ; // 5分钟过期 // 分布式锁 redis. setnx ( "lock:order:" + orderId, "1" ) ; redis. expire ( "lock:order:" + orderId, 30 ) ; // 30秒超时 1.3 Hash使用场景 // 缓存用户对象 redis. hset ( "user:1001" , "name" , "张三" ) ; redis. hset ( "user:1001" , "age" , "25" ) ; redis. hset ( "user:1001" , "city" , "北京" ) ; // 获取全部字段 Map < String , String > user= redis. hgetAll ( "user:1001" ) ; // 购物车实现 redis. hincrby ( "cart:1001:product:888" , 1 ) ; // 商品数量+1 redis. hincrby ( "cart:1001:product:888" , - 1 ) ; // 商品数量-1 二、Redis缓存策略 2.1 缓存模式 Cache Aside(旁路缓存)最常用 // 读操作 public User getUserById ( Long id) { String cacheKey= "user:" + id; User user= ( User ) redis. get ( cacheKey) ; if ( user!= null ) { return user; // 缓存命中 } // 缓存未命中,查数据库 user= userDAO. findById ( id) ; // 写入缓存,设置过期时间 redis. setex ( cacheKey, 3600 , user) ; // 1小时过期 return user; } // 写操作 public void updateUser ( User user) { // 1. 先更新数据库 userDAO. update ( user) ; // 2. 删除缓存(不是更新!) redis. del ( "user:" + user. getId ( ) ) ; } 为什么删除而不是更新? 线程A更新数据 → 线程B查询缓存 → 缓存未命中查数据库 → 数据库返回旧值 → 写入缓存 线程A更新数据 → 删除缓存 结果:缓存中是旧数据! 线程A更新数据 → 删除缓存 线程B查询缓存 → 缓存未命中 → 查数据库 → 数据库已是新值 → 写入缓存 结果:缓存中是正确数据2.2 缓存常见问题 问题 解决方案 缓存穿透 布隆过滤器 / 空值缓存 缓存击穿 互斥锁 / 热点数据永不过期 缓存雪崩 随机过期时间 / 多级缓存
缓存穿透:布隆过滤器 // 使用布隆过滤器判断数据是否存在 BloomFilter < String > bloomFilter= BloomFilter . create ( . . . ) ; // 查询前先判断 public User getUserById ( Long id) { if ( ! bloomFilter. mightContain ( "user:" + id) ) { return null ; // 一定不存在 } // 继续查缓存和数据库... } 缓存击穿:互斥锁 public User getUserById ( Long id) { String cacheKey= "user:" + id; User user= redis. get ( cacheKey) ; if ( user== null ) { String lockKey= "lock:user:" + id; String lockValue= redis. setnx ( lockKey, "1" ) ; if ( lockValue) { user= userDAO. findById ( id) ; redis. setex ( cacheKey, 3600 , user) ; redis. del ( lockKey) ; } else { Thread . sleep ( 50 ) ; return getUserById ( id) ; } } return user; } 三、Redis持久化 3.1 RDB vs AOF 特性 RDB AOF 原理 定时快照 记录所有写命令 文件大小 小 大 恢复速度 快 慢 数据完整性 可能丢数据 可配置完整性 性能影响 fork子进程 每次操作
3.2 RDB配置 # 多久触发一次快照 save900 1 # 900秒内1个key变化 save300 10 # 300秒内10个key变化 save60 10000 # 60秒内10000个key变化 # 文件名 dbfilename dump.rdb# 存放目录 dir ./3.3 AOF配置 # 开启AOF appendonlyyes # 同步策略 appendfsync always# 每次写都同步(最安全,最慢) appendfsync everysec# 每秒同步(推荐) appendfsync no# 操作系统决定 # AOF文件重写 auto-aof-rewrite-percentage100 auto-aof-rewrite-min-size 64mb四、Redis集群 4.1 主从复制 # 从节点配置 replicaof192.168 .1.1006379 replica-read-onlyyes 4.2 哨兵模式(Sentinel) # 启动哨兵 redis-sentinel sentinel.conf作用:
// Java客户端连接哨兵 JedisSentinelPool pool= new JedisSentinelPool ( "mymaster" , Set . of ( "192.168.1.101:26379" , "192.168.1.102:26379" ) , new JedisPoolConfig ( ) ) ; 4.3 Redis Cluster # 6个节点集群 redis-server --cluster-enabledyes --cluster-config-file nodes.conf# 创建集群 redis-cli--cluster create192.168 .1.1:6379.. .192.168 .1.6:6379 --cluster-replicas1 架构:
┌─────────┐ │ Slot 0-5460 │ ──→ 节点1 └─────────┘ ┌─────────┐ │Slot 5461-10922│ ──→ 节点2 └─────────┘ ┌─────────┐ │Slot 10923-16383│ ──→ 节点3 └─────────┘数据分片:
16384个slot 根据CRC16(key) % 16384 决定key落在哪个slot 每个节点负责一部分slot 五、Redis分布式锁 5.1 基础实现 public class RedisLock { private Jedis jedis; private String lockKey; private String lockValue; private int expireTime; public RedisLock ( Jedis jedis, String lockKey, int expireTime) { this . jedis= jedis; this . lockKey= lockKey; this . lockValue= UUID . randomUUID ( ) . toString ( ) ; this . expireTime= expireTime; } // 加锁 public boolean tryLock ( ) { Long result= jedis. setnx ( lockKey, lockValue) ; if ( result== 1 ) { jedis. expire ( lockKey, expireTime) ; return true ; } return false ; } // 释放锁(只能释放自己的锁) public void unlock ( ) { String script= "if redis.call('get', KEYS[1]) == ARGV[1] then " + " return redis.call('del', KEYS[1]) " + "else return 0 end" ; jedis. eval ( script, 1 , lockKey, lockValue) ; } } 5.2 Redisson(推荐) // Redisson分布式锁 Config config= new Config ( ) ; config. addServerAddress ( "redis://192.168.1.100:6379" ) ; RedissonClient redisson= Redisson . create ( config) ; // 获取锁 RLock lock= redisson. getLock ( "order:lock:1001" ) ; try { boolean locked= lock. tryLock ( 10 , 30 , TimeUnit . SECONDS ) ; if ( locked) { processOrder ( ) ; } } finally { lock. unlock ( ) ; } 5.3 分布式锁注意事项 注意事项 说明 锁必须有过期时间 防止死锁 锁值要唯一 防止误删别人的锁 加锁/解锁要原子 用Lua脚本保证 集群环境 用Redisson等成熟框架
六、Redis实战案例 6.1 抢购限流 public class SeckillService { private Jedis jedis; // 库存预减 public boolean seckill ( Long userId, Long productId) { String stockKey= "seckill:stock:" + productId; String userKey= "seckill:user:" + productId; // 1. 检查是否已抢购 if ( jedis. sismember ( userKey, userId. toString ( ) ) ) { throw new RuntimeException ( "已抢购过" ) ; } // 2. 原子性扣库存 Long stock= jedis. decr ( stockKey) ; if ( stock< 0 ) { jedis. incr ( stockKey) ; throw new RuntimeException ( "库存不足" ) ; } // 3. 记录用户 jedis. sadd ( userKey, userId. toString ( ) ) ; // 4. 异步下单(MQ) orderMQ. send ( userId, productId) ; return true ; } } 6.2 排行榜实现 public class RankService { private Jedis jedis; // 更新用户分数 public void updateScore ( Long userId, int score) { jedis. zadd ( "rank:scores" , score, userId. toString ( ) ) ; } // 获取TOP N public List < Map < String , Object > > getTopN ( int n) { Set < ZSetOperations. TypedTuple < String > > topSet= jedis. zrevrangeWithScores ( "rank:scores" , 0 , n- 1 ) ; List < Map < String , Object > > result= new ArrayList < > ( ) ; int rank= 1 ; for ( ZSetOperations. TypedTuple < String > item: topSet) { Map < String , Object > map= new HashMap < > ( ) ; map. put ( "rank" , rank++ ) ; map. put ( "userId" , item. getValue ( ) ) ; map. put ( "score" , item. getScore ( ) . intValue ( ) ) ; result. add ( map) ; } return result; } // 获取用户排名 public Long getUserRank ( Long userId) { return jedis. zrevrank ( "rank:scores" , userId. toString ( ) ) ; } } 6.3 延迟队列 public class DelayQueueService { private Jedis jedis; // 添加延迟任务 public void addTask ( Long orderId, int delaySeconds) { long executeTime= System . currentTimeMillis ( ) + delaySeconds* 1000 ; jedis. zadd ( "delay:queue" , executeTime, orderId. toString ( ) ) ; } // 轮询获取过期任务 public List < Long > getExpiredTasks ( ) { long now= System . currentTimeMillis ( ) ; Set < String > tasks= jedis. zrangeByScore ( "delay:queue" , 0 , now) ; List < Long > expiredTasks= new ArrayList < > ( ) ; for ( String taskId: tasks) { Long removed= jedis. zrem ( "delay:queue" , taskId) ; if ( removed== 1 ) { expiredTasks. add ( Long . parseLong ( taskId) ) ; } } return expiredTasks; } } 总结 模块 核心知识点 数据类型 String/Hash/List/Set/ZSet 缓存策略 Cache Aside/穿透/击穿/雪崩 持久化 RDB/AOF/混合持久化 集群 主从/哨兵/Cluster 分布式锁 setnx/Lua/Redisson 实战 抢购/排行榜/延迟队列
核心口诀:缓存三剑客穿透击穿雪崩,分布式锁setnx加过期,集群主从加哨兵,Redis在手缓存无忧。