1、产生缓存和数据库数据不一致的原因
当我们数据库压力大的时候可以对数据库做读写分离,主库负责写操作,从库负责读操作,主从数据库之间采用二进制日志文件来同步数据。
还有另一种解决方案是使用缓存,通常我们使用缓存的姿势是查询直接从缓存中取,缓存中没有则去数据库取,数据库取到结果之后再塞到缓存中去。如果有修改操作,则删除缓存,并且查询一次结果塞到缓存中。
上面两个方案都是为了解决数据库压力太大问题,两个方案看起来都没有任何问题,但是当两个方案一起实施的时候就会发生问题。
假设我们需要缓存商品信息,并且使用了主从数据库。
用户发送一个修改商品信息的请求,那么会去主库中修改商品信息,并且删除缓存,然后从从库中查询商品信息放到缓存中去,由于主从数据库之间同步数据有一定的延迟,所以从从库中查询出来的商品信息可能是未修改前的商品信息,就会将未修改前的商品信息塞到缓存中。
下一次查询商品信息的时候,会先去缓存中查询,而缓存存在有商品信息(过期的),所以直接返回缓存中的商品信息,这样就导致了缓存和数据库数据不一致的问题。
2、缓存加有效期
给缓存加上有效期是最简单的确保数据最终一致性的解决方案,可以对缓存中的数据加一个有效期,那么即使缓存中的数据是过期的,但是也只会影响一段时间的结果,可以保证数据的最终一致性。
这种缓存加有效期的解决方案虽然可以保证数据最终一致性,但是不可避免的会产生一段时间的脏数据(过期数据),只适用于修改频率较低并且准确性要求不高的使用场景。
3、双淘汰
缓存和数据库数据不一致的问题产生的根源就是主从数据库之间同步数据有延迟。
如果我们在修改商品信息之后不是立马更新缓存,而是等待从数据库从主数据库同步完数据之后再去更新缓存,那么这个问题就解决了。
所以我们可以在修改商品信息的时候,将修改信息写到主库中,并且删除缓存,然后后将这一条记录添加到一个队列中并且设置一个传入时间。
另一个线程去遍历队列中的消息记录,当消息记录的延迟时间超过主从数据库之间同步数据的延迟,则消费这一条消息记录,删除缓存,在从库中查询出商品详细信息,再将商品详细信息存到缓存中。
这个方案的思想是等到主从同步结束之后再去修改缓存中的数据,在数据的最终一致性上比缓存添加有效期上要好很多,但是在两次淘汰之间还是可能会存在脏数据。
4、双淘汰的优化
上面双淘汰的机制存在的问题就是在两次淘汰之间如果有查询请求过来还是会去请求从库,从而导致这段时间会存在脏数据。
那么我们可以基于上面的问题去优化双淘汰方案。
在修改商品信息之后,先将修改信息写道主库,并且删除缓存,然后添加一个关于这个商品信息的标记到缓存中。
在收到查询商品信息的请求之后,先去查询缓存,如果缓存中存在数据则直接返回缓存中的数据;
如果缓存中不存在,则先去查询缓存中是否有关于这个商品信息的标记,如果存在这个标记,表示查询时间是在两次淘汰之间,从库可能还没同步好主库中的数据。那么就从主库中查询商品信息,将查询结果塞到缓存中,并且删除缓存中关于商品详细信息的标记。
同样有一个线程去遍历队列中的消息记录,当消息记录的延迟时间超过主从数据库之间同步数据的延迟,则消费这一条消息记录。
消费过程先去查询缓存中有没有这个商品的记录,如果存在表示在两次淘汰之间有过查询请求,从中库中查询到商品详细放到缓存中了,所以可以不做任何操作;如果缓存中不存在,则在从库中查询出商品详细信息,再将商品详细信息存到缓存中,并且删除缓存中关于商品信息的标记。
这个优化后的双太太方案牺牲了主库的一部分性能,保证了查询数据的一致性。
5、总结
上面也只是给出了三种常见的解决方案,还有很多其他的解决方案,目前为止也没有哪种解决方案是万能的,要么牺牲性能,要么牺牲一致性。
当然了在数据一致性要求很高的常见最好不要去使用缓存,使用缓存主要就是为了性能而不是绝对的一致性。
领取专属 10元无门槛券
私享最新 技术干货