Go猜想录
大道至简,悟者天成
手搓缓存层 -- #3 缓存模式

缓存模式

常用的缓存模式有:

  • cache-aside
  • read-through
  • write-through
  • write-back
  • refresh-ahead

Cache Aside

Cache Aside:

  • 把 Cache 当成一个普通的数据源
  • 更新 Cache 和 DB 都依赖于开发者自己写代码

业务代码可以做决策:

  • 未命中的时候是否要从 DB 取数据。如果不从 DB 取,可以考虑使用默认值进行业务处理
  • 同步 or 异步读取数据并且写入
  • 采用 singleflight

cache-10.png

cache-11.png 要点:业务代码会同步从数据库读取数据,而后用 DB 数据来执行业务,同时异步刷新缓存。

cache-12.png 要点:业务代码在发现缓存没有返回数据的时候,会直接返回响应,或者使用默认值。而后异步从 DB 读取数据,刷新缓存。

cache-13.png

cache-14.png

cache-15.png

read-through 和 write - through 也会有这种问题。

Read Through

Read Through:

  • 业务代码只需要从 cache 中读取数据,cache 会在缓存不命中的时候去读取数据
  • 写数据的时候,业务代码需要自己写 DB 和写 cache

cache 可以做决策:

  • 未命中的时候是否要从 DB 取数据。如果不从 DB 取,可以考虑使用默认值进行业务处理
  • 同步 or 异步读取数据并且写入
  • 采用 singleflight

cache-16.png

cache-17.png 要点:cache 会同步从数据库读取数据,而后直接返回值,同时异步刷新缓存。

cache-18.png 要点:cache 会直接返回响应,而后异步从 DB 读取数据,刷新缓存。

read - through 对用户来说,基本上只能是一个缓存类型一个实例。

例如 userCache := &ReadThroughCache

这是因为 LoadFunc 在大多数时候,我们都没办法把它写成非常通用的。

在这个例子里面,从数据库中找 user,或者找 order,明显就是不一样的。

Read Through 泛型

于是我们会考虑使用泛型。虽然看上去和 ReadThroughCache 没什么区别。

实际上,ReadThroughCacheV1 并没有实现 Cache 接口。

在这两个版本里,大家都可以控制住在 Get 里面,当 err 为 KeyNotFound 的时候,是同步运行,还是异步执行。

Write Through

Write Through

  • 开发者只需要写入 cache,cache 自己会更新数据库
  • 在读未命中缓存的情况下,开发者需要自己去数据库捞数据,然后更新缓存(此时缓存不需要更新 DB 了)

cache 可以做决策:

  • 同步 or 异步写数据到 DB,或者到 cache
  • cache 可以自由决定先写 DB 还是先写 cache,一般是先写 DB

cache-19.png

cache-20.png 要点:cache 会同步将数据刷新到 DB,而后返回响应,同时异步刷新缓存。

cache-21.png 要点:只具备理论意义,实际上几乎不会用。

WriteThroughCache 和 ReadThroughCache 差不多,只是 LoadFunc 改成了 StoreFunc。

类似地,WriteThroughCache 也可以尝试使用泛型,但是也面临着 ReadThough 的问题。

也可以在这两个版本里面,控制同步还是异步刷新缓存。

Write Back

Write Back

  • 在写操作的时候写了缓存直接返回,不会直接更新数据库,读也是直接读缓存
  • 在缓存过期的时候,将缓存写回去数据库

优缺点:

  • 所有 goroutine 都是读写缓存,不存在一致性的问题 (如果是本地缓存依旧会有问题)
  • 数据可能丢失:如果在缓存过期刷新到数据库之前,缓存宕机,那么会丢失数据

write-back 主要是利用 OnEvicted 回调,在里面将数据刷新到 DB 里。

Refresh Ahead

refresh-ahead 依赖于 CDC(changed data capture) 接口:

  • 数据库暴露数据变更接口
  • cache 或者第四方在监听到数据变更之后自动更新数据
  • 如果读 cache 未命中,依旧要刷新缓存的话,依然会出现并发问题

cache-23.png


知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。