一、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");Stringvalue=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);// 商品数量+1redis.hincrby("cart:1001:product:888",-1);// 商品数量-1
二、Redis缓存策略
2.1 缓存模式
Cache Aside(旁路缓存)最常用
// 读操作publicUsergetUserById(Longid){StringcacheKey="user:"+id;Useruser=(User)redis.get(cacheKey);if(user!=null){returnuser;// 缓存命中}// 缓存未命中,查数据库user=userDAO.findById(id);// 写入缓存,设置过期时间redis.setex(cacheKey,3600,user);// 1小时过期returnuser;}// 写操作publicvoidupdateUser(Useruser){// 1. 先更新数据库userDAO.update(user);// 2. 删除缓存(不是更新!)redis.del("user:"+user.getId());}
为什么删除而不是更新?
线程A更新数据 → 线程B查询缓存 → 缓存未命中查数据库 → 数据库返回旧值 → 写入缓存 线程A更新数据 → 删除缓存 结果:缓存中是旧数据! 线程A更新数据 → 删除缓存 线程B查询缓存 → 缓存未命中 → 查数据库 → 数据库已是新值 → 写入缓存 结果:缓存中是正确数据
2.2 缓存常见问题
| 问题 | 解决方案 |
|---|
| 缓存穿透 | 布隆过滤器 / 空值缓存 |
| 缓存击穿 | 互斥锁 / 热点数据永不过期 |
| 缓存雪崩 | 随机过期时间 / 多级缓存 |
缓存穿透:布隆过滤器
// 使用布隆过滤器判断数据是否存在BloomFilter<String>bloomFilter=BloomFilter.create(...);// 查询前先判断publicUsergetUserById(Longid){if(!bloomFilter.mightContain("user:"+id)){returnnull;// 一定不存在}// 继续查缓存和数据库...}
缓存击穿:互斥锁
publicUsergetUserById(Longid){StringcacheKey="user:"+id;Useruser=redis.get(cacheKey);if(user==null){StringlockKey="lock:user:"+id;StringlockValue=redis.setnx(lockKey,"1");if(lockValue){user=userDAO.findById(id);redis.setex(cacheKey,3600,user);redis.del(lockKey);}else{Thread.sleep(50);returngetUserById(id);}}returnuser;}
三、Redis持久化
3.1 RDB vs AOF
| 特性 | RDB | AOF |
|---|
| 原理 | 定时快照 | 记录所有写命令 |
| 文件大小 | 小 | 大 |
| 恢复速度 | 快 | 慢 |
| 数据完整性 | 可能丢数据 | 可配置完整性 |
| 性能影响 | fork子进程 | 每次操作 |
3.2 RDB配置
# 多久触发一次快照save9001# 900秒内1个key变化save30010# 300秒内10个key变化save6010000# 60秒内10000个key变化# 文件名dbfilename dump.rdb# 存放目录dir./
3.3 AOF配置
# 开启AOFappendonlyyes# 同步策略appendfsync always# 每次写都同步(最安全,最慢)appendfsync everysec# 每秒同步(推荐)appendfsync no# 操作系统决定# AOF文件重写auto-aof-rewrite-percentage100auto-aof-rewrite-min-size 64mb
四、Redis集群
4.1 主从复制
# 从节点配置replicaof192.168.1.1006379replica-read-onlyyes
4.2 哨兵模式(Sentinel)
# 启动哨兵redis-sentinel sentinel.conf
作用:
// Java客户端连接哨兵JedisSentinelPoolpool=newJedisSentinelPool("mymaster",Set.of("192.168.1.101:26379","192.168.1.102:26379"),newJedisPoolConfig());
4.3 Redis Cluster
# 6个节点集群redis-server --cluster-enabledyes--cluster-config-file nodes.conf# 创建集群redis-cli--clustercreate192.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 基础实现
publicclassRedisLock{privateJedisjedis;privateStringlockKey;privateStringlockValue;privateintexpireTime;publicRedisLock(Jedisjedis,StringlockKey,intexpireTime){this.jedis=jedis;this.lockKey=lockKey;this.lockValue=UUID.randomUUID().toString();this.expireTime=expireTime;}// 加锁publicbooleantryLock(){Longresult=jedis.setnx(lockKey,lockValue);if(result==1){jedis.expire(lockKey,expireTime);returntrue;}returnfalse;}// 释放锁(只能释放自己的锁)publicvoidunlock(){Stringscript="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分布式锁Configconfig=newConfig();config.addServerAddress("redis://192.168.1.100:6379");RedissonClientredisson=Redisson.create(config);// 获取锁RLocklock=redisson.getLock("order:lock:1001");try{booleanlocked=lock.tryLock(10,30,TimeUnit.SECONDS);if(locked){processOrder();}}finally{lock.unlock();}
5.3 分布式锁注意事项
| 注意事项 | 说明 |
|---|
| 锁必须有过期时间 | 防止死锁 |
| 锁值要唯一 | 防止误删别人的锁 |
| 加锁/解锁要原子 | 用Lua脚本保证 |
| 集群环境 | 用Redisson等成熟框架 |
六、Redis实战案例
6.1 抢购限流
publicclassSeckillService{privateJedisjedis;// 库存预减publicbooleanseckill(LonguserId,LongproductId){StringstockKey="seckill:stock:"+productId;StringuserKey="seckill:user:"+productId;// 1. 检查是否已抢购if(jedis.sismember(userKey,userId.toString())){thrownewRuntimeException("已抢购过");}// 2. 原子性扣库存Longstock=jedis.decr(stockKey);if(stock<0){jedis.incr(stockKey);thrownewRuntimeException("库存不足");}// 3. 记录用户jedis.sadd(userKey,userId.toString());// 4. 异步下单(MQ)orderMQ.send(userId,productId);returntrue;}}
6.2 排行榜实现
publicclassRankService{privateJedisjedis;// 更新用户分数publicvoidupdateScore(LonguserId,intscore){jedis.zadd("rank:scores",score,userId.toString());}// 获取TOP NpublicList<Map<String,Object>>getTopN(intn){Set<ZSetOperations.TypedTuple<String>>topSet=jedis.zrevrangeWithScores("rank:scores",0,n-1);List<Map<String,Object>>result=newArrayList<>();intrank=1;for(ZSetOperations.TypedTuple<String>item:topSet){Map<String,Object>map=newHashMap<>();map.put("rank",rank++);map.put("userId",item.getValue());map.put("score",item.getScore().intValue());result.add(map);}returnresult;}// 获取用户排名publicLonggetUserRank(LonguserId){returnjedis.zrevrank("rank:scores",userId.toString());}}
6.3 延迟队列
publicclassDelayQueueService{privateJedisjedis;// 添加延迟任务publicvoidaddTask(LongorderId,intdelaySeconds){longexecuteTime=System.currentTimeMillis()+delaySeconds*1000;jedis.zadd("delay:queue",executeTime,orderId.toString());}// 轮询获取过期任务publicList<Long>getExpiredTasks(){longnow=System.currentTimeMillis();Set<String>tasks=jedis.zrangeByScore("delay:queue",0,now);List<Long>expiredTasks=newArrayList<>();for(StringtaskId:tasks){Longremoved=jedis.zrem("delay:queue",taskId);if(removed==1){expiredTasks.add(Long.parseLong(taskId));}}returnexpiredTasks;}}
总结
| 模块 | 核心知识点 |
|---|
| 数据类型 | String/Hash/List/Set/ZSet |
| 缓存策略 | Cache Aside/穿透/击穿/雪崩 |
| 持久化 | RDB/AOF/混合持久化 |
| 集群 | 主从/哨兵/Cluster |
| 分布式锁 | setnx/Lua/Redisson |
| 实战 | 抢购/排行榜/延迟队列 |
核心口诀:缓存三剑客穿透击穿雪崩,分布式锁setnx加过期,集群主从加哨兵,Redis在手缓存无忧。