这是面试常问的关于Redis的问题,这样回答可能对于我们面试来说是一个加分:
一般情况下来说Redis是用来实现应用和数据库之间的一个读操作的缓存层,主要目的时为了减少数据库的IO,还可以提升数据的IO性能,以下是它的整体架构
当应用程序需要去读取某个数据的时候,首先会尝试去Redis里面去加载,如果命中了会直接返回,如果没有命中就直接去数据库里面查询,查询到数据之后,再把数据缓存到Redis里面去,会出现一个问题
一份数据同时保存在数据库和Redis里面,当数据发生变化的时候,需要同时更新Redis和Mysql,由于更新是有先后顺序的,他并不像Mysql中的多表事务操作可以满足ACID的特性(指的是原子性,一致性,隔离性,持久性),所以就会出现一个数据一致性的问题,这种情况下有几种选择的方法:
- 先更新数据库再删除缓存
- 出现的问题:如果Redis更新失败就会导致数据库和Redis中的数据不一致问题
- 先删除缓存再更新数据库
- 这个理想情况下是利用下次访问Redis的时候,发现Redis里面的数据是空的,然后去访问数据库,从数据库加载保存到Redis里面,理论上是一致的,但是这个操作并不是原子操作,如果出现其他线程来访问还是会存在数据不一致的问题比如:(当线程A删除了Redis里面的数据之后,正在更新Mysql在这个同时线程B来访问发现Redis里面没有数据就从Mysql里面读取数据,注意在这时候mysql中还没有完成修改,就会读到旧数据)
- 延时双删:
- 先删除Redis缓存数据,在更新Mysql,延迟几百毫秒再删除Redis缓存数据,这样就算在更新Mysql的时候,其他线程读取了mysql,把旧数据读取到Redis中也能删除掉,从而保持数据一致。
- 所以如果在极端情况下仍然保持数据的一致性,就需要采用最终一致性方案
还可以直接通过Canal组件监控Mysql中的binlog日志,把更新后的日志同步到Redis里面去,因为这里是基于最终一致性实现的,如果业务场景不能接受数据的短期不一致的问题那么就不能使用这个方案
- 基于RocketMQ的可靠性通信,来实现数据的最终一致性