当前位置: 首页 > 科技观察

缓存架构设计细节两三件事

时间:2023-03-23 01:49:15 科技观察

本文主要讨论以下问题:(1)“缓存与数据库”需求的由来(2)“消除缓存”或“更新缓存”(3)缓存和数据库的运行时序(四)缓存和数据库架构简析1.需求产生场景缓存是提高系统读取性能的常用技术。对于读多写少的应用场景,我们往往会使用缓存来优化。例如用户的余额信息表account(uid,money),业务需求为:(1)查询用户余额,SELECTmoneyFROMaccountWHEREuid=XXX,占99%的请求(2)更改用户的余额,UPDATEaccountSETmoney=XXXWHEREuid=XXX,占请求的1%由于大部分请求都是查询,我们在缓存中创建uid到money的键值对,可以大大减轻数据库的压力。读取操作流程在数据库和缓存有两个存放数据的地方(uid->money)之后,每当需要读取相关数据(money)时,操作流程一般如下:(1)读取是否有缓存中相关数据Data,uid->money(2)如果缓存中有相关数据money,则返回[这就是所谓的数据***“命中”](3)如果没有相关数据缓存中的money,然后从数据库money中读取相关数据[这就是所谓的数据“未命中”],放入缓存uid->money中,然后返回缓存命中率=***缓存请求数/总缓存访问请求数=命中/(命中+未命中)上面举例的balance场景,99%的读,1%的写,这个缓存的攻击率很高,会在95%以上。那么当数据钱发生变化的时候问题来了:(1)缓存中的数据应该更新,还是应该淘汰缓存中的数据?(2)应该先操作数据库中的数据,再操作缓存中的数据,还是应该先操作?在缓存中的数据之后操作数据库中的数据怎么样?(3)缓存和数据库操作的架构是否有优化空间?这是本文关注的三个核心问题。2.更新缓存VS淘汰缓存什么是更新缓存:数据不仅写入数据库,还会写入缓存什么是淘汰缓存:数据只会写入数据库,不会写入缓存,而且只会从更新缓存中剔除优点:缓存不会增加miss,效率高。消除缓存的优点:简单(我去,我也觉得更新缓存很简单,楼主你太敷衍了)那你是选择更新缓存还是消除缓存呢?,主要取决于“更新缓存的复杂性”。比如上面的场景,简单的给balancemoney设置一个值,那么:(1)清除缓存的操作是deleteCache(uid)(2)更新缓存的操作是setCache(uid,money)的更新缓存的代价很小,这个时候我们应该更倾向于更新缓存,保证更高的缓存效率。如果余额是通过非常复杂的数据来计算的,比如在业务中,除了account表account,还有product表、折扣表discountaccount(uid,money)product(pid,type,price,pinfo)discount(type,zhekou)业务场景是用户购买商品商品,该商品的价格为price,该商品属于type商品类别,type商品促销活动需要打折。购买产品后,余额计算复杂。需要:(1)先取出商品的类别和价格:SELECTtype,priceFROMproductWHEREpid=XXX(2)再取出该类别的折扣:SELECTzhekouFROMdiscountWHEREtype=XXX(3)然后从缓存中查询原来的余额money=getCache(uid)(4)然后将新的余额写入缓存到setCache(uid,money-price*zhekou)更新缓存的代价非常大。这时候,我们应该更倾向于淘汰缓存。但是,消除缓存很容易,副作用只是增加了缓存未命中。建议作为一般处理方法。3.先操作数据库vs.先操作缓存。好的。当发生写操作时,假设清除缓存是对缓存的一般处理方式,有两种选择:(1)先写数据库,再清除缓存(2)先清除缓存,再写入数据库使用哪个时序?还记得《冗余表如何保证数据一致性》文章(点击查看)“先写正表还是先写负表”的结论?对于一个不能保证事务性的操作,必然涉及到“先做哪个任务,后做哪个任务”的问题,就是要解决这个问题:如果有不一致,谁先做对业务的影响就小,谁先做。因为写入数据库和清除缓存并不能保证原子性,所以谁先来也必须遵循以上原则。假设先写入数据库,再清除缓存:第一步写入数据库成功,第二步清除缓存失败,DB包含新数据,而Cache包含旧数据,并且数据不一致。假设先清除缓存,再写入数据库:第一步清除缓存成功,第二步写入数据库失败,只会触发一次Cachemiss。结论:数据和缓存操作的时机,结论很明确:先清除缓存,再写入数据库。4.缓存架构优化上述缓存架构有个缺点:业务方需要同时关注缓存和DB。是否还有进一步优化的空间?常见的方案有两种,一种是主流方案,一种是非主流方案。射击)。主流的优化方案是面向服务的:增加服务层,向上游提供帅气的数据访问接口,对上游屏蔽底层数据存储的细节,让业务线不需要关注是否数据来自缓存或数据库。非主流方案是异步缓存更新:业务线的所有写操作都去数据库,所有读操作一直缓存。异步工具用于同步数据库和缓存之间的数据。具体细节是:(1)有一个initcache进程,将所有需要缓存的数据写入缓存(2)如果DB有写操作,异步更新程序读取binlog,更新缓存在(1)和(2)的配合下。这样:(a)业务线读缓存,一定能命中(短时间内可能有脏数据),不用关注数据库(b)业务line写入DB,可以异步更新缓存,无需关注缓存,这将大大简化业务线的调用逻辑。缺点是如果缓存数据的业务逻辑比较复杂,async-update异步更新的逻辑也可能比较复杂。5.其他未尽事宜本文仅讨论缓存架构设计中需要注意的几个细节。如果数据库架构采用读写分离的主多从架构,在特殊时序下,很可能导致数据库和缓存之间出现问题。不一致,这个不一致怎么优化,我们在后续的文章中讨论。6.结论强调(1)剔除缓存是一种通用的缓存处理方式(2)先剔除缓存再写入数据库的时机是毋庸置疑的(3)服务化是为了屏蔽底层数据库和缓存的复杂性业务端通用方法【本文为专栏作者《58神剑》原创稿件,转载请联系原作者】