今年上半年,系统一直在重构,所以上半年,我的思路基本上就是按照展示层、业务层、缓存层和数据库层,这四个层次来和团队一道分层来梳理可以优化的点。对于展示层,我们H5,iOS和Android端做了很多组件化的工作;业务层的优化工作主要是微服务化的改造,这两块,我后续会开专题一一讨论。对于数据库层改造的部分工作,大家可以参考我之前写的《微服务架构优化实践》,今天我的话题会放在缓存层上,和大家分享一下我过去几个月做的思考。
1. 缓存记录不规范,增大系统开销
我去年加入团队后,发现一个比较严重的问题:一些同学喜欢把各种数据扔在缓存里,缓存里的数据格式缺乏一个统一的规范,甚至有一些KV的记录有几百k的大小,但在实际读的时候,只会用到这条记录的某几个字段,意味着程序需要从缓存去读取记录,然后再通过代码从记录中拆解出需要的字段。这就造成了我们使用了缓存,但系统开销反而比,直接从数据库对应的表中读取这个字段更大了。更可怕的事情在于,大家开始习惯制造更多“大而全”的缓存记录。
2. 缓存的命中率不高,DB的压力没有得到足够的缓解
由于第1点的原因,当缓存单个对象数据的时候(例如:单个用户的在投金额),只有当该对象对应的数据发生变化时,我们才需要更新缓存或者让移除缓存。而当我们用一条记录缓存用户的所有属性的时候,其中任何一个对应的数据发生变化时,都需要更新或移除缓存,从而导致缓存命中率会降低,查询完缓存还需要再去查询数据库,系统的负担依然在那里。
缓存数据规范化的解决方案
由于#1和#2的原因,以及前一篇提到的缓存中的坑,缓存数据规范化处理已经到了刻不容缓的时候了。所以,根据我们自身的业务,先对现有缓存数据进行了分类:
强一致性缓存:无法接受从缓存拿到过期的数据 (比如用户的在投金额/余额)
弱一致性缓存:能接受在一段时间内从缓存拿到过期的数据 (比如产品的销量/总的购买人记录)。
不变型缓存:缓存key对应的value不会变更(比方说公司用户基本属性)。
反过来说,这三种缓存类型对应了“核心业务数据”,“产品/订单相关数据”,“非业务普通数据”。所有的缓存数据的存储和分类,也是依赖于这个准则。对于缓存粒度的控制,业务层定义的基本对象,保证比较高的缓存命中率。
缓存集群的分类管理
做好缓存的数据类型分类后,我们就可以灵活的管理我们的redis缓存集群:
1. 如果对应的业务库中的数据,需要有强一致性缓存要求, 那么redis开RDB/AOF持久化,保证数据不会丢失,硬件上选用SSD的硬盘。实时的读写全部走redis,然后异步回写到数据库。由于redis集群是不能100%保证强一致性的,有些访问量不高的重要数据,建议直接读写数据库,来保证强一致性。
2. 对于弱一致性的缓存的要求,那么高可用变成了首要任务,在配置上面,会保证集群的稳定性,比方说,对于redis cluster:
a) 默认的cluster-node-timeout为15s,可以适当增大;
b) 避免使用会引起长时间阻塞的命令,比如save/flushdb等阻塞操作,或者keys pattern这种慢查询。
3. 对于不变型缓存,会单独做一个小的集群,有一个定时任务,如果数据库发生了数据改动,会主动更新缓存,同时也会定期做一次同步。
本文中提到的redis cluster服务平台化,包括里面的细节和坑就不在这里展开谈了,网上有很多资料可以查询,如果有必要,我后续也会写一篇自己经历过的redis cluster的使用心得和踩过的坑。
扫描二维码或手动搜索微信公众号【架构栈】: ForestNotes
网友评论