导航
- 为什么会产生缓存不一致的情况
- 普遍采取的更新缓存的策略
- 解决删除缓存失败的问题
为什么会产生缓存不一致的情况
一般更新缓存有下面的几种策略:
归根结底:缓存应该是更新还是删除?
1.先更新数据库,再更新缓存
并发更新数据库或者并发更新缓存时,可能会出现脏数据,并且每次都把没有读到的数据更新到缓存,也太浪费内存了。
2.先删除缓存,再更新数据库
如果一个线程更新数据,一个线程查询数据。更新数据的线程,删除缓存,写数据库前,另一个线程又将旧值读取到缓存中。这样就导致了数据库中是新值,缓存中为旧值。
3.先更新数据库,再删除缓存
删除缓存失败同样会造成数据不一致。
普遍采取的更新缓存的策略
现在大家比较认可第三种,具体的流程:
1.如果缓存中没有,读取数据库,并存入缓存中。如果缓存中存在,命中,直接返回。
2.如果更新数据,先更新数据库,然后删除缓存。
上面已经说了,删除缓存可能会失败,怎么解决。
解决删除缓存失败的问题
之前一直看到说用消息队列解决删除缓存失败的问题,并不清楚怎么用。现在终于明白了,其实也蛮简单的(以下图片来自一个博主,这个博主也是转载别人的,但是他没有备注原博客,导致我并不知道这个图片的出处,声明一下,侵权的话联系我删)。
更新数据库后,自己生产者也是消费者,将需要删除的缓存的key放在消息队列中。如果删除缓存成功,将队列中对应的key出队。否则,重试删除缓存。但是这样有个问题,就是造成代码侵入,代码依赖,显然需要改进。
阿里巴巴开源的canal中间件,全java实现,基于数据库日志解析,提供增量数据的订阅与消费。它读取分析数据库的binlog,非业务代码订阅其中数据库的更新日志,然后将需要删除的key加入队列。这样就解耦了,删除缓存以及消息队列的操作和业务代码独立,性能得到提升。下图中读取binlog和实现订阅功能,其实就可以用canal来代替实现。
binlog是什么,为什么记录着数据库的操作信息,那redo log有啥区别
- 二进制日志binlog会记录所有与MySQL数据库有关的日志记录,包括InnoDB、MyISAM、Heap等其他存储引擎的日志。而InnoDB存储引擎的重做日志只记录有关该引擎本身的事务日志。
- 记录的内容不同。无论用户将二进制日志文件记录的格式设为STATEMENT还是ROW,又或是MIXED,其记录的都是关于一个事务的具体操作内容,即该日志是逻辑日志。而InnoDB存储引擎的重做日志是关于每个页(Page)的更改的物理情况。
- 写入的时间也不同。二进制日志文件仅在事务提交后进行写入,即只写磁盘一次,不论这时该事务多大。重做日志,在事务提交前先写,再修改数据库。