Redis详细总结配置与使用

1、Redis基本介绍

Redis是开源的,位于内存中的数据结构存储系统。主要用于数据缓存、消息中间件。内置LUA脚本(支持原子性操作)、LRU驱动事件、事务和不同级别的磁盘持久化,Redis也可以通过哨兵和自动化分区提供高可用性。

Redis为什么快

首先,Redis给我们的第一印象就是快,非常快,位于内存的IO操作可以达到秒级十万的操作,从内存上的寻址速度可以达到ns级别,带宽很大,而一般sql型数据库则是从磁盘上寻址,速度则是ms级别,所以磁盘相比内存在寻址上慢了10万倍。
在这里插入图片描述
磁盘数据库如Mysql,数据和索引都是持久化保存在磁盘上的,当我们使用SQL语句查询命令时,若该数据的索引还没加载到内存,首先要把索引加载到内存,然后通过寻址定位和IO,将数据对应的磁盘块加载到内存中,最后读取数据。
硬盘数据库读取数据
Redis在内存中读取数据,是电信号的传递。
在这里插入图片描述

1.1、 Redis内核架构

epoll介绍

epoll作为Linux下高性能网络服务器必备技术至关重要。nginx、Redis、skynet 以及大部分的游戏服务器都使用这一多路复用技术。对Redis的IO起到了关键作用。
在这里插入图片描述

1.2、 Redis原理

众所周知,Redis是单进程,单线程,单实例的(Redis6之前),那么当高并发的请求发送过来时,Redis如何快速响应?这里面主要用到了epoll多路复用技术,可以理解为多个输入通过一条高速线路,n个信道,分配到多个输出(分为频分,时分,波分),大大提高速度。并且是非阻塞的多路复用。
在这里插入图片描述

2、Redis的使用

2.1、Redis数据类型

高效的数据结构

Redis底层数据结构一共有6种:简单动态字符串(StringBuffer), 双向链表, 压缩列表, 哈希表, 跳表和整数数组,与之对应的数据类型关系图如下:

在这里插入图片描述
不同的数据结构对应了不同的应用场景:

  • String: 缓存, 分布式锁, 在线人数计算等
  • List: 队列, 微博, Instagram关注人时间轴队列等
  • Hash: 收藏,加购物车等
  • Set: 去重,点赞(不可重复点赞),踩一踩,共同好友等
  • ZSet: 排行榜排名、热搜榜等

Redis的自定义协议

Redis客户端使用RESP(Redis的序列化协议)协议与Redis服务器端进行通信,实现简单、解析快速且人可读。
RESP支持一下数据类型: 简单字符串、错误、整数、批量字符串和数组。
RESP在Redis中运作的方式为:

  1. 客户端将命令作为批量字符串的RESP数组发送到Redis服务器
  2. 服务器根据命令实现其中一种RESP类型并进行回复

在RESP中,某些的数据类型取决于第一个字节

  1. 对于简单字符串, 响应的第一个字节为"+"
  2. 对于整数, 响应的第一个字节为":"
  3. 对于批量字符串, 响应的第一个字节为"$"
  4. 对于错误, 响应的第一个字节为"-"
  5. 对于数组, 响应的第一个字节为"*"
    此外,RESP使用特殊变体表示null值。在RESP中, 协议的不同部分总是以"\r\n"(CRLF)终止。

高性能Redis协议分析器

快速,高效的解析协议利于阅读,使得该协议的实现性能变得和二进制协议一样快的主要原因是Redis协议将数据的长度定义在了数据正文之前,无需因为寻找特殊字符而进行全文扫描(JSON的payload),也无需像JSON一样对发送到服务器的payload进行转义。

Redis中Value的类型

  1. 字符串
    set k1 xxxx nx.
    set k2 Hello xxxx.
    nx => 只能新建
    xx => 只能更新
    append 字符串拼接在这里插入图片描述

GETRANGE
SETRANGE

  1. 数值
    先 set k1 99
    OBJECT encoding k1
    在这里插入图片描述
    可以看到是int类型
    INCR k1 (数值增加 默认为1)
    INCRBY k1 22 (数值增加 增加数值指定)
    DECR k1 (减)
    DECRBY k1 22
    INCRBYFLOAT k1 0.5

  2. 二进制安全
    字符串分为字节流和字符流
    STRLEN k1
    redis- li --raw
    Redis设计里没有数据类型(都包含在String,Byte)
    要指定好编码集(UTF-8、GBK)
    Redis是二进制安全的(字节数组)

  3. bitmap (便于运算)
    4.1 BITOP(位与,位货,异或)
    4.2 BITCOUNT destkey 0 -1

  4. List (栈)
    lpush
    lpop
    rpush / pop
    LRANGE
    LINDEX
    LSET
    LTPIM

  5. Hash
    set shown::name “xxx”
    set shown::age 15
    set shown::id 12
    对field进行数值计算

  6. Set (集合)
    sadd k3 1 2 3 4 5 6
    SMEMBERS (去所有元素,占用网卡吞吐量建议不要使用)
    SINTER k2 k3 (取交集
    SINTERSTORE dest k2 k3 (取交集并放入dest中)
    SUNION k2 k3 (并集
    SDIFF k2 k3 (差集)

  7. Sorted_set
    ZADD
    ZCARD
    ZCOUNT

2.2、Redis的进阶使用

1、 Redis的发布订阅

有这样一个应用场景,聊天室里发布的消息可以让(不管在不在该聊天室窗口)所有人都可以看到,这里就可以用到Redis的发布订阅功能。
PUBLISH xxxx hello
SUBSCRIBE xxxx (监听改Key)
在这里插入图片描述

2、 Redis事务

Redis事务的使用帮助 -> help@transactions
Redis事务的和Mysql类似,即将一组命令放在同一事务中,MULTI开启一个事务,这些事务会放在一个队列中,不会立即执行。只有当EXEC命令被调用时,改命令才会被执行。
事务执行顺序
注意: Redis不支持回滚,Redis命令只会因为错误语法失效

3、 布隆过滤器(Bloom)使用

4、 Redis作为缓存/数据库的区别

  • 缓存不是全量数据。缓存应该随着访问而变化 => 热数据
  • Redis里的数据怎么随着业务的变化而只保留热数据(内存大小的瓶颈)
    业务逻辑 -> key的有效期(过期时间)
    业务运行 -> 淘汰冷数据
    LFU 碰到了多少次
    LRU 多长时间没碰到
    ttl k1
    如果发生了写命令(SET XXX) 会直接剔除expiretime
    Redis每秒10次做一件事 => 随机测试20个keys相关过期检测 => 删除已过期keys =>
    若有超过1/4的key过期,则重复操作。
  • 过期的判定原理
    1. 被动访问时判定
    2. 周期轮询时判定(增量)

3、Redis的持久化(RDB/AOF)

3.1、Redis-RDB(RedisDB)

RDB即Redis的持久化机制,Redis父进程会单独fork(创建)一个子进程(和自己一模一样)来进行持久化。该子进程的所有数据(变量。环境变量,程序程序计数器等)都和父进程一模一样,将数据写入一个快照,等持久化结束后,数据库的快照(snapshot)以二进制的方式保存到磁盘中。用这个快照替换上次持久化好的文件。整个过程中,主进程没有任何IO操作,这就确保了极高的性能
在这里插入图片描述
RDB的主要特征是写时复制(copy on write)
创建紫禁城**(子进程 谐音梗 O(∩_∩)O)**时并不发生复制。
通过指针,父进程增删改查数据(写), 子进程读数据。

RDB的弊端

  • 不支持拉链结构
  • 因为父子进程同步的问题, 丢失数据相对多一些
  • 相对于Java中的序列化,恢复速度相对没有那么快

3.2、Redis-AOF(APPEND ONLY FILE)

AOF是以协议文本的方式,将所有对数据库进行过写入的命令(及其参数)记录到 AOF 文件,以此达到记录数据库状态的目的。
在这里插入图片描述
AOF优点:

  1. 丢失数据少
  2. Redis中, RDB和AOF可以同时开启, 若开启了AOF, 则只会用AOF恢复
    AOF弊端:
    相对于RDB,慢, 体量无限地变大

场景复现!!

在这里插入图片描述
解决设计思路:
设计方案让日志(AOF)足够小
Redis:

  1. 4.0之前 -> 重写 -> 删除抵消命令,合并重复命令 -> 纯指令的日志文件
  2. 4.0之后 -> 重写 -> 将增量的key以指令方式Append到AOF中 -> AOF混合体,利用了RDB的恢复快,AOF的全量特点

3.3、RDB、AOF实操

首先 修改redis配置文件(vim /etc/Redis/redis.conf)

在这里插入图片描述

  1. daemonize no #关闭守护进程
  2. logfile … # 注释掉保存日志文件路径,将日志输出到控制台

  3. save RDB 配置
  4. appendonly yes # 打开AOF
  5. aof-use-rdb-preamble no # 混合模式先关闭

3.4、Redis中AKF原理

在这里插入图片描述
由于redis是单机,单实例,单进程的。就会造成熟悉的单机问题:单点故障;容量有限;压力大
此时要了解redis的AKF,即业务功能拆分,根据XYZ轴拆分成三个方向。
X: 全量,镜像
Y: 业务,功能
Z: 逻辑优先
在这里插入图片描述
这样易造成数据的强一致性,即所有的节点都会阻塞,知道数据全部一致时,但这样就会破坏可用性。要想可用,可用容忍数据丢失一部分,也可以通过异步方式(不是强一致)
通过异步方式保证强一致性

4、Redis的主从,集群,哨兵(配置)

4.1、Redis CAP理论

CAP理论,即在一个分布式系统中,一致性(Consistemce)、可用性(Available)、分区容错性(Partition),三者只能同时实现其中两个,不可三者兼顾。

在这里插入图片描述
CAP理论是在集群中最常考虑的问题,在主从复制或者主备的集群中,对主节点(机器)做高可用,自动转移故障

4.2、Redis主从模式

4.2.1、Redis主从复制原理

设置从节点(机器)

4.3、Redis集群模式

4.4、Redis哨兵模式

4、Redis缓存问题

缓存击穿,穿透,雪崩

5、Spring结合Redis

5.1、 Redis api

参考Spring-data的官方文档。Spring中对于Redis的api支持集成了Lettuce和Jedis的连接,默认连接Lettuce
在这里插入图片描述
配置Lettuce连接,首先导入maven依赖
在这里插入图片描述
接着在Spring中创建连接工厂

@Configuration
class AppConfig {

  @Bean
  public LettuceConnectionFactory redisConnectionFactory() {

    return new LettuceConnectionFactory(new RedisStandaloneConfiguration("server", 6379));
  }
}

所有的LettuceConnection连接实例都是默认由LettuceConnectionFactory连接工厂类创建的,对于所有的非阻塞以及非事务操作,所有实例都共享线程安全的连接。LettuceConnectionFactory也可以配置LettucePool连接池

@Configuration
class AppConfig {

  @Bean
  public LettuceConnectionFactory redisConnectionFactory() {

    return new LettuceConnectionFactory(new RedisSocketConfiguration("/var/run/redis.sock"));
  }
}

配置Jedis连接,也是同样的过程,可以在官方文档中查看。
同时Redis的主从/哨兵/集群配置,是Redis常见的配置。通过Lettuce连接,可以完成主从(主写从读)的配置

@Configuration
class WriteToMasterReadFromReplicaConfiguration {

  @Bean
  public LettuceConnectionFactory redisConnectionFactory() {

    LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
      .readFrom(SLAVE_PREFERRED)
      .build();

    RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration("server", 6379);

    return new LettuceConnectionFactory(serverConfig, clientConfig);
  }
}

从节点向主节点读
为了构建高可用的Redis服务,Spring Data Redis 使用RedisSentinelConfiguration配置Redis的哨兵

/**
 * Jedis
 */
@Bean
public RedisConnectionFactory jedisConnectionFactory() {
  RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
  .master("mymaster")
  .sentinel("127.0.0.1", 26379)
  .sentinel("127.0.0.1", 26380);
  return new JedisConnectionFactory(sentinelConfig);
}

/**
 * Lettuce
 */
@Bean
public RedisConnectionFactory lettuceConnectionFactory() {
  RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
  .master("mymaster")
  .sentinel("127.0.0.1", 26379)
  .sentinel("127.0.0.1", 26380);
  return new LettuceConnectionFactory(sentinelConfig);
}

更多集群连接的配置可参考官方文档:

https://docs.spring.io/spring-data/redis/docs/2.3.5.RELEASE/reference/html

5.2、 高阶api

越来越多的小伙伴会选择使用高阶的api操作Redis(简单省事呗😄) 在org.springframework.data.redis.core包中导入使用。
在这里插入图片描述
在项目中导入SpringBoot和SpringDataRedis依赖
在这里插入图片描述

redisTemplate引用

在这里插入图片描述
properties文件中Redis简单的配置
在这里插入图片描述
将此类注入到Spring容器中,测试代码
在这里插入图片描述

5.3、 hash代码实现

由于redis是二进制安全的,所以在hash操作时,需要进行序列化操作,需要用到JSON,此时我们用到了Jackson模块 下面是核心代码

        // 将jackson对象转为hashMapper对象(JSON序列化)
        Jackson2HashMapper jm = new Jackson2HashMapper(objectMapper, false);

        redisTemplate.opsForHash().putAll("shown01", jm.toHash(student));

        Map map = redisTemplate.opsForHash().entries("shown01");

        //反序列化
        Student stu = objectMapper.convertValue(map, Student.class);

        System.out.println(stu.getName());

在这里插入图片描述
在这里插入图片描述
可以看到创建的Student对象在Redis中二进制格式存储。若想在存入Redis之前就完成反序列化,就需要加入一行操作

redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));

再用StringRedisTemplate操作哈希类型
Redis中已经反序列化

5.4、 自定义template代码实现

每次操作都需要用到Spring的RedisTemplate对象,不是很好用,我们完全可以自定义一个Template自已用(嘿咻嘿咻) 核心代码如下:

@Configuration
public class SelfTemplate {
    
    //自定义的RedisTemplate,以StringRedisTemplate为范本
    @Bean
    public StringRedisTemplate selfTemplate(RedisConnectionFactory fc){

        StringRedisTemplate selfRedisTemplate = new StringRedisTemplate(fc);

        selfRedisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
        
        return selfRedisTemplate;
        
    }
}

接着注入到实例中

@Autowired
    @Qualifier("selfTemplate")
    private StringRedisTemplate selfRedisTemplate;

就可以使用自定义的Template了

5.5、 发布订阅代码实现

实现消息的发布以及消息的订阅,核心代码

stringRedisTemplate.convertAndSend("xxxx", "Hello");

        RedisConnection redisConnection = stringRedisTemplate.getConnectionFactory().getConnection();
        redisConnection.subscribe(new MessageListener() {
            @Override
            public void onMessage(Message message, byte[] pattern) {
                byte[] body = message.getBody();
                System.out.println(new String(body));

            }
        }, "xxxx".getBytes(StandardCharsets.UTF_8));

        while (true){
            stringRedisTemplate.convertAndSend("xxxx", "Hello Shown");
            //每个三秒钟给自己发一个消息
            try {
            	Thread.sleep(3000);
        } catch (InterruptedException e){
        	e.printStackTrace();
        	}

这里可以用Redis实现一个实时发布消息的小程序

未完待续

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值