Redis学习笔记

跟b站狂神学习的redis,做了一份学习笔记
视频地址:狂神redis

Redis入门
概述

Redis是什么

Redis(Remote Dictionary Server ),即远程字典服务 !

是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库, 并提供多种语言的API。

redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了 master-slave(主从)同步。

Redis能干嘛

1、内存存储、持久化,内存中是断电即失、所以说持久化很重要(rdb、aof)

2、效率高,可以用于高速缓存

3、发布订阅系统

4、地图信息分析

5、计时器、计数器(浏览量!)

6、…

特性

1、多样的数据类型

2、持久化

3、集群

4、事务 …

Window安装Redis

从官网上(https://redis.io/download)下载压缩包解压即可

image-20220108230820435

双击redis-server.exe运行服务器

####image-20220108231018404
双击redis-cli运行客户端

image-20220108231137213

一般redis都是在linux上跑,window拿来做下测试还差不多。

Linux安装Redis

1、下载安装包! redis-6.2.6.tar.gz

2、解压到linux里的/opt文件夹里

image-20220108231625984

3、基本的环境安装

因为redis是用c写的,所以要安装c的环境,在redis文件夹里运行以下命令来安装gcc-c++和redis

yum install gcc-c++

make

make install

4、redis的默认安装路径 /usr/local/bin

里面已经有redis服务器、客户端和哨兵等文件

image-20220108232128877

5、讲redis.conf复制到自己的文件夹下

image-20220108232318776

6、redis.conf修改后台启动

image-20220108232552083

7、用自己的配置文件启动redis服务,没报错就是启动成功了

image-20220108232808668

8、用redis-cli启动客户端连接到redis服务

image-20220108233156339

9、关闭redis服务

image-20220108233430100

9、查看进程是否存在

[root@Cinco bin]# ps -ef|grep redis

image-20220108233456024

基础的知识

redis默认有16个数据库

image-20220108233643130

切换数据库

127.0.0.1:6379> DBSIZE	#默认是使用0号数据库,0号数据库的大小为9
(integer) 9
127.0.0.1:6379> SELECT 3	#切换到3号数据库
OK	
127.0.0.1:6379[3]> DBSIZE
(integer) 0
127.0.0.1:6379[3]> 

清空当前数据库 flushdb

清空全部数据库 flushall

Redis五大基本类型

Redis-Key

EXISTS name # 判断当前的key是否存在

move name 1 # 移除当前的key

EXPIRE name 10 # 设置key的过期时间,单位是秒

ttl name # 查看当前key的剩余时间

type name # 查看当前key的一个类型!
 
EXISTS name # 判断当前的key是否存在
 
move name 1 # 移除当前的key

EXPIRE name 10 # 设置key的过期时间,单位是秒

ttl name # 查看当前key的剩余时间

type name # 查看当前key的一个类型!
Redis-String类型
set key1 v1 # 设置值

get key1 # 获得值

keys * # 获得所有的key

EXISTS key1 # 判断某一个key是否存在

APPEND key1 "hello" # 追加字符串,如果当前key不存在,就相当于setkey

STRLEN key1 # 获取字符串的长度!

incr views # 自增1 

decr views # 自减1 

 INCRBY views 10 # 可以设置步长,指定增量!

DECRBY views 5 # 指定减量!

GETRANGE key1 0 3 # 截取字符串 [0,3]

GETRANGE key1 0 -1 # 获取全部的字符串 和 get key是一样的

SETRANGE key2 1 xx # 替换指定位置开始的字符串!用xx替换第1个位置(从0开始数)后面的值

setex key3 30 "hello" # 设置key3 的值为 hello,30秒后过期

setnx mykey "redis" # 如果mykey 不存在,创建mykey;如果存在则会创建失败,原值不变

mset k1 v1 k2 v2 k3 v3 # 同时设置多个值

mget k1 k2 k3 # 同时获取多个值

msetnx k1 v1 k4 v4 # msetnx 是一个原子性的操作,要么一起成功,要么一起 失败!

# 对象

set user:1 {name:zhangsan,age:3} # 设置一个user:1 对象 值为 json字符来保存一个对象!

mset user:1:name zhangsan user:1:age 2  #  user:{id}:{filed} 

getset db mongodb # 如果不存在值,则返回 nil;如果存在值,获取原来的值,并设置新的值
Redis-List

可以把list玩成 ,栈、队列、阻塞队列!

lpush list one # 将一个值或者多个值,插入到列表头部 (左)
lrange list 0 1 # 通过区间获取具体的值!
lrange list 0 -1 # 获取list中所有值!
rpush list righr # 将一个值或者多个值,插入到列表位部 (右)
lpop list # 移除list的第一个元素
rpop list # 移除list的最后一个元素
lindex list 1 # 通过下标获得 list 中的某一个值!
llen list # 返回列表的长度
lrem list 1 one # 移除list集合中指定个数的value,精确匹配 
ltrim mylist 1 2 # 通过下标截取指定的长度,这个list已经被改变了,截断了只剩下截取的元素!ltrim mylist start end
rpoplpush mylist hislist # 移除列表的最后一个元素,将他移动到新的列表中!mylist的最后一个元素移动到hislist的第一位
exists list # 判断这个列表是否存在
lset list 0 item # 如果不存在列表我们去更新就会报错;# 如果存在,更新当前下标的值,把0位置的值改为item
linsert mylist before "world" "other"   # 将某个具体的value插入到列把你中某个元素的前面或者后面!输出"other", "world"
linsert mylist after world new  #输出"world",new"
Redis-Set

set中的值是不能重读的!

sadd myset "hello" # set集合中添加匀速
smember myset # 查看指定set的所有值
sismember myset hello # 判断某一个值是不是在set集合中!
scard myset # 获取set集合中的内容元素个数!
srem myset hello # 移除set集合中的指定元素
srandmember myset # 随机抽选出一个元素
srandmember myset 2 # 随机抽选出指定个数的元素
spop myset # 随机删除一些set集合中的元素!
smove myset myset2 "kuangshen" # 将一个指定的值,移动到另外一个set集合!
sdiff key1 key2 # 差集
sinter key1 key2 # 交集 共同好友就可以这样实现
sunion key1 key2 # 并集
Redis-Hash

本质和String类型没有太大区别,还是一个简单的 key-vlaue!

hset myhash field1 cinco # set一个具体 key-vlaue
hget myhash field1 # 获取一个字段值
hmset myhash field1 hello field2 world # set多个 key-vlaue
hmget myhash field1 field2 # 获取多个字段值
hgetall myhash # 获取全部的数据
hlen myhash # 获取hash表的字段数量!
HEXISTS myhash field1 # 判断hash中指定字段是否存在!
hkeys myhash # 只获得所有field
hvals myhash # 只获得所有value
hset myhash field3 5 #指定增量!
hsetnx myhash field4 hello # 如果不存在则可以设置;如果存在则不能设置
Redis-Zset

Zset 有序集合,在set的基础上,增加了一个值,set k1 v1 / zset k1 score1 v1

zadd salary 2500 one # 添加一个值
zadd salary 5000 two 500 three # 添加多个值
zrange salary 0 -1  #获取全部
zrangebyscore salary -inf +inf # 显示全部的用户 从小到大!
zrevrange salary 0 -1 # 从大到进行排序!
zrangebyscore salary -inf +inf withscores # 显示全部的用户并且附带成绩
zrangebyscore salary -inf 2500 withscores # 显示工资小于2500员工的升序排序!
zrem salary xiaohong # 移除有序集合中的指定元素
zcard salary # 获取有序集合中的个数
zcount myset 1 3 # 获取指定区间的成员数量!
Redis-三种特殊数据类型
Geospatial 地理位置
geoadd china:city 116.40 39.90 beijing	#新增一个坐标
geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shengzhen
GEOPOS china:city beijing chongqing	#获取坐标
GEODIST china:city beijing chongqing km # 查看北京到重庆的直线距离,单位km
GEORADIUS china:city 110 30 1000 km # 以110,30 这个经纬度为中心,寻找方圆1000km内的城市
GEORADIUS china:city 110 30 500 km withdist # 显示到中间距离的位置
GEORADIUS china:city 110 30 500 km withcoord # 显示他人的定位信息
GEORADIUS china:city 110 30 500 km withdist withcoord count 1 #筛选出指定的结果!count指定数量1
GEORADIUSBYMEMBER china:city beijing 1000 km # 找出位于指定元素周围的其他元素!
geohash china:city beijing chongqing  # 将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么则距离越近!

#GEO 底层的实现原理其实就是 Zset!我们可以使用Zset命令来操作geo!
ZRANGE china:city 0 -1 # 查看地图中全部的元素
 zrem china:city beijing # 移除指定元素!
  ZRANGE china:city 0 -1
Hyperloglog

优点:占用的内存是固定,2^64 不同的元素的技术,只需要废 12KB内存!如果要从内存角度来比较的 话 Hyperloglog 首选!

传统的方式, set 保存用户的id,然后就可以统计 set 中的元素数量作为标准判断 ! 这个方式如果保存大量的用户id,就会比较麻烦!我们的目的是为了计数,而不是保存用户id; 0.81% 错误率! 统计UV任务,可以忽略不计的!

如果允许容错,那么一定可以使用 Hyperloglog !

如果不允许容错,就使用 set 或者自己的数据类型即可!

127.0.0.1:6379> PFadd mykey a b c d e f g h i j # 创建第一组元素 mykey
(integer) 1
127.0.0.1:6379> PFCOUNT mykey # 统计 mykey 元素的基数数量
(integer) 10
127.0.0.1:6379> PFadd mykey2 i j z x c v b n m # 创建第二组元素 mykey2
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 9
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 # 合并两组 mykey mykey2 => mykey3 并集
OK
127.0.0.1:6379> PFCOUNT mykey3 # 看并集的数量!
(integer) 15
Bitmap

统计用户信息,活跃,不活跃! 登录 、 未登录! 打卡,365打卡! 两个状态的,都可以使用 Bitmaps!

Bitmap 位图,数据结构! 都是操作二进制位来进行记录,就只有0 和 1 两个状态!

365 天 = 365 bit 1字节 = 8bit 46 个字节左右!

#使用bitmap 来记录 周一到周日的打卡! 周一:1 周二:0 周三:0 周四:1 ......
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 6
(integer) 0
127.0.0.1:6379> bitcount sign # 统计这周的打卡记录,就可以看到是否有全勤!
(integer) 3
Redis-事务

开启事务(multi)

命令入队(…)

执行事务(exec)

127.0.0.1:6379> MULTI	#开启事务
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> get k1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> exec	#执行事务
1) OK
2) "v1"
3) OK
127.0.0.1:6379> 

取消事务(discard)

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> DISCARD	#取消事务
OK
127.0.0.1:6379> get k3
(nil)

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

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

Redis-监控

悲观锁

很悲观,认为什么时候都会出问题,无论做什么都会加锁!

乐观锁

很乐观,认为什么时候都不会出问题,所以不会上锁! 更新数据的时候去判断一下在此期间是否 有人修改过这个数据

正常执行事务成功!

127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money # 监视 money 对象
OK
127.0.0.1:6379> multi # 事务正常结束,数据期间没有发生变动,这个时候就正常执行成功!
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20

测试多线程修改值 , 使用watch 可以当做redis的乐观锁操作!

127.0.0.1:6379> watch money # 监视 money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
127.0.0.1:6379> exec # 执行之前,另外一个线程,修改了我们的值,这个时候,就会导致事务执行失败!
(nil

如果发现事务执行失败,就先用 unwatch 解锁,然后再用 watch 上锁后执行事务

Redis-Jedis

Jedis是Redis官方推荐的java连接开发工具

下面测试一下

1.导入pom依赖

    <dependencies>
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
    </dependencies>

2.代码测试

连接redis

操作命令

断开连接

public class TestPing {
    public static void main(String[] args) {
//        新建Jedis对象
        Jedis jedis = new Jedis("127.0.0.1",6379);
//        jedis的方法就是学习过的所有redis指令
        jedis.flushDB();
        System.out.println(jedis.ping());
        System.out.println(jedis.set("name", "cinco"));
        System.out.println(jedis.get("name"));
    }
}

3.事务测试

public class TestTX {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hello","world");
        jsonObject.put("name","cinco");
// 开启事务
        Transaction multi = jedis.multi();
        String result = jsonObject.toJSONString();
// jedis.watch(result)
        try {
            multi.set("user1",result);
            multi.set("user2",result);
            int i = 1/0 ; // 代码抛出异常事务,执行失败!
            multi.exec(); // 执行事务!
        } catch (Exception e) {
            multi.discard(); // 放弃事务
            e.printStackTrace();
        } finally {
            System.out.println(jedis.get("user1"));
            System.out.println(jedis.get("user2"));
            jedis.close(); // 关闭连接
        }
    }
}
Redis-整合Springboot

在 SpringBoot2.x 之后,原来使用的jedis 被替换为了 lettuce

​ 当前springboot版本的spring-boot-starter-data-redis中的子依赖为

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.6.2</version>
      <scope>compile</scope>
    </dependency>
      
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-redis</artifactId>
      <version>2.6.0</version>
      <scope>compile</scope>
    </dependency>
      
    <dependency>
      <groupId>io.lettuce</groupId>
      <artifactId>lettuce-core</artifactId>
      <version>6.1.5.RELEASE</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>

jedis : 采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用 jedis pool 连接 池! 更像 BIO 模式

lettuce : 采用netty,实例可以再多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据 了,更像 NIO 模式

整合测试

1.导入依赖

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

2.配置连接

#redis默认的host和port 就是localhost 和 6379 ,这里可以不配置
spring:
  redis:
    host: 127.0.0.1
    port: 6379

3.测试

@SpringBootTest
class Redis02SpringbootApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    void contextLoads() {
        
        // redisTemplate 操作不同的数据类型,api和我们的指令是一样的
		// opsForValue 操作字符串 类似String
		// opsForList 操作List 类似List
		// opsForSet
		// opsForHash
		// opsForZSet
		// opsForGeo
		// opsForHyperLogLog

        redisTemplate.opsForValue().set("name","cinco");
        System.out.println(redisTemplate.opsForValue().get("name"));
        
        // 除了进本的操作,我们常用的方法都可以直接通过redisTemplate操作,比如事务,和基本的CRUD
		// 获取redis的连接对象
		// RedisConnection connection =redisTemplate.getConnectionFactory().getConnection();
		// connection.flushDb();
		// connection.flushAll();

    }

}

自定义RedisTemplate固定模板

package com.cinco.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
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;

@Configuration
public class RedisConfig {

//    自定义一个RedisTemplate   固定模板
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
//        为了自己开发方便,一般直接使用 <String, Object>
        RedisTemplate<String, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);

//        json序列化配置
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        template.setDefaultSerializer(jackson2JsonRedisSerializer);

        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
//      String 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//      key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
//      hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
//       value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
//      hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();

        return template;
    }
}

在java开发中一般会封装一个RedisUtil工具类来帮助我们方便地使用redis

RedisUtil工具类:

https://blog.csdn.net/weixin_46883635/article/details/122355927

Redis-持久化
RDB持久化(Redis DataBase)

redis默认的持久化方式就是rdb持久化,rdb持久化在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快 照文件直接读到内存里。

rdb保存的文件是dump.rdb

#在redisconfig中,设置save值修改rdb操作的触发时间
save 900 1	#只要900s内修改了1次key就会触发rdb操作
save 300 10		#300s 10次
save 60 10000	#60s 10000次

触发机制

1、save的规则满足的情况下,会自动触发rdb规则

2、执行 flushall 命令,也会触发我们的rdb规则!

3、退出redis,也会产生 rdb 文件!

备份就自动生成一个 dump.rdb

如何用rdb文件恢复redis的数据

1、只需要将rdb文件放在我们redis启动目录就可以,redis启动的时候会自动检查dump.rdb 恢复其中 的数据!

2、查看需要存在的位置

127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/bin" # 如果在这个目录下存在 dump.rdb 文件,启动就会自动恢复其中的数据

AOF持久化(Append Only File)

将所有命令都记录下来,相当于history,恢复的时候就把这个文件全部在执行一遍!

以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件 但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件 的内容将写指令从前到后执行一次以完成数据的恢复工作

aof保存的文件是appendonly.aof

默认是不开启的,我们需要手动进行配置!我们只需要在redisconfig中将 appendonly 改为yes就开启了 aof! 重启,redis 就可以生效了!

appendonly no # 默认是不开启aof模式的,默认是使用rdb方式持久化的,在大部分所有的情况下,rdb完全够用!
appendfilename "appendonly.aof" # 持久化的文件的名字
# appendfsync always # 每次修改都会 sync。消耗性能
appendfsync everysec # 每秒执行一次 sync,可能会丢失这1s的数据!
# appendfsync no # 不执行 sync,这个时候操作系统自己同步数据,速度最快!

如果这个 aof 文件有错位,这时候 redis 是启动不起来的吗,我们需要修复这个aof文件 redis 给我们提供了一个工具 redis-check-aof --fix

AOF优点:

1、每一次修改都同步,文件的完整会更加好!

2、每秒同步一次,可能会丢失一秒的数据

3、从不同步,效率最高的!

AOF缺点

1、相对于数据文件来说,aof远远大于 rdb,修复的速度也比 rdb慢!

2、Aof 运行效率也要比 rdb 慢,所以我们redis默认的配置就是rdb持久化!

扩展

1、RDB 持久化方式能够在指定的时间间隔内对你的数据进行快照存储
2、AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始
的数据,AOF命令以Redis 协议追加保存每次写的操作到文件末尾,Redis还能对AOF文件进行后台重
写,使得AOF文件的体积不至于过大。
3、只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化
4、同时开启两种持久化方式
在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF
文件保存的数据集要比RDB文件保存的数据集要完整。
RDB 的数据不实时,同时使用两者时服务器重启也只会找AOF文件,那要不要只使用AOF呢?作者
建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有
AOF可能潜在的Bug,留着作为一个万一的手段。
5、性能建议
因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够
了,只保留 save 900 1 这条规则。
如果Enable AOF ,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自
己的AOF文件就可以了,代价一是带来了持续的IO,二是AOF rewrite 的最后将 rewrite 过程中产
生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite
的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上,默认超过原大小100%大小重
写可以改到适当的数值。
如果不Enable AOF ,仅靠 Master-Slave Repllcation 实现高可用性也可以,能省掉一大笔IO,也
减少了rewrite时带来的系统波动。代价是如果Master/Slave 同时倒掉,会丢失十几分钟的数据,
启动脚本也要比较两个 Master/Slave 中的 RDB文件,载入较新的那个,微博就是这种架构。

Redis-发布订阅

1 PSUBSCRIBE pattern [pattern …]
订阅一个或多个符合给定模式的频道。
2 PUBSUB subcommand [argument [argument …]]
查看订阅与发布系统状态。
3 PUBLISH channel message
将信息发送到指定的频道。
4 PUNSUBSCRIBE [pattern [pattern …]]
退订所有给定模式的频道。
5 SUBSCRIBE channel [channel …]
订阅给定的一个或多个频道的信息。
6 UNSUBSCRIBE [channel [channel …]]
指退订给定的频道。

订阅频道

127.0.0.1:6379> SUBSCRIBE cincoTest	#订阅cincoTest这个频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "cincoTest"
3) (integer) 1

往频道里发送消息

127.0.0.1:6379> PUBLISH cincoTest "hello,cinco"
(integer) 1
127.0.0.1:6379> PUBLISH cincoTest "how do you do,bro"
(integer) 1

频道收到消息会立即输出出来

127.0.0.1:6379> SUBSCRIBE cincoTest
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "cincoTest"
3) (integer) 1
1) "message"	#消息
2) "cincoTest"	#哪个频道的消息
3) "hello,cinco"	#消息的具体内容
1) "message"
2) "cincoTest"
3) "how do you do,bro"

使用场景:

1、实时消息系统!

2、事实聊天!(频道当做聊天室,将信息回显给所有人即可!)

3、订阅,关注系统都是可以的! 稍微复杂的场景我们就会使用 消息中间件 MQ ()

Redis-集群
一主二从

修改配置文件

复制三个配置文件redis-79.conf、redis-80.conf、redis-80.conf

分别修改对应的信息

1、端口

2、pidfile 名字

3、log文件名字

4、dump.rdb名字

如redis-79.conf修改如下:

port 6379
pidfile /var/run/redis_6379.pid
logfile "6379.log"
dbfilename dump6379.rdb


启动三个redis服务器

redis-server cconfig/redis-79.conf
redis-server cconfig/redis-80.conf
redis-server cconfig/redis-81.conf
[root@Cinco bin]# ps -ef|grep redis			#查看redis进程信息
root      3626     1  0 21:30 ?        00:00:00 redis-server 127.0.0.1:6380
root      3632     1  0 21:30 ?        00:00:00 redis-server 127.0.0.1:6381
root      3638  3499  0 21:32 pts/0    00:00:00 grep --color=auto redis
root     31150     1  0 Jan07 ?        00:00:34 redis-server 127.0.0.1:6379

连接三个redis客户端

默认情况下,每台redis服务器都是主节点,一般情况下只用配置从机就行了

127.0.0.1:6379> info replication
# Replication
role:master			#角色:主节点
connected_slaves:0		#从节点数:0
master_failover_state:no-failover
master_replid:691ea95096367ee4f0603ca515e5e9e4280ae074
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

从机认老大,这里我是 一主二从

6379作为主节点,6380和6381作为从节点

127.0.0.1:6380> SLAVEOF 127.0.0.1 6379			#SLAEOF host port  找谁当自己的老大
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:9
master_sync_in_progress:0
slave_read_repl_offset:14
slave_repl_offset:14
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:76a6e38647df9e6624ccf8e08ae0e4fb2091fd92
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:14
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:14

配置好6380和6381后,再查看一下6379的信息

127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:2		#从机数
slave0:ip=127.0.0.1,port=6380,state=online,offset=308,lag=0		#从机1
slave1:ip=127.0.0.1,port=6381,state=online,offset=308,lag=0		#从机2
master_failover_state:no-failover
master_replid:76a6e38647df9e6624ccf8e08ae0e4fb2091fd92
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:308
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:308

tips:以上是通过命令设置从机的,这是一次性的,在配置文件中设置从机信息才会永久保存

主从复制,读写分离

主机写入的数据会同步到全部从机中,主机负责写,从机负责读,而且从机只能读不能写

127.0.0.1:6379> set sex man			#主机存入数据
OK

127.0.0.1:6380> get sex				#从机也拥有这份数据
"man"
127.0.0.1:6380> set set woman		#但是从机不能写入数据,只能读取数据!
(error) READONLY You can't write against a read only replica.

测试:

主机断开连接,从机是依旧连接到主机的,但是没有写的操作,这个时候,主机如果回来了,从机依旧可以直接获取到主机写的信息;

从机断开连接,从机再次连接时需要重新设置为从机才能获取到主机的数据。

复制原理

Slave 启动成功连接到 master 后会发送一个sync同步命令 Master 接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,并完成一次完全同步。

全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。 简单说就是拷贝全部数据

增量复制:Master 继续将新的所有收集到的修改命令依次传给slave,完成同步 但是只要是重新连接master,一次完全同步(全量复制)将被自动执行! 我们的数据一定可以在从机中看到!简单说就是只拷贝新增的数据

如果没有老大了,这个时候能不能选择一个老大出来呢? 手动!

如果主机断开了连接,我们可以使用 SLAVEOF no one 让自己变成主机!其他的节点就可以手动连接到最新的这个主节点(手动)!如果这个时候老大修复了,那也不存在从机了,只能重新连接!

哨兵模式

(自动选取老大)

主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工 干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑 哨兵模式。Redis从2.8开始正式提供了Sentinel(哨兵) 架构来解决这个问题。

哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独 立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。

image-20220108224624390

这里的哨兵有两个作用 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服 务器,修改配置文件,让它们切换主机。 然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。 各个哨兵之间还会进行监控,这样就形成了多哨兵模式。

image-20220108224633704

测试

目前的状态是 一主二从

1、配置哨兵配置文件 sentinel.conf

# sentinel monitor 被监控的名称 host port 1
sentinel monitor myredis 127.0.0.1 6379 1

2、启动哨兵!

# redis-sentinel cconfig/sentinel.conf 
[root@Cinco bin]# redis-sentinel cconfig/sentinel.conf 
4016:X 08 Jan 2022 22:49:25.213 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
4016:X 08 Jan 2022 22:49:25.213 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=4016, just started
4016:X 08 Jan 2022 22:49:25.213 # Configuration loaded
4016:X 08 Jan 2022 22:49:25.213 * monotonic clock: POSIX clock_gettime
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 6.2.6 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                  
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
 |    `-._   `._    /     _.-'    |     PID: 4016
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           https://redis.io       
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

4016:X 08 Jan 2022 22:49:25.214 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
4016:X 08 Jan 2022 22:49:25.216 # Sentinel ID is d9401c0c3f4aa2dea1bdfb005a619080f075ddb5
4016:X 08 Jan 2022 22:49:25.216 # +monitor master myredis 127.0.0.1 6379 quorum 1
4016:X 08 Jan 2022 22:49:25.217 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
4016:X 08 Jan 2022 22:49:25.219 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379

如果Master 节点断开了,这个时候就会从从机中随机选择一个服务器! (这里面有一个投票算法!)

4016:X 08 Jan 2022 22:50:57.475 # +sdown master myredis 127.0.0.1 6379
4016:X 08 Jan 2022 22:50:57.475 # +odown master myredis 127.0.0.1 6379 #quorum 1/1
4016:X 08 Jan 2022 22:50:57.475 # +new-epoch 1
4016:X 08 Jan 2022 22:50:57.475 # +try-failover master myredis 127.0.0.1 6379
4016:X 08 Jan 2022 22:50:57.477 # +vote-for-leader d9401c0c3f4aa2dea1bdfb005a619080f075ddb5 1
4016:X 08 Jan 2022 22:50:57.477 # +elected-leader master myredis 127.0.0.1 6379
4016:X 08 Jan 2022 22:50:57.477 # +failover-state-select-slave master myredis 127.0.0.1 6379
4016:X 08 Jan 2022 22:50:57.535 # +selected-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
4016:X 08 Jan 2022 22:50:57.535 * +failover-state-send-slaveof-noone slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
4016:X 08 Jan 2022 22:50:57.588 * +failover-state-wait-promotion slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
4016:X 08 Jan 2022 22:50:58.141 # +promoted-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
4016:X 08 Jan 2022 22:50:58.141 # +failover-state-reconf-slaves master myredis 127.0.0.1 6379
4016:X 08 Jan 2022 22:50:58.201 * +slave-reconf-sent slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
4016:X 08 Jan 2022 22:50:59.197 * +slave-reconf-inprog slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
4016:X 08 Jan 2022 22:50:59.197 * +slave-reconf-done slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
4016:X 08 Jan 2022 22:50:59.297 # +failover-end master myredis 127.0.0.1 6379
4016:X 08 Jan 2022 22:50:59.297 # +switch-master myredis 127.0.0.1 6379 127.0.0.1 6381
4016:X 08 Jan 2022 22:50:59.297 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6381
4016:X 08 Jan 2022 22:50:59.297 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6381

这里发现6380从机变成了主机,6381成为6380的从机

image-20220108225603370

如果此时原主机6379重新连接,哨兵发现后会将它变为6380的从机

哨兵模式的全部配置

# 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无法正常启动成功。
#通知脚本
# shell编程
# 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 # 一般都是由运维来配
置!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值