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

Redis缓存实战:从数据类型到分布式锁,看完这篇就够了

一、Redis数据类型

1.1 五种基本数据类型

类型命令使用场景
StringSET/GET简单缓存、计数器、分布式锁
HashHSET/HGET对象缓存、购物车
ListLPUSH/LPOP队列、消息队列、粉丝列表
SetSADD/SMEMBERS标签、抽奖、去重
Sorted SetZADD/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

特性RDBAOF
原理定时快照记录所有写命令
文件大小
恢复速度
数据完整性可能丢数据可配置完整性
性能影响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在手缓存无忧。

http://www.jsqmd.com/news/699807/

相关文章:

  • 封神!C++ 对象时序管理终极解法——我发明的「构造回环策略」
  • 告别PPT内耗,从容上岸:百考通AI如何拯救你的毕业答辩
  • 宇宙学研究新突破:用 Blender 几何节点处理 CMB 数据,实现多项实用功能!
  • 20253915 2025-2026-2 《网络攻防实践》实践8报告 -
  • 现代Java开发者的工具箱:从Lombok到MapStruct
  • Giser必懂⑦:WebGIS、桌面GIS、移动GIS、三维GIS的区别
  • Unity Figma Bridge架构解析:设计开发一体化工作流实战指南
  • 猫云AI_API中小企业商用 LLM 海外 API 稳定接入解决方案
  • 部署与可视化系统:模型部署:YOLOv10 转 ONNX + 使用 ONNXRuntime 推理(CPU/GPU)
  • Yakit Web Fuzzer实战:手把手教你用{{标签}}搞定短信轰炸、撞库和Host碰撞
  • 答辩PPT,别让工具拖垮内容:用百考通AI高效搞定毕业答辩
  • BilldDesk:3个关键优势让你告别传统远程控制限制
  • 马蹄杯入门组初赛总结
  • Tauri + MSIX 一天上架微软商店——独立开发者最低成本发行路径
  • 如何快速截屏
  • VSCode AI错误修复失效应急手册(2026.3紧急修订版),含6个一键禁用AI干扰的settings.json密钥+3种安全回滚路径
  • 5分钟快速上手:知识星球内容爬取与PDF电子书制作终极指南
  • 【MATLAB程序】基于RSSI的RFID二维轨迹定位仿真介绍,EKF滤波增加轨迹定位精度。附下载链接
  • 开源吐槽大会:技术社区的治愈新姿势
  • L1-050 倒数第N个字符串(15 分)[java][python]
  • 个人博客4: Git 忽略规则优化+跨文件上下文补全功能开发
  • 在人工智能行业的我渐渐成为了AI的反对者?
  • CUDA 13.3新增的__hmma_bf16_sm80指令集实战(首曝):BERT-large QKV融合算子重构,较cuBLAS快3.8×
  • AAAI 2026 AMD论文Spark方法揭秘:查询感知的 KV 缓存通道剪枝
  • 量子投票协议:原理、实现与噪声分析
  • 2026年的 ReAct Agent架构解析:原生 Tool Calling 与 LangGraph 状态机
  • 终极指南:如何在3分钟内为Windows电脑免费扩展10个虚拟显示器
  • 部署与可视化系统:边缘设备部署:YOLOv8 量化 + NCNN 在树莓派 5 上实时检测
  • IP归属地API接入实战指南:3天内安全上线的评估与落地方法
  • 成品批次信息及全链路溯源汇报材料(大客户专用)