在erp中对主业务实体使用read_write缓存策略大概率错误,因其依赖的细粒度锁在redis中无法实现,易引发高并发阻塞;应改用nonstrict_read_write配合业务版本号校验。

直接说结论:在大型 ERP 系统中,用 Redis 做 Hibernate 二级缓存能显著降低数据库压力,但**不能无差别开启所有实体的缓存**,否则会放大脏数据风险、拖慢事务提交、甚至引发跨节点缓存不一致——尤其当 ERP 中存在大量主子表联动更新(如采购单+明细行)、状态机流转(如审批流)时,缓存策略选错比不用更危险。
为什么 READ_WRITE 在 ERP 主业务实体上大概率是错误选择
ERP 的核心单据(如 Order、Invoice、StockMovement)常被多模块并发修改。Hibernate 的 READ_WRITE 策略依赖缓存层加锁来保证一致性,但 Redis 本身不提供细粒度行级锁;Redisson 的 READ_WRITE 实现会退化为全局锁或基于时间戳的乐观校验,导致高并发下大量请求阻塞在 cache.get() 上。
实操建议:
- 对单据头(header)类实体,优先用
NONSTRICT_READ_WRITE——它不加锁,靠“写后失效”(write-through + invalidation)机制,配合 ERP 自身的业务版本号(如version字段)做应用层校验 - 对明细行(line item)类集合,禁用二级缓存;改用查询缓存(
use_query_cache)+ 显式命名缓存区域(如@Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "order_lines")),并设置较短 TTL(如 60 秒) - 绝对不要给带
@Formula或数据库函数计算字段的实体启用缓存,Hibernate 无法感知其底层依赖变更
hibernate.cache.region.factory_class 必须匹配 Hibernate 版本
你用的是 Hibernate 5.4 还是 6.2?这决定了你该引入哪个 Redisson 模块:redisson-hibernate-53 对应 5.3.3–5.6.x,redisson-hibernate-6 对应 6.0.2+。混用会导致 ClassCastException 或缓存静默失效——比如 RegionFactory 初始化成功,但 EntityRegion 写入时因序列化器不兼容而跳过,日志里却没有任何报错。
常见错误现象:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 实体查得到,但第二次查询仍打数据库(缓存未命中)
- 启用了
statistics,但secondLevelCacheHitCount始终为 0 - Redis 中能看到 key(如
org.example.Order:123),但 value 是乱码或空字节数组
根本原因通常是序列化器不匹配。Redisson 默认用 JDK 序列化,而 Hibernate 6 要求使用 StringRedisTemplate 风格的字符串键 + 可配置的值序列化器。必须显式配置:
<property name="hibernate.cache.redis.serializer">org.redisson.codec.JsonJacksonCodec</property>
并确保所有节点的实体类字段类型与 Jackson 兼容(避免 LocalDateTime 未注册模块、byte[] 被转成 Base64 后体积暴增等问题)。
缓存穿透与雪崩在 ERP 中的真实表现
ERP 常有批量导入、定时任务拉取历史单据等场景。如果缓存键设计为 Order:id,而某次任务传入 10 万个无效 ID(如已归档/删除的单据),这些请求会全部穿透到 DB,瞬间压垮连接池——这不是理论风险,是真实发生过的生产事故。
解决方案不是加布隆过滤器(ERP 单据 ID 无规律、无法预热),而是从 Hibernate 层拦截:
- 在自定义
RegionFactory中重写buildEntityRegion,包装EntityRegion,对get()返回null时自动写入一个空占位符(如"NULL"字符串),TTL 设为 5 分钟 - 对所有主键查询(
session.get()、session.load())强制走缓存,禁用session.bySimpleNaturalId()等绕过缓存的 API - 在 DAO 层统一用
@Cacheable(key = "'order_' + #id")(Spring Cache)兜底,与 Hibernate 二级缓存形成双保险,但注意清除逻辑必须同步触发
最易被忽略的一点:ERP 中的“组织机构”“用户权限”这类基础数据,看似适合 READ_ONLY,但一旦后台管理端做了热更新(如动态调整角色权限),Redis 缓存不会自动感知数据库变更。必须在修改操作后,显式调用 CacheRegion.evictAll() 或发送 Redis Pub/Sub 消息触发集群内所有节点清理对应 region —— 这个动作不能只写在 Service 层,要下沉到 Repository 或自定义 Hibernate Interceptor 中,否则运维手动 flush 就成了常态。










