我做了几年Java后端有一个很深的体会只要项目一上线、一面向真实用户Redis几乎就成了绕不开的标配。无论是缓存、分布式锁、接口限流还是排行榜Redis在Java生态里的应用深度往往直接决定系统能抗住多大的流量。这篇文章我想结合自己的实际项目经验从环境搭建、客户端选型、核心场景编码到生产环境踩坑把Redis在Java中的应用完整梳理一遍。适合正在学Java基础、准备面试八股文的朋友也适合那些项目里已经用了Redis但想搞清楚“为什么这么配、为什么还会出问题”的后端开发。1. 项目概述与现实需求1.1 为什么Java项目里几乎都离不开Redis先说个最简单的类比。你开了一家餐厅每天订单量一大厨房数据库就要不停做菜一旦碰到饭点高峰排队的人能把门挤爆。这时候你会在前台摆个“今日推荐”的展示柜Redis缓存把点击率最高的几道菜提前做好放着客人来了直接端走。餐厅不用每道菜都现炒厨房的压力瞬间降下来。Java项目里的Redis干的就是这件事。数据库系统的性能瓶颈主要卡在磁盘IO和事务处理的复杂度上。关系型数据库处理几千并发查询时响应时间往往就会明显爬升而Redis基于纯内存操作官方数据读写性能能到每秒十万次量级延迟通常在微秒到毫秒级别。把高频访问的数据放进Redis让请求尽量打在内存上数据库的QPS压力就能大幅缓解。我接触过的项目里凡是首页、商品详情、用户会话、配置信息这类“读多写少”的数据基本都是走Redis。而且Redis的定位不只是缓存。它支持String、Hash、List、Set、ZSet、BitMap、HyperLogLog等丰富的数据类型这使得同一个组件可以实现分布式锁、排行榜、计数器、延迟队列、轻量消息推送等多种功能。Java后端选型时经常面临一个选择引入一堆中间件去解决不同问题还是用一个轻量级的Redis包揽一部分场景。我的判断是在业务体量还没到必须上重型中间件的阶段Redis绝对是最划算的选择。1.2 核心应用场景与选型分析结合Java技术栈我把Redis的实际应用场景分为四类。第一类是纯缓存加速这也是绝大多数项目首先用到Redis的地方。热点数据、查询结果、模板配置、验证码全部塞进RedisTTL过期时间一到自动清理。典型实现方式就是用String类型存JSON字符串或者用Hash存对象字段。省下的数据库压力非常可观。第二类是分布式环境下的协调工具。现在Java后端基本都是多实例部署多个服务节点操作同一个资源时需要一种跨JVM的互斥机制这就是Redis分布式锁的用武之地。SETNX加过期时间、Redisson框架的看门狗续期方案已经非常成熟。第三类是数据统计与排行。ZSet有序集合天然支持按分数排序做排行榜、热点排序、时间线分页都特别顺手INCR、INCRBY命令做计数器更是原子操作不用担心多线程并发加一加重复。第四类是轻量级消息通信。Redis的Pub/Sub模式可以快速实现简单的消息通知Stream类型则提供了更可靠的消息队列能力。如果项目里不想引入Kafka、RabbitMQ这种重量级组件Redis可以顶上。日常面试中这些场景也会反复问到特别是Redis如何应对缓存穿透、击穿、雪崩以及分布式锁的稳定性边界。这些内容后面我会结合实操详细展开。2. 环境准备与客户端技术选型2.1 Redis安装的几种常见方式和避坑提醒先别急着写代码把Redis跑起来是基本功。我遇到过不少人卡在安装环节尤其是Windows环境下的同学折腾半天发现各种问题。一个容易搞混的事实是Redis官方并没有提供Windows原生版本。大家在Windows上能直接安装的Redis其实是微软团队维护的旧移植版版本长期停留在3.x和5.x功能落后而且官方早就停止维护生产环境不建议使用。我在早期学习时也踩过这个坑用Windows版Redis练手没什么大问题但到了生产Linux环境很多命令行为和内存管理表现跟旧版差异很大很容易被误导。现在比较推荐的安装方式是Linux系统直接源码编译或apt/yum安装然后注册为systemd服务。另外用Docker跑Redis非常省事一条命令就能拉起一个单机实例docker run -d --name redis \ -p 6379:6379 \ -v /usr/local/redis/data:/data \ -v /usr/local/redis/conf/redis.conf:/etc/redis/redis.conf \ redis:6.2 \ redis-server /etc/redis/redis.conf热词里出现了“docker安装redis主从”。如果没有Docker环境想本地快速验证主从复制用docker-compose拉两个Redis容器做master-slave配置是最快的version: 3 services: redis-master: image: redis:6.2 container_name: redis-master command: redis-server --appendonly yes ports: - 6379:6379 redis-slave: image: redis:6.2 container_name: redis-slave command: redis-server --slaveof redis-master 6379 --appendonly yes depends_on: - redis-master ports: - 6380:6379执行docker-compose up -d主从就搭起来了。验证方式也很简单master上set一个keyslave上立刻get得到就说明复制链路正常。可视化客户端方面热词里的Redis Desktop Manager是老牌工具但新版早就不免费了。我目前用的是Another Redis Desktop Manager免费开源支持Windows、macOS、Linux。它最大的好处是能直接查看Redis里所有key的TTL、value的类型还能以表格形式看Hash和ZSet的数据排查问题时比命令行直观得多。连接时填上Host、Port、密码即可没什么复杂的。2.2 Jedis、Lettuce、Redisson怎么选Java操作Redis的客户端主要有三个Jedis、Lettuce、Redisson。很多初学者搞不清这三者的关系其实它们各有侧重。Jedis是老牌客户端API非常贴近Redis原生命令方法名几乎和Redis命令一一对账比如jedis.set(key, value)、jedis.get(key)。它的特点是简单直接、容易上手但本身不是线程安全的。早期使用Jedis必须配合JedisPool连接池每个线程从池里借一个连接实例用完归还。Spring Boot 1.x时代默认用的就是Jedis。Lettuce是Spring Boot 2.x及以上版本的默认客户端。它的核心设计是基于Netty的响应式连接单个连接实例可以被多个线程共享底层自动管理命令的异步派发。这意味着在高并发场景下Lettuce对连接资源的占用远少于Jedis连接池配置甚至可以不用设置很大就能顶住较高流量。不过Lettuce在一些极端情况下也出过坑比如多个线程阻塞在同一个连接上时会出现命令在排队堆积的情况这时候反而需要调大连接池或者手动调整超时参数。Redisson的定位则明显不同它不是一个纯粹的Redis命令客户端而是一个基于Redis的Java分布式框架。你不需要去调get、set这些原始命令它直接提供了分布式锁、分布式Map、分布式队列、CountDownLatch、限流器等一系列现成的分布式数据结构。代码写起来非常舒服Autowired private RedissonClient redissonClient; public void demo() { RLock lock redissonClient.getLock(order:pay:12345); lock.lock(10, TimeUnit.SECONDS); try { // 业务逻辑 } finally { lock.unlock(); } }我的选型建议是如果用Spring Boot并且只是做缓存、计数器、排行榜这类常规操作直接用Spring Data Redis里的Lettuce就够了不值得再引入额外依赖如果项目中需要分布式锁、分布式集合这些高级能力Redisson一定要加上它提供的看门狗自动续期机制对手写SETNX锁是一个非常重要的可靠性提升。2.3 Spring Boot整合Redis的基础配置Spring Boot整合Redis非常顺滑依赖只需要一个dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdorg.apache.commons/groupId artifactIdcommons-pool2/artifactId /dependency第二个依赖是连接池实现Lettuce模式下也需要引入。配置文件的常用写法如下spring: redis: host: 127.0.0.1 port: 6379 password: your-redis-password database: 0 timeout: 3000ms lettuce: pool: max-active: 16 max-idle: 8 min-idle: 2 max-wait: 3000ms这些参数在压测时经常需要调。max-active表示连接池中最多能同时活跃的连接数如果业务并发很高但max-active设得太小就会出现获取连接等待表现为接口响应变慢max-wait是等待连接的最大毫秒数超过直接抛异常timeout则是读写操作超时时间网络抖动或者Redis自身阻塞时这个参数决定了请求方多久会放弃等待。把这些基础配置搞定后就可以注入StringRedisTemplate或RedisTemplate来操作Redis了。但这里马上会引出序列化问题这也是热词里出现“redis序列化”的原因。我单独用一节来讲。3. 五大核心场景实操解析3.1 缓存读写String JSON 的正确姿势缓存场景最常犯的错误是直接把Java对象序列化后塞进Redis。比如有人图省事用默认的RedisTemplate直接存对象结果Redis里存的是一堆带有二进制头的JDK序列化字节流其他语言或工具根本读不懂排查问题时看到一串乱码心里非常难受。我的做法是字符串操作统一用StringRedisTemplateJava对象统一转成JSON字符串再存储。存的时候调JSON.toJSONString()或自己封装一个JsonUtil取出来再用JSON.parseObject()反序列化。Service public class ProductCacheService { private static final String CACHE_KEY_PREFIX product:detail:; Autowired private StringRedisTemplate redisTemplate; public ProductVO getProduct(Long productId) { String key CACHE_KEY_PREFIX productId; String json redisTemplate.opsForValue().get(key); if (StrUtil.isNotBlank(json)) { return JSON.parseObject(json, ProductVO.class); } ProductVO product queryFromDb(productId); if (product ! null) { redisTemplate.opsForValue().set(key, JSON.toJSONString(product), 30, TimeUnit.MINUTES); } return product; } }这段代码是一个最基础的Cache-Aside模式也就是旁路缓存。读的时候先查缓存没命中再查数据库查完回填缓存修改数据库时则主动删除缓存等待下一次读取重建。这个模式比“先改库再写缓存”更稳妥因为可以避免并发场景下缓存与数据库数据不一致的复杂问题。TTL的设定也有讲究。过期时间太短缓存命中率上不去数据库压力大太长数据一致性风险变高。一般原则是对一致性要求不高的数据如商品描述、文章详情缓存30分钟到2小时对一致性要求较高的数据如库存、用户余额缓存时间控制在几十秒到几分钟或者干脆直接删缓存不用TTL。还要注意一个坑缓存穿透。如果数据库里也不存在这个ID那么每次都查不到也会穿透到数据库。通常做法是把NULL值也缓存起来TTL设短一点比如5~10分钟这样至少能挡住大量不存在数据的查询打挂数据库。3.2 分布式锁Redisson与手写SETNX的取舍热词里有“redis分布式锁”这正是Java后端面试和实战的高频点。我先说说手写分布式锁的基本原理。分布式锁最基础的形式是利用SETNX命令——只有当key不存在时才能设置成功String lockKey lock:order:123; Boolean success redisTemplate.opsForValue().setIfAbsent(lockKey, 1, 10, TimeUnit.SECONDS); if (Boolean.TRUE.equals(success)) { try { // 执行业务 } finally { redisTemplate.delete(lockKey); } }这段代码看似简单实际坑非常多。第一个坑是锁过期而业务没执行完。如果业务执行超过10秒锁自动释放了其他线程又抢到锁就会导致并发问题。第二个坑是释放锁时的误删。线程A持有锁10秒后锁过期线程B拿到锁并写入新值然后线程A业务终于执行完调delete把B的锁删掉了。解决方法是释放前先比较value只有自己的value才删除这个操作又得是原子的否则高并发下同样出错。第三个坑是看门狗续期机制缺失锁不能自动续期。Redisson把这些问题都解决了。它的RLock默认采用看门狗机制默认过期时间30秒业务没执行完会自动续期到30秒业务结束才真正删除锁。释放锁时Redisson内部用Lua脚本原子地校验value并删除杜绝误删。Autowired private RedissonClient redissonClient; public void createOrder(Long orderId) { RLock lock redissonClient.getLock(lock:order: orderId); lock.lock(); try { // 秒杀、下单等核心业务 } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } }用Redisson时有个注意点lock()如果不传时间就会启用看门狗自动续期如果传了leaseTime比如lock(10, TimeUnit.SECONDS)那么看门狗不会续期10秒后强制释放。业务时间波动大的场景不要轻易指定过期时间靠看门狗更安全。分布式锁实现细节是Java面试八股文里的常客面试官会追着问“锁过期了怎么办”“redis主从切换锁丢了怎么办”。我的建议是项目里能用Redisson就别手写Redisson内部很多边界情况已经考虑过了手写看似灵活实际上等于把高并发场景下的所有异常分支重新踩一遍。3.3 排行榜ZSet天然适配榜单功能几乎是互联网产品的标配从游戏战力榜到社区热帖榜。用数据库ORDER BY做排行在数据量大之后性能很差而Redis的ZSet就是为了排序而生的。ZSet的每个成员关联一个double类型的分数Redis内部用跳跃表按分数维护顺序。你可以非常方便地执行分数新增、排名查询、区间获取等操作时间复杂度都是O(log N)。// 用户积分增加 redisTemplate.opsForZSet().incrementScore(rank:daily, userId, score); // 获取Top10 SetZSetOperations.TypedTupleString top10 redisTemplate.opsForZSet().reverseRangeWithScores(rank:daily, 0, 9); // 获取某个用户的排名 Long rank redisTemplate.opsForZSet().reverseRank(rank:daily, userId);这里我踩过一个细节坑reverseRangeWithScores返回的TypedTuple里value取.getString()拿到的是成员IDscore取的是double分数。如果分数是整数还好若是小数或动态加出来的浮点数double精度会带来小尾巴。处理方式是分数全部用整数或者用分数扩大100倍的方式存储避免浮点误差。还有一点是榜单维度。日榜、周榜、总榜如果都用一个key会造成数据混乱。我的做法是key上带日期后缀比如rank:daily:20250115每天零点后自然切换到新key。历史榜单可以定期清理或者迁移到冷存储。3.4 计数器与限流INCR的艺术Redis的INCR命令在秒杀、点赞、访问统计、接口限流等场景非常实用。点赞计数的场景直接用incrementLong likeCount redisTemplate.opsForValue().increment(like:article: articleId);INCR是原子操作INCRBY支持一次加指定步长不会出现数据库update时的并发丢失问题。这个操作在Redis里是单线程串行执行的天然线程安全比在数据库里做乐观锁重试节省了不知道多少麻烦。限流这里能玩的就更多了。最简单的固定窗口限流用key记录窗口内的请求次数配合EXPIRE实现窗口重置。public boolean tryAcquire(String userId) { String key rate:limit: userId : LocalTime.now().getHour(); Long count redisTemplate.opsForValue().increment(key); if (count 1) { redisTemplate.expire(key, 3600, TimeUnit.SECONDS); } return count 100; }这种固定窗口的缺陷在于窗口切换瞬间可能出现双倍流量。更平滑的做法是用Redis的Lua脚本实现滑动窗口或者利用Redisson的RRateLimiter令牌桶实现后者内部同样用Lua脚本保证原子性适合做接口级别的精准限流。3.5 轻量级消息的发布订阅与Stream如果项目里暂时不想引入MQ中间件Redis的发布订阅可以做简单的消息通知。// 发布端 redisTemplate.convertAndSend(channel:order:create, JSON.toJSONString(orderDTO)); // 订阅端 Bean public MessageListenerAdapter messageListener() { return new MessageListenerAdapter((MessageListener) (message, pattern) - { String body new String(message.getBody(), StandardCharsets.UTF_8); // 处理消息 }); }不过要提醒的是Pub/Sub是有代价的消息发布后如果订阅者不在线或断线消息直接丢失它不具备持久化能力。所以通知类、广播类的场景可以用但可靠投递的场景别指望它。Redis 5.0推出的Stream类型补足了持久化消费的能力可以按消费者组方式消费消息接近轻量版MQ。如果只是内部系统之间做简单异步解耦Stream完全够用。4. 序列化方案与数据类型深度应用4.1 为什么默认的JDK序列化要避开热词里出现了“redis序列化”和“redis数据类型”这俩其实是分不开的。Spring Data Redis默认的RedisTemplate用的是JdkSerializationRedisSerializer序列化出来的字节流是一串带有二进制头部的Java对象流。存进Redis后你拿命令行看value基本就是 \xAC\xED\x00\x05 这类乱码完全不可读。JDK序列化的问题不止在于不可读。它的序列化结果体积大、包含大量类元信息占内存而且只有Java程序能反序列化一旦系统里混入其他语言调用或者需要做数据迁移清洗这些数据基本就是废的。生产环境我见过因为默认序列化导致Redis内存暴涨、key前缀莫名出现乱码的案例排查起来非常闹心。我的原则是项目里一律使用StringRedisTemplate做基础读写如果需要对象存取就用JSON字符串方案确实需要更复杂的对象结构时单独配置RedisTemplate的序列化器value序列化用GenericJackson2JsonRedisSerializer。4.2 手动配置RedisTemplate的正确姿势如果你决定用RedisTemplate操作Hash、ZSet等结构并希望value直接以JSON形式存取可以加一个配置类Configuration public class RedisConfig { Bean public RedisTemplateString, Object redisTemplate(RedisConnectionFactory factory) { RedisTemplateString, Object template new RedisTemplate(); template.setConnectionFactory(factory); StringRedisSerializer keySerializer new StringRedisSerializer(); GenericJackson2JsonRedisSerializer valueSerializer new GenericJackson2JsonRedisSerializer(); template.setKeySerializer(keySerializer); template.setHashKeySerializer(keySerializer); template.setValueSerializer(valueSerializer); template.setHashValueSerializer(valueSerializer); template.afterPropertiesSet(); return template; } }这里key用StringRedisSerializer保证key在命令行里可读value用GenericJackson2JsonRedisSerializer存储的是包含class信息的JSON字符串。它的好处是支持复杂对象的自动反序列化缺点是JSON里会多出class字段体积略大。如果Redis当前Key里已经有JDK序列化格式的数据切换序列化器之前务必要先清理掉或者换一套key前缀否则新旧格式混在一起反序列化会直接报错。这个我在升级老项目时踩过切换序列化方案后线上有一批老数据读不出来最后只能写脚本重置缓存。4.3 七大数据类型在Java端的落点Redis官方的大数据类型在Java项目里各有典型用法我整理成一张速查表面试和实操都用得上数据类型典型应用场景Java端常用方法String缓存、计数器、分布式ID、验证码set、get、increment、expireHash对象缓存、购物车、用户资料opsForHash().put、get、entriesList消息队列、最新动态、异步任务opsForList().leftPush、rightPopSet去重、共同好友、标签系统opsForSet().add、intersect、unionZSet排行榜、延时任务按分数取opsForZSet().add、range、scoreBitMap用户签到、在线状态setBit、getBit、bitCountHyperLogLogUV统计opsForHyperLogLog().add、sizeHash存Java对象其实也很常见。比如用户的基本信息字段多、单个字段经常修改用Hash可以单独更新某个字段不用整个对象序列化覆盖。String存JSON适合整体读写的场景Hash适合字段级读写的场景两者各有优劣别机械选择。BitMap在签到场景非常好用。每天用户签到就是一个bit位一年365天只需365个bit不到50个字节。用getBit判定某天是否签到用bitCount统计某段时间的签到总天数对比数据库动辄扫描几千条记录性能差距是数量级的。HyperLogLog做UV统计则完全是用精度换空间。它统计基数时误差在0.81%左右但1亿级别的独立访客数内存消耗只有十几KB。业务对精度要求不那么苛刻时这几乎是最优解。5. 高可用与生产环境治理5.1 主从复制与哨兵模式热词里专门有“docker安装redis主从”说明大家已经意识到单机Redis的风险。实际项目中Redis挂了或者重启会对线上产生非常大的影响所以高可用设计不能省。Redis主从复制是最基础的高可用方案。master负责写slave负责读和备份数据会异步同步到slave。当master发生故障时可以把某个slave提升为新的master。主从架构同时还可以做读写分离读请求打到slave上减轻master压力。但主从复制有一个天然的弱项如果master宕机哨兵Sentinel机制可以自动发现并完成故障转移把slave提升为master。哨兵本身也是需要集群化部署的通常至少三个哨兵节点组成哨兵集群避免单点故障导致误判。Spring Boot客户端连接哨兵模式很简单spring: redis: sentinel: master: mymaster nodes: - 127.0.0.1:26379 - 127.0.0.1:26380 - 127.0.0.1:26381还有一种更现代的方式是Redis Cluster它是真正的分片集群数据自动分布在多个节点上每个分片还有自己的副本。Cluster模式适合数据量比较大、单个实例内存已经装不下的场景。不过Cluster对多key操作有槽位限制事务和Lua脚本使用要谨慎Java端用Redisson或Lettuce已经能透明支持。5.2 缓存穿透、击穿、雪崩的实际应对这三大问题是Redis面试八股文里几乎必问的内容实际生产也确实会遇到。缓存穿透查询一个根本不存在的数据缓存和数据库都没有请求每次都打到数据库。一个恶意用户不断请求不存在的ID数据库压力立刻升高。应对手段有三个缓存空值并设置较短TTL使用布隆过滤器拦截不存在的key参数校验直接过滤明显不合法的请求。我在项目里验证过布隆过滤器在数据量大时能挡住绝大部分非法请求但要注意布隆过滤器本身也需要维护数据新增后要同步重建。缓存击穿某个热点key过期瞬间大量请求同时打到数据库。这时候可以用互斥锁让查询数据库的动作只允许一个线程执行其他线程等锁后直接读缓存。Redisson提供的分布式锁也能在这个场景用上代码逻辑是缓存没命中时尝试加锁加锁成功才去查库回填缓存其他线程短暂等待后重查缓存。缓存雪崩大量key在同一时间段集中过期或者Redis实例直接宕机导致所有请求洪峰冲垮数据库。应对手段包括设置过期时间时增加随机抖动比如30分钟加一个随机数构建Redis高可用体系主从加哨兵让宕机影响降到最低对数据库开启降级限流熔断机制。我习惯在代码里对TTL做RandomUtils.nextLong(0, 300)的秒级扰动效果立竿见影。5.3 大Key、热Key与慢日志排查生产环境的Redis经常遇到两类隐藏问题大Key和热Key。大Key指某个key的value特别大比如一个Hash里有几十万字段或者一个String值有几十MB。大Key会导致Redis删除时阻塞主线程、集群间数据迁移不均、网络传输延迟增加。Java端写入时就要有预防意识写入数据前先判断元素数量超过一定阈值就要拆分写完成后用redis-cli --bigkeys或者scan命令扫描大Key。发现大Key后的处理办法是拆分为多个小key分散存储或者对集合类型做分批删除。热Key是某一瞬间被大量请求命中。比如明星开演唱会同一张票务页面被秒杀。热Key问题表现为单节点Redis CPU飙升、带宽打满。常见的解决思路是本地缓存加Redis二级缓存把热点数据放在JVM内存里减少对Redis的访问或者热Key复制多份写入时同步写多个副本读取时随机访问其中一个。慢日志也是排查问题的重要入口。Redis提供了慢查询日志执行时间超过阈值的命令会被记录CONFIG SET slowlog-log-slower-than 10000 SLOWLOG GET 20在Java端如果发现RequestMapping接口偶发性变慢第一反应就去看Redis的slowlog很多情况下是KEYS命令或者一个O(N)级别的ZRANGEBYSCORE操作打出来的。生产环境我强烈建议禁用KEYS命令改用SCAN扫描避免大范围遍历阻塞单线程事件循环。6. 常见问题排查与面试高频点速查6.1 实战中高频故障TOP5把这两年我处理过的Redis相关故障做个记录都是非常典型的问题。连接超时或连接池耗尽表现为报错RedisConnectionFailureException、JedisException: Could not get a resource。排查顺序是先看Redis客户端连接数是否达到max-active上限再检查Redis实例的maxclients配置同时看是否发生大量慢查询拖住了连接。解决手段是调大连接池、拆分热点key、增加Redis节点。写入后发现数据读不出来大概率是序列化器不匹配。写入用的RedisTemplate与读取用的序列化器不一致导致key或value格式不同。这种情况最容易出现在公用的RedisTemplate被多处改配置的时候我的建议是同一份代码里统一封装工具类不允许各写各的。Redis变慢、CPU居高不下先看是不是内存达到maxmemory后开始淘汰数据导致频繁磁盘swap再看是不是存在大Key和热Key。用MONITOR命令观察实时命令流用SLOWLOG看慢查询基本能定位。分布式锁偶尔失效如果手写SETNX锁先评估业务执行时间是否超过锁过期时间如果用Redisson检查是不是误用了带leaseTime的重载方法导致看门狗失效另外要考虑Redis主从切换时的锁丢失问题这种情况用Redisson的RedLock方案能缓解但RedLock本身也有争议在可靠性和复杂度之间要自己权衡。缓存和数据库不一致这个没法百分百避免只能做最终一致性。我的处理方式是修改数据库后延迟双删——先删缓存再更新数据库隔几百毫秒再删一次缓存或用MQ异步通知去删缓存。业务要求强一致的场景就别用缓存了直接走数据库。6.2 面试八股文高频考点速查我梳理一下Java面试里和Redis强相关的高频考题大家准备面试时可以对着自查。Redis为什么这么快纯内存操作、单线程避免了并行切换和锁竞争、IO多路复用机制支撑高并发。单线程还能保证操作的原子性这是INCR和Lua脚本的基础。Redis的过期删除策略惰性删除加定期删除结合。惰性删除是取key时才判断是否过期定期删除是后台每100ms抽查部分key清理过期数据。这种组合不会消耗太多CPU但如果有大量过期key长时间不被访问可能堆积占用内存所以还会配合内存淘汰策略。内存淘汰策略noeviction默认不淘汰写满报错、allkeys-lru、allkeys-random、volatile-lru等。实际项目中通常配置allkeys-lru让系统自动淘汰不常用的数据。持久化机制RDB是定期生成全量快照恢复快但可能丢数据AOF是以日志形式记录写操作数据安全高但恢复慢。常见做法是AOF每秒钟同步一次兼顾安全与性能。Redis事务与LuaRedis事务通过MULTI、EXEC实现命令批量执行但不支持回滚。Lua脚本能保证多条命令原子性原子执行分布式锁、限流器底层实现都大量使用Lua。Redis主从复制的延迟与一致性问题主从异步同步极端情况下slave读取到旧数据。解决思路是敏感读操作强制走master或者对一致性要求高的数据不做读写分离。6.3 给新手的排错路径建议最后分享一套我自己排查线上Redis问题的固定路径对新手友好遇到问题不会慌张。第一步打开监控面板先看Redis服务端CPU、内存、连接数、命中率、慢查询数确认是服务端还是客户端问题第二步看应用日志里具体的异常堆栈如果是超时详细记录超时时间第三步如果把应用进程堆栈导出来查看有多少线程阻塞在redis连接获取或者Lettuce命令派发上第四步用redis-cli连上实例执行INFO统计关键指标用SLOWLOG查看是否有慢命令第五步检查key的分布和大小用bigkeys工具扫描一次。这五步走完90%的问题都能定位到大致范围。我见过不少同事一上来就重启服务或者重启Redis虽然有时候确实能“临时修复”但根本原因不解决同样的问题隔几天还会冒出来。生产环境一定要把排查路径固化成习惯不是靠运气修故障。踩过这么多坑之后说句实话。Redis在Java里的应用并不难难的是对每个场景背后原理的理解。当你搞懂了为什么Redis是单线程却这么快为什么默认序列化会埋坑为什么手写分布式锁会失效你在实际项目中就不会再把Redis当成一个只能get、set的黑盒。面试问八股文最终也是想看你到底有没有在生产环境里真正解决过问题。建议新手自己动手把主从复制、哨兵模式、Redisson分布式锁这几个基础方案都跑一遍遇到问题去读Redis源码或者官方文档一次实战胜过十篇面试总结。
企业数字化 ERP 产品动态
相关推荐
NodeWarden 二次开发手册:项目结构、数据库 Schema 演进与客户端兼容性避坑指南 NodeWarden 二次开发手册:项目结构、数据库 Schema 演进与客户端兼容性避坑指南 【免费下载链接】nodewarden Bitwarden-compatible server running on Cloudflare Workers 项目地址: https://gitcode.com/gh_mirrors/no/nodewarden
NodeWarden 是一个运行在… · 2026/9/26 5:29:09
团队协同逆向工程:Ghidra MCP 集成Ghidra Server的版本控制与多用户协作 团队协同逆向工程:Ghidra MCP 集成Ghidra Server的版本控制与多用户协作 【免费下载链接】ghidra-mcp Ghidra MCP Server — 200 MCP tools for AI-powered reverse engineering. GUI plugin headless server, lazy tool loading, convention enforcement, batch o… · 2026/9/26 5:28:57
开源本地化AI代码评审工具open-code-review实战指南 1. 项目概述:这不是又一个“AI写代码”玩具,而是一套可嵌入日常开发流水线的开源代码评审协作者“open-code-review”这个名字乍看平平无奇,甚至有点拗口——它既不像“Copilot”那样直击眼球,也不像“Cursor”那样自带产品感。但… · 2026/9/26 5:28:57
WorkBuddy实战指南:15个可写进简历的AI桌面Agent项目 1. 这不是又一个“AI概念课”,而是一套可直接复用的桌面生产力操作系统WorkBuddy这个词最近在技术圈里出现的频率,已经快赶上当年“Docker”刚火起来时的状态了——不是因为大家突然都爱上了桌面应用,而是因为真正用过的人发现:它… · 2026/9/26 6:06:38
B+Tree如何扛住千万级数据检索?底层原理与工程实践详解 1. 先说结论:一棵BTree凭什么扛住千万级查询干这一行久了,你会发现面试官特别爱问一句:MySQL的InnoDB索引为什么用BTree,不用B-Tree,也不用红黑树?很多人在准备阶段能背出答案,但真到线上排查慢… · 2026/9/26 6:06:38
中介效应分析实操:逐步检验法与Sobel检验避坑指南 简介:中介效应检验是社会科学、心理学与市场营销实证研究中的常用分析工具。这份Word文档系统整理了逐步检验法、Sobel检验与Bootstrap检验的操作流程,面向需要完成中介效应分析或论文实证的高校师生与科研人员。文档从原理说明、方程设定到判定标准均有… · 2026/9/26 6:06:38
JS获取客户端IP/MAC/主机名:7个方法里真正能跑的只有这几条 简介:针对前端开发中需要获取客户端IP、MAC与主机名的实际需求,内容整理成一套可对照查阅的PDF文档,适合JavaScript开发者和需要做访问来源定位、个性化展示或简单安全校验的网页工程师。文档围绕7种实现路径展开:既包括IE下通过A… · 2026/9/26 6:06:38
Oracle期末复习题实战指南:从SQL*Plus连库到PL/SQL分页查询避坑 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 6:06:38
AI Agent实战:RAG、MCP与LangGraph工程化落地指南 1. 这不是“速成课”,而是一份AI Agent开发者的实操手记你点开这个标题,大概率是被“吊打付费”“最全最细”“零基础全套”这几个词戳中了。别急,先放下期待——这不是那种“30分钟带你跑通Hello World”的短视频脚本,也不是把一… · 2026/9/26 6:06:31
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46