缓存一致性
将数据不一致分为三种情况:
1.数据库有数据,缓存没有数据;
2.数据库有数据,缓存有数据,数据不相等;
3.数据库没有数据,缓存有数据。
缓存策略:Cache Aside Pattern
1.首先尝试从缓存读取,读到数据则直接返回;如果读不到,就读数据库,并将数据写到缓存,并返回。
2.需要更新数据时,先更新数据库,然后把缓存里对应的数据失效掉(删掉)。
读的逻辑很好理解,主要是更新的逻辑。如果不这样做,采用先删除缓存,再更新数据库,会有什么问题呢? 这样做引发的问题是,如果有A、B两个线程同时更新数据,并且A、B已经都做完删除缓存这一步了,接下来A更新数据库,C线程读取数据,没有命中缓存会去数据库中读取A更新的数据并且将数据写到缓存中,这时候B再更新数据库,会导致缓存中的数据和数据库中的不一致,缓存中存在着脏数据,且会一直存在下去。
如果是先更新数据库,然后更新缓存又会引起什么问题呢?如果有A、B两个线程同时做更新数据,A先更新了数据库,B后更新了数据库,则此时数据库中是B的数据,而更新缓存时,确实B先更新缓存,A后更新缓存,则此时缓存中是A的数据,这样缓存和数据库结果又不一致了。
那么是不是Cache Aside这个就不会有并发问题了?不是的,比如一个是读操作,未命中然后就去数据库中读取数据,此时来一个写操作,写完数据后,让缓存失效,然后之前的那个读操作再把老的数据放进去,所以,会造成脏数据。
但,这个case理论上会出现,不过,实际上出现的概率可能非常低,因为这个条件需要发生在读缓存时缓存失效,而且并发有一个写操作。而实际上数据库的写操作会比读操作慢很多,而且还要锁表,而读操作必须在写操作之前进入数据库操作,而又要晚于写操作更新缓存,所有的条件都具备的概率基本不大。
所以,对于缓存一致性,要么通过2PC或者Paxos协议保证一致性,要么就是拼命的降低并发时脏数据概率,具体根据系统对一致性的要求程度。Facebool就是使用了这个降低概率的玩法,因为2PC太慢了,而Paxos协议太复杂。当然最好的就是给缓存设置上过期时间。
现在再看看上面的三种不一致的情况。
1.对于第一种,在读数据的时候,会自动把数据库的数据写到缓存,因此不一致自动消除。
2.对于第二种情况,数据最终变得不一致,但是他们在之前的某个时间点一定是想等的。这种不一致,一定是由于你更新数据所引发的。使用上面的策略,先更新数据库,再删除缓存。不一致的原因除了小概率的读写并发导致,就是数据库更新了,但是删除缓存失败了。
3.对于第三种情况,情况与第二种类似,你把数据库的数据删了,但是删除缓存失败了。
因此,最终的结论是,需要解决的不一致,产生的原因是更新数据库成功,但是删除缓存失败。
解决方案大概有以下几种:
1.对删除缓存进行重试,数据的一致性要求越高,越是重试得快。
2.定期全量更新,简单的说,全部删除再全部加载。
3.给所有缓存一个失效期。
第三种方案可以说是一个大杀器,任何不一致,都可以靠失效期解决,失效期越短,数据一致性越高。但是失效期越短,查数据库就会越频繁。因此失效期应该根据业务来定。