目录

Redis学习-与数据库一致性问题

背景

我们使用redis作为缓存,查询的时候先去redis查,如果有数据直接返回,降低数据库的压力。如果没有的话,查数据库,如果数据库中查到了数据则把数据写入到redis,并返回给调用方。

如果只是这样的读就没有啥问题了,如果是更新呢? 先操作数据库?还是先操作缓存? 操作缓存是直接删缓存还是更新缓存?

是更新还是删除缓存

一般我们都是采取删除缓存缓存策略的,原因如下:

  • 高并发环境下,无论是先操作数据库还是后操作数据库而言,如果加上更新缓存,那就更加容易导致数据库与缓存数据不一致问题。(删除缓存直接和简单很多)

  • 如果每次更新了数据库,都要更新缓存【这里指的是频繁更新的场景,这会耗费一定的性能】,倒不如直接删除掉。等再次读取时,缓存里没有,那我到数据库找,在数据库找到再写到缓存里边 (体现懒加载)

先数据库还是先缓存

不管是先写MySQL数据库,再删除Redis缓存;还是先删除缓存,再写库,都有可能出现数据不一致的情况。举一个例子:

  • 如果删除了缓存Redis,还没有来得及写库MySQL,另一个线程就来读取,发现缓存为空,则去数据库中读取数据写入缓存,此时缓存中为脏数据。

  • 如果先写了库,在删除缓存前,写库的线程宕机了,没有删除掉缓存,则也会出现数据不一致情况。

缓存和数据库一致性解决方案

1.第一种方案:采用延时双删策略

在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。

public void write( String key, Object data )
{
	redis.delKey( key );
	db.updateData( data );
	Thread.sleep( 500 );
	redis.delKey( key );
}

2、第二种方案:异步更新缓存(基于订阅binlog的同步机制)

技术整体思路:MySQL binlog增量订阅消费+消息队列+增量数据更新到redis

读取binlog后分析 ,利用消息队列,推送更新各台的redis缓存数据。这样一旦MySQL中产生了新的写入、更新、删除等操作,就可以把binlog相关的消息推送至Redis,Redis再根据binlog中的记录,对Redis进行更新。其实这种机制,很类似MySQL的主从备份机制,因为MySQL的主备也是通过binlog来实现的数据一致性。

这里可以结合使用canal(阿里的一款开源框架),通过该框架可以对MySQL的binlog进行订阅,而canal正是模仿了mysql的slave数据库的备份请求,使得Redis的数据更新达到了相同的效果。