您现在的位置是:首页 >其他 >redis知识复习网站首页其他

redis知识复习

Lin_Stack 2024-10-07 00:01:03
简介redis知识复习

redis基础知识

一. redis的认识

1. 非关系型数据库 与 传统数据库 的区别

背会这张表就行了

2. 安装redis并设置自启动

  1. 在Linux环境下 安装redis依赖

    yum install -y gcc tcl
    
  2. (/usr/local/src目录下) 下载对应的redis安装包(本次为v6.2.6,如果有之前下载过的redis,记得提前删除干净,以防配置环境等因素造成安装的异常)

    wget https://download.redis.io/releases/redis-6.2.6.tar.gz
    
  3. 解压压缩包获得 redis程序安装包

    tar -xvf redis-6.2.6.tar.gz
    
  4. 在该程序包目录下执行 编译安装命令(默认该步骤会将redis软件安装到/usr/local/bin目录下)

    make && make install
    
  5. 执行redis服务命令 即可启动redis,该方式为前台启动方式(不友好,不推荐使用)

    redis-server 
    
  6. 修改配置文件,完成指定配置下的启动准备(记得对redis.conf做备份,以防修改失误)

    cp redis.conf redis.conf.bck
    vi redis.conf
    

    添加任意进程可访问 守护进程开启 开启密码 日志记录

    # 文本内部的修改(供复制粘贴)
    
    # 任意ip可访问
    bind 0.0.0.0 
    # 守护进程打开,可后台运行
    daemonize yes
    # 密码设置123321
    requirepass 123321
    # 打开日志记录,并命名
    logfile "redis.log"
    
  7. 根据指令,完成指定配置文件下的启动

    redis-server redis.conf
    
  8. 查看redis进程命令,以及杀死进程命令

    ps -ef | grep redis
    
    kill -9 PID(PID为对应的进程序列号)
    
  9. 开机自启动(在 system系统文件夹中 新建一个配置类文件)

    vi /etc/systemd/system/redis.service
    

    配置类文件内容如下:

    [Unit]
    Description=redis-server
    After=network.target
    
    [Service]
    Type=forking
    # 这行配置内容要根据redis的安装目录自定义路径
    ExecStart=/usr/local/bin/redis-server /usr/local/src/redis-6.2.6/redis.conf
    PrivateTmp=true
    
    [Install]
    WantedBy=multi-user.target
    

    重载系统服务,以便配置文件生效

    systemctl daemon-reload
    

    此时可以使用系统命令实现redis的启动、查看状态或关闭

    systemctl start redis
    
    systemctl status redis
    
    systemctl stop redis
    

    执行下面的命令,实现开机自启:

    systemctl enable redis
    

    查看此时,redis 服务的状态:

    systemctl status redis
    

3. 熟悉命令行客户端

  1. 在/usr/local/bin/目录下,使用redis-cli实现连通redis

    redis-cli -h 192.168.2.190 -p 6379 -a 123321
    
    >ping
    
  2. 存取数据set/get,换库select [index]

    set/get/select命令的输入

4. 熟悉图形化工具RDM

RDM工具

二. redis的命令与数据结构

1. 数据结构介绍

常用数据结构

2. redis通用命令(熟练掌握)

# keys:查看所有key
keys * 
# set:设置添加k-v  mset:批量添加
set k1 v1
mset k1 v1 k2 v2 k3 v3
# del:删除
del k1
# exist 查看是否存在
exist k1
# expire:设置有效期时间,单位s,没有特殊设置则为-1表示永久有效
expire k1 20
# ttl:查看有效期剩余时间(-1表示永久,-2表示过期,正数表示剩余秒数)
ttl k1

String类型(可存string,int,float)
String类型常用命令
redis的key的格式:

层级存储:[项目名]:[业务名]:[类型]:[id],这种存储的方式,有一个好处,那就是在使用gui图形界面能看到层级结构

Hash类型

Hash类型存储结构Hash类型数据常用命令
List类型(对比Java的双向链表)
List类型存储结构List类型常用命令
list的总结:可以广泛模拟 栈(同一个方向先进后出) 队列(不同方向进出) 阻塞队列(一头取,一头放,需要设置等待时间)

Set类型(对比Java的hashset,相当于底层使用hashmap实现)
Set类型数据结构
Set类型常用命令
SortedSet类型(功能上类似TreeSet,底层数据结构不同)
SortedSet类型存储数据结构SortedSet类型常用命令

三. redis的Java客户端

1. Jedis

单例使用流程

单例使用redis

测试

Jedis连接池用法

Jedis连接池使用redis

创建连接池对象,设置参数,完成连接池的创建,在使用过程中,与上述直接创建连接不同的是直接从连接池中获取一个连接,其他基本一致

// jedis = new Jedis("192.168.2.190",6379);
jedis = JedisConnectionFactory.getJedis(); //直接从连接池中获取一个

2. SpringDataRedis

SpringRedisTemplate
操作
使用流程(写pom,写yml,写测试用例,完成测试)

  1. 创建项目,引入依赖
    依赖2. 完成配置文件的设置 yml文件3. 注入装配,实施测试 装配测试
    redisTemplate的序列化操作存在的问题
    序列化造成的数据不可读
    基于可读性差的因素,可以自定义序列化方式,规避序列化造成的可读性问题

  2. 加依赖

<!--jackson序列化工具-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
  1. 自定义序列化方式(最好能理解!实际上由于该方式进行反序列化的必要操作时,会必定携带@class信息,造成占用内存产生大量冗余,并不推荐使用,后续会使用StringRedisTemplate操作key,value则手动进行序列化与反序列化操作)
/**
 * redis反序列化自定义操作工具类
 */

@Configuration
public class RedisConfig {
    /**
     * @param redisConnectionFactory 引入工厂
     * @return 返回经过处理的redisTemplate模板
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        // 创建RedisTemplate对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 设置连接工厂
        template.setConnectionFactory(redisConnectionFactory);
        // 创建JSON序列化工具
        GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        // 设置key的序列化
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        // 设置value的序列化
        template.setValueSerializer(jsonRedisSerializer);
        template.setHashKeySerializer(jsonRedisSerializer);
        // 返回RedisTemplate对象
        return template;
    }
}

3. StringRedisTemplate(推荐)

(基于内存占用问题,使用StringRedisTemplate来改善内存问题,StringRedisTemplate操作key,value则手动进行序列化与反序列化操作)
SDR
RedisTemplate与StringRedisTemplate处理后两者存取的数据对比:
对比对比1

redis应用(未完结…)

四. 处理登录验证

1. 设计登录拦截

在这里插入图片描述

五. 处理热点数据的查询工作

1. 处理缓存穿透

缓存穿透:浏览器不断发送未命中的请求,redis一直未命中,一直查询数据库,给数据库造成很大压力
穿透
实例:用户查询一个热点商铺/商品/文章信息,信息不存在,持续访问造成数据库压力
解决方案:
安排

2. 处理缓存雪崩

在这里插入图片描述
实例:用户分时段查询多个热点商铺/商品/文章信息,结果在某个时间节点该信息全部失效,导致该时间节点需要大量访问数据库造成数据库压力

解决方案:给redis中的缓存数据设置不同的TTL

3. 处理缓存击穿

实例:多名用户在一个定时活动的时间节点访问某个热点商铺/商品/文章信息,结果造成缓存失效,结果造成访问数据库造成数据库的压力过大

  1. 使用互斥锁处理缓存击穿
    在这里插入图片描述
  2. 使用【逻辑过期时间】处理缓存击穿
    在这里插入图片描述

六. 处理秒杀任务(优惠券)

1. 处理优惠券全局ID的生成唯一性

全局ID
ID生成类

@Component
public class RedisIdWorker {
    //开始时间戳
    private static final long BEGIN_TIMESTAMP = 1674086400L;

    //序列号位数
    private static final int COUNT_BITS = 32;

    private StringRedisTemplate stringRedisTemplate;

    public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    public long nextId(String keyPrefix){
        //1.生成时间戳
        LocalDateTime time = LocalDateTime.now();
        long nowSecond = time.toEpochSecond(ZoneOffset.UTC);
        long timestamp = nowSecond - BEGIN_TIMESTAMP;
        //2.生成序列号,redis自增长,redis单个key自增长有上限,2的64次方
        //2.1获取当前日期,精确到天
        String date = time.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);
        //3.拼接并返回,不能使用字符串方式拼接
        return timestamp << COUNT_BITS | count;//先向左移32位,那么低32位全为0,跟序列号进行或操作
    }
    /**
     * 生成开始时间戳
     * @param args
     */
    public static void main(String[] args) {
        LocalDateTime time = LocalDateTime.of(2023, 1, 19, 0, 0, 0);
        long second = time.toEpochSecond(ZoneOffset.UTC);
        System.out.println(second);
    }
}

其他方案:
在这里插入图片描述

2. 优惠券秒杀流程(抢优惠券)

2.1 单体模式下的优惠券秒杀流程

在这里插入图片描述

2.2 集群环境下的优惠券秒杀流程(setnx分布式锁)

(在集群模式下,加锁只是该台jvm给当前这台服务器处理的请求加锁,而集群是多台服务器轮询处理请求,会造成每台服务器都有一个加锁的线程,每台服务器都会有一个新订单创建处理)
在这里插入图片描述
在这里插入图片描述
解决原子性问题,造成的锁无法及时释放的Lua脚本代码

-- 这里的 KEYS[1] 就是锁的 key,这里的 ARGV[1] 就是当前线程标识
-- 获取锁中的线程标识 get key
local id = redis.call('get', KEYS[1]);
-- 比较线程标识与锁中的标识是否一致
if (id == ARGV[1]) then
    -- 释放锁 del key
    return redis.call('del', KEYS[1])
end
return 0

七. redis分布式锁——Redisson(重要掌握)

上述集群的基于 setnx 实现的分布式锁存在下面的问题
1.不可重入:同一个线程无法多次获取同一把锁
2.不可重试:获取锁只尝试一次就返回 false,没有重试机制
3.超时释放:锁超时释放虽然可以避免死锁,但如果是业务执行耗时较长,也会导致锁释放,存在安全隐患
4.主从一致性:如果 Redis 提供了主从集群,主从延同步在延迟,当主机宕机时,如果从机同步主机中的数据,则会出现锁失效

Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格
它不仅提供了一系列的分布式的 Java 常用对象,还提供了许多分布式服务,其中就包含了各种分布式锁的实现。

1. 快速入门

  1. 导依赖
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.13.6</version>
</dependency>
  1. 建Redisson类
@Configuration
public class RedisConfig {
    @Bean
    public RedissonClient redissionClient() {
        // 配置类
        Config config = new Config();

        // 添加 Redis 地址,此处添加了单点的地址,也可以使用 config.useClusterServers() 添加集群地址
        config.useSingleServer().setAddress("redis://192.168.2.12:6379").setPassword("123321");

        // 创建客户端
        return Redisson.create(config);
    }
}

  1. 测试基础使用
@Resource
private RedissonClient redissonClient;

@Test
void testRedisson() throws InterruptedException {
    // 获取锁(可重入),指定锁的名称
    RLock lock = redissonClient.getLock("anyLock");
    // 尝试获取锁,参数分别是:获取锁的最大等待时间(期间会重试过),锁自动释放时间,时间单位
    boolean isLock = lock.tryLock(1, 10, TimeUnit.SECONDS);
    // 判断锁是否获取成功
    if (isLock) {
        try {
            System.out.println("执行业务");
        } finally {
            //释放锁
            lock.unlock();
        }
    }
}

2. Redisson的重要原理

2.1 可重入锁原理

在这里插入图片描述

2.2 可重试锁,

@Resource
private RedissonClient redissonClient;

@Test
void testRedisson() throws InterruptedException {
    // 获取锁(可重入),指定锁的名称
    RLock lock = redissonClient.getLock("anyLock");
    // 尝试获取锁,参数分别是:获取锁的最大等待时间(期间会重试过),锁自动释放时间,时间单位
    boolean isLock = lock.tryLock(1, 10, TimeUnit.SECONDS);
    // 判断锁是否获取成功
    if (isLock) {
        try {
            System.out.println("执行业务");
        } finally {
            //释放锁
            lock.unlock();
        }
    }
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。