redis双写一致性

​一、是什么

概念:修改数据库的同时要更新缓存,让数据库和缓存保持一致

为什么数据库和缓存会不一样呢?

二、原因

因为线程1把缓存删了,线程2没命中,直接把旧数据读出来写到缓存中,而此时还未把新数据写到数据库,导致缓存里是旧数据

先操作数据库,再删除缓存也会出现脏数据

也就是说线程1未命中查询到的数据库是旧数据,直接写入缓存了,返回数据了,而此时,线程2正把新数据写入数据库呢,线程1没读到新数据

总结一句话,就是不管先删缓存,还是先改数据库,都可能会出现把旧数据缓存到redis,出现脏数据的情况,所以我们需要在修改数据库前后删两次缓存来保证数据库和redis数据的一致性。

三、解决方案

解决方案1:延迟双删

为什么第二次删除缓存的时候要延时呢?

因为主数据库要把数据写到从数据库上需要时间,但是因为有延时所以还是会有脏数据。

解决方案2:读操作使用共享锁,写操作使用排他锁

共享锁:读不互斥,写互斥

排他锁:读写都互斥

代码示例

读操作使用共享锁

public Item getById(Integer id) {
    // 获取读写锁
    RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("ITEM_READ_WRITE_LOCK");
    
    // 获取读锁(读写锁中的读操作部分)
    RLock readLock = readWriteLock.readLock();
    
    try {
        // 上锁
        readLock.lock();
        System.out.println("readLock...");

        // 从 Redis 缓存中尝试获取项
        Item item = (Item) redisTemplate.opsForValue().get("item:" + id);
        
        // 如果缓存中有项,则直接返回
        if (item != null) {
            return item;
        }

        // 如果缓存中没有项,创建新项
        item = new Item(id, "华为手机", "华为手机", 5999.00);
        
        // 将新项存入 Redis 缓存
        redisTemplate.opsForValue().set("item:" + id, item);
        
        // 返回新创建的项
        return item;
    } finally {
        // 解锁
        readLock.unlock();
    }
}

写操作使用排他锁

public void updateById(Integer id) {
    // 获取读写锁
    RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("ITEM_READ_WRITE_LOCK");

    // 获取写锁(读写锁中的写操作部分)
    RLock writeLock = readWriteLock.writeLock();

    try {
        // 上锁
        writeLock.lock();
        System.out.println("writeLock...");

        // 模拟更新操作
        Item item = new Item(id, "华为手机", "华为手机", 5299.00);

        // 模拟延迟操作,可能表示某种复杂的更新过程
        try {
            Thread.sleep(10000); // 延迟 10 秒
        } catch (InterruptedException e) {
            e.printStackTrace(); // 捕获并打印中断异常
        }

        // 删除 Redis 缓存中的项
        redisTemplate.delete("item:" + id);
    } finally {
        // 解锁
        writeLock.unlock();
    }
}

优点:强一致性

缺点:性能较差

解决方案3:使用MQ异步通知

每次修改数据库,我们都发布消息给MQ,缓存随时监听MQ的变化,如果有新的消息,再更新缓存,在高并发下会有数据不一致的情况,但是我们可以保证最终数据的一致性。

解决方案4:使用Canal异步通知

canal记录了所有数据定义数据操作的语句,不包含查询语句

流程如上,每次修改数据库,我们都把消息发给canal,由canal通知缓存数据变化情况,再更新数据,也能保证数据最终的一致性

视频地址:

4.redis双写一致性_哔哩哔哩_bilibili

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值