Cache Aside 模型中,读缓存 Miss 的回填操作,和修改数据同步更新缓存,包括消息队列的异步补偿缓存,都无法满足 “Happens Before”,会存在相互覆盖的情况。

cache aside

注:Happens Before既是明确的代码执行的先后关系

读写缓存

读/写同时操作:

  1. 读操作,读缓存,缓存 MISS
  2. 读操作,读 DB,读取到数据
  3. 写操作,更新 DB 数据
  4. 写操作 SET/DELETE Cache(可 Job 异步操作)
  5. 读操作,SET操作数据回写缓存(可 Job 异步操作)

这种交互下,由于4和5操作步骤都是设置缓存,导致写入的值互相覆盖;并且操作的顺序性不确定,从而导致 cache 存在脏缓存的情况。

改进:

读写缓存

读/写同时操作:

  1. 读操作,读缓存,缓存 MISS
  2. 读操作,读 DB,读取到数据
  3. 写操作,更新 DB 数据
  4. 写操作 SET Cache(可异步 job 操作,Redis 可以使用 SETEX 操作)
  5. 读操作,ADD 操作数据回写缓存(可 Job异步操作,Redis 可以使用 SETNX 操作)

写操作使用 SET 操作命令,覆盖写缓存;读操作,使用 ADD 操作回写 MISS 数据,从而保证写操作的最新数据不会被读操作的回写数据覆盖。

针对更新操作,service端直接使用“DEL”指令删除缓存,并重新写入,若service端失败,则是job进行补偿。

直接使用set更新key,若binlog订阅延迟,则数据容易出现A ——> B ——> A的情况。可能有一个操作是读了之前的数据的,被延迟投递到Kafka了。

对于缓存上,job的更新原则,采用"best Effort",一定要投递完成此消息。