问:如何保证缓存和数据库一致性?
1、读写请求串行化
将读请求和写请求串到一个队列里面,可以保证一定不会出现不一致的问题,但是系统耦合度高,吞吐量大幅度降低,需要用比平时大几倍的机器去支撑服务请求;
2、Cache Aside Pattern
读请求进来之后,先从缓存中获取,如果缓存中不存在,从数据库中读取,同时放入缓存中;
更新数据时,先更新数据库中的数据,然后在删除(更新)缓存;
为什么是删除缓存呢?
在我们大量实际业务请求中,大多数缓存不单单只是从数据库从取值,还会牵涉到一系列的多表关联甚至计算,在这类的业务中,我们更新缓存的代价会不会太大,如果你频繁的更新缓存,却没有被频繁的访问,那我们更新缓存的付出和收获相比,是不是真的划算?
举个栗子,一个缓存涉及的表的字段,在 1 分钟内就修改了 20 次,或者是 100 次,那么缓存更新 20 次、100 次;但是这个缓存在 1 分钟内只被读取了 1 次,有大量的冷数据。
实际上,如果你只是删除缓存的话,那么在 1 分钟内,这个缓存不过就重新计算一次而已,开销大幅度降低,用到缓存才去算缓存。
第二问:如果更新数据库成功了,但是缓存到缓存的时候失败了,怎么办?
这个问题跟上面 cache aside pattern有关,当我们更新的时候,更新数据库,然后去删除或者更新缓存,这时候失败了,那么我们缓存中依旧是旧的数据,于是新的线程来访问的时候,肯定出错了,这时候我们该怎么办呢?
针对这类问题,我们可以更改我们的逻辑,当更新数据时,我们先删除缓存,然后去更新数据库;这时候不管是更新数据库失败还是缓存备份的时候失败,其他的线程来访问的时候都不得不直接从数据库中获取,永远是最新的数据,当然也就解决了不一致的问题;
第三问:如果更新数据库的时候还没执行完,另一个线程来访问,获取到旧的数据,该如何?
这种情况在分布式中并不罕见,尤其是我们需要多线程去访问的时候;
针对该类问题,我们想到的是设立一个标志,告诉后面的线程我们正在执行更新任务,请你稍等,在我们执行完毕之后,你再继续;
分布式锁,类似于redis.setNx(),如果缓存中存在该标识,说明我们正在执行其他操作,当我们执行完毕之后,执行删除操作,后面等待的成员去轮询的时候,自然也就继续抢占‘;
|
|