1,读取数据
当线程读取数据时不会发生数据变化
2,读写
解决流程无非下面几种怎么选择呢:
先 更新缓存 再 更新数据库
先 删除缓存 再 更新数据库
先 更新数据库 再 更新缓存
先 更新数据库 再 删除缓存 (数据库指存储数据的mysql,缓存是redis里的数据)
选择方法
与其更新缓存不如直接删除更新成本高,直接删除缓存,修改数据库,当第二次访问redis拿数据如果没有,就会访问musql并且把数据缓存到redis中。
一,那先操作缓存还是操作数据库呢
先操作redis再修改数据库:线程1发出修改,先把redis删除了再去修改mysql,此时线程2在发现redis没有数据,去找数据库,但是数据库是老数据,线程A还没完成。这样redis又存入了老数据。只能到redis过期时间到了,才能查到新数据。
解决办法就是:每次mysql中数据更新之后删除一次redis中数据 *** 这就是双删**
但是线程2可能会有一次数据不一致。 解决就是保证强一致性,就是保证redis与mysql是原子性的,使用分布式锁可以实现但是影响系统并发性贼菜,一般保证最终一致性,而不是选择强一致性
第二次删除要延迟好比几百毫秒,应为万一线程A写数据太快而B已经拿到老数据会覆盖缓存中的新数据*** 也就是常说的 延迟双删
二,当先操作数据库在操作缓存
当线程1修改数据库未删除redis中数据,其他线程拿到脏数据,也能保证最终一致。
这种办法主要问题是在极端情况下修改完数据库删除redis失败了,redis数据就一直是脏数据知道失效
解决办法:删除重试,可以借用Mq,如果删除失败发送异步请求到mq,然后mq通知客户端重新删除redis.
但是这样代码耦合度高
解耦办法;使用另一款组件canal,原理读取binlog异步删除,(数据库实现主从复制也是通过读取binlog)
就是如果有数据变动会通知canal,然后canal客户端(监听canal的springboot应用)完成缓存更新