Redis学习

Redis学习

一.NoSQL概述

什么是NoSQL

NoSQL=Not Only SQL 不仅仅是sql
泛指非关系型数据库
nosql的产生是为了解决大规模数据集群问题,包括大规模数据的存储

nosql和传统数据库有什么区别?

更快、类型更多
不需要事先设计好数据库,只要往里放就可以了 随取随用
rdbms 每列的数据类型都是一样的,每一行都是一个对象
nosql 键和值之间不一定有关系

为什么nosql存取比较快?

缓存数据库 放在缓存里的

MyISAM数据库引擎(表锁) InnoDB引擎支持事务(行锁)
行锁是细颗粒度的

表锁和行锁

锁:访问数据时上锁,别人就无法进行其他操作,必须等待
表锁:整张表都锁起来,这样的话,比如查找张三的密码,这时候就不能查找李四的密码,如果两个人同时在登陆呢??是不是造成了第二个人登陆不上的情况,不利于并发编程
行锁:只对这一行数据上锁,不影响其他行的数据

NoSQL的特点

1.易扩展

去掉关系型数据库的关系型特点,数据之间无关系,增加了可扩展性

2.大数据量高性能

nosql数据库都具有非常高的读写性能,得益于它的非关系型,数据库结构简单
MySql使用query cache 是一种大力度的cache,而Nosql使用的是记录级的,是一种细粒度的cache,所以nosql的性能更高

官方记录:redis 一秒可以写8万次 读11万次

3.多样灵活的数据模型

nosql无需事先建立数据的字段,随时可以存储自定义数据类型的数据

传统的RDBMS和NoSQL

RDBMS:关系型数据库关系系统
在这里插入图片描述

NoSQL四大分类

  1. KV键值 :redis 通常用hash table来实现
  2. 文档型数据库
    CouchDB
    MongoDB:基于分布式文件存储的数据库(C++)
    介于关系型和非关系型之间的数据库
  3. 列存储数据库
  4. 图关系数据库
    不是放图的,是放关系的,社交网络,推荐系统 广告推荐
    Neo4J,infoGrid

CAP+BASE

传统的ACID特性

事务transaction
1.原子性atomicity
要么全部做完,要么都不做
2.一致性consistency
数据完整性保持一致
3.隔离性isolation
事务之间不会相互影响
4.持久性durability

CAP(三进二)

C:强一致性
A:可用性
P:分区容错性
cap理论在分布式存储系统中,最多只能实现上面的两点
分区容错性必须实现
分布式架构必须取舍,平衡C和A,因为没有一个nosql能同时满足三点

二.Redis入门

redis:Remote Dictionary sever 远程字典服务器
是高性能的kv分布式内存数据库

存放键值对的nosql数据库

特点:
1.支持数据持久化
2.不仅支持简单的kv型数据,还提供list、set、zset、hash等数据结构的存储
3.支持数据的备份

redis能做什么

内存存储、数据持久化
效率高,可用于高速缓存
发布订阅、类似消息队列
地图信息分析
计时器、计数器
如取最新的10条评论的id放在list中

redis安装

直接解压,然后运行服务
在这里插入图片描述
在这里插入图片描述

redis性能测试(官方自带)

在这里插入图片描述

基本数据库常识

默认16个数据库,从零号库开始
默认端口号:6379

1.使用select切换数据库 select 3(代表3号库)

在这里插入图片描述

2.dbsize 查看数据库大小 (不区分大小写)针对当前库,不是所有

在这里插入图片描述

3.放入: set key value
取值:get key

在这里插入图片描述

4.keys * 查看所有的key

在这里插入图片描述

5.清空当前库 清空全部

在这里插入图片描述

为什么redis是单线程

redis很快!!redis是基于内存的操作,与cpu无关
redis与机器的内存大小有关,单线程容易实现

多线程不一定比单线程效率高

五大数据类型

1.String(字符串类型)是redis最基本的数据类型
2.Hash(哈希)类似java里的Map
3.List(列表) 可以在头部或尾部添加,底层是双向链表
4.Set(集合) String类型的无序集合,通过hashtable实现
5.Zset(sorted set有序集合) 不允许重复的成员
每个元素都关联一个double类型的分数,进行排序

查看所有的key:
在这里插入图片描述
判断某个key是否存在
在这里插入图片描述
移除key,当前库就没有了
在这里插入图片描述
设置生存时间,当key过期时(生存时间为0)会被删除
ttl key :查看还有多少秒过期 -1永不过期 -2已经过期

在这里插入图片描述
查看key是什么类型的
在这里插入图片描述

常用命令

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
sadd: set添加

String 字符串类型

基本操作回顾

首先我们选择使用3号库来练习String
在这里插入图片描述

append

append时,不存在的key,将会自动创建,相当于set key value
在这里插入图片描述

i++,i–和按指定步长增加的操作

在这里插入图片描述
应用场景:浏览量

字符串范围,截取

在这里插入图片描述

替换

在这里插入图片描述

setex(set with expire) setnx(set if not expire)

setex(set with expire) 设置过期时间
setnx(set if not expire) 不存在在设置(在分布式锁中常常使用)存在的话,创建失败,不会替换

以秒为单位
在这里插入图片描述
-2:已过期

批量获取值,批量设置值

在这里插入图片描述
msetnx是一个原子性的操作:要么都做,要么都不做
不可能出现一个成功,另一个失败的情况

对象

第一种方法:

设置一个user:1对象,值为json字符串来保存一个对象 set user:1{name:zhangsan,age:3} (需要解析json字符串)

第二种方法保存对象:

将key这样设计 user:{id}:{field}

在这里插入图片描述
1:代表这个对象的id
优点:不需要解析json,就能取到对象和他的属性值

getset 先get再et

在这里插入图片描述

场景

String:除了value还可以是数字,
用作计数器,
文章的阅读量,点击量,粉丝数,关注数
对象缓存存储

List 列表(值可重复)

在redis里面可以把list玩成(先进后出)
队列阻塞队列

所有list命令都是以l开头的 Redis不区分大小写

LPUSH 和 RPUSH

在这里插入图片描述
Lpush:放入列表头部(即从左边插入)
注意:倒着的

在这里插入图片描述
Rpush:放在列表的尾部
看上去像是:双端队列,头和尾都能插入

移除Lpop Rpop

在这里插入图片描述
把它看成数组,那么。。。他有下标,通过下标来操做
在这里插入图片描述

列表的长度

llen key

移除指定的值(精确匹配)

如:取消关注
在这里插入图片描述

trim 截断(只保留局部)

在这里插入图片描述

rpoplpush

这是一个组合命令
移除列表的最后一个并将它添加到一个新的列表

在这里插入图片描述

判断list是否存在exists

0:不存在

lset:将列表中指定下标的值进行替换(更新操作)
不存在时会报错

在这里插入图片描述

linsert 插入

将某个具体的值,插入到列表中某个元素的前面或者后面
在这里插入图片描述

List总结

list:实际上是一个链表,before node after
不存在时,创建
存在时,追加
移除了所有的value,代表不存在
插入和更新效率更高
处理中间数据时,效率更低一点

应用场景

消息排队,消息队列
Lpush Rpop 队列
Lpush Lpop 栈

Set 集合(无序不重复)

sadd、smembers

在这里插入图片描述

判断是否包含

在这里插入图片描述

scard 返回集合元素的个数

scard myset

移除指定元素(一个或多个)

srem mtset hello

随机取出一个(多个)

在这里插入图片描述

随机移除一个

在这里插入图片描述

移动到另一个set

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

并集、交集、差集

在这里插入图片描述

set的应用场景

粉丝数,关注数,共同好友,共同爱好
抽奖

Hash

hset key field value 放

hget key field 取

hmset、hmget、hgetall

在这里插入图片描述

删除 hdel key field1

在这里插入图片描述

求个数hlen key

在这里插入图片描述

hsetnx 存在则创建,不存在则失败

hincrby key field count 自增

在这里插入图片描述

hkeys myhash

返回所有键

hvals myhash

返回所有值

zset

在set的基础上增加了一个值,score用于排序
在这里插入图片描述

zrangebyscore

在这里插入图片描述
从大到小排序
在这里插入图片描述

zrem 移除

在这里插入图片描述

zcard salary

在这里插入图片描述

zcount key min max

计算区间内成员个数
在这里插入图片描述

应用场景

排序:成绩表,工资表
普通消息1重要消息2,带权重进行判断
排行榜应用实现,取top N

三种特殊数据类型

1.GEO地理位置

geospatial 朋友的定位,附近的人,打车,距离计算
redis3.2 可以推算地理位置的信息,两地之间的距离,方圆几里的人
城市经纬度查询网站
只有6个命令

1.geoadd添加城市数据

注意: 1.两极(南极北极)无法直接添加
            2.注意顺序 key 纬度 经度 名称
            3.不要超过范围,纬度(-180,180) 经度(-85,85)

在这里插入图片描述

2.geopos 获取经度纬度

geopos 获取指定城市(可多个)的经度纬度:是一个坐标值
在这里插入图片描述

geodist 返回两地之间的直线距离

指定单位的参数 unit 必须是以下单位的其中一个:
m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
在这里插入图片描述

GEORADIUS

以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
场景:我附近的人,半径查询
还可以获得指定数量的且符合条件的
在这里插入图片描述
注意:地理数据必须在china:city中
用java导入,而非手动(很慢)

GEORADIUSBYMEMBER以城市为中心查找

在这里插入图片描述

geohash 返回一个位置或多个的geohash表示

返回11个字符的geohash字符串
就是将二维的经纬度转换成一维的hash,两个字符串越接近,代表距离越近
在这里插入图片描述

GEO底层的实现原理

底层是:Zset 可以使用zset操作geo
如:
在这里插入图片描述

2.HyperLogLog基数统计算法(去重计数算法)

基数:不重复的元素的个数(可以接受误差)
在这里插入图片描述
场景:网页的浏览量,一个人多次,算作一个浏览量
传统方案:set集合保存用户id,不重复的元素个数,作为浏览量的标准(麻烦,占内存)
但我们的目的是计数而不是用户id
优点:所占内存固定,2^64只要12KB内存!
0.81%的错误率

全部命令以P开头
在这里插入图片描述

3.BitMaps 位图 底层是对字符串的操作 活跃度

如365天的打卡记录可以表现为0110001001…

位存储
统计用户信息,活跃的,不活跃的,登陆状态0 ,1,其他两个状态的场景都可以用bitmaps

如打卡情况可以这样存储:
0:未打卡 1:已打卡
在这里插入图片描述
查看某一天是否打卡(getbit 方法)
在这里插入图片描述
统计打卡的天数
在这里插入图片描述

事务

事务的本质:一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行
特点:一次性,顺序性,排他性,来执行一系列命令(队列)

redis事务没有隔离级别的概念,所有命令在事务中并没有被执行,只有发起执行命令时,才会执行exec

redis单条命令保证原子性:要么同时成功,要么同时失败
但事务不保证原子性

redis的事务:
1,启动事务 multi
2,命令入队 …
3,执行事务 exec

在这里插入图片描述
放弃事务discard
在这里插入图片描述
事务队列中的命令都不会执行

编译型异常(命令有错,事务中的命令都不会执行)

在这里插入图片描述

运行时异常(1/0 语法性错误,执行时,其他命令可以正常执行)错误命令会抛出异常

在这里插入图片描述

监控Watch

悲观锁:认为什么时候都会出问题,无论做什么都加锁,效率低
乐观锁:认为什么时候都不会出问题,所以不会上锁,更新数据的时候判断一下,在此期间有没有人修改数据(秒杀业务)

  • 获取version
  • 更新的时候比较version

正常执行成功的例子:场景模拟:支付
在这里插入图片描述
多线程修改值,监视失败 watch可以当乐观锁
在这里插入图片描述
如果修改失败,获取最新的值
在这里插入图片描述

三、Jedis

官方推荐的java连接开发工具Jedis
1.新建一个maven项目
2.导入redis依赖

 <!--导入redis依赖-->
    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>3.2.0</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.58</version>
    </dependency>

3.编写测试代码

import redis.clients.jedis.Jedis;

public class Ping {
    public static void main(String[] args) {
        Jedis jedis=new Jedis("localhost",6379);
        /*jedis.auth("");*/
        jedis.connect();
        System.out.println("连接成功");
        //查看服务是否运行
        System.out.println("服务正在运行:"+jedis.ping());
    }
}

运行结果如下
在这里插入图片描述
注意:要先把redis 服务手动启动

一些测试

      Jedis jedis=new Jedis("localhost",6379);

        System.out.println("清空数据:"+jedis.flushDB());
        System.out.println("判断某个键是否存在:"+jedis.exists("username"));
        System.out.println("新增键值对:"+jedis.set("username","sxh"));
        System.out.println("新增键值对:"+jedis.set("password","password"));
        System.out.println("系统中所有的键:");
        Set<String> keys=jedis.keys("*");
        System.out.println(keys);
        System.out.println("删除键:"+jedis.del("password"));
        System.out.println("查看键所存储的数据的类型:"+jedis.type("username"));
        System.out.println("随机返回key空间的一个:"+jedis.randomKey());
        System.out.println("重命名key:"+jedis.rename("username", "name"));
        System.out.println("取出改后的name:"+jedis.get("name"));
        System.out.println("按索引查询:"+jedis.select(0));
        System.out.println("删除当前数据库中的所有key:"+jedis.flushDB());
        System.out.println("当前库中key的数目:"+jedis.dbSize());
        System.out.println("删除所有库中的所有key:"+jedis.flushAll());

事务测试

public class TestTX {
    public static void main(String[] args) {
        Jedis jedis=new Jedis("127.0.0.1",6379);
        //数据准备
        JSONObject jsonObject=new JSONObject();
        jsonObject.put("hello","world");
        jsonObject.put("name","sxh");
        String result=jsonObject.toJSONString();
        //开启事务
          Transaction multi=jedis.multi();
        try {
            multi.set("user1",result);
            multi.set("user2",result);
            multi.exec();  //执行事务
        } catch (Exception e) {
            //失败了,放弃事务
            multi.discard();
            e.printStackTrace();
        } finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close();//关闭连接
        }

    }
}

四、SpringBoot整合redis

1.创建一个maven项目
2.导入依赖

 <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.RELEASE</version>
    <relativePath />
  </parent>

 <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <!--启动redis的依赖-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
      <version>2.4.5</version>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
      <optional>true</optional>
    </dependency>
  </dependencies>

3.SpringBootRedisApplication 类

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@RunWith(SpringRunner.class)
@SpringBootTest
public  class SpringBootRedisApplication {

    @Resource
     private RedisTemplate<String,String> redisTemplate;

//使用RedisTemplate来操作redis
        @Test
        public void contextLoader(){
         redisTemplate.opsForValue().set("myKey","myValue");
         System.out.println(redisTemplate.opsForValue().get("myKey"));
     }
}

运行结果
在这里插入图片描述
补充:

     @Test
     public void flush(){
         RedisConnection redisConnection=redisTemplate.getConnectionFactory().getConnection();
         redisConnection.flushDb();
         redisConnection.flushAll();
     }

五、看一眼底层是啥样(学会看底层)

在这里插入图片描述

在这里插入图片描述
然后。。。找redis的自动配置类

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

回到之前的配置类
在这里插入图片描述
所以我们需要自定义RedisTemplate,因为。。redis对象都是需要序列化才能使用的

六、Jedis和lettuce的区别

springboot2.0之前都采用jedis 直连方式,会导致线程不安全,线程数量多等问题
springboot2.0之后采用lettuce 的方式(底层是netty 高性能网络框架)解决线程不安全的问题

中文乱码问题

如果我们在redis里存放了中文,然后在redis-cli中查看时,会出现一串乱码,如下:
在这里插入图片描述
在这里插入图片描述
去看看底层吧
在这里插入图片描述
在这里插入图片描述

造成字符串转义

所以。。。你需要。。自定义config来解决这个问题

自定义RedisTemplate

新建一个RedisConfig

   @Bean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 此项必须配置,否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        //String序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        //key采用String序列化
        template.setKeySerializer(stringRedisSerializer);
        //value采用jackjson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //hash key采用String
        template.setHashKeySerializer(stringRedisSerializer);
        //hash value采用jackjson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

自定义工具类

redis 进阶

在这里插入图片描述

1.熟悉配置文件

1.units 配置数据大小的单位 只支持bytes 不支持bit 对大小写不敏感
在这里插入图片描述

2.includes 包含
在这里插入图片描述
3.network 网络配置
在这里插入图片描述
在这里插入图片描述

4.general 通用

daemonize no 守护进程(默认no)

pid进程文件

loglevel notice 日志级别
notice :生产环境

debug (a lot of information, useful for development/testing)
verbose (many rarely useful info, but not a mess like the debug level)
notice (moderately verbose, what you want in production probably)
warning (only very important / critical messages are logged)

在这里插入图片描述

logfile “” 日志位置文件名
databases 16 默认的数据库数量

5.限制 LIMITS

maxclients 10000 最大客户端数
maxmemory <bytes> 最大内存容量
maxmemory-policy noeviction 内存上限处理策略

maxmemory-policy 六种方式
1、volatile-lru:只对设置了过期时间的key进行LRU(默认值)

2、allkeys-lru : 删除lru算法的key

3、volatile-random:随机删除即将过期key

4、allkeys-random:随机删除

5、volatile-ttl : 删除即将过期的

6、noeviction : 永不过期,返回错误
6.快照
持久化,在规定的时间内执行了多少次操作,则会持久化到文件 .rdb .aof 文件

redis是内存数据库,断电即失,如果没有持久化,会丢数据
默认的持久化规则

save 900 1  如果900s内至少一个key修改了一次,就持久化(自动)
save 300 10
save 60 10000
可以自定义持久化规则

stop-writes-on-bgsave-error yes 持久化出错,是否继续工作
rdbcompression yes 是否压缩rdb文件 消耗cpu
rdbchecksum yes 是否校验rdb文件
dir ./ 持久化目录

7.REPLICATION 复制
8.security 安全
在这里插入图片描述

可以设置密码(默认没有密码)
在这里插入图片描述
9.APPEND ONLY MODE

appendonly no 默认不开启,使用rdb模式
appendfilename “appendonly.aof” 持久化文件的名字

appendfsync always
appendfsync everysec 每秒执行一次同步,可能会丢1s的数据
appendfsync no

2.Redis持久化

redis是内存数据库,如果不将内存中的数据保存到磁盘,那么可能会断电即失,所以redis必须持久化

RDB(Redis DataBase)

在指定的时间间隔内,将数据从内存中写入到磁盘,即快照,生成的快照文件,恢复时直接读取快照文件然后读到内存

创建fork子进程来进行持久化,适合大规模数据恢复
默认rdb,不需要修改配置,比aof效率更高。所以rdb是默认的持久化方式
rdb保存的文件dump.rdb 见快照配置
在这里插入图片描述

触发机制

  1. save规则满足的情况下,会自动触发
  2. 执行flushdb命令,也会触发rdb规则
  3. 退出redis时,也会产生rdb文件
    备份就自动生成rdb文件
    在这里插入图片描述

如何恢复rdb文件

只要放在redis启动目录下,即可自动检查dump.rdb,自动恢复其中的数据

rdb文件可以放在哪里?看下图
在这里插入图片描述

优点:

  1. 适合大规模的数据的恢复
  2. 对数据完整性要求不高

缺点:

  1. 需要一定的时间间隔进行操作,最后一次持久化后的数据可能丢失
  2. fork进程会占用一定的内存空间

后备用途,在主从复制中,在从机上使用rdb

AOF(Append Only File)

以日志的形式将redis的指令记录下来,只能追加不能改写,默无限追加,文件越来越大,需要重写
redis重启后,会从前到后执行一次

默认不开启 appendonly no 重启redis即可生效

aof保存的文件appendonly.aof

如果aof文件有问题,redis无法启动,这时候需要用redis-check-aof来修复aof文件

使用命令:

redis-check-aof --fix appendonly.aof

在这里插入图片描述
优点:

  1. 每一次修改都同步,文件更完整
  2. 每秒同步一次,可能会丢失疫苗的数据,
  3. 从不同步,效率最高

缺点:

  1. 相对于数据文件来说,aof远远大于rdb,修复速度比rdb慢
  2. aof运行速度也比rdb慢,所以redis默认的配置是rdb

如果同时开启两种持久化方式,会优先载入aof文件来恢复,因为aof文件更完整

字符串和hash表有什么区别

在这里插入图片描述
hash:field会覆盖

订阅发布

redis发布订阅是一种消息通信模式,发送者发送信息,订阅者接受信息如微信订阅,微博,关注系统等

订阅发布消息模型:
在这里插入图片描述

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

原理
redis是利用C实现的,订阅某个频道后,redis-server维护了一个字典,字典的键就是一个频道,字典的值是一个链表,链表中保存了订阅这个频道的订阅者的客户端,一旦订阅,就将订阅者加入链表中,一旦发布,就会立即同步到订阅者链表

使用场景

  1. 实时消息系统
  2. 实时聊天,聊天室,将信息,回显给所有人
  3. 订阅,关注系统
    更复杂的场景,使用消息中间件来实现,MQ

主从复制

主从复制,数据的复制是单向的,只能从主节点到从节点
主从复制,读写分离,解读写问题,减轻服务器的压力,最低配一主二从
默认情况下,每台redis服务器都是主机,一对多

在这里插入图片描述
主从复制的作用:

  1. 数据冗余
  2. 故障恢复 一个节点的问题,不用想其他节点
  3. 负载均衡 主节点写,从节点读,减轻负担
  4. 高可用基石

一台redis会出现宕机问题,原因:
1.单个服务器可能会发生单点故障,且一台服务器的压力大
2.一台服务器内存容量为256G,一般来说,单台redia最大内存使用不应该超过20G

网上商城,一般都是一次上传,多次浏览,也就是,少写多读的情况

在linux下搭建redis环境

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

环境配置

只配置从库不配置主库
在这里插入图片描述

复制3个配置文件,并修改配置

在这里插入图片描述

然后,修改相关配置
使用vim编辑 redis79.conf redis80.conf redis81.conf
配置示例:(6379为例子)

port 6379

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

在这里插入图片描述
主要是避免重复

启动服务
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
集群环境就搭建好了

一主二从

默认都是主节点
把两个80 81配置成从节点
在这里插入图片描述
配置完成之后,可以看到主机的信息如下,可以看到它下面挂着两个从机
在这里插入图片描述
真实的主从配置应该在配置文件中配置,这样的话是永久的,使用命令配置是暂时的
在这里插入图片描述
主机可以写,从机不能写,只能读数据,主机中的所有信息都会被从机自动保存
主机断开连接(宕机),从机仍然连接到主机,这个时候,如果主机回来了,从机仍然可以直接获取到信息
如果使用命令行配置的主从,如果重启之后,从机,就变成主机了,重新变成从机后,立马就会从主机中获取,可以拿到数据

复制原理

从机连接到主机后会发送一个sync同步命令,主机接收到命令,启动后台存盘进程,同步收集数据命令集,主机将整个数据文件发送给从机,并完成一次完全同步,即全量复制。
全量复制:从机接到数据文件后,一次性进行存盘
增量复制:主机的后续修改命令,单次进行追加

只要重连主机,便会以进行一次完全同步,数据一定会在从机中看到
在这里插入图片描述

一主二仆的两种架构

在这里插入图片描述
还可以这样(层层链路,上M下S)
在这里插入图片描述
此时的80依旧是从节点,不能写

如果没有老大了,手动配置一个老大

如果主机断开连接,我们可以手动配置一个主机,让其他从机,连接到这个最新的主机,这个时候如果原主机恢复,只能重新配置并连接
在这里插入图片描述

哨兵模式

自动选取老大的模式,能够监视主机是否故障,如果发生故障,自动投票,选出一个作为主机,哨兵是一种特殊的模式,首先redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,他会独立运行,他会发送命令,等待redis服务器响应,从而监控多个redis实例

在这里插入图片描述

在这里插入图片描述
当其中一个哨兵发现主机宕机后不会立马进行投票,而是将主机认定为主观下线,当其他哨兵也发现这个主机的问题后,将会通过发布订阅模式,通知其他从机,并(随机一个哨兵发起投票)进行投票(投票算法)选出新的主机,有了新主机后,之前的主机则被认定为客观下线

配置哨兵模式

在这里插入图片描述
配置哨兵配置文件sentinel.conf
1代表主机挂了之后,从机投票,选新主机,得票搞得称为主机
在这里插入图片描述

启动哨兵

在这里插入图片描述
在这里插入图片描述
模拟主机崩溃的场景:
在这里插入图片描述
哨兵监控到主机的问题,并进行投票,选择了81作为新主机
哨兵日志:
在这里插入图片描述
此时在看看81,已经变成主机了
在这里插入图片描述
优点:

  1. 哨兵集群,基于主从复制,所有主从复制的优点,全有
  2. 主次可以切换,故障可以转移,系统的科鹦形更好
  3. 哨兵模式是主从模式的升级,手动到自动,更加健壮

缺点:

  1. Redis不好在线扩容,集群容量一旦达到上限,扩容十分麻烦
  2. 实现哨兵模式的配置很麻烦

Redis缓存穿透和雪崩

服务的高可用问题

缓存穿透(查不到)

用户查询数据时,发现redis中没有,于是去持久层数据库查询,但是也没有,当用户很多的时候(比如秒杀)缓存都没有命中,于是都去请求数据库,导致数据库压力和崩溃,相当于出现了缓存穿透

解决方案

1.布隆过滤器
在这里插入图片描述

2.缓存空对象
在这里插入图片描述
缺点:

  1. 空值放在缓存中,需要占用更多空间
  2. 即使对空值设置了过期时间,还是会在缓存层和存储层的数据存在不一致性,这就对数据一致性产生了影响

缓存击穿(量太大,缓存过期)

某一个key非常热点,在不停扛着大并发量,大并发对这个key持续访问,当key在消失的瞬间,持续的大并发穿破缓存,直接请求数据库,就像在墙上凿出了与一个洞

解决方案

1.设置热点key永不过期

没有设置过期时间,所以不会出现key过期后的瞬间

2.加互斥锁

分布式锁:
使用分布式锁,保障对每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁,只能等待,这种方法是将高并发压力转向了分布式锁,对分布式锁考验比较大

缓存雪崩

在某一个时间段,缓存集中过期失效或redis宕机
在这里插入图片描述

解决方案

redis高可用

如果redis会宕机,那么多增设几台redis,这样一台服务器宕机,其他的还可以继续工作,就是搭建集权环境

限流降级

在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量,比如对一个key,只允许一个线程查询数据和写缓存,其他线程等待

数据预热

在正式部署之前,先把可能的数据先与i热访问一遍,这样部分大访问量的数据会加载到缓存中,在即将发生大并发访问前手动触发加载不同的key,设置不同的过期时间,让缓存失效的时间尽量均匀

Redis进阶

了解布隆过滤器

1970年
一个很长的二进制向量,有0,1
作用:判断一个数据在不在这个二进制向量(数组)中
在这里插入图片描述
布隆过滤器的存入过程
在这里插入图片描述
也就是哈希值映射到数组下标

查询过程:某个数据,计算后的哈希值对应的下标都是1,则表明这个数据存在

删除过程:布隆过滤器很难完成删除操作,因为,可能存在两个不同的数据的哈希值是相同的,这样的话,如果要删除,不知道哪个被删除了,或者,说,会导致,数据不完整,有的数据被误删了

优点:

  1. 二进制数据,所占内存小
  2. 插入和查询速度快,因为计算哈希值和映射到数组速度快,数组的下标特性,可以做到随机存取
  3. 时间复杂度为O(K) K:哈希函数的个数
  4. 保密性好,只存储0和1,不存原始数据

缺点:

  1. 难删除
  2. 可能会误判,某数据不存在,但是经过计算后,哈希值和一个已存在的一样,导致它认为这个数据是存在的,、其实不存在

为了解决误判的问题,只能降低误判的概率

布隆过滤器的原理

了解一下误判率的底层原理是什么:
在布隆过滤器的底层,可以设置误判率,误判率越低,所用时间越长,所用哈希函数越多,性能越差

布隆过滤器是怎么解决缓存穿透的

那么,布隆过滤器是怎么解决缓存穿透的问题的呢?

当一个查询请求过来时,先经过布隆过滤器进行查,如果判断请求查询值存在,则继续查(向redis层查);如果判断请求查询不存在,直接丢弃

这样就不会给redis层或者持久层带来压力

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值