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

几种缓存更新的设计方法,值得一看

时间:2023-03-17 12:13:03 科技观察

几种缓存更新设计方法值得一看缓存更新设计,由于本人经验有限,所以这些缓存设计均来自于网上,在此简单总结一下,如有不妥欢迎指出。CacheWarming解决Cache冷启动什么是CacheWarming?我们都知道跑前热身可以防止肌肉拉伤等好??处。所以cachewarming的原理是一样的。我们的新系统上线后,直接将相关的缓存数据加载到缓存系统中即可。这样可以避免用户请求时先查询数据库,再缓存数据的问题。用户可以直接查询预先预热好的缓存数据。缓存预热其实就是为了解决缓存冷启动的问题。我们新系统上线后,redis集群启动后,没有缓存数据。这就是redis的冷启动。如上图所示,如果不进行预热,Redis初始状态数据将为空。在系统上线初期,高并发流量会访问数据库,对数据库造成流量压力。如何解决我们已经知道会有缓存预热的问题,所以我们要思考对策。可以分析以下两点:需要对访问频率高的热点数据进行统计,使用LRU数据删除策略构建数据保留队列。因此,我们可以设计如下解决方案:首先,通过nginx+lua,将访问流量数据上报给Kafka,也可以是其他mq队列。然后使用实时计算框架(如storm、sparkstreaming、flume)消费来自kafka的访问流量数据,实时计算访问频率高的数据。这里统计的可能只是序号信息,比如产品ID或者博客ID等,最后根据序号从mysql数据库查询具体信息,写入redis,开始提供服务。缓存更新的几种设计1.先删除缓存,再更新数据库。虽然这是一个错误的方法,但是这个设计也是一种缓存更新的方法,所以大家还是要知道为什么不能这样做。还是那句话:知道为什么。这种方法是在更新数据库的时候先删除缓存,然后再更新数据库,后面的操作都会把数据加载到缓存中。这个逻辑在并发的时候会先把数据弄脏,如下图所示:解释一下上面的操作,有两个并发操作,一个是更新操作,一个是查询操作。更新操作删除缓存后,查询操作不会命中缓存。首先将旧数据读出放入缓存,然后更新操作更新缓存。数据库。所以缓存中的数据还是旧数据,导致缓存中的数据脏了,一直这样脏下去。所以这个设计是错误的,不推荐。2.Cacheaside这个是我们最常用的设计模式,它的逻辑如下:查询:程序先从缓存中获取数据,直接返回数据,没有获取到数据,再进行更新成功后到缓存中。update:先将数据存入数据库,成功后再使缓存失效。这种设计正好可以解决上面的脏数据问题。我们来看一下,一个是查询操作,一个是并发更新操作。没有删除缓存数据的操作,而是先更新数据库中的数据。这个时候缓存还是有效的,所以并发查询操作取最重要的是没有更新的数据,但是更新操作马上就让缓存失效了,后续的查询操作把数据拉出数据库。而不是文章开头的逻辑导致的问题,后面的查询操作总是在取旧数据。那么这样设计就没有并发问题了吗?不是,比如一个是读操作,但是没有命中缓存,然后从数据库中取数据。这时,发生写操作。数据库写入后,缓存失效。数据是放入的,所以会造成脏数据。不过这种情况理论上会发生,但实际发生的概率可能很低,因为这种情况需要发生在读缓存和缓存失效的时候,并且有并发的写操作。事实上,数据库的写操作会比读操作慢很多,而且表必须加锁,而且读操作必须在写操作之前进入数据库操作,比写操作晚更新缓存。这些条件都满足的概率基本不大。我们可以给缓存设置一个过期时间,可以有效的解决这个问题。3.Read/WriteThrough模式实际上是以缓存服务为主存储。应用程序的所有读写请求都直接与缓存服务打交道,与端到端的数据库无关。数据库中的数据由缓存服务维护和更新。但是,当缓存中的数据发生变化时,数据库会同步更新。在应用程序看来,只有缓存服务。过程如下:ReadThroughReadThrough例程是在查询操作期间更新缓存。也就是说,当缓存失效(过期或LRU被换出)时,调用者负责将数据加载到缓存中,而ReadThrough使用缓存服务自行加载,因此对应用程序是透明的边。WriteThroughWriteThrough例程类似于ReadThrough,但发生在更新数据时。当有数据更新时,如果没有命中缓存,直接更新数据库返回。如果命中缓存,则更新缓存,然后由Cache自己更新数据库(这是一个同步操作)。这种模式的特点是脏数据出现的概率比较低,但是对缓存的依赖性很强,影响了缓存服务的稳定性。有比较大的要求。另外,在添加新的缓存节点时,会出现初始状态为空数据的问题。4.WriteBehindCachingWriteBehindCaching也称为WriteBack。更新数据时,只更新缓存,不更新数据库。缓存会异步地批量更新数据库。这种设计的好处是可以快速进行数据I/O操作,异步操作还可以组合对同一个数据的多个操作,在性能上是非常可观的。但是它带来的问题是数据不是强一致的,可能会丢失。在软件设计中,我们基本上不可能做出没有缺陷的设计,就像算法设计一样,时间换空间,空间换时间。有时候,强一致性和高性能、高可用和高性能是矛盾的。软件设计一直是一个权衡取舍。另外,WriteBack的实现逻辑比较复杂,因为需要跟踪哪些数据被更新了,需要刷到持久层。操作系统的回写只有在缓存需要失效的时候才会真正持久化,比如内存不够用,或者进程退出等,这也叫惰性写。这种模式的特点是速度快、效率高,但是数据一致性比较差,可能会出现数据丢失,实现逻辑也比较复杂。综上所述,上面提到的缓存更新设计是一些前辈使用的总结。这些设计并不完美。这个世界上没有完美的设计,所以我们的设计或多或少都会有问题,比如我们没有考虑缓存(Cache)和持久层(Repository)整体事务的问题。比如缓存更新成功,数据库更新失败怎么办?或相反亦然。关于这件事,如果需要强一致性,就要慎重考虑如何解决这个问题。在软件开发或设计中,我强烈建议大家参考已有的设计和想法,看看相应的指南、最佳实践或设计模式,彻底了解这些已有的东西,再决定是否重新发明轮子。不要似是而非,认为软件设计是理所当然的。