Redis学习

NOSQL: Not only SQL

NOSQL的四大分类

KV键值对:

  • 新浪:Redis
  • 美团:Redis + Tail
  • 阿里、百度: Redis + memecache

文档型数据库(bson格式 和json一样)

  • MongoDB(一般必须要掌握)
    MongoDB是一个基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档!
    MongoDB是一个介于关系型数据库和非关系型数据库数据中间的产品!MongoDB是非关系型数据库中功能最丰富,最像关系型数据库的!

列存储数据库

图关系数据库

Redis入门

Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

Redis能干嘛?

  1. 内存存储、持久化,内存中是断电即失,持久化很重要(rdb、aof)
  2. 效率高,可以用于高速缓存
  3. 发布订阅系统
  4. 地图信息分析
  5. 计时器、计数器(浏览量)

特性

  1. 多样的数据类型
  2. 持久化
  3. 集群
  4. 事务

Linux下安装Redis

1.官网下载Redis

https://www.redis.com.cn/

2.放到Linux的opt目录下
mv *** /opt
3.解压
4.进入redis文件下

Redis6以上需要gcc版本在7以上。
运行下面命令升级gcc
#第一步
sudo yum install centos-release-scl
#第二步
sudo yum install devtoolset-7-gcc*
#第三步
scl enable devtoolset-7 bash

5.make
6.make install
7.

cd /usr/local/bin

在这里插入图片描述
8.将redis配置文件,复制到当前目录下

 mkdir myconfig
 cp /opt/redis-6.0.6/redis.conf myconfig
 cd myconfig
 ls

![在这里插入图片描述](https://img-blog.csdnimg.cn/f8c8bceae7a64db6bf92afa6969f07e2.png

9.redis默认不是后台启动,修改配置文件

vim redis.conf 

在这里插入图片描述
10.启动redis服务

通过指定的配置文件启动服务

redis-server myconf/redis.conf

在这里插入图片描述
11.测试连接

redis-cli -p 6379

在这里插入图片描述
12.查看redis进程是否开启
在这里插入图片描述
13.如何关闭redis服务
在这里插入图片描述

基础命令

在这里插入图片描述
清除当前数据库

127.0.0.1:6379[3]> flushdb

清除所有数据库

127.0.0.1:6379[3]> flushdall

将当前数据库的key移动到4号数据库

move key 4

设置过期时间10s

expire key10

查看当前key的剩余时间

ttl key

查看当前key的类型

type key

在后面加上value2

append key value2

Redis是单线程的!

Redis为什么单线程还这么快?
1、误区一:高性能的服务器一定是多线程的?
2、误区二:多线程(CPU上下文切换)一定比单线程效率高

速度:CPU>内存》硬盘

核心:redis是将所有的数据全部放在内存中,所以说使用单线程去操作效率是最高的。多线程(CPU上下文切换是耗时的操作),对于内存系统来说,如果没有上下文切换,效率就是最高的!redis多次读写都是在一个CPU上的,在内存情况下,就是最佳的方案!

五大数据类型(String,List,Set,Hash,Zset)

String(字符串)

127.0.0.1:6379[4]>  set name zeng     #设置值
OK
127.0.0.1:6379[4]> get name              #获取值
"zeng"
127.0.0.1:6379[4]> keys *                  #获得所有的key
1) "name"
2) "age"
127.0.0.1:6379[4]> exists name       #判断某一个key是否存在,如果不存在则相当于set key
(integer) 1
127.0.0.1:6379[4]> append name "qing"    #追加字符串
(integer) 8
127.0.0.1:6379[4]> get name
"zengqing"
127.0.0.1:6379[4]> strlen name                  #获取key的长度
(integer) 8
127.0.0.1:6379[4]> 


#对象
set user:1 {name:zhangsan,age:3}           #设置一个user:1对象  值为json字符来保存一个对象
#user:{id}:{filed}       如此设计在redis中是可以的
127.0.0.1:6379[4]> mset user:1:name zhangsan user:1:age 2
OK
127.0.0.1:6379[4]> mget user:1:name user:1:age
1) "zhangsan"
2) "2"



#自增操作  相当于i++
127.0.0.1:6379[4]>  set article:1000:views 0   #第1000篇文章的初始浏览量为0
OK
127.0.0.1:6379[4]> incr article:1000:views
(integer) 1



getset  #先get再set

127.0.0.1:6379[4]> getset db redis   #如果不存在值,则返回nil
(nil)
127.0.0.1:6379[4]> get db
"redis"
127.0.0.1:6379[4]> getset db mongodb  #如果存在值,获取原来的值,并设置新的值
"redis"
127.0.0.1:6379[4]> get db
"mongodb"

List


127.0.0.1:6379> lpush list one       #将一个值或多个值插入到列表头部(左)
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1            #获取list中的值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1             #通过区间获取具体的值
1) "three"
2) "two"

127.0.0.1:6379> rpush list four  #将一个值或多个值插入到列表尾部(右)
(integer) 4
127.0.0.1:6379> lrange list 0 -1    #获取list中的值
1) "three"
2) "two"
3) "one"
4) "four"

127.0.0.1:6379> lpop list              #移除左边第一个
"three"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "four"

127.0.0.1:6379> rpop list            #移除右边第一个
"four"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"

127.0.0.1:6379> lindex list 0         #通过下标获取值
"two"

127.0.0.1:6379> llen list                #返回列表的长度
(integer) 2


#移除指定的值
127.0.0.1:6379> lrem list 1 one
(integer) 1

127.0.0.1:6379> rpush mylist "hello"
(integer) 1
127.0.0.1:6379> rpush mylist "hello1"
(integer) 2
127.0.0.1:6379> rpush mylist "hello2"
(integer) 3
127.0.0.1:6379> rpush mylist "hello3"
(integer) 4
127.0.0.1:6379> ltrim mylist 1 2     #通过下标剪裁指定长度
OK
127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello2"

Set

Hash

Zset

geospatial地理位置(geo底层的实现原理就是Zset)

geoadd 添加地理位置

#geoadd  添加地理位置
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shengzhen
(integer) 2

geopos 获取地理位置(经度、纬度)

#geopos 获取地理位置
127.0.0.1:6379> geopos china:city beijing
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
127.0.0.1:6379> geopos china:city beijing chongqing
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
2) 1) "106.49999767541885376"
   2) "29.52999957900659211"

geodist 返回两个给定位置之间的距离

#geodist 返回两个给定位置之间的距离
127.0.0.1:6379> geodist china:city beijing shanghai    #查看上海到北京的直线距离
"1067378.7564"
127.0.0.1:6379> geodist china:city beijing chongqing    #查看北京到重庆的直线距离
"1464070.8051"

georadius 以给定的经纬度为中心,找出某一半径内的元素

#georadius 以给定的经纬度为中心,找出某一半径内的元素
127.0.0.1:6379> georadius china:city 110 30 1000 km      #获取以 110 30 为中心方圆1000km内的城市
1) "chongqing"
2) "shengzhen"
127.0.0.1:6379> georadius china:city 110 30 500 km
1) "chongqing"
127.0.0.1:6379> georadius china:city 110 30 1000 km withdist   #显示到中心位置的距离
1) 1) "chongqing"
   2) "341.9374"
2) 1) "shengzhen"
   2) "924.6408"
127.0.0.1:6379> georadius china:city 110 30 1000 km withcoord   #显示他人的定位信息
1) 1) "chongqing"
   2) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "shengzhen"
   2) 1) "114.04999762773513794"
      2) "22.5200000879503861"
127.0.0.1:6379> georadius china:city 110 30 1000 km count 1   #筛选指定数量
1) "chongqing"

事务

  • 事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行的过程中,按照顺序执行
  • 一次性、顺序性、排他性
  • 事务管理(ACID):原子性(atomicity,或称不可分割性)、一致性(consistency)、隔离性(isolation,又称独立性)、持久性(durability)
  • Redis单条命令保证原子性,但Redis事务不保证原子性!
  • Redis事务没有隔离级别的概念:所有的事务在命令中,并没有直接被执行,只有发起执行命令的时候才会被执行

Redis事务:

  • 开启事务()
  • 命令入队()
  • 执行事务()

正常执行事务!

127.0.0.1:6379> multi                #开启事务
OK
#命令入队
127.0.0.1:6379> set key1 v1
QUEUED
127.0.0.1:6379> get key1
QUEUED
127.0.0.1:6379> set key v2
QUEUED
127.0.0.1:6379> get key
QUEUED
127.0.0.1:6379> exec                #执行事务
1) OK
2) "v1"
3) OK
4) "v2"

放弃事务!

127.0.0.1:6379> set k1 v1         
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED 
127.0.0.1:6379> discard            #取消事务
OK
127.0.0.1:6379> get k4             #事务队列中的命令都不会被执行!
(nil)

编译型异常(代码有问题!命令有错),事务中所有的命令都不会被执行!

运行时异常(1/0),如果事务队列中存在语法性错误,那么执行命令的时候,其他命令是可以正常执行的,错误命令会抛出异常!

监控(Watch)

悲观锁:

乐观锁:




Jedis

Jedis是官方推荐的java连接工具!

测试

1、导入对应的依赖

<!--导入JRedis包-->
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependencies>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.7.0</version>
    </dependency>
    <!--  fastjson      -->
    <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.78</version>
    </dependency>
</dependencies>

2、编码测试:

  • 连接数据库
  • 操作命令
  • 断开连接
package com.zeng;

import redis.clients.jedis.Jedis;

public class TestPing {
    public static void main(String[] args) {
        //1、new Jedis对象即可
        Jedis jedis = new Jedis("127.0.0.1",6379);

        System.out.println(jedis.ping());
    }
}

输出:
在这里插入图片描述



SpringBoot整合

说明:在SpringBoot2.X之后,原来使用的jedis被替换成了lettuce
jedis:采用的直连,多个线程操作的话是不安全的,如果想要避免不安全的,使用jedis pool连接池!
lettuce:采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况!

测试

1、导入依赖

<!--  操作redis      -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2、配置连接

#配置redis
spring.redis.host=127.0.0.1
spring.redis.port=6379

3、测试

package com.zeng;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
class Redis02SpringbootApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    void contextLoads() {
        //opsForValue  操作字符串,类似String
        //opsForList  操作List

        //获取redis的连接对象
        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
        System.out.println(connection.ping());


        redisTemplate.opsForValue().set("name","zeng");
        System.out.println(redisTemplate.opsForValue().get("name"));
    }

}

直接传递对象会报错!需要序列化
在这里插入图片描述

package com.zeng;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zeng.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
class Redis02SpringbootApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    void contextLoads() {
        //opsForValue  操作字符串,类似String
        //opsForList  操作List

        //获取redis的连接对象
        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
        System.out.println(connection.ping());


        redisTemplate.opsForValue().set("name","zeng");
        System.out.println(redisTemplate.opsForValue().get("name"));
    }

    @Test
    public void test(){
        //真实的开发一般都使用json来传递对象
        User user = new User("狂神说", 3);
        String jsonUser = null;
        try {
            jsonUser = new ObjectMapper().writeValueAsString(user);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        redisTemplate.opsForValue().set("user",jsonUser);
        System.out.println(redisTemplate.opsForValue().get("user"));
    }

}

输出:在这里插入图片描述

两个持久化方案:

RDB(Redis Database)

RDB 触发机制分为使用指令手动触发和 redis.conf 配置自动触发。

手动触发 Redis 进行 RDB 持久化的指令的为:

  • save ,该指令会阻塞当前 Redis 服务器,执行 save 指令期间,Redis 不能处理其他命令,直到 RDB 过程完成为止。
  • bgsave,执行该命令时,Redis 会在后台异步执行快照操作,此时 Redis 仍然可以相应客户端请求。具体操作是 Redis 进程执行 fork 操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束。Redis 只会在 fork期间发生阻塞,但是一般时间都很短。但是如果 Redis 数据量特别大,fork 时间就会变长,而且占用内存会加倍,这一点需要特别注意。

自动触发 RDB 的默认配置如下所示:

save 900 1 # 表示900 秒内如果至少有 1 个 key 的值变化,则触发RDB
save 300 10 # 表示300 秒内如果至少有 10 个 key 的值变化,则触发RDB
save 60 10000 # 表示60 秒内如果至少有 10000 个 key 的值变化,则触发RDB

如果不需要 Redis 进行持久化,那么可以注释掉所有的 save 行来停用保存功能,也可以直接一个空字符串来停用持久化:save “”。

rdb文件

1、save的规则满足条件下,会触发rdb规则生产rdb文件
2、执行flushall也会触发rdb规则生产rdb文件,flushdb不会触发
3、退出redis,也会产生rdb文件

AOF(Append Only File)

配置文件:
在这里插入图片描述
默认是不开启的,我们只需要将appendonly改为yes就开启了aof,重启redis就可以生效!
如果这个aof文件有错误,这时候redis是启动不起来的,需要修复这个aof文件,redis给我们提供了一个工具 redi-check=aof --fix

Redis发布订阅

Pub/Sub 从字面上理解就是发布(Publish)与订阅(Subscribe),在Redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。

PSUBSCRIBE pattern [pattern …]

订阅一个或者多个符合模式匹配的频道

【假设客户端同时订阅了某种模式和符合该模式的某个频道,那么发送给这个频道的消息将被客户端接收到两次,只不过这两条消息的类型不同,一个是message类型,一个是pmessage类型,但其内容相同。 】

PUBSUB subcommand [argument [argument …]]

返回由活跃频道组成的列表,即可以查询订阅与发布系统的状态

PUBLISH channel message

发送消息到指定的频道
【其返回值为接收到该消息的订阅者的数量】

SUBSCRIBE channel [channel …]

订阅一个或多个频道

【其返回值包括客户端订阅的频道,目前已订阅的频道数量,以及接收到的消息,其中subscribe表示已经成功订阅了某个频道】

PUNSUBSCRIBE [pattern [pattern …]]

退订所有符合模式匹配的频道

UNSUBSCRIBE [channel [channel …]]

退订一个或多个频道

【Redis采用UNSUBSCRIBE和PUNSUBSCRIBE命令取消订阅,其返回值与订阅类似。 
由于Redis的订阅操作是阻塞式的,因此一旦客户端订阅了某个频道或模式,就将会一直处于订阅状态直到退出。

在SUBSCRIBE,PSUBSCRIBE,UNSUBSCRIBE和PUNSUBSCRIBE命令中,

其返回值都包含了该客户端当前订阅的频道和模式的数量,当这个数量变为0时,该客户端会自动退出订阅状态。

Redis主从复制

概念

主从复制:指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master/Leader),后者称为从节点(Slave/Follower), 数据的复制是单向的!只能由主节点复制到从节点(主节点以写为主、从节点以读为主)。Redis的主从复制是异步复制,异步分为两个方面,一个是master服务器在将数据同步到slave时是异步的,因此master服务器在这里仍然可以接收其他请求,一个是slave在接收同步数据也是异步的。

默认情况下,每台redis服务器都是主机。

主从复制的作用

  1. 数据备份:主从复制实现了数据的热备份,是持久化之外的另一种数据备份方式。
  2. 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复。
  3. 读写分离:可以用于实现读写分离,主库写、从库读,读写分离不仅可以提高服务器的负载能力,同时可根据需求的变化,改变从库的数量;
  4. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  5. 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

环境配置

复制三个配置文件,然后修改相应的配置信息
1、端口
2、pid
3、log文件名字
4、dump.rdb名字

在这里插入图片描述

127.0.0.1:6380> SLAVEOF 127.0.0.1 6379         #认定6379为主机
OK
127.0.0.1:6380> info replication
# Replication
role:slave                                  #当前角色是从机
master_host:127.0.0.1                       #可以看到主机的信息
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:42
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:2341cf975492a6edc83d59d54d117c8ba0b47406
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:42
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:42
127.0.0.1:6380> 

读写分离

主机可以写,从机不能写只能读

工作原理

master服务器会记录一个replicationId的伪随机字符串,用于标识当前的数据集版本,还会记录一个当前数据集的偏移量offset,不管master是否有配置slave服务器,replication Id和offset会一直记录并成对存在。

当master与slave正常连接时,slave使用PSYNC命令向master发送自己记录的旧master的replication id和offset,而master会计算与slave之间的数据偏移量,并将缓冲区中的偏移数据同步到slave,此时master和slave的数据一致。
而如果slave引用的replication太旧了,master与slave之间的数据差异太大,则master与slave之间会使用全量复制的进行数据同步。

全量复制:slave服务在接受到数据库文件数据后,将其存盘并加载到内存中。
增量复制:Master继续将新的收集到的修改命令依次传给slave,完成同步

只要重新连接master,一次完全同步(全量复制)将被自动执行!

哨兵模式

Redis的主从复制模式下, 一旦主节点由于故障不能提供服务, 需要人工将从节点晋升为主节点, 同时还要通知应用方更新主节点地址, 对于很多应用场景这种故障处理的方式是无法接受的。 可喜的是Redis从2.8开始正式提供了Redis Sentinel(哨兵) 架构来解决这个问题。

Redis主从复制的缺点:没有办法对master进行动态选举,需要使用Sentinel机制完成动态选举

1.哨兵模式介绍

  • Sentinel(哨兵)进程是用于监控redis集群中Master主服务器工作的状态
  • 在Master主服务器发生故障的时候,可以实现Master和Slave服务器的切换,保证系统的高可用(HA)
  • 其已经被集成在redis2.6+的版本中,Redis的哨兵模式到了2.8版本之后就稳定了下来。

2.哨兵进程的作用

  1. 监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master和Slave是否运作正常。
  2. 提醒(Notification):当被监控的某个Redis节点出现问题时, 哨兵(sentinel) 可以通过 API
    向管理员或者其他应用程序发送通知。
  3. 自动故障迁移(Automatic failover):当一个Master不能正常工作时,哨兵(sentinel)
    会开始一次自动故障迁移操作。
    (1)它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其他Slave改为复制新的Master;
    (2)当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用现在的Master替换失效Master。
    (3)Master和Slave服务器切换后,Master的redis.conf、Slave的redis.conf和sentinel.conf的配置文件的内容都会发生相应的改变,即,Master主服务器的redis.conf配置文件中会多一行slaveof的配置,sentinel.conf的监控目标会随之调换

3.哨兵进程的工作方式

  1. 每个Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的Master主服务器,Slave从服务器以及其他Sentinel(哨兵)进程发送一个 PING 命令。
  2. 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds
    选项所指定的值,则这个实例会被 Sentinel(哨兵)进程标记为主观下线(SDOWN)。
  3. 如果一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的所有
    Sentinel(哨兵)进程要以每秒一次的频率确认Master主服务器的确进入了主观下线状态。
  4. 当有足够数量的Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认Master主服务器进入了主观下线状态(SDOWN),则Master主服务器会被标记为客观下线(ODOWN)。
  5. 在一般情况下, 每个Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的所有Master主服务器、Slave从服务器发送 INFO 命令。
  6. 当Master主服务器被 Sentinel(哨兵)进程标记为客观下线(ODOWN)时,Sentinel(哨兵)进程向下线的Master主服务器的所有 Slave从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。

4.哨兵模式的配置

# Example sentinel.conf
 
# 哨兵sentinel实例运行的端口 默认26379
port 26379
 
# 哨兵sentinel的工作目录
dir /tmp
 
# 哨兵sentinel监控的redis主节点的 ip port 
# master-name  可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 配置多少个sentinel哨兵统一认为master主节点失联 那么这时客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
  sentinel monitor mymaster 127.0.0.1 6379 2
 
# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
 
 
# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000
 
# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,
这个数字越小,完成failover所需的时间就越长,
但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1
 
 
 
# 故障转移的超时时间 failover-timeout 可以用在以下这些方面: 
#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
#3.当想要取消一个正在进行的failover所需要的时间。  
#4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
# 默认三分钟
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000
 
# SCRIPTS EXECUTION
 
#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
#对于脚本的运行结果有以下规则:
#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
#若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
#一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
 
#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,
这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,
一个是事件的类型,
一个是事件的描述。
如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
#通知脚本
# sentinel notification-script <master-name> <script-path>
  sentinel notification-script mymaster /var/redis/notify.sh
 
# 客户端重新配置主节点参数脚本
# 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
# 以下参数将会在调用脚本时传给脚本:
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
# 目前<state>总是“failover”,
# <role>是“leader”或者“observer”中的一个。 
# 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
 sentinel client-reconfig-script mymaster /var/redis/reconfig.sh


Redis缓存穿透和雪崩

一、缓存穿透

1、概念

缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。

这里需要注意和缓存击穿的区别,缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

为了避免缓存穿透其实有很多种解决方案。下面介绍几种。

2、解决方案

(1)布隆过滤器

布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力。

在这里插入图片描述

2、缓存空对象

当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源;

在这里插入图片描述
但是这种方法会存在两个问题:

  1. 如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;
  2. 即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。

二、缓存击穿

系统中存在以下两个问题时需要引起注意:

  • 当前key是一个热点key(例如一个秒杀活动),并发量非常大。
  • 重建缓存不能在短时间完成,可能是一个复杂计算,例如复杂的SQL、多次IO、多个依赖等。

在缓存失效的瞬间,有大量线程来重建缓存,造成后端负载加大,甚至可能会让应用崩溃。

解决方案:

1. 分布式互斥锁

只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据即可。set(key,value,timeout)
在这里插入图片描述

2. 永不过期
  • 从缓存层面来看,确实没有设置过期时间,所以不会出现热点key过期后产生的问题,也就是“物理”不过期。

  • 从功能层面来看,为每个value设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独的线程去更新缓
    在这里插入图片描述
    2种方案对比:

  • 分布式互斥锁:这种方案思路比较简单,但是存在一定的隐患,如果在查询数据库 + 和重建缓存(key失效后进行了大量的计算)时间过长,也可能会存在死锁和线程池阻塞的风险,高并发情景下吞吐量会大大降低!但是这种方法能够较好地降低后端存储负载,并在一致性上做得比较好。

  • “永远不过期”:这种方案由于没有设置真正的过期时间,实际上已经不存在热点key产生的一系列危害,但是会存在数据不一致的情况,同时代码复杂度会增大。

三、缓存雪崩

1、概念

缓存雪崩是指,缓存层出现了错误,不能正常工作了。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。

在这里插入图片描述

2、解决方案

(1)redis高可用

这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。

(2)限流降级

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

(3)数据预热

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值