[001-07-001].Redis7缓存双写一致性之更新策略探讨

1、面试题:

  • 1.只要使用缓存,就可能会涉及到redis缓存与数据库双存储双写,只要是双写,就存在数据一致性问题,那么是如何解决数据一致性问题的
  • 2.双写一致性,你先动缓存redis还是数据库MySQL,哪一个?why
  • 3.延时双删做过吗?会有哪些问题
  • 4.有这么一种情况,微服务查询redis无,mysql有,为了保证数据的一致性回写redis需要注意什么?双检加锁你了解吗?如何尽量避免缓存击穿?
  • 5.redis和MySQL双写100%会出现披露,做不到强一致性,应该如何保证最终一致性

2、缓存双写一致性的理解:

2.1.理解:

2.2.编码实现:

a.需求介绍:

b.采用双边加锁:

多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个 互斥锁来锁住它。其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存。

c.代码:

java 复制代码
package com.atguigu.redis.service;

import com.atguigu.redis.entities.User;
import com.atguigu.redis.mapper.UserMapper;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;


@Service
@Slf4j
public class UserService {
    public static final String CACHE_KEY_USER = "user:";
    @Resource
    private UserMapper userMapper;
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 业务逻辑没有写错,对于小厂中厂(QPS《=1000)可以使用,但是大厂不行
     * @param id
     * @return
     */
    public User findUserById(Integer id)
    {
        User user = null;
        String key = CACHE_KEY_USER+id;

        //1 先从redis里面查询,如果有直接返回结果,如果没有再去查询mysql
        user = (User) redisTemplate.opsForValue().get(key);

        if(user == null)
        {
            //2 redis里面无,继续查询mysql
            user = userMapper.selectByPrimaryKey(id);
            if(user == null)
            {
                //3.1 redis+mysql 都无数据
                //你具体细化,防止多次穿透,我们业务规定,记录下导致穿透的这个key回写redis
                return user;
            }else{
                //3.2 mysql有,需要将数据写回redis,保证下一次的缓存命中率
                redisTemplate.opsForValue().set(key,user);
            }
        }
        return user;
    }


    /**
     * 加强补充,避免突然key失效了,打爆mysql,做一下预防,尽量不出现击穿的情况。
     * @param id
     * @return
     */
    public User findUserById2(Integer id)
    {
        User user = null;
        String key = CACHE_KEY_USER+id;

        //1 先从redis里面查询,如果有直接返回结果,如果没有再去查询mysql,
        // 第1次查询redis,加锁前
        user = (User) redisTemplate.opsForValue().get(key);
        if(user == null) {
            //2 大厂用,对于高QPS的优化,进来就先加锁,保证一个请求操作,让外面的redis等待一下,避免击穿mysql
            synchronized (UserService.class){
                //第2次查询redis,加锁后
                user = (User) redisTemplate.opsForValue().get(key);
                //3 二次查redis还是null,可以去查mysql了(mysql默认有数据)
                if (user == null) {
                    //4 查询mysql拿数据(mysql默认有数据)
                    user = userMapper.selectByPrimaryKey(id);
                    if (user == null) {
                        return null;
                    }else{
                        //5 mysql里面有数据的,需要回写redis,完成数据一致性的同步工作
                        redisTemplate.opsForValue().setIfAbsent(key,user,7L,TimeUnit.DAYS);
                    }
                }
            }
        }
        return user;
    }

}

3、数据库与缓存一致性的更新策略:

无论身操作,我们的目的就是保证最终一致性

  • 一般我们都给缓存设置过期时间,定期清理缓存并回写,是保证最终一致性的解决方案
  • 2.我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存,达到一致性,切记要以mysql的数据库写入库为准

3.2.可停机的情况更新策略:

  • 1.挂牌报错、凌晨升级、服务降级、温馨提示
  • 2.最好单线程操作(对于重量级的数据操作

3.3.不停机情况4种更新策略:

四种更新策略(推荐最后一种,看场景)

a.先更新数据库,在更新缓存

  • 1.异常情况1:
  • 2.异常情况2:

b.先更新缓存,再更新数据库

  • 1.这种方式不太推荐,一般业务会将mysq作为底单数据库,有最终解释权
  • 2.异常情况:

c.先删除缓存,再更新数据库

c1.异常问题分析:




上述三个步骤的总结

c2.解决方案:

1.采用延时双删策略解决上面的异常


2.双删方案面试:

  • 1.这个删除应该休眠多久呢
  • 2上述同步淘汰策略的改善,防止系统吞吐量降低:
  • 3.后续看门狗WatchDog源码分析:

d.先更新数据库,再删除缓存

d1.异常问题:
d2.解决方案:

4.3.总结:


4.3.编码实现:Redis与MySQL数据库案例一致性的

编码实现需要使用到Canal,在Cana篇章再做具体的整理

相关推荐
梅孔立13 小时前
两种免费的翻译API调用方式详解 - Edge官方API与个人缓存服务
前端·缓存·edge
gwf21613 小时前
SSD读写速度深度解析:顺序读写vs随机读写、IOPS、延迟,你的硬盘性能到底怎么看?
git·嵌入式硬件·缓存·github·智能硬件
番茄炒鸡蛋加糖15 小时前
缓存三大问题
缓存
江晓鱼未暖20 小时前
十七、Redis 核心原理与架构详解
大数据·数据库·数据仓库·redis·缓存·架构
小罗水20 小时前
第13章 Redis 缓存、幂等锁与任务状态
数据库·redis·缓存
热心市民lcj21 小时前
Spring Boot 整合 Caffeine 本地缓存实战
spring boot·后端·缓存
zx1154501 天前
【热点 Key 探测与加长缓存 TTL 实战】
缓存
toooooop81 天前
如何用 ss + ps 精准定位本机 Redis 的“隐形”消费者?
linux·数据库·redis·缓存
软萌萌的11 天前
C#中的多级缓存架构设计与实现深度解析
缓存·c#·wpf
醉城夜风~2 天前
HTML表单域学习博客:表单标签、输入框、按钮详解
android·java·缓存