关于web缓存的思考
公司缓存出了些问题,解决的过程中总结了一些缓存使用的方式方法。
公司缓存遇到的问题
1) 缓存无过期时间导致内存占用过高
2) 缓存清除后,失效后仍用到,不可从数据库恢复
造成这些问题的原因是开发人员对缓存的设计和使用原则不清楚,架构上也没有明确的说明和引导。
关于上面的两个问题
1) 缓存添加时一定要有清除策略。程序状态完结时主动删除或者设置合理过期时间,不然就会造成内存泄漏,当然架构层面可以在内存资源紧张时使用一定策略进行回收,但是被强制清除的缓存如何恢复,能否恢复也是一个需要考虑的问题,正确的做法应该是清除策略和资源回收两方面同时进行,清除策略为主,资源回收只能是预防性的工作,资源回收也容易造成问题。另外,正确的监控和统计工作可以发现异常的缓存key,防止程序错误日积月累占用大量资源突发严重问题。
2) 缓存过期或者被错误清除仍用到不可从数据库恢复。缓存清除仍用到是程序设计上的问题造成的,但不可从数据库恢复涉及缓存数据恢复问题。缓存数据应该是数据库数据的某种映射,因为缓存是临时存储,本身就是不可靠的,数据库是持久存储,是可靠的,缓存是为了加速数据访问,降低数据库压力而存在的,所以缓存数据应该很容易从数据库恢复,这里把缓存当做不可恢复的持久存储用是不对的。如果需要高性能的持久存储,比如保存程序中间结果,可以考虑使用Redis等(使用时不需要恢复策略,但也需要清除策略)。简单的说,缓存作为非持久性存储,数据应该是可以很轻易的从持久存储恢复的。
那缓存应该怎样设计和使用呢?
从设计一个网站的缓存角度来考虑能想到的方面,缓存应该分类为数据库缓存 (或者说持久存储,数据都能从DB恢复)、临时数据缓存(只需要存储较短时间的中间数据,能由持久存储恢复,但时间较长,比如报表数据)、前端缓存(页面缓存、页面片段缓存、由html加动态数据混合而成,缓存已加快渲染和返回速度),这里我们只考虑持久存储缓存或者简单的说数据库缓存。
数据库缓存
定义:数据库缓存是为了加速数据访问,减轻DB压力而存在的一种临时性高速存储。
1) 缓存添加
什么样的数据应该放到缓存里?访问频繁的数据应该放到缓存里,还是说为了提高性能,把大部分数据库数据放到缓存里?
从性能优化的角度来说,不应该提前优化,应该是遇到性能问题、性能问题初漏端倪、为了预防性能问题而使用缓存(最好基于性能测试和监控),所以最开始应该为预估的热点数据设置缓存,或者遇到性能问题时为相关数据添加缓存(设置缓存应该是开发人员的工作,架构方面应提供缓存服务,也可以通过统一的数据访问层来实现,如果架构方面能根据热点数据自动应用缓存最好,多级缓存技术中,靠近底层的的缓存应该是根据预估或者统计的数据热点手动或自动添加的,更高级的缓存应该是应用程序或者说调用方添加的) 。
2) 缓存更新
缓存的更新可以分为过期更新和程序主动更新。先更新缓存还是先更新数据库?如果先更新缓存后更新数据库,这样客户使用的可能是缓存的数据,如果此时出现故障数据库更新失败,那么用户使用的数据跟数据库中数据是不对应的,所以要先更新数据库再更相信缓存,这也印证了数据库是主存储,缓存是DB数据的某种映射,是副存储的设计原则 。要更新数据库,又要更新缓存,这样在实时性要求较高的高并发场景下就肯定会遇到数据一致性的问题。 对于读多写少的需求来说,可以在写数据库后同步更新缓存,这样除了在实时性要求非常高且并发很高的情况下才会出现数据不一致的情况,大多数情况下都能运行良好。对于实时性要求较低需求来说,只需要设置缓存的过期时间,等待缓存过期后从DB重新加载。对于写非常频繁、容忍一定误差的需求来讲,先写缓存,定时刷回数据库是一种可行的方案。
3) 缓存清除
缓存清除策略是必要的,不然就容易发生内存泄漏的情况,及时清除缓存也可以为新的缓存腾出空间,提高缓存性能。清除策略应该是程序主动清除和过期时间相结合的方式,在程序层面要做清除,在架构层面要做监控、统计和在资源不足时根据一定策略清除,两方面都要做。
临时数据缓存
定义:只需要存储较短时间的中间数据。
临时数据缓存要考虑更新策略和清除策略。更新不是问题,直接更新就行,清除策略可以是主动清除,设置过期时间的话需要明确在过期时间之内不会再次用到该缓存,但是设置过期时间往往不太靠谱,因为临时数据缓存不像数据库缓存那样容易恢复,不设置过期时间因程序bug没有主动清除缓存会累积垃圾数据,不过可以通过定时清理功能来清除,但这需要依赖开发与运维协作,可能效率低下,不过由于程序bug造成累积垃圾数据是不可避免的,定期的开发与运维相配合的架构清理应该也是必要的。