您现在的位置是:首页 >其他 >redis知识复习网站首页其他
redis知识复习
redis知识复习
redis基础知识
一. redis的认识
1. 非关系型数据库 与 传统数据库 的区别
背会这张表就行了
 
2. 安装redis并设置自启动
-  
在Linux环境下 安装redis依赖
yum install -y gcc tcl -  
(/usr/local/src目录下) 下载对应的redis安装包(本次为v6.2.6,如果有之前下载过的redis,记得提前删除干净,以防配置环境等因素造成安装的异常)
wget https://download.redis.io/releases/redis-6.2.6.tar.gz -  
解压压缩包获得 redis程序安装包
tar -xvf redis-6.2.6.tar.gz -  
在该程序包目录下执行 编译安装命令(默认该步骤会将redis软件安装到/usr/local/bin目录下)
make && make install -  
执行redis服务命令 即可启动redis,该方式为前台启动方式(不友好,不推荐使用)
redis-server -  
修改配置文件,完成指定配置下的启动准备(记得对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" -  
根据指令,完成指定配置文件下的启动
redis-server redis.conf -  
查看redis进程命令,以及杀死进程命令
ps -ef | grep redis kill -9 PID(PID为对应的进程序列号) -  
开机自启动(在 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. 熟悉命令行客户端
-  
在/usr/local/bin/目录下,使用redis-cli实现连通redis
redis-cli -h 192.168.2.190 -p 6379 -a 123321 >ping -  
存取数据set/get,换库select [index]

 
4. 熟悉图形化工具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)
 
 redis的key的格式:
层级存储:[项目名]:[业务名]:[类型]:[id],这种存储的方式,有一个好处,那就是在使用gui图形界面能看到层级结构
Hash类型


 List类型(对比Java的双向链表)
 

 list的总结:可以广泛模拟 栈(同一个方向先进后出) 队列(不同方向进出) 阻塞队列(一头取,一头放,需要设置等待时间)
Set类型(对比Java的hashset,相当于底层使用hashmap实现)
 
 
 SortedSet类型(功能上类似TreeSet,底层数据结构不同)
 

三. redis的Java客户端
1. Jedis
单例使用流程


Jedis连接池用法

创建连接池对象,设置参数,完成连接池的创建,在使用过程中,与上述直接创建连接不同的是直接从连接池中获取一个连接,其他基本一致
// jedis = new Jedis("192.168.2.190",6379);
jedis = JedisConnectionFactory.getJedis(); //直接从连接池中获取一个
 
2. SpringDataRedis

 
 使用流程(写pom,写yml,写测试用例,完成测试)
-  
创建项目,引入依赖
2. 完成配置文件的设置 
3. 注入装配,实施测试 
redisTemplate的序列化操作存在的问题

基于可读性差的因素,可以自定义序列化方式,规避序列化造成的可读性问题 -  
加依赖
 
<!--jackson序列化工具-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
 
- 自定义序列化方式(最好能理解!实际上由于该方式进行反序列化的必要操作时,会必定携带@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则手动进行序列化与反序列化操作)
 

 RedisTemplate与StringRedisTemplate处理后两者存取的数据对比:
 

redis应用(未完结…)
四. 处理登录验证
1. 设计登录拦截

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

 实例:用户分时段查询多个热点商铺/商品/文章信息,结果在某个时间节点该信息全部失效,导致该时间节点需要大量访问数据库造成数据库压力
解决方案:给redis中的缓存数据设置不同的TTL
3. 处理缓存击穿
实例:多名用户在一个定时活动的时间节点访问某个热点商铺/商品/文章信息,结果造成缓存失效,结果造成访问数据库造成数据库的压力过大
- 使用互斥锁处理缓存击穿

 - 使用【逻辑过期时间】处理缓存击穿

 
六. 处理秒杀任务(优惠券)
1. 处理优惠券全局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. 快速入门
- 导依赖
 
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.13.6</version>
</dependency>
 
- 建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);
    }
}
 
- 测试基础使用
 
@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();
        }
    }
}
                
            




U8W/U8W-Mini使用与常见问题解决
QT多线程的5种用法,通过使用线程解决UI主界面的耗时操作代码,防止界面卡死。...
stm32使用HAL库配置串口中断收发数据(保姆级教程)
分享几个国内免费的ChatGPT镜像网址(亲测有效)
Allegro16.6差分等长设置及走线总结