NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netcore,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode。
整个系列教程会大量结合示例代码和运行日志来进行深入分析,蕴含多年开发经验于其中,代表作有百亿级大数据实时计算项目。
开源地址:https://github.com/NewLifeX/X (求star, 770+)
单对象缓存
前文提到了以sql为key的数据层缓存,以及整表缓存的实体列表缓存,各自有其优缺点,适用于不同场合。
当单表数据较大(10万+)时,两者就无能为力了。天空一道巨响,对象字典缓存隆重登场!
对象字典缓存:以主键为key,缓存实体对象,以满足应用层的高频单点查询需求!
例程跑起来:
先给学生表加了100万行,再随机生成1024个编号,然后查询1000万次。速度200万qps,命中率99.99%。
缓存本质
Student.FindByID内使用了单对象缓存:Meta.SingleCache[id];
单对象缓存本质就是并发字典,因此使用方式上只需要直接索引器查找即可。
在Meta.SingleCache内查找id时,如果已存在,则直接返回,否则执行委托Student.FindByKey查找对象后缓存起来。
非XCode用户,也可以根据该思想设计自己的缓存。
单对象缓存也有过期时间,默认10秒,过期后异步更新(老规矩,为了性能)。
单对象缓存还会根据LRU定期清理缓存,此时采用最后访问时间而不是过期时间。
对象缓存还有最大缓存数限制,默认10000个,超过时删除最久未访问缓存数据。
因此,单对象缓存特别适用于单行特点很突出且修改不多的数据,如用户表、产品表等。
即使目标表有千万级数据,单对象字典缓存仍然可以轻松的取敌首级!^_^
从键查询
前面实例展示了根据ID查找缓存对象,实际应用场景,还可能会根据名称进行查找,总不能另外搞一个对象缓存吧?
来自深圳的海洋饼干,很有创意的解决了这个问题,发明了从键缓存!
如上,Meta.SingleCache.GetItemWithSlaveKey(name) 实现了根据 name 查询用户。
在此之前,需要手工配置查询方法,因为XCode已经无法猜测得知。
使用从键缓存后,等于有两个入口(FindKey/FindSlaveKey)查询数据库,得到一个实体对象后,分别加入主键字典和从键字典,用于两个维度索引查询。
因此,从键缓存需要配置根据从键查数据库的方法FindSlaveKeyMethod,同时还需要配置实体对象获取从键值的方法GetSlaveKey,因为主键查库后加入从键缓存时,需要从对象内得到从键值。
过期策略
所有缓存都必须有过期策略。单对象字典缓存的过期策略有以下:
- 初始化。首次访问缓存时,无需阻塞,并行查询。
- 定时过期。缓存过期后,开异步线程更新并同时返回旧数据,确保应用层性能。设置文件的 SingleCacheExpire, 默认10秒
- 添删改过期。对实体类的添删改操作完成后,都会直接修改缓存对应项。
显然,初始化加载以后,将来访问的永远是定时更新的缓存数据,应用层可以得到非常好的性能!
由于缓存的添删改过期跟实体操作绑定在一起,因此,越过实体类直接DAL执行更新操作,或者其它服务器修改数据,此时无法影响实体缓存,导致数据更新不及时。
早期版本XCode缓存默认过期时间60秒,随着数据库性能提升,默认值修改为10秒,可根据实际场景设置。