一、前言

当今最风行的缓存中间件当属redis了,因为redis是基于内存操作性能优越,所以被宽泛应用。

应用缓存的个别步骤如下:

  1. 先查问缓存,如果缓存命中,间接返回数据
  2. 如果缓存不命中,则查询数据库返回数据,并将查问到的数据放入缓存中

然而当咱们想要更新数据时,这时可能会呈现缓存与数据库中的数据不统一问题,这外面有各种更新缓存的操作,比方先更新缓存、再更新数据库先更新数据库、再更新缓存,这里不一一列举了,可查阅站内大神写的 https://segmentfault.com/a/11...

二、提早双删计划

在咱们外部个别是通过先更新数据,再删除缓存,再提早删除的计划来更新缓存的,这样能够使缓存与数据库达到最终一致性。伪代码如下

tx.begin();  // 开启事务boolean result = updateDB(data);if (result) {    boolean cacheResult = deleteCache(dataId);  // 删除缓存    if (!cacheResult) {        tx.rollback();  // 回滚事务        return;    }} else {    tx.rollback();  // 回滚事务    return;}tx.commit();  // 提交事务// 将dataId放入提早队列,通过异步地形式再次删除该缓存// 异步删除缓存失败能够进行重试,如果失败次数达到n,则发送告警信息delayQueue.offer(dataId);  

这种计划优缺点很显著,长处就是实现简略,毛病就是只能让缓存和数据库达到最终一致性,依然可能呈现一小段时间的不统一

三、分布式锁计划

那如果真的有某些场景想要达到强一致性,这里咱们外部抉择的是应用分布式锁为了不引入其余组件,应用redis来实现分布式锁)。

那代码如何来实现缓存与数据库的强一致性,伪代码如下:

3.1 查问数据

Object result = getCache(dataId);    // 查问缓存if (result == null) {                // 缓存未命中    RLock lock = getRLock();         // 获取分布式锁    lock.lock();    try {             result = getCache(dataId);    // 再次查问缓存,如果命中缓存,间接返回        if (result != null)             return result;                result = queryDB(dataId);    // 查询数据库        putCache(dataId, result);    // 将查问后果置入缓存中    } finally {        lock.unlock();  // 开释锁    }}return result;

3.2 更新数据

// 获取分布式锁RLock lock = getRLock();lock.lock();try {    tx.begin();                         // 开启事务    boolean result = updateDB(data);    // 更新数据库    if (result) {                       // 如果更新数据库胜利        boolean cacheResult = deleteCache(dataId);  // 删除缓存        if (!cacheResult) {             // 删除缓存失败            tx.rollback();              // 回滚事务            return;        }    } else {                            // 更新数据库失败        tx.rollback();                  // 回滚事务        return;    }    tx.commit();                        // 提交事务} finally {    lock.unlock();                      // 开释锁}

下面的伪代码还有许多能够优化的中央,这里只是把外围局部贴出来,仅供参考。

3.3、存在的问题

一旦引入分布式锁,也将引入新的问题

  • 如果是单点redis,无奈保障高可用
  • 如果是redis哨兵或集群模式,极其状况下会存在锁失落在主从切换时,master还没来的及将锁信息同步到slave时,master挂掉,slave切换为master,此时锁失落)的状况,如何取舍?(集体偏差于应用独自的单点Redis来做分布式锁,因为在曾经须要强一致性的前提下,当该用作分布式锁的redis挂掉时,该业务将不能进行,集体认为也绝对正当)

四、总结

在不同的利用场景下应用不同的实现计划:

  1. 不须要强一致性的场景下,首选第一种计划,其实现简略、效率高,不须要引入分布式锁
  2. 须要强一致性的场景下,无奈只能抉择第二种计划,但分布式锁的引入也减少了保护难度