redis详细解析,面试绝对没有问题!!!

** 讲之前先来了解以下技术的发展归类,这对于学习java有一定的引导型**

技术的分类

  1. 解决功能性的问题:Java , Jsp , RDBMS , Tomcat , HTML , Linux , JDBC , SVN
  2. 解决扩展性的问题:Struts , Spring , SpringMvc , Hibernate , Mybatis
  3. 解决性能问题:NoSQL , Java线程 , Hadoop , Nginx , MQ , ElasticSearch

redis提高性能,提高的是哪些地方?

  1. 解决CPU及内存压力
    当客户量少的情况下访问服务器时,对于单体的服务器而言是够用的,但是当用户很多访问量很多时,单体服务器的访问次数急剧增大,这时服务器就有点受不了了,CPU的处理性能会大大降低,内存的压力会大大增加。当服务器变成多个时就可以解决这些问题。
  2. 减轻访问数据库时的IO压力
    访问单体数据库服务时,它会向数据库会发起IO操作以流的形式传输数据,IO流会非常耗时,就跟CPU遇到一些IO流的指令时它可能会执行耗时短的指令。也跟多线程遇到IO操作时可能会阻塞是一样的情况。

接下来看看现在的解决方案

在这里插入图片描述
可以采用集群的方式,也可以采用分布式的方式。但是会发现有一个问题?
用户访问ngnix,然后nginx把服务分配给了登陆服务器它去数据库查了一些用户信息,但是当用户再次发送请求到nginx时,nginx分配给其它服务器但是用户的信息没有带过来,这就是个很大的问题?怎么解决呢。

利用session就好了,session的范围是整个浏览器的,只要会话没关就行,实现session传数据有几种方式。

  1. session存到cookie中,当再次访问时浏览器会携带sessionid到服务器中,但是cookie是在浏览器中的,所以它不安全。
  2. session复制,把一开始得到的数据放进session中然后复制多份分别放到不同的服务器中,但是这样会造成数据冗余。浪费内存,实现session复制的几种方式

方案对比
方案1:使用Tomcat内置的Session复制方案SimpleTcpCluster 优点:内置,便于服务器水平扩展,对应用无入侵
缺点:只适合Tomcat小集群,不适合大集群,因为session复制是all to all的方式,性能低,内存消耗
方案2:使用第三方(个人)基于Tomcat实现的Session管理 tomcat-session-manager 优点:已经实现对tomcat7的支持,对应用无入侵
缺点:第三方支持,支持力度不够,尤其是不能提供对Tomcat8的支持
方案3:使用Spring Session实现 基于redis存储实现 优点:不依赖于特定容器,官方支持,适合大集群,能适应各种负载均衡策略,扩展能力强
缺点:对应用有入侵,需增加相关配置。
方案4:nginx ip_hash技术
优点:配置简单,无代码修改
缺点:服务器重启丢失问题,单点负载过高,单点故障问题

  1. 放入NoSQL中,其它服务器会先去该服务器中去查。有就拿过来直接使用。它是直接放入到内存中,所以不需要经过io流操作,减轻数据库io压力。

其实减轻io压力还有其它的方式
把数据库服务器进行分类,有文档数据库,列式数据库,还可以把数据库进行水平切分和垂直切分。
在这里插入图片描述

总结:NoSQL可以减轻CPU及服务器访问压力,直接从内存中取,可以做为缓存数据库减少io的读操作

NoSQL的概念

点击了解详情
几种常见的NoSQL数据库

在这里插入图片描述
在这里插入图片描述

大数据时代产生的几种数据库类型

  1. 行式数据库
    在这里插入图片描述
    就是把数据按照行的形式进行存储,以行为单位存储。
  2. 列式数据库
    在这里插入图片描述
    就是以列为单位存储
    点击了解详情

正式进入redis
** 引言**
在Web应用发展的初期,那时关系型数据库受到了较为广泛的关注和应用,原因是因为那时候Web站点基本上访问和并发不高、交互也较少。而在后来,随着访问量的提升,使用关系型数据库的Web站点多多少少都开始在性能上出现了一些瓶颈,而瓶颈的源头一般是在磁盘的I/O上。而随着互联网技术的进一步发展,各种类型的应用层出不穷,这导致在当今云计算、大数据盛行的时代,对性能有了更多的需求,主要体现在以下四个方面:

低延迟的读写速度:应用快速地反应能极大地提升用户的满意度
支撑海量的数据和流量:对于搜索这样大型应用而言,需要利用PB级别的数据和能应对百万级的流量
大规模集群的管理:系统管理员希望分布式应用能更简单的部署和管理
庞大运营成本的考量:IT部门希望在硬件成本、软件成本和人力成本能够有大幅度地降低
为了克服这一问题,NoSQL应运而生,它同时具备了高性能、可扩展性强、高可用等优点,受到广泛开发人员和仓库管理人员的青睐。

redis概述

Redis是现在最受欢迎的NoSQL数据库之一,Redis是一个使用ANSI C编写的开源、包含多种数据结构、支持网络、基于内存、可选持久性的键值对存储数据库,其具备如下特性:

  • 基于内存运行,性能高效
  • 支持分布式,理论上可以无限扩展
  • key-value存储系统
  • 开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API
  • 可以配合关系型数据库进行高速缓存

应用场景

在这里插入图片描述
Redis 的应用场景包括:缓存系统(“热点”数据:高频读、低频写)、计数器、消息队列系统、排行榜、社交网络和实时系统。

redis安装步骤

点击了解详情

redis相关知识

在这里插入图片描述
特点:单线程+多路IO复用=提高效率

在这里插入图片描述
详解:
黄牛买票是一个单线程,但是其他人找黄牛买票就是一个多路io复用
黄牛值专心做一件事那就是买票,而其他人可以只要告诉黄牛我需要哪里的票就行了,告诉黄牛一声之后,该人不需要在那一直等待票,该人可以去干其它的事情,当黄牛买到后就会通知你,你来取票即可。这样cpu就会一直工作就实现了单线程高效率的目的。

redis 常用命令

redis命令大全

key的相关命令
在这里插入图片描述
演示一下expire命令:
在这里插入图片描述
在这里插入图片描述
演示:
在这里插入图片描述
在这里插入图片描述

String的相关命令
在这里插入图片描述
在这里插入图片描述
演示append命令:
在这里插入图片描述
演示strlen:
在这里插入图片描述
演示setnx:
在这里插入图片描述
在这里插入图片描述
演示incr
在这里插入图片描述
** 如果你想根据情况去加或减可以使用下面命令**
在这里插入图片描述
演示:
在这里插入图片描述

在这里插入图片描述
演示msetnx:
在这里插入图片描述

在这里插入图片描述
演示getrange :
在这里插入图片描述
演示setrange:
在这里插入图片描述

在这里插入图片描述
演示setex:
在这里插入图片描述
演示getset:
在这里插入图片描述

List常用命令

简介:
在这里插入图片描述
命令:
在这里插入图片描述
演示lpush:
在这里插入图片描述
演示rpush:
在这里插入图片描述
演示lpop:
在这里插入图片描述
在这里插入图片描述
演示lindex:
在这里插入图片描述
演示linsert:
在这里插入图片描述
演示lrem:
在这里插入图片描述
演示lset:
在这里插入图片描述

集合set
简介:
set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复的数据时,set是一个很好的选择。并且set提供了判断某个成员是否在一个set集合内的重要接口,这也是List不能提供的。set是一个无序集合,底层其实就是一个value为null的hash表,所以增删,查的复杂的都是O(1)。

命令:
在这里插入图片描述
演示sadd,smenbers:
在这里插入图片描述
演示sismenber:
在这里插入图片描述

演示spop:
在这里插入图片描述
演示smove:
在这里插入图片描述
在这里插入图片描述

演示sunion:
在这里插入图片描述
演示sdiff:
在这里插入图片描述

==哈希(hash)
简介:
在这里插入图片描述
映射表举例:
在这里插入图片描述
当要存储一个对象时:用户ID为查找的key,存储d的value用户对象包括:年龄,姓名,生日等,如果用普通的key/value结构存储主要有两种方式:
在这里插入图片描述
详解:
在这里插入图片描述
第一种缺点:当需要修给用户的属性时,要先反序列化,改好后在序列化回去,开销大
第二种缺点:用户数据冗余,存储太分散

于是就引出了第三种的方式:
在这里插入图片描述
解决了以上两种方式的缺点(利用映射表)

命令:

在这里插入图片描述

演示hset,hget:
在这里插入图片描述
在这里插入图片描述

演示hkeys,hvals:
在这里插入图片描述
在这里插入图片描述
演示hincrby:
在这里插入图片描述
演示hsetnx:
在这里插入图片描述

有序集合Zset

简介
redis有序集合zset和普通集合set非常相似,是一个没有重复元素的字符串集合,不同之处是有序集合的每个成员都关联一个评分,这个评分被用来按照从最低分到最高分的方式排序集合中的成员,==集合的成员是唯一的,但是评分可以是重复的。==因为元素是有序的,所以你可以很快根据评分或次序去获得一个范围的值。访问有序集合中间的元素时也是非常快的。

** 当评分重复时它是按照顺序进行排序的**

在这里插入图片描述

命令:
在这里插入图片描述

演示zadd,zrange:
在这里插入图片描述

演示zrangebyscores:
在这里插入图片描述

在这里插入图片描述

演示zincrby:
在这里插入图片描述

演示zcount:
在这里插入图片描述

演示zrank:

在这里插入图片描述

** zset底层使用了两个数据结构**

在这里插入图片描述
实例:
对比有序链表和跳跃表,从链表中查询出51

链表:

在这里插入图片描述

跳跃表:
在这里插入图片描述

geospatial(地理信息)
简介:
在这里插入图片描述
命令:
在这里插入图片描述

演示geoadd:
在这里插入图片描述
在这里插入图片描述
** 注意:两级无法直接添加,一般会下载城市数据,直接通过java程序一次性导入。有效的经度从-180到180,纬度从-85.05112878到+85.05112878度,当坐标位置超过指定范围时,该命令将会返回一个错误**

在这里插入图片描述
演示geopos:
在这里插入图片描述

在这里插入图片描述
演示geodist:
在这里插入图片描述
** 默认为m,也可以自己设置,设置如下图**
在这里插入图片描述

在这里插入图片描述
演示georadius:
在这里插入图片描述

redis配置文件详解

  1. 开头文件
    在这里插入图片描述
  2. includes
    在这里插入图片描述
  3. NETWORK
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  4. general
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  5. security
    在这里插入图片描述
    ** 还有一种设置密码的方式:通过config set requirepass 123456命令设置,通过config get requirepass
    命令查看密码,但是这种设置只是临时的,重启redis服务,密码久还原了,要想永久设置还是要在配置文件中设置**

redis发布和订阅

它是一种消息通信模式:发送者发送消息,订阅者接收消息。redis客户端可以订阅任意数量的频道。

在这里插入图片描述
演示发布和订阅:
打开两个客户端:一个作订阅者,一个作发布者
在这里插入图片描述
在这里插入图片描述

订阅者接受信息:
在这里插入图片描述

测试Jedis

  1. 导包
 <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>3.3.0</version>
    </dependency>
  1. 创建测试类
package org.example;

import redis.clients.jedis.Jedis;

import java.util.Set;

public class TestJedis {

        public static void main(String[] args) {
            //创建redis连接
            Jedis jedis = new Jedis("192.168.98.134", 6379);
            //jedis.auth("123");//输入密码,没有设置则不需要写
            String set = jedis.set("username", "john");
            System.out.println(set);//加入数据后的返回结果
            String username = jedis.get("username");
            System.out.println(username);
            String mset = jedis.mset("k2", "v2", "k3", "v3");//设置多个值
            Set<String> keys = jedis.keys("*");
            for (String key : keys) {
                System.out.println(key);
            }
            System.out.println(jedis.exists("k1"));
            jedis.hset("user:1001","name","zhangsan");//hash类型
            System.out.println(jedis.hget("user:1001", "name"));
            jedis.close();//关闭连接避免浪费资源
        }
    }


输出结果:

springboot整合redis

  1. 导包
 <!--redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.4.4</version>
        </dependency>
        <!--对象池:将用过的对象保存起来,当需要时就把它拿来用,减少了重新创建对象的资源-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.6.0</version>
        </dependency>
  1. 配置文件
#redis服务器地址
spring.redis.host=192.168.98.134
#redis服务端口
spring.redis.port=6379
#redis数据库索引
spring.redis.database=0
#连接超时时间
spring.redis.timeout=1800000
#连接池最大连接数(负值表示没有限制)
spring.redis.lettuce.pool.max-active=20
#最大阻塞等待时间(负值表示没有限制)
spring.redis.lettuce.pool.max-wait=-1
#连接池最大空闲连接
spring.redis.lettuce.pool.max-idle=5
#连接池最小空闲连接
spring.redis.lettuce.pool.min-idle=0
  1. 配置类
package com.chao.config;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@EnableCaching
@Configuration
public class RedisConfig {

        @Bean
        public RedisTemplate<String, Object>redisTemplate(RedisConnectionFactory factory){
            RedisTemplate<String,Object>template=new RedisTemplate<>();
            //关联
            template.setConnectionFactory(factory);
            //设置key的序列化器
            template.setKeySerializer(new StringRedisSerializer());
            //设置value的序列化器
            template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
            return template;
        }

}

** 为什么要序列化呢?因为对象是以json进行存取的,需要序列化和反序列化**

  1. 测试类
package com.chao.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/redisTest")
public class TestController {
    @Autowired
    private RedisTemplate redisTemplate;
    @GetMapping
    public String testRedis() {
        redisTemplate.opsForValue().set("name","xiaoming");
        String value = (String) redisTemplate.opsForValue().get("name");
        return value;
    }
}

  1. 访问结果
    在这里插入图片描述
    redis数据库的信息:
    在这里插入图片描述

redis的原子操作

在这里插入图片描述
在这里插入图片描述
详解:redis底层就是一条条的指令,这些指令就是一个原子性的操作,cpu对指令只有两种情况,1. 执行失败。2. 执行成功。中断是发生在指令之间的操作。在java中很常见,因为一条Java语句被cpu执行起来可能是多条指令,而cpu只认指令不人其它的。所以这样的就不是原子操作。原子性是最小的执行单元。

redis事务(跟mysql事务完全是两个概念)

简介:
redis事务是一个单独的隔离操作,事务中的所有的命令都会序列化,按顺序执行。事务在执行过程中,不会被其它客户端发送的命令请求打断。redis事务主要作用就是串联多个命令防止别的命令插队。
命令:
在这里插入图片描述

演示:
在这里插入图片描述

在这里插入图片描述

以上演示是执行成功的事务,当事务出现错误会出现什么呢?

事务的错误的两种情况

  1. 组队中某个命令出现了报告错误,执行时队列中的所有命令都会被取消。
    在这里插入图片描述
    演示:
    在这里插入图片描述
  2. 组队过程中没有检查出错误,但是在执行中某个命令报出了错误,则只有报错的命令不会被执行,而其它的命令都会被执行,不会回滚。
    在这里插入图片描述
    演示:
    在这里插入图片描述

解决事务冲突的锁机制

  1. 悲观锁:效率很低
    在这里插入图片描述
  2. 乐观锁
    在这里插入图片描述
    演示乐观锁:
    启动两个客户端:分别对同一个数据进行修改操作:对这个数据进行watch监听。
    第一个客户端:
    在这里插入图片描述

第二个客户端:
在这里插入图片描述
第一个加10 :
在这里插入图片描述
第二个加20:
在这里插入图片描述
为什么会出现回滚呢?

因为当开启该key的监听时,针对该值会有一个版本号,假设为v1.0,两个客户端分别开启事务并组队,这时原版本号v1.0会同步到两个客户端中,当第一个客户端执行的时候会先检查我拿的版本号是否和原版本号相同,一比较发现相同,则exec(执行)成功,结果加10原值为110,此时会生成一个新的版本号,假设为v1.1,第二个客户端在执行的时候也会先检查我拿的版本号是否和现在的版本号相同,一比较发现不同,证明该值已经别其它事务修改过,所以该事务执行失败(回滚)。当事务执行完后(不管是否成功其监听模式都会被取消)

redis事务的三个特性

在这里插入图片描述

事务的秒杀案例

redis持久化

持久化有两种方式RDB,AOF

RDB

  1. 什么时RDB
    在指定时间间隔内将内存中的数据快照写入磁盘,恢复时将快照文件直接读到内存里。

  2. RDB是如何执行过程或原理
    在这里插入图片描述
    为什么不直接同步到.rdb文件中,而是采用临时文件替换上次持久化好的文件?
    如果直接就同步到.rdb文件中会导致数据的不完整性,数据的不一致性,如果现在有10个数据但是当同步到第8个数据时服务器挂掉了,就造成了数据的不完整性

rdb的相关配置
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
演示save:
在这里插入图片描述
修改配置文件重启redis服务
在这里插入图片描述

在这里插入图片描述
当在20s内写入至少三个数据时看该文件大小是否有变化
在这里插入图片描述

在这里插入图片描述

RDB的优缺点

  • 优点
  1. 适合大规模的数据恢复
  2. 对数据完整性和一致性要求不高的更适用
  3. 节省磁盘空间
  4. 恢复速度快
  • 缺点
  1. Fork(子进程)的时候,内存中的数据会先同步到临时文件中,相当于又克隆了一份,大致有2倍的空间要被占。
  2. 虽然redis在fork时使用了写时拷贝技术,但是如果数据过于庞大还是比较消耗性能的
  3. 在最后一次持久化中如果redis服务挂了,就可能会造成数据丢失。
    针对第七点做一个详解:
    在这里插入图片描述
    规则是20s内至少要有3个变化才会进行持久化,假如第一次在20s内有5个变化,那么这些变化就会被持久化,当最后一次时在20s内有2个变化,正准备set第三个数据时redis服务突然挂了,由于这时还没有达到进行持久化的条件,所以之前的那两个数据就丢失了。

rdb的备份
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
重启redis服务
在这里插入图片描述
检查数据是否恢复:
在这里插入图片描述

AOF

  1. AOF是什么?
    在这里插入图片描述
    或者:
    在这里插入图片描述
    它即保存数据又保存指令,它只记录写操作,不记录读操作

配置文件
AOF默认是不开启的
在这里插入图片描述

先把它改为yes:
在这里插入图片描述
在这里插入图片描述
aof的保存路径是和rdb一样的,rdb变它就变

修改配置文件后要重新启动redis服务会发现bin目录下多了一个文件
在这里插入图片描述

AOF和RDB同时启动后,redis是怎么加载的呢?
在这里插入图片描述
因为当两个同时存在时,redis优先加载AOF的数据,但此时该文件数据为空,所以redis里啥都没有。
为什么优先读AOF呢?因为AOF相比于RDB数据更完整,一致性更高。

记住:redis要是没有进行持久化,只要该台redis挂了那么在重启之前的数据是不会有的,就相当于一个全新的redis服务器,只有加了持久化redis在重启时会先读取持久化文件,该文件里的数据会被恢复到redis服务器里。

在这里插入图片描述

AOF可以进行异常恢复
如果遇到AOF文件损坏,则可以用/usr/local/bin/redis-check-aof–fix appendonly.aof进行恢复。

现在制造个异常:在该文件中添加一个hello,很显然这个数据格格不入,因为该文件保存数据的格式不是这样的。所以这就是一个异常。
在这里插入图片描述
重启服务,重连客户端:
在这里插入图片描述

连接不上,这是因为在启动redis时会读取 appendonly.aof配置文件,这时肯定会出错

解决方法
在这里插入图片描述

在这里插入图片描述
重启服务重新连接客户端:成功
在这里插入图片描述
数据的备份和rdb相同这里就不再赘述

AOF的同步频率设置就跟rdb里设置持久化策略一样
在这里插入图片描述

Rewrite 压缩
是什么?
在这里插入图片描述
简单来说就是当该文件的数据存不下了,达到阈值了,就会触发重写机制,它会把之前多个指令合并为一个指令,不注重过程了只注重结果了,只要能恢复数据就行,其它的都可以不要以达到压缩的效果。

重写的原理相当于rdb的写时复制技术
重写虽然可以节省磁盘空间,减少恢复时间,但是每次重写还是有一定的负担,因此一定要设置重写的条件:

在这里插入图片描述

AOF持久化的流程
在这里插入图片描述
最后一步重启redis服务,会加载AOF文件中的写操作达到恢复数据的目的。

AOF的优点:

  1. AOF可以更好的保护数据不丢失,丢失数据更低
  2. 写入性能非常的高
  3. 刻可读的日志文件,可以处理误操作。如果某人不小心用flushall命令清空了所有数据,只要这个时候还没有执行rewrite,那么就可以将日志文件中的flushall删除,进行恢复。
    AOF的缺点:
  4. 对于同一份数据备份文件,AOF比RDB大(占用更多的磁盘空间)
  5. 数据恢复比较慢,不适合做冷备。
  6. 由于是每秒或者更短的速度做的备份,所以比较消耗资源

在实际应用场景中该怎么用呢?
在这里插入图片描述
对上述进行一个总结:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

redis主从复制

概念:
主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,master以写为主,slaver以读为主。
在这里插入图片描述

能干什么呢?

  1. 读写分离分担压力
  2. 容灾的快速恢复(假如有一台从服务器在读的时候突然挂了,这是会立刻被其它从服务器替代)
  3. 性能扩展

如果主服务器挂了该怎么办呢?主服务器就一台啊。能不能搞个多主多从呢?肯定是不行的,为什么呢?
因为:假如有两台主服务器同时都set相同的key但是value不同,这时从服务器就不知道该复制谁的数据了。
该怎么解决呢?
用集群即可。复制多个一主多从,主服务器要是挂了,可以用其它的主服务器来保证正常运行。

搭建一主多从策略

这里是用单机操作,在实际场景中是在不同的电脑上搭建的。
有两种方法:第一复制文件该端口号就行,第二是新建配置文件,把之前配置文件中数据包含进去,然后写一些配置就行,因为启动多个服务就会有多个配置文件要设置,但是多个配置文件中有一些数据时公共的,所以可以用第二种更为简单。本文配置采用第二种:

  1. 自己随便建一个文件夹来存放集群的配置文件:
    在这里插入图片描述

  2. 复制之前的redis.conf到自己建的目录下:
    在这里插入图片描述

  3. 利用touch命令取创建6379,6380,6381配置文件结果如下:
    在这里插入图片描述

  4. 分别对配置文件进行配置:我只展示6379的配置,剩下的自行配置
    在这里插入图片描述

  5. 启动三个服务器
    在这里插入图片描述
    在这里插入图片描述

  6. 此时这三个还都是独立的服务器,可以用info replication查看运行情况

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
7. 在从机上通过slaveof + 主机ip + 端口号:
在这里插入图片描述
到6379看一下信息:
在这里插入图片描述

依照上面的步骤把6381也加进去:
最后结果如下:
在这里插入图片描述
至此一主两从就搭建好了

接下来演示一下读写效果
在主机中写入:
在这里插入图片描述
在两个从机中能读到:
在这里插入图片描述
在这里插入图片描述
如果在从机中进行写操作则会报:
在这里插入图片描述

一主多从的几个问题

当从服务器挂掉后,重新启动该服务器会发生什么呢?
假设6380从机挂了
6379主机会只有一台从服务器了:
在这里插入图片描述
当重启后该从机会变成主机:
在这里插入图片描述
现在重新把它设置为从服务器:
在这里插入图片描述

当重新成为从机时它会重新从主机中复制数据(这是从机挂在从机的一个特点)

当主服务器挂掉会发生什么呢?

在这里插入图片描述

从机会:
在这里插入图片描述
重新启动6379服务:
在这里插入图片描述
6379还是主机吗?它的两个从机还在吗?
在这里插入图片描述

说明一下:192.168.98.135和127.0.0.1是一样的,后者映射到前者

主从复制原理:

在这里插入图片描述
讲解:第一次进行数据同步是从服务器主动向主服务器发送请求信息的,什么是第一次呢?就是在从服务器设置了slaveof + ip地址+ 端口号时。
之后呢,每次主机进行写操作时,数据的同步是主机主动发给从机的,而从机主动发送请求只发生在第一次。

薪火相传策略

概念:
从服务器只对应一个从机,但是从机又跟一个从机,以此类推

在这里插入图片描述
薪火相传策略的问题的答案是和一主多从一样:从服务器挂了或者主服务器挂了会怎么样。

反客为主策略

概念:
当主服务器挂了,让从服务器变成主服务器的这一过程被称为反客为主
演示一下:
让主机挂掉:
在这里插入图片描述
让6380变成主机:
在这里插入图片描述
执行该命令会:redis从属服务器执行命令 SLAVEOF NO ONE 将使得这个从属服务器关闭复制功能,并从从属服务器转变回主服务器,原来同步所得的数据集不会被丢弃。
当原来的主机重启后那么它不再是主机了,而是6380的从机。

但是这中模式有一个问题:在实际的业务场景中,主机挂了后,可能不能第一时间手动让从机变为主机,于是就引入了哨兵模式:让从机自动的变为主机。

哨兵模式

概念:
在这里插入图片描述

在这里插入图片描述
讲解:主机挂了,哨兵立刻检测到并同时让一台从机切换为主机,当主机好了时,它却变成了从机。

演示:
先把模式调为一主而从的模式

  1. 自定义一个哨兵配置文件:sentinel.conf
    原来是在该目录下:
    在这里插入图片描述
    说一下:redis自带的配置文是在解压的那个文件中,而命令文件是你在make install时的安装目录下默认为/usr/local/bin,一定要搞清除

创建并编辑sentinel.conf

在这里插入图片描述

里面只写一句话:

在这里插入图片描述
2. 启动哨兵
在这里插入图片描述
这时当6379主机挂了后:
在这里插入图片描述

主从复制的缺点

在这里插入图片描述

哨兵模式是怎么选举哪一个从服务器作为主机的呢?

选择条件依次是:

  1. 选择优先级靠前的
  2. 选择偏移量最大的
  3. 选择runid最小的

在这里插入图片描述
讲解:优先级就不用说了,上面写的很清楚了。当优先级一样就看偏移量:就是从机中谁里面的数据和主机的数据差距最小,就选谁,比如:主机中有10个数据,从机1同步到了9个,从机2同步到了8个,这时就选从机1作为主机,当偏移量一样就看runid(选择最小的)。

redis集群

解决问题?

  1. 容量不够,集群可以进行扩容
  2. 并发写操作,集群可以很好的分摊
  3. 若主从模式,薪火相传,主机挂了导致ip地址发生变化,应用程序中配置需要修改对应的地址,端口信息,可以用集群解决。
    两种方式解决:1. 代理主机方式集群。2. redis无中心化方式集群
  • 代理主机:采用nginx服务器进行代理
    在这里插入图片描述
    缺点:需要的服务器太多,若后期增加需求,则维护起来会很麻烦,比较占用资源,它只能通过代理服务器进行访问。若访问数量太大,会增加代理服务器的压力。
  • 无中心化集群:
    在这里插入图片描述
    每一台服务器都可以作为服务的入口,不需要像代理那样,只能通过代理服务器才能访问其它的服务器,无中心化集群的每一台都可以都当作服务的入口,即使用户请求的不是此服务器,那么该服务器会把这个请求分到对应的服务器中,这种方式需要的服务器也比代理方式要少

引入一个知识点吧:单机和集群和分布式的概念或联系
在这里插入图片描述
详解:

单机结构我想大家最最最熟悉的就是单机结构,一个系统业务量很小的时候所有的代码都放在一个项目中就好了,然后这个项目部署在一台服务器上就好了。整个项目所有的服务都由这台服务器提供。这就是单机结构。

那么,单机结构有啥缺点呢?我想缺点是显而易见的,单机的处理能力毕竟是有限的,当你的业务增长到一定程度的时候,单机的硬件资源将无法满足你的业务需求。此时便出现了集群模式,往下接着看。

集群结构集群模式在程序猿界有各种装逼解释,有的让你根本无法理解,其实就是一个很简单的玩意儿,且听我一一道来。单机处理到达瓶颈的时候,你就把单机复制几份,这样就构成了一个“集群”。集群中每台服务器就叫做这个集群的一个“节点”,所有节点构成了一个集群。每个节点都提供相同的服务,那么这样系统的处理能力就相当于提升了好几倍(有几个节点就相当于提升了这么多倍)。

但问题是用户的请求究竟由哪个节点来处理呢?最好能够让此时此刻负载较小的节点来处理,这样使得每个节点的压力都比较平均。要实现这个功能,就需要在所有节点之前增加一个“调度者”的角色,用户的所有请求都先交给它,然后它根据当前所有节点的负载情况,决定将这个请求交给哪个节点处理。这个“调度者”有个牛逼了名字——负载均衡服务器。

集群结构的好处就是系统扩展非常容易。如果随着你们系统业务的发展,当前的系统又支撑不住了,那么给这个集群再增加节点就行了。但是,当你的业务发展到一定程度的时候,你会发现一个问题——无论怎么增加节点,貌似整个集群性能的提升效果并不明显了。这时候,你就需要使用微服务结构了。

分布式结构先来对前面的知识点做个总结。从单机结构到集群结构,你的代码基本无需要作任何修改,你要做的仅仅是多部署几台服务器,每台服务器上运行相同的代码就行了。但是,当你要从集群结构演进到微服务结构的时候,之前的那套代码就需要发生较大的改动了。所以对于新系统我们建议,系统设计之初就采用微服务架构,这样后期运维的成本更低。但如果一套老系统需要升级成微服务结构的话,那就得对代码大动干戈了。所以,对于老系统而言,究竟是继续保持集群模式,还是升级成微服务架构,这需要你们的架构师深思熟虑、权衡投入产出比。OK,下面开始介绍所谓的分布式结构。

分布式结构就是将一个完整的系统,按照业务功能,拆分成一个个独立的子系统,在分布式结构中,每个子系统就被称为“服务”。这些子系统能够独立运行在web容器中,它们之间通过RPC方式通信。

举个例子,假设需要开发一个在线商城。按照微服务的思想,我们需要按照功能模块拆分成多个独立的服务,如:用户服务、产品服务、订单服务、后台管理服务、数据分析服务等等。这一个个服务都是一个个独立的项目,可以独立运行。如果服务之间有依赖关系,那么通过RPC方式调用。

这样的好处有很多:系统之间的耦合度大大降低,可以独立开发、独立部署、独立测试,系统与系统之间的边界非常明确,排错也变得相当容易,开发效率大大提升。

系统之间的耦合度降低,从而系统更易于扩展。我们可以针对性地扩展某些服务。假设这个商城要搞一次大促,下单量可能会大大提升,因此我们可以针对性地提升订单系统、产品系统的节点数量,而对于后台管理系统、数据分析系统而言,节点数量维持原有水平即可。

服务的复用性更高。比如,当我们将用户系统作为单独的服务后,该公司所有的产品都可以使用该系统作为用户系统,无需重复开发。

搭建集群

  1. 找到原先配置主从复制的文件目录
    删除该目录下的文件持久化文件
    在这里插入图片描述
  2. 修改配置文件
    在这里插入图片描述
    6379的配置:
    在这里插入图片描述
    把6379的配置问价复制多份,结果如下:

先把以前的配置文件删除
在这里插入图片描述

在这里插入图片描述
对相应的配置文件进行跟6379相同的配置:这里就举一个例子,剩余的自己解决:修改6381的配置文件:
在这里插入图片描述
3. 启动6个服务

在这里插入图片描述
4. 将6个节点合成一个集群

首先你要检查一下你的redis有没有ruby环境
到你自己安装的redis目录下:我的在这个目录下:
在这里插入图片描述
在这里插入图片描述
版本低的redis没有集成这个环境,但是高的redis已经集成了

命令合成集群:

在这里插入图片描述
演示:必须在src目录下执行该命令:因为该命令是依赖于rb环境的:
在这里插入图片描述
在这里插入图片描述
从上面可以看到6390为6379的从机,6391为6380的从机,6389为6381的从机

点击yes:

在这里插入图片描述
这样集群就建好了接下来进行测试

集群测试

注意:不能用普通方式连接客户端,否则就不是集群了

用该命令连接才是集群的方式:才能体现出redis的无中心化(各个服务都可以作为入口,它可以自动切换你要访问的那个服务)
在这里插入图片描述
演示:
在这里插入图片描述
查看节点信息(查看集群状态)
在这里插入图片描述

redis集群如何分配这节点的呢?

在这里插入图片描述

插槽是什么

插槽就是为了保证数据被平均分配到不同的服务器中

redis集群共有16384个插槽,然后平均分配到三个主机上,结果如下:

在这里插入图片描述
在这里插入图片描述
演示:
在这里插入图片描述

在这里插入图片描述
注意:当你同时插入多条值时会:
在这里插入图片描述
那么该如何添加多个值呢?可以利用组的概念。
在这里插入图片描述

在这里插入图片描述
演示:cluster keyslot +key值
在这里插入图片描述
演示:cluster getkeysinslot + 插槽值 +个数
在这里插入图片描述
当主机挂了后,从机立刻上位成主机,当原来的主机恢复后只能当从机了。

当主机和从机都挂了呢?整个服务就会瘫痪吗?不是的,要根据你的配置决定。

在这里插入图片描述

Jedis测试集群

用Jedis主要是通过java去控制redis服务,只需要用Java连进去就行,所以连接集群的方式为HostAndPort hostAndPort = new HostAndPort("192.168.98.135",6379); JedisCluster jedisCluster = new JedisCluster(hostAndPort);

缓存击穿,缓存穿透,缓存雪崩

点击了解详情

redis分布式锁

为什么使用分布式锁,(分布式锁的概念)

在这里插入图片描述
实现分布式锁的几种方式:

在这里插入图片描述

基于redis的分布式锁的实现

用setnx命令去操作数据(setnx只能加数据库里不存在的key值,若存在则不会加成功)

在这里插入图片描述
解释:也就是说setnx相当于有锁的机制,当你set一个k1时,会对这个k1上一把锁,其它服务则不能去操作它了,知道这把锁被释放,那么怎么被释放呢?其实把它删除就行了。

在这里插入图片描述
但是这样就万事大吉了吗。当然不是,它会有问题的:要是该锁因外部原因(服务挂了等原因)导致锁一直内有释放,那么其它服务就不能用适用该数据了,解决它很简单:加一个过期时间就好了

演示:
在这里插入图片描述

这样虽然解决了上述问题,但是又引发了另一个问题,如果由于某种原因导致没有执行到设置过期时间,那么问题就还是之前那个问题了,所以可以采用边塞值边设置过期时间,也就是说让它俩同时进行
演示:

在这里插入图片描述
这样虽然可以解决上述问题,但是它真的适用于任何场景吗?假如有一个场景如下:

在这里插入图片描述
场景详解:对于同一个数据,假如a先抢到该数据,那么它会被上锁,然后执行具体操作,但是在执行过程中服务器突然卡顿,卡顿的时间超过了过期时间10s,这时锁会被自动释放。锁一被释放,假如服务b抢到了这把锁,然后它也去做具体操作,但在执行过程中,服务a又反应过来了,执行之前未完成的操作,然后手动释放锁,那么问题来了:它这时释放的是服务b的锁。这肯定是不对的,锁只能释放自己的,不可以去释放被人的,否则不就乱套了吗。于是就引入了(UUID防止误删)。

解决方案

  1. 设置一个UUID来标识不同的操作
  2. 释放锁之前,首先判断当前uuid和要释放的操作的uuid是否一样。如果一样就释放,如果不一样就不释放。

虽然这样可以解决上述问题,但是你想想服务卡顿是在具体操作的过程中发生的,如果它发生在删除操纵过程中呢?还是会出现删除别人的锁。这就是原子性造成的问题。

删除锁的原子性问题

场景如下图:

在这里插入图片描述

解决方案
使用lua脚本:

在这里插入图片描述

也就是说一旦上锁了直到释放锁后,别人才能拿到这把锁,不管在上锁和释放锁的过程中发生了什么,别人是不肯拿到这把锁的

至此redis分布式锁就讲完了

ACL

常用命令:

在这里插入图片描述
创建用户:
在这里插入图片描述
在这里插入图片描述
查看是哪个用户进行操作的:

在这里插入图片描述
设置一个有权限,有密码,并开启的用户:

在这里插入图片描述
详解:user1代表用户名,on 代表开启,123456代表密码,(~cached:)代表该用户只能对cached:的key进行操作,+get代表该用户只能执行get命令。
切换用户:

在这里插入图片描述
验证该用户权限:
在这里插入图片描述

在这里插入图片描述

redis至此就结束了,后续如有研究会继续更新

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值