redis做为缓存,mysql如何与redis进行同步(双写一致性)
双写一致:
当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致
读操作: 缓存命中,直接返回;未命中查询数据库,写入缓存,设定超时时间。
写操作: 延迟双删(有两种情况:1.先删除缓存,再跟更新数据库。 这种方法可能会造成脏数据。比如有两个线程,线程一来了先删除缓存,线程二来了 查询缓存后发现没有数据,就回去数据库里读,读到一个不一样的数据 然后放入缓存 线程一更新数据库 就导致缓存数据库不一致了。
2 先操作数据库 后删除缓存:
更新数据库 为一个不同的值 然后再删除缓存 另一个线程 查数据 发现缓存没有 就去把数据库的数据拿到缓存中 这样就没有问题了 。
但是还有一种情况 线程查缓存的时候 刚好过期了 线程就拿到数据库的数据 但是没有同步到缓存之前 另一个线程 更新数据库了 然后删除缓存 然后回到线程1 线程1 要去写入缓存 就会把之前没有更新的数据写入 这样就会导致 脏数据 但是 这种情况很少见
这两种情况 都不太行 所以我们要进行双删 还要延时 当修改数据库之后 等一会再次删除缓存 这样就能极大避免脏数据 但还是不能完全避免 。
非要保持强一致性 怎么办呢?
像那些存入缓存中的数据 都是 读多写少 所以 我们可以上一个读写锁 1 共享锁:读锁readLock,加锁之后,其他线程可以共享读操作 2 排他锁:独占锁 writeLock 也叫写锁,加锁之后,阻塞其他线程读写操作 ,在写数据时,添加,能堵塞其他进程的读写
这是读锁代码实现
这是写锁代码实现
使用读写锁 能保证强一致 性能低 (大多数要进行强一致去用)
最常用的 还是图下 用MQ的异步通知 这个我后面会说
)
那我们关于这道面试题怎么回答呢?
1 介绍自己的业务,我当时是把文章的热点数据存入缓存,因为实时性没有那么高,所以当时采用的是异步的方案去同步数据
2 如商城系统,我当时是把一些库存存到了缓存中,需要进行实时同步,要保证强一致性,所以当时采用是redisson提供的读写锁来保证数据的同步。
如果面试官继续发问:那你来介绍一下异步和同步的方案
我:
我们一般允许延时一致的业务,采用异步通知, 我使用了MQ中间件,更新数据之后 通知缓存去删除
我们要强一致性的,采用Redisson提供的读写锁
1 共享锁:独锁readLock,加锁之后,其他线程可以共享读操作
2 排他锁:独占锁writeLock也叫写锁,加锁之后,阻塞其他线程的读写操作