写在前面:2020年面试必备的Java后端进阶面试题总结了一份温习指南在Github上,内容详细,图文并茂,有需要学习的同伙可以Star一下!
GitHub地址:https://github.com/abel-max/Java-Study-Note/tree/master

需求原由

在高并发的营业场景下,数据库大多数情形都是用户并发接见最微弱的环节。以是,就需要使用redis做一个缓冲操作,让请求先接见到redis,而不是直接接见MySQL等数据库。

这个营业场景,主要是解决读数据从Redis缓存,一样平常都是凭据下图的流程来举行营业操作。

读取缓存步骤一样平常没有什么问题,然则一旦涉及到数据更新:数据库和缓存更新,就容易泛起缓存(Redis)和数据库(MySQL)间的数据一致性问题

不管是先写MySQL数据库,再删除Redis缓存;照样先删除缓存,再写库,都有可能泛起数据不一致的情形。举一个例子:

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

2.若是先写了库,在删除缓存前,写库的线程宕机了,没有删除掉缓存,则也会泛起数据不一致情形。

由于写和读是并发的,没法保证顺序,就会泛起缓存和数据库的数据不一致的问题。

如来解决?这里给出两个解决方案,先易后难,连系营业和手艺价值选择使用。

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

1.第一种方案:接纳延时双删计谋

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

伪代码如下

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

2.详细的步骤就是:

  1. 先删除缓存
  2. 再写数据库
  3. 休眠500毫秒
  4. 再次删除缓存

那么,这个500毫秒怎么确定的,详细该休眠多久呢?

需要评估自己的项目的读数据营业逻辑的耗时。这么做的目的,就是确保读请求竣事,写请求可以删除读请求造成的缓存脏数据。

固然这种计谋还要思量redis和数据库主从同步的耗时。最后的的写数据的休眠时间:则在读数据营业逻辑的耗时基础上,加几百ms即可。好比:休眠1秒。

3.设置缓存过时时间

从理论上来说,给缓存设置过时时间,是保证最终一致性的解决方案。所有的写操作以数据库为准,只要到达缓存过时时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。

4.该方案的坏处

连系双删计谋+缓存超时设置,这样最差的情形就是在超时时间内数据存在不一致,而且又增加了写请求的耗时。

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

1.手艺整体思绪:

MySQL binlog增量订阅消费+新闻行列+增量数据更新到redis

  • 读Redis:热数据基本都在Redis
  • 写MySQL:增删改都是操作MySQL
  • 更新Redis数据:MySQ的数据操作binlog,来更新到Redis

2.Redis更新

(1)数据操作主要分为两大块:

  • 一个是全量(将所有数据一次写入到redis)
  • 一个是增量(实时更新)

这里说的是增量,指的是mysql的update、insert、delate调换数据。

(2)读取binlog后剖析 ,行使新闻行列,推送更新各台的redis缓存数据。

这样一旦MySQL中产生了新的写入、更新、删除等操作,就可以把binlog相关的新闻推送至Redis,Redis再凭据binlog中的纪录,对Redis举行更新。

实在这种机制,很类似MySQL的主从备份机制,由于MySQL的主备也是通过binlog来实现的数据一致性。

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

固然,这里的新闻推送工具你也可以接纳其余第三方:kafka、rabbitMQ等来实现推送更新Redis!