一个与 Ehcache 相关的死锁案例

最近朋友分享一个与 Ehcache 相关的死锁案例,记录下。

0. 一个思考题

缓存击穿是指高并发的请求访问同一个 key,当这个 key 失效后,所有的请求都会传递到数据库,数据库因而崩溃。

为了防止数据库崩溃,我们需要在应用层面拦截这些请求,比如控制一个应用加载一个 key 时只有一个请求到数据库。

如果用加锁的方法,每个 key 一个锁,那么 100 万个 key 就会有 100 万个锁实例在 JVM 里。如果 key 过期了,锁实例可能还得过期,特麻烦。

如果所有的 key 共享同一个锁,那么这个锁就会成为瓶颈。

该如何控制是好?

1. 背景说明

一个面向海量用户的 api 服务,需要提供用户信息查询的功能。

缓存有两层,第一层是 JVM 内基于 Ehcache 的,第二层是 memcached ,查找缓存时首先从本机 Ehcache 查询,找不到再去 memcached 查找,还找不到则从 Loader 接口加载数据并存入相应的缓存。

用户信息由 会员信息、个人信息、家庭信息 等组成,这些信息位于不同的模块,存储在不同的表或库里。

在 Ehcache 里,用户信息有一层缓存,会员信息、个人信息、家庭信息 也有对应的缓存。

以 ID 为 “007” 的用户举例,用户信息缓存对应的 key 为 “User:007″,会员信息对应的缓存 key 为 “member:007″,个人信息对应的缓存 key 为 “personal:007″,家庭信息对应的缓存 key 为 “family:007″。
其中用户信息对应的缓存的过期时间是比较短的。

当用户信息在缓存里都查找不到时,它的 Loader 实现会调用 MemerbService 加载会员信息、调用 PersonalService 加载个人信息,FamilyService 加载家庭信息,然后合并成用户信息保存在缓存里。MemerbService/PersonalService/FamilyService加载信息时也是先从缓存查找,两级缓存都查找不到时也调用对应的 Loader 从表里加载数据。

使用的 Ehcache 的版本是 ehcache-core-2.6.9 。

继续阅读