ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

中间件学习笔记-Redis入门

2022-02-27 16:33:28  阅读:213  来源: 互联网

标签:127.0 入门 0.1 中间件 Redis 6379 master integer


以下学习内容笔记全部来源于B站教学视频:【狂神说Java】Redis最新超详细版教程通俗易懂,非常感谢来自狂神老师的教学系列,附B站学习视频链接:https://www.bilibili.com/video/av840034966

1. Redis(缓存)

1.1 Nosql

1.1.1 概述

单机mysql时代:

90年代,一个基本的网站访问量不会太大,单个数据库足够!

更多的使用静态网页html

网站瓶颈:

  • 数据量太大,一个机器放不下
  • 数据的索引,一个机器的内存也放不下
  • 访问量太大,读写混合,性能问题

缓存时代:

memcached(缓存)+mysql+读写分离(垂直拆分)

网站百分之80情况都是在读,每次都要去数据库查询太麻烦,减轻数据库的压力,可以使用缓存来保证效率(Cache)-------解决读的问题

发展过程:优化Mysql底层结构和索引–>文件缓存(IO操作)—>Memcached 当时最热门的技术!

分库分表+水平拆分+mysql集群:

技术和业务发展同时,对技术要求也越来越高

本质: 数据库(读、写)

早些年 MyISAM:表锁,影响效率,高并发下出现严重的锁问题

早些年Innodb:行锁

慢慢地使用分库分表来解决写的压力!并没有多少公司使用

Mysql集群,很好满足了个别需求

最近的年代:

2010-2020 技术发展迅速(定位、音乐、热榜)

Mysql等关系型数据库就不够用了,数据量很多,变化很快

目前一个基本的互联网项目:

在这里插入图片描述

为什么要用Nosql

  • 用户的个人信息、地理位置、社交网络、用户自己产生的数据、用户日志等等爆发式增长!

这时候可以使用Nosql数据库处理以上的情况!

1.1.2 什么是NoSQL

NoSQL

NoSQL=not only sql

SQL:表格,行,列

泛指非关系型数据库,随着web2.0互联网的诞生,传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社区!

NoSQL在当今大数据时代发展迅速,REDIS发展是最快的,、

NoSQL特点

  • 方便扩展(数据之间没有关系,很好扩展,没有耦合性)

  • 大数据量下的高性能(redis一秒可以写8W次,可以读取11w次,NoSQL的缓存级别,是一种细粒度的缓存,性能会比较高!)

  • 多类型(不需要事先设计数据库!随取随用!如果是数据量十分大的表,很多人无法设计)

  • 传统RDBM和NoSQL:

    结构化组织

    SQL

    数据和关系都存在单独的表中

    数据操作语言,数据定义语言,

    严格的一致性

    基础的事务

​ 不仅仅是数据

​ 没有固定的查询语言

​ 键值对存储,列存储,文档存储,图形数据库(社交关系)

​ 最终一致性

​ CAP定理、 BASE (异地多活)初级架构师!

​ 基本的高性能、高可用、高可扩展

​ …

扩展:大数据的3v和3高

描述问题:海量、多样、实时

程序要求:高并发、高可用、高性能

1.1.3 NoSQL的分类

KV键值对

  • 新浪:redis
  • 美团:redis+Tair
  • 阿里、百度:redis+memcache

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

  • MongoDB(一般必须掌握)

    基于分布式文件存储的数据库,C++编写,处理大量的文档!

    是一个介于关系型数据库和非关系型数据库中间的产品,是非关系型数据库中功能最丰富的,最像关系型数据库的!

  • ConthDB

列存储数据库

  • HBase
  • 分布式文件系统

图形关系数据库

在这里插入图片描述

  • 不是放图片的,是放关系的:朋友圈社交网络、广告推荐!

  • Ne04j, infoGrid

NoSQL的对比

在这里插入图片描述

1.1.4 Redis

redis–Remote Dictionary Server

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

能干嘛?

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

  • 效率高、可以用于高速缓存

  • 发布订阅系统

  • 地图信息分析

  • 计时器、计数器(浏览量)

特性

  • 多样的数据类型
  • 持久化
  • 集群
  • 事务

学习中需要用到的东西

  • 官网
  • 中文网
  • 下载地址:通过官网(官方不建议用windows开发使用)

1.2 Redis的安装

1.2.1 windows安装

  • 下载安装包 https://github.com/microsoftarchive/redis/releases/tag/win-3.2.100

  • 解压到特定文件夹

在这里插入图片描述

  • 开启Redis:双击运行运行服务(秒级启动)

在这里插入图片描述

默认端口:6379

  • 使用Redis客户端连接

在这里插入图片描述

测试连接:

在这里插入图片描述

  • ping测试 返回PONG
  • 设置一个key值 然后get获取

windows下使用确实简单,但是推荐使用linux去开发使用

1.2.2 Linux安装

1.2.2.1 下载安装包

redis-6.0.6.tar.gz

在这里插入图片描述

在这里插入图片描述

1.2.2.2 解压安装包

并进入

tar -zxvf redis-6.0.6.tar.gz

在这里插入图片描述

配置文件:redis.conf

1.2.2.3 基础环境安装

  1. yum install gcc-c++

  2. gcc -v

在这里插入图片描述

  1. make(需要一点时间)

在这里插入图片描述

报错,原因分析:gcc版本过低,升级一下

# 升级gcc版本
$ yum -y install centos-release-scl
$ yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
$ scl enable devtoolset-9 bash
# 升级后查看下版本
$ gcc -v

在这里插入图片描述

重新执行make

在这里插入图片描述

  1. make install (也可以不用执行,确认一下安装)

在这里插入图片描述

所有东西已安装好了

1.2.2.4 确认安装路径

Redis默认安装路径:/usr/local/bin

在这里插入图片描述

1.2.2.5 复制配置文件

复制Redis配置文件到当前目录下

# 创建配置文件文件夹并复制配置文件
$ mkdir hconfig
$ cp /opt/redis-6.0.6/redis.conf hconfig

在这里插入图片描述

之后就使用这个配置文件进行启动

1.2.2.6 配置后台启动

Redis默认不是后台启动的,需要修改配置文件

# 修改daemonize为yes
$ vim redis.conf

在这里插入图片描述

1.2.2.7 启动Redis服务

启动Redis服务

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

redis-server hconfig/redis.conf

在这里插入图片描述

1.2.2.8 测试连接

测试连接

使用Redis客户端进行连接

# 连接
$ redis-cli -p 6379
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set name yunmx   # 设置一个key
OK
127.0.0.1:6379> get name		# 读取一个key
"yunmx"
127.0.0.1:6379> keys *			# 查询所有的key
1) "name"
127.0.0.1:6379> 

在这里插入图片描述

1.2.2.9 查看进程

查看Redis的进程是否开启

ps -ef |grep redis

在这里插入图片描述

1.2.2.10 关闭Redis服务

关闭redis服务

shutdown

127.0.0.1:6379> ping
PONG
127.0.0.1:6379> shutdown
not connected> exit

在这里插入图片描述

1.3 Redis性能测试工具

1.3.1 redis-benchmark

官方自带的性能测试工具—>

redis-benchmark

命令参数:

在这里插入图片描述

简单测试:

# 测试100个并发连接,每个并发20个请求
$ redis-benchmark -h localhost -p 6379 -c 100 -n 20

在这里插入图片描述

1.3.2 如何分析

查看分析:

====== PING_INLINE ======
  100000 requests completed in 1.46 seconds   	# 100000次ping请求在1.46s内完成
  100 parallel clients							# 100的并发量
  3 bytes payload								# 3字节数据写入
  keep alive: 1									# 保证一台服务器测试(单机性能)
  host configuration "save": 900 1 300 10 60 10000
  host configuration "appendonly": no
  multi-thread: no

0.00% <= 0.5 milliseconds						# 0.5毫秒内完成百分之0的请求
1.37% <= 0.6 milliseconds
11.42% <= 0.7 milliseconds
23.22% <= 0.8 milliseconds
35.29% <= 0.9 milliseconds
47.60% <= 1.0 milliseconds
60.01% <= 1.1 milliseconds
72.40% <= 1.2 milliseconds
84.82% <= 1.3 milliseconds
94.78% <= 1.4 milliseconds
97.65% <= 1.5 milliseconds
98.50% <= 1.6 milliseconds
98.95% <= 1.7 milliseconds
99.28% <= 1.8 milliseconds
99.51% <= 1.9 milliseconds
99.67% <= 2 milliseconds
99.99% <= 3 milliseconds
100.00% <= 3 milliseconds						# 3毫秒内完成100%的请求(100000)
68587.11 requests per second					# 平均每秒完成的请求数

1.4 Redis基础知识

1.4.1 默认数据库

Redis默认数据库数量有16个,redis.conf配置文件可查看,默认使用的数据库为0

在这里插入图片描述

在这里插入图片描述

1.4.2 切换数据库

# 使用select 命令进行切换
[root@yunmx bin]# redis-cli -p 6379
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> select 2
OK
127.0.0.1:6379[2]> select 16
(error) ERR DB index is out of range
127.0.0.1:6379[2]> select 15
OK
127.0.0.1:6379[15]> 

1.4.3 查看数据库大小

# DBSIZE 查看数据库大小
127.0.0.1:6379> set name hejie
OK
127.0.0.1:6379> DBSIZE
(integer) 1
127.0.0.1:6379> SET AGE NAN
OK
127.0.0.1:6379> DBSIZE
(integer) 2
127.0.0.1:6379>

1.4.4 查看所有的key

# 使用keys *
127.0.0.1:6379> keys *
1) "AGE"
2) "name"
127.0.0.1:6379>

1.4.5 清空数据库

# 清空当前数据库flushdb
127.0.0.1:6379> keys *
1) "AGE"
2) "name"
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379>
# 清空所有数据库flush
127.0.0.1:6379> select 0
OK
127.0.0.1:6379> set name hejie
OK
127.0.0.1:6379> set key1 hejie1
OK
127.0.0.1:6379> set key2 hejie2
OK
127.0.0.1:6379> keys *
1) "key1"
2) "key2"
3) "name"
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> set name yunmx
OK
127.0.0.1:6379[1]> set key1 yunmx1
OK
127.0.0.1:6379[1]> set key2 yunmx2
OK
127.0.0.1:6379[1]> keys *
1) "key1"
2) "key2"
3) "name"
127.0.0.1:6379[1]> flushall
OK
127.0.0.1:6379[1]> keys *
(empty array)
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379>

1.4.6 判断key是否存在

# 使用exists 判断
127.0.0.1:6379> set name hejie
OK
127.0.0.1:6379> set age 1
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> exists age     
(integer) 1							# 存在
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> exists key1  
(integer) 0							# 不存在
127.0.0.1:6379>

1.4.7 移动一个key

# move移动一个key
127.0.0.1:6379> set key1 hejie
OK
127.0.0.1:6379> set key2 hejie1
OK
127.0.0.1:6379> set key3 hejie2
OK
127.0.0.1:6379> move key1 1      	# 将key1移动给数据库1
(integer) 1
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
1) "key1"
127.0.0.1:6379[1]>

1.4.8 设置key的生效时间

# EXPIRE 设置一个key的过期时间,单位时间:s
# TTL 查看一个key的过期时间
127.0.0.1:6379> keys *
1) "key3"
2) "key2"
127.0.0.1:6379> get key3
"hejie2"
127.0.0.1:6379> EXPIRE key3 10     # 设置key3 10秒后过期
(integer) 1
127.0.0.1:6379> ttl key3
(integer) -2					   # 表示已经过期
127.0.0.1:6379> ttl key3
(integer) -2
127.0.0.1:6379> keys *
1) "key2"
127.0.0.1:6379>

1.4.9 Redis单线程

单线程为什么这么快?

C语言写的,官方表示每秒的QPS是100000+,基于内存操作,Redis的瓶颈和服务器的内存和网络带宽有关,既然可以用单线程实现,就用单线程喽。

误区:

高性能服务器一定是多线程的?

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

核心:Redis是将所有数据放在内存中的,所以说使用单线程去操作效率就是最高的,因为多线程会产生CPU上下文切换,消耗时间的操作,对于内存系统来说,没有上下文切换,效率就是最高的,多次读写都是在一个CPU上的

1.5 Redis数据类型

五大数据类型

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

在这里插入图片描述

使用场景:value除了是字符串还可以是我们的数字

  • 计数器
  • 统计多单位的数量
  • 粉丝数
  • 对象缓存存储

1.5.1 String(字符串)

### 常见用法
127.0.0.1:6379> set kye1 yunmx								# 设置一个key
OK
127.0.0.1:6379> get key1									# 获取一个key的值
(nil)
127.0.0.1:6379> keys *										# 查看当前库的所有key,输出key名
1) "kye1"
127.0.0.1:6379> EXISTS kye1									# 判断key是否存在
(integer) 1
127.0.0.1:6379> EXISTS key1
(integer) 0
127.0.0.1:6379> APPEND kye1 "helloworld"					# 向key追加内容,如果key不存在,就设置一个key
(integer) 15
127.0.0.1:6379> get kye1
"yunmxhelloworld"
127.0.0.1:6379> STRLEN kye1 								# 获取一个key的长度
(integer) 15
127.0.0.1:6379> 

### 自增长用法
127.0.0.1:6379> set count 0
OK
127.0.0.1:6379> get count
"0"
127.0.0.1:6379> incr count									# 自增1
(integer) 1
127.0.0.1:6379> get count
"1"
127.0.0.1:6379> incr count									
(integer) 2
127.0.0.1:6379> get count
"2"
127.0.0.1:6379> decr count									# 自减1
(integer) 1
127.0.0.1:6379> get count
"1"
127.0.0.1:6379> decr count
(integer) 0
127.0.0.1:6379> get count
"0"
127.0.0.1:6379> incrby count 5								# 自增;步长5
(integer) 5
127.0.0.1:6379> get count
"5"
127.0.0.1:6379> decrby count 5								# 自减;步长5
(integer) 0
127.0.0.1:6379> get count
"0"

### 获取字符串范围
127.0.0.1:6379> set key1 yunmxhejie							# 设置一个key
OK
127.0.0.1:6379> getrange key1 0 4							# 获取从标0到4范围的字符串
"yunmx"
127.0.0.1:6379> getrange key1 0 -1							# 获取全部的字符串
"yunmxhejie"
127.0.0.1:6379> 

### 替换
127.0.0.1:6379> set key1 yunmx123456						# 设置一个key
OK
127.0.0.1:6379> SETRANGE key1 0 hejie						# 从下标为0的位置开始替换
(integer) 11
127.0.0.1:6379> get key1
"hejie123456"
127.0.0.1:6379> 

### 设置过期时间
127.0.0.1:6379> setex key1 10 yunmx							# 设置一个key,过期时间为10秒
OK
127.0.0.1:6379> ttl key1									# 查看key的过期时间
(integer) 7
127.0.0.1:6379> ttl key1									# 返回-2表示已经过期
(integer) -2
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> 

### 不存在设置(分布式锁中会经常用到)
127.0.0.1:6379> setnx key2 redis							# setnx:不存在key值则创建
(integer) 1
127.0.0.1:6379> setnx key2 mysql							# setnx:存在key值则创建失败
(integer) 0
127.0.0.1:6379> keys *
1) "key2"
127.0.0.1:6379> 

### 批量设置和获取
127.0.0.1:6379> mset key1 1 key2 2 key3 3 key4 4			# 同时设置多个值
OK
127.0.0.1:6379> keys *
1) "key1"
2) "key4"
3) "key3"
4) "key2"
127.0.0.1:6379> mget key1 key2 key3 key4					# 同时获取多个值
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> msetnx key5 5 key4 4						# 原子性操作:要么一起成功,要么一起失败
(integer) 0
127.0.0.1:6379> get key5
(nil)
127.0.0.1:6379> 

### 对象
127.0.0.1:6379> set user:1 {name:yunmx,age:25}				# 普通设置对象:key加json字符串
OK
127.0.0.1:6379> get user:1
"{name:yunmx,age:25}"
127.0.0.1:6379> mset user:1:name yunmx user:1:age 25		# 巧妙设计对象;如此设计,是完全可以的
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "yunmx"
2) "25"

### 组合命令
127.0.0.1:6379> getset db mysql								# 如果不存在,则设置
(nil)
127.0.0.1:6379> get db
"mysql"
127.0.0.1:6379> getset db MongoDB							# 如果存在,返回当前值,再设置新的值
"mysql"
127.0.0.1:6379> get db
"MongoDB"
127.0.0.1:6379> 

1.5.2 List(列表)

基本的数据类型

在这里插入图片描述

基本的数据类型,列表

在redis里面,list可以完成栈、队列、阻塞队列!

用法:

  • 他实际是一个链表,before node after ,left right都可以插入值
  • 如果key不存在,创建新的链表
  • 如果key存在,新增内容
  • 如果移除了所有值,空链表,代表不存在
  • 两边插入或者改动值,效率最高;修改中间元素,相对来说,效率偏低一点

消息排队、消息队列(lpush Rpop) 栈(lpush lpop)

### 基础命令
127.0.0.1:6379> LPUSH key1 1								# 将一个值或者多个值添加到头部
(integer) 1
127.0.0.1:6379> LPUSH key1 2
(integer) 2
127.0.0.1:6379> LPUSH key1 3
(integer) 3
127.0.0.1:6379> LRANGE key1 0 -1							
1) "3"
2) "2"
3) "1"
127.0.0.1:6379> LRANGE key1 0 1
1) "3"
2) "2"
127.0.0.1:6379> rpush key1 5								# 将一个值添加到尾部
(integer) 4
127.0.0.1:6379> LRANGE key1 0 -1
1) "3"
2) "2"
3) "1"
4) "5"

### 移除元素
127.0.0.1:6379> LRANGE key1 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
127.0.0.1:6379> LPOP key1									# 移除列表的第一个元素
"5"
127.0.0.1:6379> rPOP key1									# 移除列表的最后一个元素
"1"
127.0.0.1:6379> LRANGE key1 0 -1
1) "4"
2) "3"
3) "2"

### 获取元素、列表长度
127.0.0.1:6379> LRANGE key1 0 -1
1) "4"
2) "3"
3) "2"
127.0.0.1:6379> LINDEX key1 0								# 获取列表中的第一个元素
"4"
127.0.0.1:6379> LINDEX key1 1
"3"
127.0.0.1:6379> LINDEX key1 2
"2"
127.0.0.1:6379> LINDEX key1 3
(nil)
127.0.0.1:6379> LLEN key1									# 获取列表长度
(integer) 3

### 移除指定的值
 127.0.0.1:6379> LRANGE key1 0 -1
1) "2"
2) "4"
3) "4"
4) "3"
5) "2"
127.0.0.1:6379> LREM key1 1 2								# 移除一个2
(integer) 1
127.0.0.1:6379> LRANGE key1 0 -1
1) "4"
2) "4"
3) "3"
4) "2"
127.0.0.1:6379> LREM key1 2 4								# 移除2个4
(integer) 2
127.0.0.1:6379> LRANGE key1 0 -1
1) "3"
2) "2"

### 截取
127.0.0.1:6379> LRANGE key1 0 -1
1) "15"
2) "14"
3) "13"
4) "12"
5) "11"
6) "10"
127.0.0.1:6379> LTRIM key1 0 3								# 通过下标截取指定的长度,list已经被改变了
OK
127.0.0.1:6379> LRANGE key1 0 -1
1) "15"
2) "14"
3) "13"
4) "12"

### 组合命令:rpoppush
127.0.0.1:6379> rpush list1 1 2 3 4 5 
(integer) 5
127.0.0.1:6379> LRANGE list1 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> rpoplpush list1 list2					# 移除列表中的最后一个元素,并加入到新的列表中
"5"
127.0.0.1:6379> LRANGE list1 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
127.0.0.1:6379> LRANGE list2 0 -1
1) "5"
127.0.0.1:6379> 

### 元素更新
127.0.0.1:6379> EXISTS list1						# 判断列表是否存在
(integer) 0
127.0.0.1:6379> lset list1 0 yunmx					# 不存在报错
(error) ERR no such key
127.0.0.1:6379> lpush lista1 1 2 3 4
(integer) 4
127.0.0.1:6379> LRANGE list1 0 0
(empty array)
127.0.0.1:6379> LRANGE list1 0 -1
(empty array)
127.0.0.1:6379> LRANGE lista1 0 0
1) "4"
127.0.0.1:6379> lset lista1 0 yunmx						# 存在修改当前下标的值
OK
127.0.0.1:6379> LRANGE lista1 0 0
1) "yunmx"

### 元素插入
127.0.0.1:6379> LRANGE lista1 0 -1
1) "yunmx"
2) "3"
3) "2"
4) "1"
127.0.0.1:6379> LINSERT lista1 before yunmx hejie		# 插入当前元素的前面
(integer) 5
127.0.0.1:6379> LINSERT lista1 after yunmx hejie1		# 插入当前元素的后面
(integer) 6
127.0.0.1:6379> LRANGE lista1 0 -1
1) "hejie"
2) "yunmx"
3) "hejie1"
4) "3"
5) "2"
6) "1"

1.5.3 Set(集合)

set中的值不能重复d ,是无序的

### 基础用法
127.0.0.1:6379> sadd set1 yunmx													# 往集合里面存值
(integer) 1
127.0.0.1:6379> sadd set1 hejie1
(integer) 1
127.0.0.1:6379> sadd set1 hejie01
(integer) 1
127.0.0.1:6379> sadd set1 hejie02
(integer) 1
127.0.0.1:6379> sadd set1 hejie02
(integer) 0
127.0.0.1:6379> SMEMBERS set1													# 查询集合里面所有的元素
1) "yunmx"
2) "hejie02"
3) "hejie01"
4) "hejie1"
127.0.0.1:6379> SISMEMBER set1 hejie											# 判断元素是否存在
(integer) 0
127.0.0.1:6379> SISMEMBER set1 hejie5
(integer) 0
127.0.0.1:6379> SISMEMBER set1 hejie1											# 存在返回1
(integer) 1

### 获取集合中的元素的个数
127.0.0.1:6379> SCARD set1
(integer) 4
127.0.0.1:6379> sadd set1 hejie09
(integer) 1
127.0.0.1:6379> SCARD set1
(integer) 5

### 移除set集合中的指定元素
127.0.0.1:6379> SREM set1 yunmx													# 移除set集合中的:yunmx
(integer) 1
127.0.0.1:6379> SCARD set1							
(integer) 4
127.0.0.1:6379> SMEMBERS set1												
1) "hejie01"
2) "hejie09"
3) "hejie1"
4) "hejie02"

### 抽随机用法(无序不重复)
127.0.0.1:6379> SRANDMEMBER set1												# 随机抽选出一个元素
"hejie02"
127.0.0.1:6379> SRANDMEMBER set1
"hejie09"
127.0.0.1:6379> SRANDMEMBER set1
"hejie02"
127.0.0.1:6379> SRANDMEMBER set1
"hejie09"
127.0.0.1:6379> SRANDMEMBER set1
"hejie01"
127.0.0.1:6379> SRANDMEMBER set1 2												# 随机抽选出指定的数量元素
1) "hejie1"
2) "hejie01"
127.0.0.1:6379> SRANDMEMBER set1 2
1) "hejie09"
2) "hejie02"
127.0.0.1:6379> SRANDMEMBER set1 2
1) "hejie1"
2) "hejie01"

### 随机移除元素
127.0.0.1:6379> SMEMBERS set1
1) "hejie01"
2) "hejie09"
3) "hejie1"
4) "hejie02"
127.0.0.1:6379> spop set1														# 随机移除一个元素
"hejie02"
127.0.0.1:6379> spop set1
"hejie1"
127.0.0.1:6379> SMEMBERS set1
1) "hejie01"
2) "hejie09"

### 指定的值移动另一个集合中
127.0.0.1:6379> SMEMBERS set1
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> SMEMBERS set2
1) "er"
2) "san"
3) "yi"
127.0.0.1:6379> smove set1 set2 1												# 将set1中的元素移动到set2,需要移动的值为set1中的1
(integer) 1
127.0.0.1:6379> SMEMBERS set1
1) "2"
2) "3"
127.0.0.1:6379> SMEMBERS set2
1) "1"
2) "er"
3) "san"
4) "yi"

### 共同关注:B站,微博(并集)
# 数字集合:差集、交集、并集
127.0.0.1:6379> sadd set1 1 2 3 4 5 6
(integer) 6
127.0.0.1:6379> sadd set2 4 5 6 7 8 9
(integer) 6
127.0.0.1:6379>
127.0.0.1:6379> SDIFF set1 set2												# 差集:set1中的元素不存在于set2
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> SINTER set1 set2											# 交集
1) "4"
2) "5"
3) "6"
127.0.0.1:6379> SDIFF set2 set1
1) "7"
2) "8"
3) "9"
127.0.0.1:6379> SUNION set1 set2										  	# 并集
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"
9) "9"

1.5.4 Hash(哈希)

Map集合,key-Map集合,本质和string没有太大区别,还是一个简单的key-vlaue

应用

  • 用户信息之类的
  • 经常变动的信息
  • 更适合存储对象,string更适合字符串存储
### 基础用法
127.0.0.1:6379> hset hash1 name yunmx										# 设置一个key-vlaue 
(integer) 1
127.0.0.1:6379> hget hash1 name												# 获取一个字段值
"yunmx"
127.0.0.1:6379> hmset hash1 name hejie age 28								# 设置多个
OK
127.0.0.1:6379> hgetall hash1												# 获取所有元素
1) "name"
2) "hejie"
3) "age"
4) "28"
127.0.0.1:6379> hget hash1 age												# 获取一个字段值
"28"

### 删除一个值
127.0.0.1:6379> HDEL hash1 name												# 删除指定的key字段
(integer) 1
127.0.0.1:6379> hgetall hash1
1) "age"
2) "28"

### 获取hash所有的长度(键值对)
127.0.0.1:6379> HLEN hash1													# 获取hash中的键值对数量
(integer) 1
127.0.0.1:6379> hset hash1 age 18
(integer) 0
127.0.0.1:6379> hset hash1 age1 19
(integer) 1
127.0.0.1:6379> hset hash1 name hejie
(integer) 1
127.0.0.1:6379> HLEN hash1
(integer) 3

### 判断hash中字段是否存在
127.0.0.1:6379> HGETALL hash1
1) "age"
2) "18"
3) "age1"
4) "19"
5) "name"
6) "hejie"
127.0.0.1:6379> HEXISTS hash1 age											# 判断哈希中是否存在age字段
(integer) 1
127.0.0.1:6379> HEXISTS hash1 yunmx
(integer) 0

### 只获取所有的字段或值
127.0.0.1:6379> hkeys hash1
1) "age"
2) "age1"
3) "name"
127.0.0.1:6379> HVALS hash1
1) "18"
2) "19"
3) "hejie"

### 指定自增
127.0.0.1:6379> HINCRBY hash1 age 1											# 自增1
(integer) 19
127.0.0.1:6379> HINCRBY hash1 age -1
(integer) 18
127.0.0.1:6379> hsetnx hash1 age 1											# 如果存在,不设置
(integer) 0
127.0.0.1:6379> hsetnx hash1 age2 1											# 不存在,则添加新的键值对
(integer) 1

1.5.5 Zset(有序集合)

在set的基础上增加了一个值,多了一个计数位

场景思路:

  • set能做的东西它都能做,存储班级成绩表、工资表排序
  • 普通消息1,重要消息2,带权重进行判断
  • 排行榜应用实现:TOP N
### 基础用法
127.0.0.1:6379> zadd myset 1 one											# 添加一个值
(integer) 1
127.0.0.1:6379> zadd myset 2 two
(integer) 1
127.0.0.1:6379> zadd myset 3 three
(integer) 1
127.0.0.1:6379> zadd myset 4 four 5 five									# 添加多个值
(integer) 2
127.0.0.1:6379> ZRANGE myset 0 -1											# 查看所有的值
1) "one"
2) "two"
3) "three"
4) "four"
5) "five"

### 排序如何实现
# 用法:从最小到最大
# ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
127.0.0.1:6379> zadd salary 12000 yunmx										# 添加4个用户
(integer) 1
127.0.0.1:6379> zadd salary 11000 hejie
(integer) 1
127.0.0.1:6379> zadd salary 10000 hejie01
(integer) 1
127.0.0.1:6379> zadd salary 9000 yunmx01
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf								# -inf +inf  负无穷到正无穷 从小到大
1) "yunmx01"
2) "hejie01"
3) "hejie"
4) "yunmx"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores					# withscores表示带一些其他参数说明
1) "yunmx01"
2) "9000"
3) "hejie01"
4) "10000"
5) "hejie"
6) "11000"
7) "yunmx"
8) "12000"

### 移除元素
127.0.0.1:6379> ZRANGE salary 0 -1											# 查看所有的元素
1) "yunmx01"
2) "hejie01"
3) "hejie"
4) "yunmx"
127.0.0.1:6379> zrem salary yunmx01											# 移除特定的元素
(integer) 1
127.0.0.1:6379> ZRANGE salary 0 -1
1) "hejie01"
2) "hejie"
3) "yunmx"

### 获取有序集合中的个数
127.0.0.1:6379> ZRANGE salary 0 -1
1) "hejie01"
2) "hejie"
3) "yunmx"
127.0.0.1:6379> ZCARD salary
(integer) 3

### 按区间计算
127.0.0.1:6379> ZRANGE salary 0 -1
1) "hejie01"
2) "hejie"
3) "yunmx"
127.0.0.1:6379> zcount salary 1000 9000										# 查看1000到9000之间的数量
(integer) 0
127.0.0.1:6379> zcount salary 1000 10000									# 查看1000到1-000之间的数量值
(integer) 1

1.5.6 geospatial(地理空间)

城市经纬度查询:http://www.jsons.cn/lngcode/

  • 朋友圈的定位
  • 附近的人
  • 打车距离计算

GEO实现的底层原理其实就是Zset!可以使用Zset来操作GEO!

只有6个命令

  • GEOADD
  • GEODIST
  • GEOHASH
  • GEOPOS
  • GEORADIUS
  • GEORADIUSBYMEMBER
### 添加位置
# geoadd key longitude(经度) latitude(维度) member [longitude latitude member ...]  可添加多个
# 参数:key 值(经度 维度 名称)
127.0.0.1:6379> geoadd china:city 104.065735 30.659462 chengdu
(integer) 1
127.0.0.1:6379> geoadd china:city 106.504962 29.533155 chongqing
(integer) 1

### 获取当前定位经纬度
127.0.0.1:6379> GEOPOS china:city chengdu									# 获取成都的经纬度
1) 1) "104.06573742628097534"
   2) "30.65946118872339099"
127.0.0.1:6379> GEOPOS china:city chongqing									# 获取重庆的经纬度
1) 1) "106.50495976209640503"
   2) "29.53315530684997015"

### 计算两个地方的距离
#单位:m/km/mi/ft
127.0.0.1:6379> geodist china:city chengdu chongqing m						# 计算出成都到重庆的直线距离  单位m 
"266056.2971"
127.0.0.1:6379> geodist china:city chengdu chongqing km						# 计算出成都到重庆的直线距离  单位km
"266.0563"

### 以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
# GEORADIUS:
127.0.0.1:6379> GEORADIUS china:city 103 30 1 km							# 以103 30这个点为中心,以1km为半径
(empty array)
127.0.0.1:6379> GEORADIUS china:city 103 30 2 km
(empty array)
127.0.0.1:6379> GEORADIUS china:city 103 30 20 km
(empty array)
127.0.0.1:6379> GEORADIUS china:city 103 30 200 km
1) "chengdu-qingyangqu"
2) "chengdu"
3) "chengdu-jinjiangqu"
127.0.0.1:6379> GEORADIUS china:city 103 30 20000 km
1) "chengdu-qingyangqu"
2) "chongqing"
3) "chengdu"
4) "chengdu-jinjiangqu"					
127.0.0.1:6379> GEORADIUS china:city 103 30 20000 km withcoord				# 附带显示经纬度	
1) 1) "chengdu-qingyangqu"
   2) 1) "104.06151026487350464"
      2) "30.67387107851423167"
2) 1) "chongqing"
   2) 1) "106.50495976209640503"
      2) "29.53315530684997015"
3) 1) "chengdu"
   2) 1) "104.06573742628097534"
      2) "30.65946118872339099"
4) 1) "chengdu-jinjiangqu"
   2) 1) "104.06573742628097534"
      2) "30.65946118872339099"

### 找出位于指定范围内的元素,中心点是由给定的位置元素决定
# GEORADIUSBYMEMBER
127.0.0.1:6379> GEORADIUSBYMEMBER china:city chengdu 10 km					# chengdu城市方圆10km的坐标
1) "chengdu"
2) "chengdu-jinjiangqu"
3) "chengdu-qingyangqu"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city chengdu 100 km
1) "chengdu"
2) "chengdu-jinjiangqu"
3) "chengdu-qingyangqu"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city chengdu 10 km
1) "chengdu"
2) "chengdu-jinjiangqu"
3) "chengdu-qingyangqu"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city chengdu 1000 km
1) "chengdu-qingyangqu"
2) "chongqing"
3) "chengdu"
4) "chengdu-jinjiangqu"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city chengdu 1000 km withcoord		# 附带坐标信息
1) 1) "chengdu-qingyangqu"
   2) 1) "104.06151026487350464"
      2) "30.67387107851423167"
2) 1) "chongqing"
   2) 1) "106.50495976209640503"
      2) "29.53315530684997015"
3) 1) "chengdu"
   2) 1) "104.06573742628097534"
      2) "30.65946118872339099"
4) 1) "chengdu-jinjiangqu"
   2) 1) "104.06573742628097534"
      2) "30.65946118872339099"

### Redis GEOHASH 命令 - 返回一个或多个位置元素的 Geohash 表示  不常用

1.5.7 HyperLogLog(基数统计)

基数:不重复的元素个数,可以接受误差

A(1 2 3 45 5 78)

B(3 4 6 7 8 9 0 9 78 )

基数统计的算法

  • 网页的UV(一个人访问一个网站,但是还是算做一个)

    传统的方式:set保存用户的ID,然后就可以统计set中的元素数量作为标准判断,如果保存大量的用户ID,就会比较麻烦,我们的目的是为了计数而不是保存ID

  • 占用内存是固定的,2^64不同的元素技术,只需要废12KB内存

0.81%的错误率!统计UV时候,可以忽略不计

127.0.0.1:6379> PFADD key1 a 1 2 3 4 b c 6 7 9								# 设置一个HyperLOGLOG		
(integer) 1
127.0.0.1:6379> PFCOUNT key1												# 查看里面不重复的数量个数			
(integer) 10
127.0.0.1:6379> PFADD key2 a a  1 2 3 4 b c 6 7 9 10 10 101 10 10 8 6
(integer) 1
127.0.0.1:6379> PFCOUNT key2
(integer) 13
127.0.0.1:6379> PFMERGE key3 key1 key2										# 合并两个key的元素数量
OK
127.0.0.1:6379> PFCOUNT key3												# 查看不重复的元素数量
(integer) 13

1.5.8 Bitmap(位图)

位存储0和1

2个状态的都可以使用此数据类型

  • 活跃、不活跃
  • 感染人数、未感染人数
127.0.0.1:6379> SETBIT key 0 1												# 使用Bitmaps记录一周的打卡
(integer) 0
127.0.0.1:6379> SETBIT key 1 0
(integer) 0
127.0.0.1:6379> SETBIT key 2 0
(integer) 0
127.0.0.1:6379> SETBIT key 3 1
(integer) 0
## 查看某一天是否有打卡
127.0.0.1:6379> getbit key 3
(integer) 1
127.0.0.1:6379> getbit key 2
(integer) 0

### 统计操作
# 统计打开天数
127.0.0.1:6379> BITCOUNT key
(integer) 2

1.6 Redis事务

事务可以一次执行多个命令, 并且带有以下两个重要的保证:

  • 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
  • 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行

1.6.1 基本的事务操作

Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,事务执行中,按照顺序执行

Redis单条命令保证原子性,但是事务不保证原子性!

  • 一次性
  • 顺序性
  • 排他性
  • 没有隔离级别的概念,没有幻读、脏读…
---入队---
command1
command2
command2
---执行---

三个阶段:

  1. 开启事务(MULTI)
  2. 命令入队(commands)
  3. 执行事务(EXEC)
### 正常执行
127.0.0.1:6379> MULTI								# 开启事务
OK
127.0.0.1:6379> set key1 1							# 命令入队
QUEUED
127.0.0.1:6379> set key2 2
QUEUED
127.0.0.1:6379> set key3 3
QUEUED
127.0.0.1:6379> get key1
QUEUED
127.0.0.1:6379> get key2
QUEUED
127.0.0.1:6379> exec								# 事务执行,按照顺序执行
1) OK
2) OK
3) OK
4) "1"
5) "2"

### 放弃事务 
127.0.0.1:6379> MULTI								# 开启事务
OK
127.0.0.1:6379> set key1 1
QUEUED
127.0.0.1:6379> set key2 2
QUEUED
127.0.0.1:6379> set key3 3
QUEUED
127.0.0.1:6379> DISCARD								# 取消事务
OK
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI
127.0.0.1:6379> get key1							# 放弃事务取不到相关的值
(nil)

### 事务错误一:命令有错,事务中所有的命令都不会被执行!
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set key1 1
QUEUED
127.0.0.1:6379> set key2 2
QUEUED
127.0.0.1:6379> getset key3											# 错误的命令,直接报错
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set key3 3
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.	# 执行事务报错
127.0.0.1:6379> get key1											# 结果是所有的命令都不会执行
(nil)

### 事务错误二:运行异常,如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set key1 "yunmx"
QUEUED
127.0.0.1:6379> incr key1
QUEUED
127.0.0.1:6379> set key2 2
QUEUED
127.0.0.1:6379> set key3 3
QUEUED
127.0.0.1:6379> get key3
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range				# 代码结构没问题,抛出异常
3) OK
4) OK
5) "3"
127.0.0.1:6379> get key2
"2"

1.6.2 乐观锁

监控:watch

悲观锁:很悲观;认为任何时候都会出现问题,无论做什么都加锁,影响性能

乐观锁:很乐观,任务任何时候都不会出现问题,所以不会上锁,更新数据的时候去判断一下,在此期间,是否有人修改过这个数据,mysql用的version字段!

### Redis的测试监视
127.0.0.1:6379> set mymoney 1000
OK
127.0.0.1:6379> set myout 0
OK
127.0.0.1:6379> WATCH mymoney										# 监视
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRby mymoney 20
QUEUED
127.0.0.1:6379> INCRBY myout 20
QUEUED
127.0.0.1:6379> exec												# 事务正常结束,数据期间没有发生变动,正常执行了
1) (integer) 980
2) (integer) 20
127.0.0.1:6379>

### 使用watch当乐观锁,测试多线程修改值后事务提交失败
# 第一个进程开启事务
127.0.0.1:6379>  watch mymoney
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY mymoney 10
QUEUED
127.0.0.1:6379> INCRBY myout 10
QUEUED
127.0.0.1:6379> exec												# 未提交前执行第二个事务进行数据的修改
(nil)																# 事务提交后返回修改失败,
127.0.0.1:6379>
# 第二个进程修改我们的值
127.0.0.1:6379> get mymoney
"980"
127.0.0.1:6379> set mymoney 20
OK
127.0.0.1:6379> get mymoney
"20"
127.0.0.1:6379> set mymoney 1000
OK
127.0.0.1:6379> get mymoney
"1000"

### 可以使用unwatch解锁,再次使用watch获取最新的值,再去执行事务

1.7 Redis配置文件详解

1.7.1 redis.conf

启动时候就是通过配置文件来启动的

配置文件对大小写不敏感

在这里插入图片描述

包含其他配置文件

在这里插入图片描述

网络

在这里插入图片描述

bind 127.0.0.1		# 绑定的ip
protected-mode yes	# 保护模式
port 6379			# 端口设置

GENERAL通用配置

在这里插入图片描述

daemonize yes		# 已守护进程方式运行,默认是no,我们需要自己开启
pidfile /var/run/redis_6379.pid	# 如果以后台的方式运行,我们就需要指定一个pid文件
# 日志(4个级别)
# Specify the server verbosity level.
# This can be one of:
# 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)
loglevel notice	#
logfile ""	#日志的文件位置名
databases 16	# 默认的数据库数量,16个
always-show-logo yes	# 是否总是显示logo 可以不显示,默认显示

快照SNAPSHOTTING

在这里插入图片描述

# 规则
save 900 1		# 如果900秒内,至少有一个key进行修改,我们就进行持久化操作
save 300 10		
save 60 10000
# 我们可以设置自己的一个规则
stop-writes-on-bgsave-error yes	# 持久化出现错误的时候,是否继续进行工作,默认是yes
rdbcompression yes				# 是否压缩rdb文件,压缩就会消耗CPU资源
rdbchecksum yes					# 是否校验rdb文件,出错会进行修复
dir ./							# rdb文件保存的目录

快照

持久化,在规定的时间内,执行了多少次操作,则会持久化到文件,.rdb .aof,如果没有持久化,那么数据就会断电及失

主从复制 REPLICATION

在这里插入图片描述

安全SECURITY

在这里插入图片描述

# requirepass foobared   默认没有密码
# 可以通过修改配置文件来设置密码
# 比如:
requirepass 123456

# 验证
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>
# 也可以通过命令进行密码设置
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "123456"
127.0.0.1:6379> config set requirepass "110"
OK

127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 110
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>

客户端CLIENTS

在这里插入图片描述

# 客户端的一些限制
maxclients 10000   # 设置能连接上redis的客户端数量限制
maxmemory <bytes>	# redis配置的最大内存容量
maxmemory-policy noeviction	# 内存到达上限后的处理策略
	# 移除一些过期的key
	# 报错
	# ...
1、volatile-lru:只对设置了过期时间的key进行LRU(默认值) 
2、allkeys-lru : 删除lru算法的key   
3、volatile-random:随机删除即将过期key   
4、allkeys-random:随机删除   
5、volatile-ttl : 删除即将过期的   
6、noeviction : 永不过期,返回错误

APPEND ONLY MODE模式 aof配置

在这里插入图片描述

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

# appendfsync always			# 每次修改值都会写入,速度比较慢
appendfsync everysec			# 每秒执行一次sync,可能会丢失1s的数据!
# appendfsync no				# 不同步,这个时候操作系统自己同步数据,速度最快!

1.8 Redis持久化

Redis是内存数据库,如果不将内存中的数据库状态保存到硬盘中,那么一旦服务器进程退出,服务器中的数据状态也会消失,所以Redis提供了持久化功能

1.8.1 RDB(Redis DataBase)

RDB

在指定的时间间隔内,将内存中的数据集体快照写入磁盘中,也就是进行Snapshot快照,它恢复时是将快照文件直接读到内存里。

操作

单独创建(fork)一个子进程来进行持久化,会将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程不进行任何IO操作。这就确保了极高的性能,如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高校。RDB的缺点是嘴鸥一次持久化后的数据可能丢失。我们默认的就是RDB,一般情况下不需要修改此配置!

rdb保存的文件是dump.rdb

在这里插入图片描述

在这里插入图片描述

# 触发机制
1.save的规则满足的情况下
2.执行了FLUSHALL命令
3.退出redis也会触发

如何恢复rdb文件

  1. 只需要将rdb文件放在redis启动目录下即可,启动的时候会自动检查dump.rdb恢复其中的数据
# 查看存放的位置
127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/bin"

优缺点

优点

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

缺点

  1. 需要一定的时间间隔进行操作!如果redis意外宕机,最后一次修改的数据就没有了
  2. fork进程的时候,会占用一定的内存空间

1.8.2 AOF(Append Only File)

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

AOF

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

aof保存的文件是appendonly.aof文件

在这里插入图片描述

# 破坏aof文件后,尝试连接
[root@yunmx bin]# redis-cli
Could not connect to Redis at 127.0.0.1:6379: Connection refused
# 使用检测文件自动修复这个AOF文件:redis-check-aof
[root@yunmx bin]# redis-check-aof --fix appendonly.aof
0x              62: Expected \r\n, got: 6173
AOF analyzed: size=122, ok_up_to=23, diff=99
This will shrink the AOF from 122 bytes, with 99 bytes, to 23 bytes
Continue? [y/N]: y
Successfully truncated AOF

优缺点

优点

  1. 每次修改都同步,文件完整性更好,
  2. 每秒同步一次,这样会丢失一秒的数据
  3. 从不同步,效率最高的!

缺点

  1. 相对于数据文件来说,aof远远大于rdb,修复的速度比rdb慢
  2. aof运行效率要比rdb慢,会进行IO操作

扩展:

  • RBD
    RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储。

  • AOF
    AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以Redis协议追加保存每次写的操作到文件末尾。Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。

  • 只做缓存
    如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式.。

  • 同时开启两种持久化方式

    • 在这种情况下,当Redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整
    • RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。
  • 性能建议

    • 因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。
    • 如果Enalbe AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。
    • 如果不Enable AOF ,仅靠Master-Slave Replication 实现高可用性也可以。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。新浪微博就选用了这种架构。

1.9 Redis发布订阅

1.9.1 Redis发布订阅

订阅、发布消息图:

在这里插入图片描述

Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息

Redis客户端可以订阅任意数量的频道

第一个是:消息发送者 第二个是:频道 第三个是:消息订阅者

下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:

在这里插入图片描述

当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

在这里插入图片描述

Redis 发布订阅命令
下表列出了 redis 发布订阅常用命令:

  • PSUBSCRIBE pattern [pattern …]
    订阅一个或多个符合给定模式的频道。
  • PUBSUB subcommand [argument [argument …]]
    查看订阅与发布系统状态。
  • PUBLISH channel message
    将信息发送到指定的频道。
  • PUNSUBSCRIBE [pattern [pattern …]]
    退订所有给定模式的频道。
  • SUBSCRIBE channel [channel …]
    订阅给定的一个或多个频道的信息。
  • UNSUBSCRIBE [channel [channel …]]
    指退订给定的频道。
# 测试
# 订阅一个频道
127.0.0.1:6379> SUBSCRIBE yunmx										# 订阅一个频道到yunmx
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "yunmx"
3) (integer) 1

# 新窗口发送一个消息:发布者
[root@yunmx ~]# redis-cli
127.0.0.1:6379> PUBLISH yunmx test									# 发布者发布消息到指定名称的频道
(integer) 1

# 接收到的消息
127.0.0.1:6379> SUBSCRIBE yunmx		
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "yunmx"
3) (integer) 1
1) "message"														# 接收的消息
2) "yunmx"															# 哪个频道
3) "test"															# 消息内容

Redis发布订阅的原理

使用C实现的,通过分析Redis源码里面的pubsub.c文件,了解发布和订阅机制的底层实现逻辑,加深对redis的理解。(俺也不懂C,俺也不是开发,咋整)

通过PUBLISH/SUBSCRIBE/PSUBSCRIBE等命令实现发布订阅功能

  • SUBSCRIBE:通过此命令订阅频道以后,redis-server里维护一个字典,字典的键就是一个个channel(频道),而字典的值则是一个链表,链表中保存了所有订阅这个频道的客户端。-----就是将客户端添加到给定channel的订阅链表中
  • PUBLISH:向订阅者发送消息,redis-server会使用给定的频道作为键,在它所维护的channel字典中查询了记录了订阅这个频道所有的客户端连接,遍历整个链表,将消息发送给所有订阅者
  • PUB、SUB:就是发布、订阅,在redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值进行了消息发布以后,所有订阅它的客户端都会收到相应的消息,这一功能明显的用法就是用作实时消息系统,比如普通的即时聊天、群聊等功能

1.9.2 使用场景

  • 实时消息系统
  • 实时聊天------频道当作聊天室,将信息回显给所有人即可
  • 订阅,公众号订阅等
  • 稍微复杂的场景我们会使用消息中间件(MQ)

2.0 Redis主从复制

2.0.1 概念

主从复制

是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。master以写为主,slave以读为主。

默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

主从复制的作用

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

一般来说,要将redis运用于生产项目中,只使用一台redis是万万不能的

  • 从结构上,单个redis服务器会发生单点故障,且单机处理所有的请求会导致负载过大
  • 从容量上,单机内存容量有限,一般来说,单台redis最大使用内存不应该超过20G

在这里插入图片描述

2.0.2 配置主从

主从复制,读写分离!80%的情况都是在进行读操作!减缓服务器的压力,架构中经常使用!一主二 从!

测试场景

环境配置

只配置从库,不配置主库!

# 启动一个redis,查看信息
127.0.0.1:6379> info replication
# Replication
role:master														# 角色
connected_slaves:0												# 从机数量
master_replid:fa0e795a3e369ed7e46a40ee8818a51255ab6df3
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

复制一个配置文件,修改一下端口号、日志保存的文件名、rdb文件名、pid文件 ,在启动2个redis

[root@yunmx bin]# redis-server redis-conf/redis.conf1			# 6380
6038:C 12 Dec 2021 13:06:38.166 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
6038:C 12 Dec 2021 13:06:38.166 # Redis version=6.0.6, bits=64, commit=00000000, modified=0, pid=6038, just started
6038:C 12 Dec 2021 13:06:38.166 # Configuration loaded
[root@yunmx bin]# redis-server redis-conf/redis.conf2			# 6381
6045:C 12 Dec 2021 13:06:39.640 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
6045:C 12 Dec 2021 13:06:39.641 # Redis version=6.0.6, bits=64, commit=00000000, modified=0, pid=6045, just started
6045:C 12 Dec 2021 13:06:39.641 # Configuration loaded

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

未配置主从之前,三个节点都是主节点

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

认老大

一主(79)二从(80,81)

# 6380配置
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379							# 认本机的6379端口服务的redis做老大
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:8
master_sync_in_progress:0
slave_repl_offset:0
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:55c3146a93d180ad5aa89e4d64ff894a451e77e5
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:0
# 查看主机的信息
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=168,lag=0			# 从机的信息
slave1:ip=127.0.0.1,port=6381,state=online,offset=168,lag=0
master_replid:55c3146a93d180ad5aa89e4d64ff894a451e77e5
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:168
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:168

真实的主从配置应该在配置文件中配置,这样的话是永久的,上述使用的是命令,只是暂时的

在这里插入图片描述

replicaof <masterip> <masterport>   # 主机的IP地址和端口号
masterauth <master-password>		# 如果设定有密码,配置密码即可

2.0.3 特性验证

主机可以设置值,从机不能写,主机中所有信息和数据,从机都会保存

# 主机设置一个key
127.0.0.1:6379> set key1 yunmx
OK
127.0.0.1:6379>
# 从机中也会有,从机无法设置key
127.0.0.1:6380> keys *
1) "key1"
127.0.0.1:6380> get key1
"yunmx"
127.0.0.1:6380> set key2 yunmx2
(error) READONLY You can't write against a read only replica.
127.0.0.1:6380>

老大宕机后,从机还是从机,只是会显示主机状态不正常;主机恢复,从机依旧可以直接获取到主机写入的信息,保证了一定的高可用性

如果使用的是我们命令行配置的主从,如果从机宕机后,从机就会脱离主从了,需要再次命令行配置从机,变成从机以后,就能获取到key的值了

# 停掉6379主机服务,查看从机集群状态
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1065
master_link_down_since_seconds:10
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:55c3146a93d180ad5aa89e4d64ff894a451e77e5
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1065
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1065

# 恢复6379主机服务,验证是否还会同步数据
[root@yunmx ~]# redis-cli -p 6379
127.0.0.1:6379> set key2 yunmx2													# 恢复主节点,设置一个key
OK
127.0.0.1:6379>
# 从节点读取主机恢复后设置的key
127.0.0.1:6380> get key2														# 能够正常读取
"yunmx2"

# 停止从机服务,主机设定一个key,在恢复从机,进行验证
127.0.0.1:6379> set key3 yunmx3
OK

127.0.0.1:6380> get key3														# 从机无法获得key3的数据
(nil)

127.0.0.1:6381> get key3
"yunmx3"

2.0.4 复制原理

Slave启动成功连接到master后会发送一个sync同步命令

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

全量复制:slave服务将接收到数据库文件数据后,将其存盘并加载到内存中

增量复制:Mster继续将新的所有收集到的修改命令依次传给slave,完成同步

但是只要重新连接master,一次完全同步将被自动执行!我们的数据一定可以在从机中看到!

2.0.5 层层链路

上一个M链接下一个S!

可以完成主从复制!

在这里插入图片描述

如果没有79,这个时候能不能选择一个老大出来呢,这个时候需要手动去配置!

谋朝篡位:slaveof no one 使自己变成主机!如果老大回来了,也是需要手动配置

2.1 Redis哨兵模式

自动版选老大的模式

2.1.1 概述

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

  • 能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库

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

2.1.2 基本架构

在这里插入图片描述

哨兵模式的作用:

在这里插入图片描述

  • 通过发送命令,让Redis服务器返回监控运行状态,包括主服务和从服务
  • 当哨兵检测到master宕机后,会自动将slave切换成master,然后通过发布订阅模式通过其他的从服务器,修改配置文件,让他们切换主机

一个哨兵进程对redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控,各个哨兵之间还会进行监控,这样就形成了多哨兵模式

在这里插入图片描述

使用哨兵模式,至少都会启动6个进程

假设主服务宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务不可用,这个现场叫主观下线。当后面的哨兵也检测到主服务不可用,并且数量达到一定值后,那么哨兵就会进行一次投票,投票的结果由一个哨兵发起,进行failover故障转移操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务实现切换主机,这个过程被称为客观下线

2.1.3 场景测试

我们目前测试的架构是一主二从

配置哨兵模式的配置文件

# 新建配置文件并编辑以下内容
sentinel monitor myredis 127.0.0.1 6379 1# 语法:sentinel monitor 被监控的名称 host port 1(1代表主机宕机后,从机投票让谁来接替成为主机,)

启动哨兵

[root@yunmx bin]# redis-sentinel redis-conf/sentinel.conf				# 启动一个哨兵
12680:X 12 Dec 2021 15:13:42.570 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
12680:X 12 Dec 2021 15:13:42.570 # Redis version=6.0.6, bits=64, commit=00000000, modified=0, pid=12680, just started
12680:X 12 Dec 2021 15:13:42.570 # Configuration loaded
                _._
           _.-``__ ''-._
      _.-``    `.  `_.  ''-._           Redis 6.0.6 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26379
 |    `-._   `._    /     _.-'    |     PID: 12680
  `-._    `-._  `-./  _.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |           http://redis.io
  `-._    `-._`-.__.-'_.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |
  `-._    `-._`-.__.-'_.-'    _.-'
      `-._    `-.__.-'    _.-'
          `-._        _.-'
              `-.__.-'

12680:X 12 Dec 2021 15:13:42.571 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
12680:X 12 Dec 2021 15:13:42.575 # Sentinel ID is ea7ccf0119a4cf2873cf3bb108da5c7af86d36bd
12680:X 12 Dec 2021 15:13:42.575 # +monitor master myredis 127.0.0.1 6379 quorum 1
12680:X 12 Dec 2021 15:13:42.575 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
12680:X 12 Dec 2021 15:13:42.580 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379

手动宕机测试

# 关掉主机
127.0.0.1:6379> SHUTDOWN
not connected>
# 哨兵监控的一些信息
12680:X 12 Dec 2021 15:16:49.174 # +failover-state-select-slave master myredis 127.0.0.1 6379
12680:X 12 Dec 2021 15:16:49.241 # +selected-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
12680:X 12 Dec 2021 15:16:49.241 * +failover-state-send-slaveof-noone slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
12680:X 12 Dec 2021 15:16:49.324 * +failover-state-wait-promotion slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
12680:X 12 Dec 2021 15:16:50.181 # +promoted-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
12680:X 12 Dec 2021 15:16:50.181 # +failover-state-reconf-slaves master myredis 127.0.0.1 6379
12680:X 12 Dec 2021 15:16:50.237 * +slave-reconf-sent slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
12680:X 12 Dec 2021 15:16:51.182 * +slave-reconf-inprog slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
12680:X 12 Dec 2021 15:16:51.182 * +slave-reconf-done slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
12680:X 12 Dec 2021 15:16:51.234 # +failover-end master myredis 127.0.0.1 6379
12680:X 12 Dec 2021 15:16:51.234 # +switch-master myredis 127.0.0.1 6379 127.0.0.1 6381		# 哨兵显示主机自动切换到了6381
12680:X 12 Dec 2021 15:16:51.234 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6381
12680:X 12 Dec 2021 15:16:51.234 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6381
12680:X 12 Dec 2021 15:17:21.244 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6381
# 检查6381主机的信息
127.0.0.1:6381> info replication
# Replication
role:master								# 变成了master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=21944,lag=0
master_replid:a4719b795f6c088ed1a11408a2bc52cc48ece215
master_replid2:367d9493cb151b433bd535ad9e49603d1fa35013
master_repl_offset:21944
second_repl_offset:11812
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:21944

如果master宕机以后,这个时候就会从从机中随机选择一个服务器(有一个自己的投票算法)作为主机

主机恢复

# 重新开启之前宕机的主机。观察哨兵的反应
12680:X 12 Dec 2021 15:23:41.461 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6381
# 查看先前主机的信息
127.0.0.1:6379> info replication
# Replication
role:slave								# 变成从机
master_host:127.0.0.1
master_port:6381
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:41645
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:a4719b795f6c088ed1a11408a2bc52cc48ece215
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:41645
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:39205
repl_backlog_histlen:2441
# 只能归并到新的主机下,当作从机,这就是哨兵模式的规则!

优缺点

  • 哨兵集群,基于主从复制模式,所有的主从配置优点它多有

  • 主从可以切换,故障可以转移,系统的可用性会更好

  • 就是主从模式的升级,手动到自动,更加健壮!

  • 不好在线扩容,集群容量一旦达到上线,在线扩容就十分麻烦!

  • 实现哨兵模式的配置其实很麻烦的,里面有很多选择!

哨兵模式的全部配置

<后续进行详细学习>

# Example sentinel.conf
 
# port <sentinel-port>
# The port that this sentinel instance will run on
# sentinel实例运行的端口
port 26379												# 哨兵进程运行的端口号
 
# sentinel announce-ip <ip>
# sentinel announce-port <port>
#
# The above two configuration directives are useful in environments where,
# because of NAT, Sentinel is reachable from outside via a non-local address.
#
# When announce-ip is provided, the Sentinel will claim the specified IP address
# in HELLO messages used to gossip its presence, instead of auto-detecting the
# local address as it usually does.
#
# Similarly when announce-port is provided and is valid and non-zero, Sentinel
# will announce the specified TCP port.
#
# The two options don't need to be used together, if only announce-ip is
# provided, the Sentinel will announce the specified IP and the server port
# as specified by the "port" option. If only announce-port is provided, the
# Sentinel will announce the auto-detected local IP and the specified port.
#
# Example:
#
# sentinel announce-ip 1.2.3.4
 
# dir <working-directory>
# Every long running process should have a well-defined working directory.
# For Redis Sentinel to chdir to /tmp at startup is the simplest thing
# for the process to don't interferer with administrative tasks such as
# unmounting filesystems.
dir /tmp
 
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
# master-name : master Redis Server名称
# ip : master Redis Server的IP地址
# redis-port : master Redis Server的端口号
# quorum : 主实例判断为失效至少需要 quorum 个 Sentinel 进程的同意,只要同意 Sentinel 的数量不达标,自动failover就不会执行
#
# Tells Sentinel to monitor this master, and to consider it in O_DOWN
# (Objectively Down) state only if at least <quorum> sentinels agree.
#
# Note that whatever is the ODOWN quorum, a Sentinel will require to
# be elected by the majority of the known Sentinels in order to
# start a failover, so no failover can be performed in minority.
#
# Slaves are auto-discovered, so you don't need to specify slaves in
# any way. Sentinel itself will rewrite this configuration file adding
# the slaves using additional configuration options.
# Also note that the configuration file is rewritten when a
# slave is promoted to master.
#
# Note: master name should not include special characters or spaces.
# The valid charset is A-z 0-9 and the three characters ".-_".
#
sentinel monitor mymaster 127.0.0.1 6379 2
 
# sentinel auth-pass <master-name> <password>
#
# Set the password to use to authenticate with the master and slaves.
# Useful if there is a password set in the Redis instances to monitor.
#
# Note that the master password is also used for slaves, so it is not
# possible to set a different password in masters and slaves instances
# if you want to be able to monitor these instances with Sentinel.
#
# However you can have Redis instances without the authentication enabled
# mixed with Redis instances requiring the authentication (as long as the
# password set is the same for all the instances requiring the password) as
# the AUTH command will have no effect in Redis instances with authentication
# switched off.
#
# Example:
#
# sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
 
# sentinel down-after-milliseconds <master-name> <milliseconds>
#
# Number of milliseconds the master (or any attached slave or sentinel) should
# be unreachable (as in, not acceptable reply to PING, continuously, for the
# specified period) in order to consider it in S_DOWN state (Subjectively
# Down).
# 选项指定了 Sentinel 认为Redis实例已经失效所需的毫秒数。当实例超过该时间没有返回PING,或者直接返回错误, 那么 Sentinel 将这个实例标记为主观下线(subjectively down,简称 SDOWN )
#
# Default is 30 seconds.
sentinel down-after-milliseconds mymaster 30000
 
# sentinel parallel-syncs <master-name> <numslaves>
#
# How many slaves we can reconfigure to point to the new slave simultaneously
# during the failover. Use a low number if you use the slaves to serve query
# to avoid that all the slaves will be unreachable at about the same
# time while performing the synchronization with the master.
# 选项指定了在执行故障转移时, 最多可以有多少个从Redis实例在同步新的主实例, 在从Redis实例较多的情况下这个数字越小,同步的时间越长,完成故障转移所需的时间就越长。
sentinel parallel-syncs mymaster 1
 
# sentinel failover-timeout <master-name> <milliseconds>
#
# Specifies the failover timeout in milliseconds. It is used in many ways:
#
# - The time needed to re-start a failover after a previous failover was
# already tried against the same master by a given Sentinel, is two
# times the failover timeout.
#
# - The time needed for a slave replicating to a wrong master according
# to a Sentinel current configuration, to be forced to replicate
# with the right master, is exactly the failover timeout (counting since
# the moment a Sentinel detected the misconfiguration).
#
# - The time needed to cancel a failover that is already in progress but
# did not produced any configuration change (SLAVEOF NO ONE yet not
# acknowledged by the promoted slave).
#
# - The maximum time a failover in progress waits for all the slaves to be
# reconfigured as slaves of the new master. However even after this time
# the slaves will be reconfigured by the Sentinels anyway, but not with
# the exact parallel-syncs progression as specified.
# 如果在该时间(ms)内未能完成failover操作,则认为该failover失败
#
# Default is 3 minutes.
sentinel failover-timeout mymaster 180000
 
# SCRIPTS EXECUTION
#
# sentinel notification-script and sentinel reconfig-script are used in order
# to configure scripts that are called to notify the system administrator
# or to reconfigure clients after a failover. The scripts are executed
# with the following rules for error handling:
#
# If script exits with "1" the execution is retried later (up to a maximum
# number of times currently set to 10).
#
# If script exits with "2" (or an higher value) the script execution is
# not retried.
#
# If script terminates because it receives a signal the behavior is the same
# as exit code 1.
#
# A script has a maximum running time of 60 seconds. After this limit is
# reached the script is terminated with a SIGKILL and the execution retried.
 
# NOTIFICATION SCRIPT
#
# sentinel notification-script <master-name> <script-path>
#
# Call the specified notification script for any sentinel event that is
# generated in the WARNING level (for instance -sdown, -odown, and so forth).
# This script should notify the system administrator via email, SMS, or any
# other messaging system, that there is something wrong with the monitored
# Redis systems.
#
# The script is called with just two arguments: the first is the event type
# and the second the event description.
#
# The script must exist and be executable in order for sentinel to start if
# this option is provided.
# 指定sentinel检测到该监控的redis实例指向的实例异常时,调用的报警脚本。该配置项可选,但是很常用。
#
# Example:
#
# sentinel notification-script mymaster /var/redis/notify.sh
 
# CLIENTS RECONFIGURATION SCRIPT
#
# sentinel client-reconfig-script <master-name> <script-path>
#
# When the master changed because of a failover a script can be called in
# order to perform application-specific tasks to notify the clients that the
# configuration has changed and the master is at a different address.
#
# The following arguments are passed to the script:
#
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
#
# <state> is currently always "failover"
# <role> is either "leader" or "observer"
#
# The arguments from-ip, from-port, to-ip, to-port are used to communicate
# the old address of the master and the new address of the elected slave
# (now a master).
#
# This script should be resistant to multiple invocations.
#
# Example:
#
# sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

2.2 Redis缓存穿透和雪崩

服务的高可用问题!

Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面,但同时也会带来一些问题,其中,最要害的就是数据的一致性问题,从严格意义上来讲,这个问题无解,如果对数据一致性要求较高的,那么就不能使用缓存。还有一些典型的问题就是:缓存穿透、缓存雪崩、缓存击穿。目前,业界也都有比较流行的解决方案。

2.2.1 缓存穿透

查不到导致的

概念

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

在这里插入图片描述

解决方案

  • 布隆过滤器

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

在这里插入图片描述

  • 缓存空对象

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

在这里插入图片描述

缓存空对象的2个问题:

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

2.2.2 缓存击穿

量太大,缓存过期!

概念

  • 缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

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

  • 当某个key在过期的瞬间,会大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导致数据库瞬间压力过大

解决方案

  • 设置热点数据不过期

从缓存层面来看,没有设置过期时间,所以不会出现热点key过期后产生的问题

  • 加互斥锁

分布式锁:保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的 权限,因此只需要等待即可,这种方式将高并发的压力转移到了分布式锁,因为对分布式锁的考验很大

2.2.3 缓存雪崩

概念

是指某一个时间段,缓存集中过期失效,redis宕机

在这里插入图片描述

解决方案

  • redis高可用

redis集群,保证服务的高可用(异地多活)

  • 限流降级

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

  • 数据预热

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

标签:127.0,入门,0.1,中间件,Redis,6379,master,integer
来源: https://blog.csdn.net/qq_39616920/article/details/123165738

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有