这个问题是个常见的面试题,面试官主要考察的还是一个并发下的安全问题
首先这个问题的答案是,redis和mysql作为两种不同的存储介质,我们的业务需要做到的是保证两者的最终一致性。
一般我们引入redis是为了查询的时候提高效率,所以我们在查询的时候先查询redis,如果redis中有数据,直接返回给用户,如果没有数据,查询mysql然后回写到redis,这是使用缓存后正常的一个查询流程
但是在进行修改的时候,通常有两种方式:
1、先修改mysql中的数据,然后再删除缓存,等下个线程查询的时候发现缓存没有数据,再次查询回写
这个方案在不考虑特殊情况下可以保证数据的最终一致性,但是在并发的情况下,在第二步删除redis的操作没有完成时,有另外的线程进来查询数据,那么查询到的就是旧的数据,但是优点是只需要删除一次缓存的数据
2、先删除缓存,然后更新mysql,等待一段时间(这个时间要保证mysql数据更新完成)再次删除缓存,这种方案叫做延时双删
延时双删也是可以保证数据的最终一致性的,但是在整个事务执行的过程中,如果在并发情况下,有其他线程在第二步没有更新完mysql之前进行查询,那么查询到的依然是旧的数据,并且在更新mysql之后也要保证等待的时间要在mysql更新之后,最后再次删除redis中的数据
所以我们通常在不考虑特殊情况下,要保证redis和mysql数据最终一致性,用到方案1就OK。
拓展,如果要考虑特殊情况,比如mysql数据在向redis同步的时候reids宕机了,那么可以使用MQ进行消息投递,因为MQ有消息失败重试机制,此外还有第三种方案就是使用阿里的组件Cnanal监控mysql的binlog日志进行异步的推送
无论是使用MQ还是Cannal我个人都不推荐,在业务链路上添加越多的技术点,伴随的风险点就越多!!!
此外,如果在特殊的场景下,比如下单后扣减库存等特殊的场景,那么需要做的就是将mysql和redis的修改操作当做同一事物进行加锁,以达到数据最终一致性。但是普通的业务场景不推荐这么做,因为使用缓存的目的就是为了提高系统的吞吐量,这种操作有点儿南辕北辙的意思。