首先要明确的几个问题

  1. 明确一个原则,如果想要保证redis和mysql数据库实时一致性的话,其实就是实现一个两阶段事务,这个模型是比较困难的,所以各种方案都是最终一致性。
  2. 对于缓存的数据,都要设置有效期,通过缓存的失效保证最终的一致性。

方案

  1. 读走redis,如果redis不存在,从数据库里读,读完了更新redis。增删改先更新数据库,再更新redis。 好处是简单,但是如果更新数据库成功了,更新redis失败了,会查询到不一致的数据。
  2. 读走redis,增先插入数据库,读走redis,如果redis不存在,从数据库里读,读完了更新redis。删和改先置缓存失效,然后更新数据库。问题是在并发场景下,并发的线程可能读取到数据库里旧的数据,然后把redis里的数据更新成了旧的。
  3. 引入消息队列,把增删改的消费放到消息队列里,数据库和redis去进行消费,还是有redis和数据库状态不一致的情况
  4. 引入类似cannel一类的工具,增量消费mysql的binlog,自动同步redis。binlog的读取等也是有延迟的,适用于对失效要求不严格的场景。
  5. 新建服务器的内存队列。对查询的数据比如商品id,根据hash取模,分散到不同的队列里。对增删改请求,先放队列里,对于读请求,先查一下队列里是否有增删改的请求,有的话把读请求放对应的队列主题里。另外读请求还要检查一下队列里是否已经有读请求了,有的话直接等一段时间(几十毫秒)再直接去读缓存。如果缓存还没有就去读数据库。
图片来自Java基基的公众号
  1. 对于高并发类似秒杀的场景,可以直接更新缓存,异步的更新数据库。