缓存-分布式锁-缓存一致性解决

public Map> getCatalogJsonFromDbWithRedissonLock() {

    //1、占分布式锁。去redis占坑
    //(锁的粒度,越细越快:具体缓存的是某个数据,11号商品) product-11-lock
    //RLock catalogJsonLock = redissonClient.getLock("catalogJson-lock");
    //创建读锁
    RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("catalogJson-lock");

    RLock rLock = readWriteLock.readLock();

    Map> dataFromDb = null;
    try {
        rLock.lock();
        //加锁成功...执行业务
        dataFromDb = getDataFromDb();
    } finally {
        rLock.unlock();
    }
    //先去redis查询下保证当前的锁是自己的
    //获取值对比,对比成功删除=原子性 lua脚本解锁
    // String lockValue = stringRedisTemplate.opsForValue().get("lock");
    // if (uuid.equals(lockValue)) {
    //     //删除我自己的锁
    //     stringRedisTemplate.delete("lock");
    // }

    return dataFromDb;

}

接下来又到了一个关键环节:如何解决缓存数据一致性?

有两种方案:

  • 双写模式
  • 失效模式

来画图分析下双写模式的工作流程:

缓存-分布式锁-缓存一致性解决_第1张图片

再来画图分析下失效模式的工作流程:

缓存-分布式锁-缓存一致性解决_第2张图片

其实这两种方案都会导致数据不一致性的问题;比如在双写模式下,两个写的请求先后打过来,处理后,在写缓存是由于网络延迟等原因导致后写的请求先写缓存,先写的请求后写入缓存,这就导致了数据不一致性,缓存中的数据不是最新的数据;再比如在失效模式下,看图可知道,当我在第二个写请求还没完成时,我去读缓存,没有读到,然后去数据库中查,当我读到之后假设第二请求还没完成,当第二个请求完成之后,删掉缓存,我再更新到缓存中,也会导致数据不一致性的问题。

针对上面的问题,我们怎么解决呢?

解决方案:

  • 如果是用户纬度数据(订单数据、用户数据),这种并发几率非常小的,就不用考虑数据不一致的问题,缓存数据加上过期时间,每隔一段时间触发读主动更新即可
  • 如果是菜单、商品介绍等基础数据,也可以采用canal订阅binlog的方式,数据库中信息改变,canal采集这些信息,再做些处理然后同步到redis当中即可
  • 缓存数据+过期时间足够解决大部分业务对于缓存的要求
  • 如果写入操作稍多的话,我们可以通过加锁的方式去保证并发读写,写写的时候排好队,保证顺序,读的时候不加锁,所以适用读写锁(业务不关心脏数据,允许临时脏数据可忽略)

总结

对于我们能够放入缓存的数据就不应该是实时性、数据一致性要求高的。所以缓存数据的时候加上过期时间,保证每天拿到当前最新的数据即可。我们不应该过度的设计,增加系统的复杂性,遇到那些实时性、一致性要求高的数据,就应该去查询数据库,慢点就慢点。

 

你可能感兴趣的:(缓存-分布式锁-缓存一致性解决)