Redis核心原理与应用实操

一、基本概念

1、noSQL

关系数据库MySQL的IO操作慢!noSQL为内存操作 快、高并发。

2、Redis基本概念

存储形式:K-V键值对

优点:

对数据高并发读写(直接在内存中操作)

单线程操作(所谓的多线程只是多个命令队伍排队 CPU处理时仍然是单线程)

Redis -----提供缓存服务!!!!

Redis定位是缓存,提高数据读写速度,减轻对数据库读写与访问的压力。

3、Redis安装与使用

mac安装redis教程:mac安装redis(一条龙服务) - 掘金

安装到/usr/local目录下 访达内使用command+shift+g 可以进入此隐藏文件夹!

终端使用redis-server 启动服务端

终端再打开一个窗口 使用redis-cli 启动客户端 将自动本地连接

--->打开服务端 redis-server

--->打开客户端 redis-cli

也可以下载图形界面客户端连接Redis服务就好了

linux内通用安装:

进入/usr/redis/bin 下 ./redis-server ./redis-cli 就可以打开服务端、客户端

二、Redis常用类型及命令

1、String类型-命令-应用

应用--共享session--Redis的String类型键值对缓存

2、Hash类型-命令-应用

键一个---但是值里面又套了一个键值对

应用--共享session设计--Redis的key-value

3、List类型-命令-应用

Redis中List本质是双端链表 可以看做队列。 --有序

应用--收藏列表--多键值

String与Hash键值对都是一对一,而List键值对是一对多。

收藏列表:

key:favorite_list

value:id1,id2,id3...

4、Set类型--命令--应用

重要命令:

差集 -- sdiff key1 key2 ---返回key1特有元素

交集 -- sinter key1 key2 ---返回key1与key2共有元素

并集 -- sunion key1 key2 ---返回key1与key2 集合的元素(不含重复元素)

4、Zset类型-命令-应用

Sorted set = set + 排序效果

为了实现排序效果,要求每个元素关联一个double类型的分数,根据分数排序。

应用-排行榜

5、全局命令

对所有的Key都有效

《Redis类型总结》

思考:啥时候项目中使用Redis? 怎么在项目中使用Redis?

1、是否需要缓存 --首选Redis

2、是否使用Redis

3、怎么设计KEY-VALUE?

Value设计核心----Value类型选择: String、Hash、List、Set、Sorted set

· 是否需要排序?要 使用Sorted set

· 缓存的数据是多个值还是单个值?

· 单个值:简单类型String 对象值 Hash

· 多个值:不允许重复set;允许重复List

公司常用:

排序用Sorted set;其他都用String 方便转化为JSON数据存进内存

Key设计核心----可读性、唯一性、灵活性、时效性

常用模板:

· 表名:主键名:主键值:列名

· 业务模块名:业务逻辑含义:其他:Value类型

三、Redis事务

MySQL事务 要么全部执行成功,要么回滚。

但Redis事务不会回滚。

Redis事务过程:

开始事务(MULTI) --> 命令入队 --> 执行事务(EXEC)

Redis事务可一次执行多个命令,具有以下特征:

· 在multi命令后,在exec命令触发事务执行前,所有命令都被放入队列缓存;

· exec命令后开始执行事务,某条命令执行失败不影响其他命令;

· 其他客户端的命令不会插入到事务命令队列中。

四、Redis持久化

为啥要持久化?

Redis将数据全部存储在内存中,一旦断电或者崩溃,数据就将全部丢失。重启后想要回到原来的状态,只能重来。所以为了实现重启后任然可以快速恢复到原来的状态,就将 全部数据 备份成二进制文件xx.rdb 存储在硬盘中,这属于IO操作,为了提升效率,固定时间创建子进程快照存储一次。

Redis持久化机制:

快照方式RDB(Redis DataBase)

文件追加方式AOF(Append Only File)

混合持久化方式

1、RDB

将内存中所有数据以快照方式写入到二进制文件中,默认为dump.rdb

触发机制

  • 手动触发:save 但会造成Redis服务器进程阻塞;bgsave 执行fork指令创建子进程执行bgsave。
  • 自动触发(默认):save m n 表示在m秒内数据集修改n次自动触发bgsave命令。

优点:恢复数据快于AOF、快照文件是紧压缩文件,适用于全景备份,容灾备份。

缺点:无法实时/秒级持久化;快照文件不同版本格式不一致不兼容。

RDB机制,倘若在俩次快照之间宕机,必然造成部分数据丢失,所以仍然不足;

将所有Redis写命令记录到日志,可以保证所有数据的绝对安全

2、AOF

只追加日志文件(命令)

将Redis所有写命令记录到日志文件中,重启后再次执行命令达到恢复数据目的。

打开redis.conf配置文件修改。打开后就将生成appendonly.aof

rewrite目的就是将指令重写合并,实现减小AOF文件大小。

本质还是文件替换,不是文件内部修改!!!

手动:客户端执行bgrewriteaof命令

自动:redis.conf配置

优点:数据安全性高最多损失1s数据量;AOF文件过大后台自动重写

缺点:文件体积大(未压缩);持久化慢

3、RDB-AOF混合方式

(Redis4.0后默认配置)

RDB是定时将内存中数据存入硬盘;AOF是将全部写命令存入硬盘

RDB-AOF混合方式就是 把当前数据以RDB方式写入文件开头,再将后续操作命令以AOF方式存入文件。即以RDB作为全量备份,AOF作为增量备份。RDB可保证重启时的速度,AOF可保证数据避免丢失。

4、持久化机制选择

允许数据丢失 选RDB。

五、Redis内存淘汰机制

Redis配置: maxmemory <byte> 设置Redis内存上限

----Key内卷机制

六、过期Key处理

惰性删除:当访问Key时才判断是否过期,过期则删除。对CPU友好,但是浪费内存;

定时删除:设置键过期时间的同时设置定时器,对内存友好,对CPU不好,需要CPU维护定时器;

定期删除:定期检查一遍Key 过期则删除。

Redis服务器采用惰性删除与定时删除结合,在对CPU减负与避免内存浪费之间平衡。

七、缓存管理机制

总结:为了防止内存吃满,使用多策略综合

  • 缓存管理机制 : 定时删除 + 惰性删除 + 内存淘汰
  • 缓存穿透:
  • 缓存击穿:
  • 缓存雪崩:

为了防止 内存穿透(内存中热点数据过期被删,大量请求到达数据库) 多策略综合

为了防止 缓存雪崩()

过期时间均匀分布 + 热点数据永不过期

八、主从模式与哨兵--高可用

实现高可用 俩大策略: 主从复制 + 哨兵

1、主-从模式

主节点主要负责写数据与数据同步,从节点主要负责读数据,读写分离提高性能;

主节点崩溃从节点顶替,实现高可用。

也就是冗余备份-- 针对重要数据

从节点不可以写,只可以读。

2、数据同步策略

基本数据同步方式:主节点向从节点传输RDB文件,同时传输命令执行。(RDB与AOF混合)

从节点数据恢复:

主节点设置缓冲区,存储最近同步的数据,若某个从节点丢失数据,主节点将缓冲区文件传输,从节点根据复制偏移量得知缺失哪些数据从而弥补。

复制偏移量(游标):从0开始,随着数据复制和同步,主从节点同时更新,比较各自偏移量即可知道缺失哪些数据。

3、哨兵(管理员)

作用:监视所有节点;自动故障转移

负责自动将从节点升级为主节点;监控主从节点工作;

工作:

  1. 每隔10秒使用info命令 问候主节点,返回从节点信息;
  2. 每隔1s使用ping命令 查看所有节点是否掉线;多个管理员判定某个节点下线才能将该节点判定为下线才可以启动故障转移。

主节点挂掉做故障转移:

选择优先级高(设备配置高),复制偏移量大(数据更全)的从节点作为主节点。

哨兵机制解决了主节点宕机后的自动故障转移,但是无法解决单节点并发压力以及单节点内存上限问题。接下来就出现厄redis集群。

集群一下子解决掉自动故障转移、单节点并发压力、单节点内存上限三大问题。

九、Redis集群--扩容

1、入门核心

将多个节点的容量合并,实现容量扩增。

2、扩容方式

三次握手加入集群

集群内任一成员发MEET信息发起握手;对方回复PONG信息同意加入;成员再发PING成功加入。

3、数据存储分配

Redis-Cluster模式使用哈希槽(Hash slot)实现数据分片。

槽位分配:

手动为每个节点分配来负责一定的槽位。

槽位计算:

数据读写时,对键值进行哈希运算,映射到哪个槽位就由槽位负责节点负责读写。

实现信息一致

启动时每个节点将自己负责的槽位的信息通知其他节点,将自己负责的每个槽位用1表示,其他用0表示,那么一次出书的信息量就是16384bit,即2KB。

为了确定某个槽位由谁负责,直接建数组存储每个槽位对应的节点。

集群工作 + 主从复制

总:

十、java操作Redis服务

1、添加jedis依赖

2、写业务类:

  • 创建jedis对象(填写Redis服务端的IP及端口号)
  • 选择使用的库(redis会有16个库 序号0-15)
  • 对Redis进行操作,直接Redis对象调用方法(与命令同名)
  • 释放资源
java 复制代码
public static void main(String[] args) {
        //创建jedis客户端对象
        Jedis jedis = new Jedis("127.0.0.1",6379);

        //选择使用一个库 默认使用0号库
        jedis.select(0);

        //开始对redis进行操作
        System.out.println(jedis.get("mylove"));
        Set<String> keys = jedis.keys("*");
        keys.forEach(key -> System.out.println("key : " + key));
        System.out.println(jedis.dbSize());

        System.out.println(jedis.exists("name"));
//        jedis.expire("name",100);
//        System.out.println(jedis.ttl("name"));

//        System.out.println(jedis.randomKey());

        jedis.rename("s1","s111");
        
        //释放资源
        jedis.close();
    }

十一、SpringBoot操作Redis

spring data提供了RedisTemplate、StringRedisTemplate俩父子接口。redisTemplate为父接口,俩个参数都必须是object对象;StringRedisTemplate为子接口俩参数都是String类型。

1、添加依赖

spring-boot-starter-data-redis

开启远程权限:/usr/local/etc/redis.conf 进入后直接 / 表示搜索 bind 将bind后面改为0.0.0.0

检查防火墙是否打开6379端口

2、application.properties文件

中添加配置: IP、端口、库

java 复制代码
server.port=8989
#redis
spring.redis.host=172.16.170.137
spring.redis.port=6379
spring.redis.database=0

3、StringRedisTemplate

<String>类型

java 复制代码
//启动SpringBoot应用
@SpringBootTest(classes=RedisTestApplication.class)
@RunWith(SpringRunner.class)
class RedisTestApplicationTests {

    //1、注入StringRedisTemplate
     @Resource
    private StringRedisTemplate stringRedisTemplate; //字符串友好型 -- key、vaue都是String类型
     //2、操作redis中字符串opsForValue 实际操作就是redis中String类型

    @Test
    public void testString(){
        stringRedisTemplate.opsForValue().set("name","woshinidaye");
        System.out.println(stringRedisTemplate.opsForValue().get("name"));
    }
    @Test
    public void testKey(){
        stringRedisTemplate.opsForValue().set("name","woshinizuzong");
        System.out.println(stringRedisTemplate.hasKey("name"));
        System.out.println(stringRedisTemplate.type("name"));
        Set<String>keys = stringRedisTemplate.keys("*");
        keys.forEach(key -> System.out.println("key : " + key));
        stringRedisTemplate.delete("name");
        System.out.println(stringRedisTemplate.hasKey("name"));
    }
}

实际应用:手机号验证码超时设置

java 复制代码
//        设置一个Key超时时间 用于手机号验证码超时
        stringRedisTemplate.opsForValue().set("phonenumber","3698",120, TimeUnit.SECONDS);

<List> -- 有序可重复

java 复制代码
public void testList(){
        stringRedisTemplate.opsForList().leftPush("names","小王八羔子");//创建一个列表并放入一个元素
        stringRedisTemplate.opsForList().leftPushAll("names","小明","小张","任爸爸");
        List<String> names = new ArrayList<String>();
        names.add("xiaosan");
        names.add("xiaodongzi");
        stringRedisTemplate.opsForList().leftPushAll("names","xiaosansiwu"); //创建一个列表放入多个元素
        List<String> strlist = stringRedisTemplate.opsForList().range("names",0,-1);
        strlist.forEach(value-> System.out.println("value : " + value));
    }

<Set> -- 无序不可重复

java 复制代码
public void testSet(){
//        stringRedisTemplate.opsForSet().add("sets","小明","小张","小人","郑仁");//创建sets集合添加元素
        Set<String> sets = stringRedisTemplate.opsForSet().members("sets");
        sets.forEach(value-> System.out.println("value : " + value));
        System.out.println(stringRedisTemplate.opsForSet().size("sets"));
    }

<Zset>

java 复制代码
public void testZset(){
        stringRedisTemplate.opsForZSet().add("Score","小明",100);
        stringRedisTemplate.opsForZSet().add("Score","小红",77);
        stringRedisTemplate.opsForZSet().add("Score","小薛",89);
        stringRedisTemplate.opsForZSet().add("Score","小卡",39);
        stringRedisTemplate.opsForZSet().add("Score","小热",80);
        Set<String> zsets = stringRedisTemplate.opsForZSet().range("Score",0,-1);
        zsets.forEach(value-> System.out.println("valus : " + value));
    }

4、RedisTemplate

stringRedisTemplate接口:key-value都是字符串类型直接存储在redis内存中

RedisTemplate接口:key,value要通过序列化成一个序列化结果 ,最终存储在Redis内存中的是 序列化对象。(内存中序列化key -- 序列化value)

但是这种的话无法直接在终端直接通过String类型的key获取对应的value。一般实际开发中是String类型key -- Object类型value

对于String、Set、Zset、List都只需要调整一个键的类型为StringRedisSerializer()

但是Hash要调整key与hashkey二者

java 复制代码
public void testRedisTemplate(){
        /**
         redisTemplate对象中key-value序列化都是JdkSerializationRedisSerializer
         为了实现 key:string  value:object
         修改默认的key方案 :key  StringRedisSerializer
         */
        //修改key序列化方案 String类型序列
        redisTemplate.setKeySerializer(new StringRedisSerializer());

//        修改Hash key序列化方案
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        User user = new User();
        user.setId(UUID.randomUUID().toString());
        user.setName("xiaosdsdsd");
        user.setAge(19);
        user.setBir(new Date());
        redisTemplate.opsForValue().set("user",user);

        //如果未修改key的序列化类型 只可以通过java通过序列化处理来获取键值 无法通过终端快速获取
        User user1 = (User) redisTemplate.opsForValue().get("user");
        System.out.println(user1);

        redisTemplate.opsForList().leftPush("listUser",user);
        redisTemplate.opsForSet().add("setUser",user);
        redisTemplate.opsForZSet().add("zsetUser",user,89);
        redisTemplate.opsForHash().put("hashUser","hashkey",user);
    }

5、绑定key简化

java 复制代码
public class boundRedisTemplate {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private RedisTemplate redisTemplate;

//    spring data为了对redis操作更简便,提供了bound API
    @Test
    public void boundTest(){
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());

//        将一个对key进行连续操作进行绑定,本质上对key绑定
//        stringRedisTemplate.opsForValue().set("name","zhangsan");
//        stringRedisTemplate.opsForValue().append("name","是我的好大儿");
//        String name = stringRedisTemplate.opsForValue().get("name");
//        System.out.println(name);

//        对字符串类型key进行绑定 后续所有操作都是基于这个key 的操作

        BoundValueOperations<String,String> nameValue = stringRedisTemplate.boundValueOps("name");
        nameValue.set("zhangsan");
        nameValue.append("是我的好大儿");
        String s = nameValue.get();
        System.out.println(s);

//        set
//        BoundSetOperations boundSetOperations = redisTemplate.boundSetOps();
//        BoundSetOperations<String, String> stringStringBoundSetOperations = stringRedisTemplate.boundSetOps();
//        ZSet
//        BoundZSetOperations<String, String> stringStringBoundZSetOperations = stringRedisTemplate.boundZSetOps();
//        BoundZSetOperations boundZSetOperations = redisTemplate.boundZSetOps();
//        hash
//        BoundHashOperations<String, Object, Object> stringObjectObjectBoundHashOperations = stringRedisTemplate.boundHashOps();
//        BoundHashOperations boundHashOperations = redisTemplate.boundHashOps();
//
    }
}

6、总结

复制代码
1、针对处理key value 都是 String 使用 stringRedisTemplate
2、针对处理 key value存在对象 使用 redisTemplate 并且将key & hashkey 使用 StringRedisSerializer
3、针对同一个key多次操作 可以使用boundxxxops() value List Set Zset Hash 的API简化

十二、redis应用场景--redis分布式缓存

应用很重要:4.Redis中主从复制架构_哔哩哔哩_bilibili

十三、redis集群

十四、redis实现分布式session管理

memcache本质上还是Tomcat服务器存储应用Session,并且同步到memcache上,首先访问的还是Tomcat;

redis是所有Session直接存储到redis,有请求,Tomcat直接到redis取Session。

up主----编程不良人

相关推荐
指尖上跳动的旋律15 分钟前
shell脚本定义特殊字符导致执行mysql文件错误的问题
数据库·mysql
一勺菠萝丶26 分钟前
MongoDB 常用操作指南(Docker 环境下)
数据库·mongodb·docker
麦香--老农1 小时前
windows 钉钉缓存路径不能修改 默认C盘解决方案
缓存·钉钉
m0_748244831 小时前
StarRocks 排查单副本表
大数据·数据库·python
C++忠实粉丝1 小时前
Redis 介绍和安装
数据库·redis·缓存
wmd131643067122 小时前
将微信配置信息存到数据库并进行调用
数据库·微信
丰云2 小时前
一个简单封装的的nodejs缓存对象
缓存·node.js
Oneforlove_twoforjob2 小时前
【Java基础面试题025】什么是Java的Integer缓存池?
java·开发语言·缓存
泰伦闲鱼2 小时前
nestjs:GET REQUEST 缓存问题
服务器·前端·缓存·node.js·nestjs
是阿建吖!2 小时前
【Linux】基础IO(磁盘文件)
linux·服务器·数据库