程序媛雪儿
Java后端
·2024-07-14
大家好,我是程序媛雪儿,今天发早点,因为雪儿要早睡早起,睡太晚不好哦,大家也要注意规律作息,保护好我们自己的身体,雪儿今天给大家带来的知识碎片是redis双写一致性 ​ 一、是什么 概念:修改数据库的同时要更新缓存,让数据库和缓存保持一致 为什么数据库和缓存会不一样呢? 二、原因 情况1:先删除缓存,再操作数据库 因为线程1把缓存删了,线程2没命中,直接把旧数据读出来写到缓存中,而此时还未把新数据写到数据库,导致缓存里是旧数据 情况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 带图解文章: redis双写一致性-CSDN博客 也欢迎关注我的同名公众号,程序媛雪儿,雪儿和你一起学八股,祝各位宝子们学有所获~
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP