ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Redisson 事务与 RLocalCachedMap 本地缓存不一致,3 个实战解法

2026/9/8 16:13:25 拓冰建站 浏览量
Redisson 事务与 RLocalCachedMap 本地缓存不一致,3 个实战解法 Redisson 事务与 RLocalCachedMap 本地缓存不一致3 个实战解法【免费下载链接】redissonRedisson: Valkey Redis Java Client and Real-Time Data Platform. Sync/Async/RxJava/Reactive API. Over 50 Valkey and Redis based Java objects and services: Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Spring, Tomcat, Scheduler, JCache API, Hibernate, RPC, local cache..项目地址: https://gitcode.com/GitHub_Trending/re/redisson用 Redisson 做分布式缓存时RTransaction负责事务原子性RLocalCachedMap负责加速读。两者单独用都没问题混在一起时却容易翻车commit()明明返回成功另一个实例再从本地缓存读拿到的还是旧值。这篇文章带你定位这个不一致从哪来并给出 3 个按实施成本排序的解法。一、先复现这个坑场景库存扣减走事务读路径走本地缓存。// 节点 A事务内写入并提交 RLocalCachedMapString, Integer stock redisson.getLocalCachedMap(stock:product); RTransaction tx redisson.createTransaction(); RLocalCachedMapString, Integer txStock tx.getLocalCachedMap(stock); // 事务代理必须基于已有实例获取 txStock.put(product_1001, 99); tx.commit(); // 节点 B或同节点的非事务实例立刻读 int v stock.get(product_1001); // 可能仍是旧值症状通常是间歇性的并发高、网络抖的时候才出现。这说明问题不在某一行代码而在两套机制的衔接处。二、原理拆解两套机制在哪里错位先说结论本地缓存不是事务的参与者它的同步信号来自写入真正落库之后的广播而不是commit()本身。RLocalCachedMap每个实例各自维护一份本地缓存靠 Redisson 在 entry 变更后广播失效/更新消息来保持同步。本地缓存机制文档对同步策略有详细说明。RTransaction的隔离级别是READ_COMMITTED写操作会加锁并把变更缓存在客户端commit()才批量落库事务文档。⚠️reconnectionStrategy默认是NONE连接断开期间本地缓存静默保留旧值没有任何报错。所以不一致的触发点有三个提交后广播消息还在路上竞态、实例断线期间漏收消息、读方拿本地缓存的值当库里的值参与业务决策。三、实战解法按成本从低到高解法 1commit 之后显式清空本地缓存适用条件写频率不高、只想先止血。// 这段代码解决提交后所有实例的本地缓存强制对齐 RLocalCachedMapString, Integer stock redisson.getLocalCachedMap(stock:product); RTransaction tx redisson.createTransaction(); RLocalCachedMapString, Integer txStock tx.getLocalCachedMap(stock); txStock.put(product_1001, 99); tx.commit(); stock.clearLocalCache(); // 广播到所有实例下次读必回源取舍一行代码解决竞态和断线遗留代价是该 Map 全量回源一次适合低频写场景。解法 2换 UPDATE 同步策略 防断线兜底适用条件读多写少希望其他实例直接拿到新值而不是只剩洞。// 这段代码解决把只发失效通知升级为广播完整 entry并处理断线场景 LocalCachedMapOptionsString, Integer options LocalCachedMapOptions.String, Integername(stock:product) .cacheSize(1000) .evictionPolicy(LocalCachedMapOptions.EvictionPolicy.LRU) .syncStrategy(LocalCachedMapOptions.SyncStrategy.UPDATE) // 默认 INVALIDATE 只删不补 .reconnectionStrategy(LocalCachedMapOptions.ReconnectionStrategy.CLEAR); // 断线重连后清缓存避免静默过期 RLocalCachedMapString, Integer stock redisson.getLocalCachedMap(stock:product, options);取舍广播内容从 16 字节哈希变成完整 keyvalue网络开销变大换来的是各实例无需回源。CLEAR/LOAD二选一前者简单后者能在断线 10 分钟内做增量补回。解法 3决策型读绕开本地缓存 分布式锁适用条件扣减库存这类值决定钱的路径一致性优先级高于性能。// 这段代码解决用真实数据源做读-校验-写本地缓存只承担普通读 RLock lock redisson.getLock(stock:lock:product_1001); lock.lock(3, TimeUnit.SECONDS); try { RMapString, Integer real redisson.getMap(stock:product); Integer old real.get(product_1001); // fastRemove(key, value) 是原子比较并删除等价于 CAS if (old ! null real.fastRemove(product_1001, old)) { real.put(product_1001, old - 1); } } finally { lock.unlock(); }取舍最稳但最慢只用在临界路径上日常读仍然走RLocalCachedMap享受加速。四、避坑清单❌ 在事务内用非事务实例读自己刚 put 的值——READ_COMMITTED下你只能看到已提交数据事务内请用transaction.getLocalCachedMap(instance)拿到的代理。❌ 以为 Redisson 提供了事务提交监听器挂到 commit 事件上——它没有这个内置钩子提交后的一致性靠clearLocalCache()或缓存自身的同步广播。⚠️ 集群模式下事务涉及多个 key 要加{hashTag}保证同 slot否则会抛 CROSSSLOT。⚠️ 排查时用stock.cachedKeySet()看看本地缓存里到底存了哪些 key别凭猜。五、选型建议场景推荐做法低频写、先止血解法 1commit 后clearLocalCache()读多写少、多实例解法 2UPDATECLEAR/LOAD扣减/资金类关键路径解法 3锁 真实数据源 CAS延伸阅读事务官方文档、客户端缓存文档、核心实现。上线前建议用并发测试覆盖提交瞬间 断线重连两个时刻验证你选的方案在你的流量模型下是否够用。【免费下载链接】redissonRedisson: Valkey Redis Java Client and Real-Time Data Platform. Sync/Async/RxJava/Reactive API. Over 50 Valkey and Redis based Java objects and services: Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Spring, Tomcat, Scheduler, JCache API, Hibernate, RPC, local cache..项目地址: https://gitcode.com/GitHub_Trending/re/redisson创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考