“计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决”,这是计算机领域一句名言。任何问题都可以通过增加一个中间层来解决。互联网业务系统在应对大并发时候通常会选择引入缓存,当然可以Scale UP,但是响应成本上升,引入缓存是一种比较经济有效方法。在面对各种缓存更新与访问策略时候我们可能会眼花缭乱,不合适的缓存更新策略可能达不到预期效果。
为什么要引入缓存呢?
- DB查询慢,通过分库分表或者对数据库进行垂直扩展,通过索引加速查询速度。查询通常在数十毫秒这个级别,这个还是在DB负载和IO正常情况,单实际上会碰到大负载查询情况。
- 扩展成本高,通过增加SLAVE数量,再结合读写分离可以使数据库QPS达到较高水平,但是相比内存方式,基于磁盘的DB扩展成本更高
- 数据访问复杂,通常业务需要进行复杂查询,跨表甚至跨库连接。基于缓存可以轻松实现复杂查询。
引入缓存钱我们最好问自己三个问题
- 系统是否存在读多写少?
- 数据是否写入一次,多次读取?
- 数据是否始终唯一?
1 数据访问策略
1.1 Cache-Aside
Cache Aside作为最常见一种缓存更新策略,在后台使用最为广泛。
Cache Aside把工作都丢给应用层来做,适合访问数据入口较少情况。如果数据入口比较多,开发相对来说会比较麻烦。简单介绍些流程。
- 应用程序首先访问Redis,看是否命中所需数据,命中则此次访问结束。
- 若数据未命中,那么会触发读DB操作,从DB中直接读取所需要数据
- 应用程序负责把数据写入Cache,本次数据访问结束。
1.2 Read-Through Cache
在Read-Through中应用程序不再那么累了,只需关心从Cache中访问数据,不再需要从DB中读取数据。
1. 应用程序读取Cache,如果命中直接返回
2. 如果未命中,应用程序等待Cache服务从DB中读取数据。这里可以直接返回。
1.3 Write-Through
Write Through作为一种写策略,应用程序直接写入Cache,Cache服务同步将数据写入DB。
1. 应用程序写入Cache
2. Cache服务同步进行数据落地。
1.4 Write-Back
Write-Back又称Write-Behind
1. 应用程序直接写入Cache,写完Cache后直接返回。
2.Cache服务将数据异步方式落地到DB,可以是基于时间或者基于空间来作为落地策略。
这种策略通常适用于写入比较频繁,但是可以容忍异常情况下数据丢失风险。写负载很重可以考虑采用这种模式。
1.5 Refresh Ahead
Refresh Ahead写入策略和Write-Back策略类似。通过设置过期因子,当访问到该Key时候确定是否需要刷新改数据。
在过期时间达到因子之后,发起异步更新。如果未到则直接返回数据。超过间隔时间则同步访问。
假设更新时间为m,单位为秒,更新因子为p(范围0-1)
1 应用程序访问Cache,如果距离上次更新时间小于m*p,那么可以直接使用Cache数据
2 如果距离上次访问时间大于m*p,小于m,那么触发异步更新,同时返回数据给应用。异步更新负责将DB数据写入DB
3 如果距离上次访问时间大于m秒,那么只能同步访问DB。
1.6 Write By UDF
MySQL提供用户定义函数和触发器,集合两者可以实时知道数据更新。通过编写MySQL UDF插件,结合插入或者更新触发,将数据写入Redis.
这种方式对于数据风险比较大,需要侵入MySQL.如果MySQL插件崩溃,很可能倒数MySQL崩溃,所以这种方式风险是比较高的。
1.7 Write By BINLOG
我们知道MySQL主从复制是通过Replication来实现的,在Master进行更新后会将变更数据以binlog形式写入binary log
Slave中的IO线程从Master获取数据,然后写入relay log。Slave同时会启动一个SQL Thread将Relay Log中的数据写入Slave.以此来达到主从同步。
通过模拟Slave从MySQL获得增量更新数据,同时结合MySQLdump获取全量现存数据。可以实现MySQ增量更新。
注:文中部分图片引自 https://codeahoy.com/2017/08/11/caching-strategies-and-how-to-choose-the-right-one/