数据异构缓存策略

Posted 白色的野骆驼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据异构缓存策略相关的知识,希望对你有一定的参考价值。

一、数据异构方案(以mysql数据库+redis缓存为选型方案)


1、双写

2、通过消息中间件写入缓存

3、通过获取数据库binlog日志写入缓存


以下来简述下这3种方案的流程和优缺点


双写:


主线程写入数据库,子线程写入缓存,也就是使用多线程方案,异步执行。也可以使用串行同步方案,但是更加推荐使用多线程方案。

  • 优点:双写方案相比其他两种,使用逻辑更为简单,减低风险点

  • 缺点:与业务逻辑耦合性高;存在数据不一致问题


通过消息中间件写入缓存:


引入消息中间件MQ(kafka,RocketMQ,RabbitMQ等),在写入到数据库之前做一道切面,把写入数据库的数据投递一份到MQ中,再由订阅MQ程序写入缓存中。

  • 优点:与业务代码解耦,不会对业务代码造成大量的代码入侵;MQ有重试机制,在写入缓存失败的时候,可以进行多次重试操作。

  • 缺点:大多数MQ本身没有提供消息的有序性,为了保证消息有序性的,采用串行发送,减低了写入缓存的效率;加大了系统的复杂性;存在数据不一致问题



通过获取数据库binlog日志写入缓存:


mysql主从备份的方案,就是通过binlog日志进行备份,所以,也可以利用binlog日志来进行缓存的写入。订阅binlog程序,监听binlog日志,当数据库的数据变更时,数据库会将操作信息写入binlog日志,订阅binlog程序获取更新的日志,把信息写入到缓存中。

  • 优点:与业务代码解耦,不会对业务代码造成大量的代码入侵;技术成熟,稳定性高;

  • 缺点:我们知道,有很多缓存数据并不是单一数据,而是聚合数据(举个例子:订单列表里,除了有订单信息,还有订单明细),binlog里面的日志信息,是单一数据信息,这样如果要重新聚合数据,难免会重新查询数据库,以得到聚合数据信息,给数据库增加了很多查询压力;性能不高,串行消费速度低;串行分发时,有堵塞住的风险(为了避免主库堵塞风险,可以采用读取从库的方案,但同时会带来查询效率低的问题);同时也存在数据不一致问题;


二、当数据变更时,删除缓存还是更新缓存


关于删除缓存还是更新缓存这个问题,我的意见是:不能一刀切,得需要结合业务和技术方案来确定的。


一般来说,当数据结构比较复杂,而更新多查询少的场景,会优先考虑删除缓存。

理由很简单,因为数据结构比较复杂,往往后牵动多张表,有可能会反查数据库数据,需要多表数据运算才能得出结果,这时候,更新缓存的代价就比较大了,而且如果更新数据比较多的情况下,就会频繁更新缓存数据,就会造成很多额外没有必要的性能开销。所以,这种情况下,是要优先考虑删除缓存。


但是也有一些业务场景会考虑更新缓存,一般体现在用户操作完数据后,希望立马见到结果,同时系统访问压力又有可能比较大的情况下,例如:用户下单支付后,跳转到订单列表页,同时也是大促时间段(秒杀等)。


三、技术方案


删除缓存的方案,使用的场景比较多,我主要想描述下以上面的例子,设计更新缓存的方案。


设计思路

订单列表(业务模块):

  1. 我们先考虑订单列表的情况,一般情况下,大多数用户下单完后,只浏览第一页的订单数据,所以,我们只要在缓存中保存第一页的数据即可。

  2. 在做列表分页缓存的时候,我们使用redis的sortset结构来存储订单id,指令zrevrange来实现分页和排序,使用Hash结构来存储每一条订单信息。

  3. 我们可以把第一页的订单列表数据当初一个对象来看待,由此,我们可以引入一个版本号的概念,主要用于后续的数据库和缓存校验订正。

  4. 查询订单,在查询订单的时候,先获取最新第一页的订单列表数据的版本号,redis的key拼接上版本号,找到更新的缓存第一页的订单列表数据。


下订单(业务模块):

使用线程池,主线程把订单数据保存到数据库,子线程异步把订单数据保存到缓存,把旧的订单id列表数据合并到新的订单id列表数,同时更新最新版本号(注:采用最新一条数据的版本号作为第一页的订单列表数据的版本号),订单信息添加到Hash结构中。


数据库和缓存数据校验订正(非业务模块):

  1. 开启数据库的binlog日志;

  2. 使用canal(https://github.com/alibaba/canal),订阅binlog日志,获取更新的数据信息;

  3. 从binlog日志中,获取最新订单数据的版本号,与缓存中的第一页的订单列表数据的版本号做对比,不一致的话,执行数据补偿或者执行通知程序。


四、思考


为什么会选择双写方案呢?


在实际项目中,以实际业务和团队效率为第一位考虑,如果使用中间件MQ的方案,会在增加系统的复杂度,让团队的成本增加,无形的增加项目风险性,虽然可以解耦,但是到头来,说不定还适得其反。


第三种使用binlog日志,就如同上述所说的,缺点一样,本来缓存的目的就是为了抗压,结果有可能还增加了数据库的压力。


技术的目的是解决问题的,所以,能够最好解决问题的方案就是好方案。



©白色的野骆驼

数据异构缓存策略

bug与你同在

以上是关于数据异构缓存策略的主要内容,如果未能解决你的问题,请参考以下文章

缓存策略的选择

高并发系统:缓存使用-读写策略

LRU缓存替换策略及C#实现

缓存策略:你需要了解的一切

缓存策略,输出缓存与数据缓存还是两者兼而有之?

缓存策略