Linux5.10 NoSQL 之 Redis配置与优化及数据类型

计算机系统

5G云计算

第四章 LINUX NOSQL 之 Redis配置与优化及数据类型

一、关系数据库与非关系型数据库
1.关系型数据库

关系型数据库是一个结构化的数据库,创建在关系模型(二维表格模型)基础上,一般面向于记录。
SQL 语句(标准数据查询语言)就是一种基于关系型数据库的语言,用于执行对关系型数据库中数据的检索和操作
主流的关系型数据库包括 Oracle、MySQL、SQL Server、Microsoft Access、DB2、PostgreSQL
以上数据库在使用的时候必须先建库建表设计表结构,然后存储数据的时候按表结构去存,如果数据与表结构不匹配就会存储失败

2.非关系型数据库

NoSQL(NoSQL = Not Only SQL ),意思是“不仅仅是 SQL”,是非关系型数据库的总称
除了主流的关系型数据库外的数据库,都认为是非关系型
不需要预先建库建表定义数据存储表结构,每条记录可以有不同的数据类型和字段个数(比如微信群聊里的文字、图片、视频、音乐等)
主流的 NoSQL 数据库有 Redis、MongBD、Hbase、Memcached、ElasticSearch、TSDB

3.关系型数据库和非关系型数据库区别

1)数据存储方式不同
关系型和非关系型数据库的主要差异是数据存储的方式。关系型数据天然就是表格式的,因此存储在数据表的行和列中。数据表可以彼此关联协作存储,也很容易提取数据
与其相反,非关系型数据不适合存储在数据表的行和列中,而是大块组合在一起。非关系型数据通常存储在数据集中,就像文档、键值对或者图结构。你的数据及其特性是选择数据存储和提取方式的首要影响因素

2)扩展方式不同
SQL和NoSQL数据库最大的差别可能是在扩展方式上,要支持日益增长的需求当然要扩展
要支持更多并发量,SQL数据库是纵向扩展,也就是说提高处理能力,使用速度更快速的计算机,这样处理相同的数据集就更快了。因为数据存储在关系表中,操作的性能瓶颈可能涉及很多个表,这都需要通过提高计算机性能来克服。虽然SQL数据库有很大扩展空间,但最终肯定会达到纵向扩展的上限
NoSQL数据库是横向扩展的。因为非关系型数据存储天然就是分布式的,NoSQL数据库的扩展可以通过给资源池添加更多普通的数据库服务器(节点)来分担负载

3)对事务性的支持不同
如果数据操作需要高事务性或者复杂数据查询需要控制执行计划,那么传统的SQL数据库从性能和稳定性方面考虑是你的最佳选择。SQL数据库支持对事务原子性细粒度控制,并且易于回滚事务
虽然NoSQL数据库也可以使用事务操作,但稳定性方面没法和关系型数据库比较,所以它们真正闪亮的价值是在操作的扩展性和大数据量处理方面

4.非关系型数据库产生背景

可用于应对 Web2.0 纯动态网站类型的三高问题(高并发、高性能、高可用)

1)High performance——对数据库高并发读写需求
2)Huge Storage——对海量数据高效存储与访问需求
3)High Scalability && High Availability——对数据库高可扩展性与高可用性需求

关系型数据库和非关系型数据库都有各自的特点与应用场景,两者的紧密结合将会给Web2.0的数据库发展带来新的思路。让关系型数据库关注在关系上和对数据的一致性保障,非关系型数据库关注在存储和高效率上。例如,在读写分离的MySQL数据库环境中,可以把经常访问的数据存储在非关系型数据库中,提升访问速度

5.总结

关系型数据库
实例–>数据库–>表(table)–>记录行(row)、数据字段(column)

非关系型数据库
实例–>数据库–>集合(collection)–>键值对(key-value)
非关系型数据库不需要手动建数据库和集合(表)

二、Redis简介

Redis(远程字典服务器) 是一个开源的、使用 C 语言编写的 NoSQL 数据库
Redis 基于内存运行并支持持久化,采用key-value(键值对)的存储形式,是目前分布式架构中不可或缺的一环。

Redis服务器程序是单进程模型,也就是在一台服务器上可以同时启动多个Redis进程,Redis的实际处理速度则是完全依靠于主进程的执行效率。若在服务器上只运行一个Redis进程,当多个客户端同时访问时,服务器的处理能力是会有一定程度的下降;若在同一台服务器上开启多个Redis进程,Redis在提高并发处理能力的同时会给服务器的CPU造成很大压力。即:在实际生产环境中,需要根据实际的需求来决定开启多少个Redis进程。若对高并发要求更高一些,可能会考虑在同一台服务器上开启多个进程。若CPU资源比较紧张,采用单进程即可

1.Redis 具有以下几个优点

1)具有极高的数据读写速度:数据读取的速度最高可达到 110000 次/s,数据写入速度最高可达到 81000 次/s
2)支持丰富的数据类型:支持 key-value、Strings、Lists、Hashes、Sets 及 Sorted Sets 等数据类型操作
3)支持数据的持久化:可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用
4)原子性:Redis 所有操作都是原子性的
5)支持数据备份:即 master-salve 模式的数据备份

2.使用场景

Redis作为基于内存运行的数据库,是一个高性能的缓存,一般应用在Session缓存、队列、排行榜、计数器、最近最热文章、最近最热评论、发布订阅等
Redis 适用于数据实时性要求高、数据存储有过期和淘汰特征的、不需要持久化或者只需要保证弱一致性、逻辑简单的场景

我们通常会将部分数据放入缓存中,来提高访问速度,然后数据库承担存储的工作

3.哪些数据适合放入缓存中

1)即时性。例如查询最新的物流状态信息。
2)数据一致性要求不高。例如门店信息,修改后,数据库中已经改了,五分钟后缓存中才是最新的,但不影响功能使用
3)访问量大且更新频率不高,例如网站首页的广告信息,访问量大,但是不会经常变化

4.Redis为什么这么快

1)Redis是一款纯内存结构避免了磁盘I/O等耗时操作
2)Redis命令处理的核心模块为单线程不存在多线程切换而消耗CPU不用考虑各种锁的问题,不存在加锁、释放锁的操作,没有因为可能出现死锁而导致性能消耗
3)采用了 I/O 多路复用机制,大大提升了并发效率。

注:在 Redis 6.0新增加的多线程只是针对处理网络请求过程采用了多线性,而数据的读写命令,仍然是单线程处理

三、Redis 安装部署
###环境准备
systemctl stop firewalld
systemctl disable firewalld
setenforce 0

###修改内核参数
vim /etc/sysctl.conf
vm.overcommit_memory = 1
net.core.somaxconn = 2048

sysctl -p


###安装redis
yum install -y gcc gcc-c++ make

tar zxvf /opt/redis-7.0.9.tar.gz -C /opt/
cd /opt/redis-7.0.9
make
make PREFIX=/usr/local/redis install
###由于Redis源码包中直接提供了 Makefile 文件,所以在解压完软件包后,不用先执行 ./configure 进行配置,可直接执行 make 与 make install 命令进行安装。

###创建redis工作目录
mkdir /usr/local/redis/{conf,log,data}

cp /opt/redis-7.0.9/redis.conf /usr/local/redis/conf/

useradd -M -s /sbin/nologin redis
chown -R redis.redis /usr/local/redis/

###环境变量
vim /etc/profile 
PATH=$PATH:/usr/local/redis/bin		#增加一行

source /etc/profile


###修改配置文件
vim /usr/local/redis/conf/redis.conf
bind 192.168.58.61 192.168.58.61					#87行,添加 监听的主机地址
protected-mode no					            #111行,将本机访问保护模式设置no。如果开启了,那么在没有设定bind ip且没有设密码的情况下,Redis只允许接受本机的响应
port 6379										#138行,Redis默认的监听6379端口
daemonize yes									#309行,设置为守护进程,后台启动
pidfile /usr/local/redis/log/redis_6379.pid		#341行,指定 PID 文件
logfile "/usr/local/redis/log/redis_6379.log"	#354行,指定日志文件
dir /usr/local/redis/data						#504行,指定持久化文件所在目录
requirepass 123								    #1037行,增加一行,设置redis密码


###定义systemd服务管理脚本
vim /usr/lib/systemd/system/redis-server.service
[Unit]
Description=Redis Server
After=network.target

[Service]
User=redis
Group=redis
Type=forking
TimeoutSec=0
PIDFile=/usr/local/redis/log/redis_6379.pid
ExecStart=/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

###启动服务
systemctl start redis-server
systemctl enable redis-server

netstat -lntp | grep 6379

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

四、Redis 命令工具
redis-server:Redis 服务器启动命令
redis-benchmark:性能测试工具,用于检测 Redis 在本机的运行效率
redis-check-aof:修复有问题的 AOF 持久化文件
redis-check-rdb:修复有问题的 RDB 持久化文件
redis-cli:Redis 客户端命令行工具
redis-sentinel:Redis 哨兵集群使用
1.redis-cli 命令行工具
语法:redis-cli -h host -p port [-a password]
redis-cli -h 192.168.58.61 -p 6379 -a '123'
redis-cli选项说明
-h指定远程主机
-p指定 Redis 服务的端口号
-a指定密码,未设置数据库密码可以省略-a 选项
若不添加任何选项表示,则使用 192.168.58.61:6379 连接本机上的 Redis 数据库

在这里插入图片描述

2.redis-benchmark 测试工具
redis-benchmark 是官方自带的 Redis 性能测试工具,可以有效的测试 Redis 服务的性能。
基本的测试语法:redis-benchmark [选项] [选项值]###向 IP 地址为 192.168.58.61、端口为 6379 的 Redis 服务器发送 100 个并发连接与 100000 个请求测试性能
redis-benchmark -h 192.168.58.61 -p 6379 -a '123' -c 100 -n 100000

###测试存取大小为 100 字节的数据包的性能
redis-benchmark -h 192.168.58.61 -p 6379 -a '123' -q -d 100

###测试本机上 Redis 服务在进行 set 与 lpush 操作时的性能
redis-benchmark -a '123' -t set,lpush -n 100000 -q
redis-benchmark选项说明
-h指定服务器主机名
-p指定服务器端口
-s指定服务器 socket
-c指定并发连接数
-n指定请求数
-d以字节的形式指定 SET/GET 值的数据大小
-k1=keep alive 0=reconnect
-rSET/GET/INCR 使用随机 key, SADD 使用随机值
-P通过管道传输请求
-q强制退出 redis。仅显示 query/sec 值
–csv以 CSV 格式输出
-l生成循环,永久执行测试
-t仅运行以逗号分隔的测试命令列表
-IIdle 模式。仅打开 N 个 idle 连接并等待

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

3.Redis 数据库常用命令
set:存放数据,命令格式为 set key value
get:获取数据,命令格式为 get key

192.168.58.61:6379> set teacher zhangsan
OK
192.168.58.61:6379> get teacher
"zhangsan"

### keys 命令可以取符合规则的键值列表,通常情况可以结合*、?等选项来使用。
192.168.58.61:6379> set k1 1
192.168.58.61:6379> set k2 2
192.168.58.61:6379> set k3 3
192.168.58.61:6379> set v1 4
192.168.58.61:6379> set v5 5
192.168.58.61:6379> set v22 5

192.168.58.61:6379> KEYS *				
###查看当前数据库中所有键

192.168.58.61:6379> KEYS v*				
###查看当前数据库中以 v 开头的数据

192.168.58.61:6379> KEYS v?				
###查看当前数据库中以 v 开头后面包含任意一位的数据

192.168.58.61:6379> KEYS v??				
###查看当前数据库中以 v 开头 v 开头后面包含任意两位的数据

### exists 命令可以判断键值是否存在。
192.168.58.61:6379> exists teacher		###判断 teacher 键是否存在
(integer) 1							    ### 1 表示 teacher 键是存在
192.168.58.61:6379> exists tea
(integer) 0							    ### 0 表示 tea 键不存在

### del 命令可以删除当前数据库的指定 key。
192.168.58.61:6379> keys *
192.168.58.61:6379> del v5
192.168.58.61:6379> get v5

### type 命令可以获取 key 对应的 value 值类型。
192.168.58.61:6379> type k1
string

### expire 命令可以为给定的 key 设置过期时间
192.168.58.61:6379> expire k1 10		###设置 k1 键的过期时间为 10 秒

###ttl 命令可以查看 key 还有多少秒过期,-1表示永不过期,-2表示已过期
192.168.58.61:6379> ttl k1

### rename 命令是对已有 key 进行重命名。(覆盖)
命令格式:renamekey 目标key
使用rename命令进行重命名时,无论目标key是否存在都进行重命名,且源key的值会覆盖目标key的值。在实际使用过程中,建议先用 exists 命令查看目标 key 是否存在,然后再决定是否执行 rename 命令,以避免覆盖重要数据。

192.168.58.61:6379> keys v*
1) "v1"
2) "v22"
192.168.58.61:6379> rename v22 v2
OK
192.168.58.61:6379> keys v*
1) "v1"
2) "v2"
192.168.58.61:6379> get v1
"4"
192.168.58.61:6379> get v2
"5"
192.168.58.61:6379> rename v1 v2
OK
192.168.58.61:6379> get v1
(nil)
192.168.58.61:6379> get v2
"4"

### renamenx 命令的作用是对已有 key 进行重命名,并检测新名是否存在,如果目标 key 存在则不进行重命名。(不覆盖)
命令格式:renamenx 源key 目标key
192.168.58.61:6379> keys *
192.168.58.61:6379> get teacher
"zhangsan"
192.168.58.61:6379> get v2
"4"
192.168.58.61:6379> renamenx v2 teacher
(integer) 0
192.168.58.61:6379> keys *
192.168.58.61:6379> get teacher
"zhangsan"
192.168.58.61:6379> get v2
"4"

### dbsize 命令的作用是查看当前数据库中 key 的数目。
192.168.58.61:6379> dbsize


###使用config set requirepass yourpassword命令设置密码
192.168.58.61:6379> config set requirepass 123456

###使用config get requirepass命令查看密码(一旦设置密码,必须先验证通过密码,否则所有操作不可用)
192.168.58.61:6379> auth 123456
192.168.58.61:6379> config get requirepass

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

4.Redis 多数据库常用命令
### Redis 支持多数据库,Redis 默认情况下包含 16 个数据库,数据库名称是用数字 0-15 来依次命名的
多数据库相互独立,互不干扰

###多数据库间切换
命令格式:select 序号
使用 redis-cli 连接 Redis 数据库后,默认使用的是序号为 0 的数据库。

192.168.58.61:6379> select 10			   ###切换至序号为 10 的数据库

192.168.58.61:6379[10]> select 15		   ###切换至序号为 15 的数据库

192.168.58.61:6379[15]> select 0		   ###切换至序号为 0 的数据库

###多数据库间移动数据
格式:move 键值 序号

192.168.58.61:6379> set k1 100
OK
192.168.58.61:6379> get k1
"100"
192.168.58.61:6379> select 1
OK
192.168.58.61:6379[1]> get k1
(nil)
192.168.58.61:6379[1]> select 0			###切换至目标数据库 0
OK
192.168.58.61:6379> get k1				###查看目标数据是否存在
"100"
192.168.58.61:6379> move k1 1			###将数据库 0 中 k1 移动到数据库 1 中
(integer) 1
192.168.58.61:6379> select 1			###切换至目标数据库 1
OK
192.168.58.61:6379[1]> get k1			###查看被移动数据
"100"
192.168.58.61:6379[1]> select 0
OK
192.168.58.61:6379> get k1				###在数据库 0 中无法查看到 k1 的值
(nil)

###清除数据库内数据
FLUSHDB :清空当前数据库数据
FLUSHALL :清空所有数据库的数据,慎用!

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

五、Redis 数据类型
1.String数据类型

概述

String是redis最基本的类型,最大能存储512MB的数据,String类型是二进制安全的,即可以存储任何数据、比如数字、图片、序列化对象等

1. SET/GET/APPEND/STRLEN:
redis 192.168.58.61:6379> exists mykey						
###判断该键是否存在,存在返回1,否则返回0
(integer) 0		

redis 192.168.58.61:6379> append mykey "hello"				
###该键并不存在,因此append命令返回当前Value的长度
(integer) 5		

redis 192.168.58.61:6379> append mykey " world"				
###该键已经存在,因此返回追加后Value的长度
(integer) 11

redis 192.168.58.61:6379> get mykey							
###通过get命令获取该键,以判断append的结果
"hello world"

redis 192.168.58.61:6379> set mykey "this is a test"		
###通过set命令为键设置新值,并覆盖原有值
OK

redis 192.168.58.61:6379> get mykey
"this is a test"

redis 192.168.58.61:6379> strlen mykey						
###获取指定Key的字符长度
(integer) 14

2. INCR/DECR/INCRBY/DECRBY:
redis 192.168.58.61:6379> set mykey 20						
###设置Key的值为20
OK

redis 192.168.58.61:6379> incr mykey						
###该Key的值递增1
(integer) 21

redis 192.168.58.61:6379> decr mykey						
###该Key的值递减1
(integer) 20

redis 192.168.58.61:6379> del mykey							
###删除已有键
(integer) 1

redis 192.168.58.61:6379> decr mykey						
###对空值执行递减操作,其原值被设定为0,递减后的值为-1
(integer) -1

redis 192.168.58.61:6379> del mykey   
(integer) 1

redis 192.168.58.61:6379> incr mykey						
###对空值执行递增操作,其原值被设定为0,递增后的值为1
(integer) 1

redis 192.168.58.61:6379> set mykey hello					
###将该键的Value设置为不能转换为整型的普通字符串
OK

redis 192.168.58.61:6379> incr mykey
(error) ERR value is not an integer or out of range

redis 192.168.58.61:6379> set mykey 10
OK

redis 192.168.58.61:6379> decrby mykey 5					
###减少指定的整数
(integer) 5

redis 192.168.58.61:6379> incrby mykey 10					
###增加指定的整数
(integer) 15

3. GETSET:
redis 192.168.58.61:6379> incr mycounter					
###将计数器的值原子性的递增1
(integer) 1

redis 192.168.58.61:6379> getset mycounter 0	
###在获取计数器原有值的同时,并将其设置为新值,这两个操作原子性的同时完成
"1"

redis 192.168.58.61:6379> get mycounter						
###查看设置后的结果
"0"

4. SETEX:
redis 192.168.58.61:6379> setex mykey 15 "hello"			
###设置指定Key的过期时间为10秒
OK    

redis 192.168.58.61:6379> ttl mykey			
###通过ttl命令查看一下指定Key的剩余存活时间(秒数),-2表示已经过期,-1表示永不过期。
(integer) 4

redis 192.168.58.61:6379> get mykey						
###在该键的存活期内我们仍然可以获取到它的Value
"hello"

redis 192.168.58.61:6379> ttl mykey							
###该ttl命令的返回值显示,该Key已经过期
(integer) -2

redis 192.168.58.61:6379> get mykey							
###获取已过期的Key将返回nil
(nil)

5. SETNX:
redis 192.168.58.61:6379> del mykey							
###删除该键,以便于下面的测试验证
(integer) 1

redis 192.168.58.61:6379> setnx mykey "hello"				
###该键并不存在,因此setnx命令执行成功
(integer) 1

redis 192.168.58.61:6379> setnx mykey "world"				
###该键已经存在,因此本次设置没有产生任何效果
(integer) 0

redis 192.168.58.61:6379> get mykey							
###从结果可以看出,返回的值仍为第一次设置的值
"hello"

6. MSET/MGET/MSETNX:
redis 192.168.58.61:6379> mset key1 "hello" key2 "world"	
###批量设置了key1和key2两个键
OK

redis 192.168.58.61:6379> mget key1 key2					
###批量获取了key1和key2两个键的值
1) "hello"
2) "world"

redis 192.168.58.61:6379> msetnx key3 "zhang" key4 "san" 	
###批量设置了key3和key4两个键,因为之前他们并不存在,所以msetnx命令执行成功并返回1
(integer) 1

redis 192.168.58.61:6379> mget key3 key4
1) "zhang"
2) "san"

redis 192.168.58.61:6379> msetnx key3 "hello" key5 "world"	
###批量设置了key3和key5两个键,但是key3已经存在,所以msetnx命令执行失败并返回0
(integer) 0

redis 192.168.58.61:6379> mget key3 key5					
###批量获取key3和key5,由于key5没有设置成功,所以返回nil
1) "zhang"
2) (nil)

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

2.List数据类型

概述

列表的元素类型为string,按照插入顺序排序,在列表的头部或尾部添加元素

1. LPUSH/LPUSHX/LRANGE:
redis 192.168.58.61:6379> del mykey
(integer) 1

redis 192.168.58.61:6379> lpush mykey a b c d	
###mykey键并不存在,该命令会创建该键及与其关联的List,之后在将参数中的values从左到右依次插入
(integer) 4

redis 192.168.58.61:6379> lrange mykey 0 2		
###取从位置0开始到位置2结束的3个元素
1) "d"
2) "c"
3) "b"

redis 192.168.58.61:6379> lrange mykey 0 -1		
###取链表中的全部元素,其中0表示第一个元素,-1表示最后一个元素
1) "d"
2) "c"
3) "b"
4) "a"

redis 192.168.58.61:6379> lpushx mykey2 e		
###mykey2键此时并不存在,因此lpushx命令将不会进行任何操作,其返回值为0
(integer) 0

redis 192.168.58.61:6379> lrange mykey2 0 -1	
###可以看到mykey2没有关联任何List Value
(empty list or set)

redis 192.168.58.61:6379> lpushx mykey e		
###mykey键此时已经存在,所以lpushx命令插入成功,并返回链表中当前元素的数量
(integer) 5

redis 192.168.58.61:6379> lrange mykey 0 0		
###获取该键的List Value的头部元素
1) "e"

2. LPOP/LLEN:
redis 192.168.58.61:6379> del mykey
(integer) 1

redis 192.168.58.61:6379> lpush mykey a b c d
(integer) 4

redis 192.168.58.61:6379> lpop mykey			
###移除并返回mykey键的第一个元素,从左取
"d"

redis 192.168.58.61:6379> lpop mykey
"c"

redis 192.168.58.61:6379> llen mykey			
###在执行lpop命令两次后,链表头部的两个元素已经被弹出,此时链表中元素的数量是2
(integer) 2

3. LREM/LSET/LINDEX/LTRIM:
redis 192.168.58.61:6379> del mykey
(integer) 1

redis 192.168.58.61:6379> lpush mykey a b c d a c		
###为后面的示例准备测试数据
(integer) 6

redis 192.168.58.61:6379> lrem mykey 2 a		
###从头部(left)向尾部(right)变量链表,删除2个值等于a的元素,返回值为实际删除的数量
(integer) 2

redis 192.168.58.61:6379> lrange mykey 0 -1		
###看出删除后链表中的全部元素
1) "c"
2) "d"
3) "c"
4) "b"

redis 192.168.58.61:6379> lindex mykey 1		
###获取索引值为1(头部的第二个元素)的元素值
"d"

redis 192.168.58.61:6379> lset mykey 1 e		
###将索引值为1(头部的第二个元素)的元素值设置为新值e
OK

redis 192.168.58.61:6379> lindex mykey 1		
###查看是否设置成功
"e"

redis 192.168.58.61:6379> lindex mykey 6		
###索引值6超过了链表中元素的数量,该命令返回nil
(nil)

redis 192.168.58.61:6379> lset mykey 6 hh		
###设置的索引值6超过了链表中元素的数量,设置失败,该命令返回错误信息
(error) ERR index out of range

redis 192.168.58.61:6379> ltrim mykey 0 2		
###仅保留索引值0到2之间的3个元素,注意第0个和第2个元素均被保留
OK

redis 192.168.58.61:6379> lrange mykey 0 -1		
###查看trim后的结果
1) "c"
2) "e"
3) "c" 

4. LINSERT:
redis 192.168.58.61:6379> del mykey						
###删除该键便于后面的测试
(integer) 1

redis 192.168.58.61:6379> lpush mykey a b c d e			
###为后面的示例准备测试数据
(integer) 5

redis 192.168.58.61:6379> linsert mykey before a a1		
###在a的前面插入新元素a1
(integer) 6

redis 192.168.58.61:6379> lrange mykey 0 -1				
###查看是否插入成功,从结果看已经插入
1) "e"
2) "d"
3) "c"
4) "b"
5) "a1"
6) "a"
redis 192.168.58.61:6379> linsert mykey after e e2		
###在e的后面插入新元素e2,从返回结果看已经插入成功
(integer) 7

redis 192.168.58.61:6379> lindex mykey 1				
###再次查看是否插入成功
"e2"

redis 192.168.58.61:6379> linsert mykey after k a		
###在不存在的元素之前或之后插入新元素,linsert命令操作失败,并返回-1
(integer) -1

redis 192.168.58.61:6379> linsert mykey1 after a a2		
###为不存在的Key插入新元素,linsert命令操作失败,返回0
(integer) 0

5. RPUSH/RPUSHX/RPOP/RPOPLPUSH:
redis 192.168.58.61:6379> del mykey						
###删除该键,以便于后面的测试
(integer) 1

redis 192.168.58.61:6379> rpush mykey a b c d			
###从链表的尾部插入参数中给出的values,插入顺序是从右到左依次插入
(integer) 4

redis 192.168.58.61:6379> lrange mykey 0 -1				
###通过lrange命令可以获悉rpush在插入多值时的插入顺序
1) "a"
2) "b"
3) "c"
4) "d"

redis 192.168.58.61:6379> rpushx mykey e	
###该键已经存在并且包含4个元素,rpushx命令将执行成功,并将元素e插入到链表的尾部
(integer) 5

redis 192.168.58.61:6379> lindex mykey 4	
###通过lindex命令可以看出之前的rpushx命令确实执行成功,因为索引值为4的元素已经是新元素了
"e"

redis 192.168.58.61:6379> rpushx mykey2 e		
###由于mykey2键并不存在,因此rpushx命令不会插入数据,其返回值为0
(integer) 0

redis 192.168.58.61:6379> lrange mykey 0 -1		
###在执行rpoplpush命令前,先看一下mykey中链表的元素有哪些,注意他们的位置关系
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"

192.168.58.61:6379> RPOP mykey						
###移除并返回mykey键的第一个元素,从右取
"e"

192.168.58.61:6379> LRANGE mykey 0 -1
1) "a"
2) "b"
3) "c"
4) "d"

redis 192.168.58.61:6379> rpoplpush mykey mykey2	
###将mykey的尾部元素e弹出,同时再插入到mykey2的头部(原子性的完成这两步操作)
"d"

redis 192.168.58.61:6379> lrange mykey 0 -1			
###通过lrange命令查看mykey在弹出尾部元素后的结果
1) "a"
2) "b"
3) "c"

redis 192.168.58.61:6379> lrange mykey2 0 -1		
###通过lrange命令查看mykey2在插入元素后的结果
1) "d"

redis 192.168.58.61:6379> rpoplpush mykey mykey		
###将source和destination设为同一键,将mykey中的尾部元素移到其头部
"c"

redis 192.168.58.61:6379> lrange mykey 0 -1			
###查看移动结果
1) "c"
2) "a"
3) "b"

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

3.Hash数据类型(散列类型)

概述

hash用于存储对象。可以采用这样的命名方式:对象类别和ID构成键名,使用字段表示对象的属性,而字段值则存储属性值。 如:存储 ID 为 2 的汽车对象
如果Hash中包含很少的字段,那么该类型的数据也将仅占用很少的磁盘空间。每一个Hash可以存储4294967295个键值对

1. HSET/HGET/HDEL/HEXISTS/HLEN/HSETNX:
redis 192.168.58.61:6379> hset myhash field1 "zhang"		
###给键值为myhash的键设置字段为field1,值为zhang
(integer) 1

redis 192.168.58.61:6379> hget myhash field1				
###获取键值为myhash,字段为field1的值
"zhang"

redis 192.168.58.61:6379> hget myhash field2				
###myhash键中不存在field2字段,因此返回nil
(nil)

redis 192.168.58.61:6379> hset myhash field2 "san"			
###给myhash添加一个新的字段field2,其值为san
(integer) 1

redis 192.168.58.61:6379> hlen myhash						
###hlen命令获取myhash键的字段数量
(integer) 2

redis 192.168.58.61:6379> hexists myhash field1			
###判断myhash键中是否存在字段名为field1的字段,由于存在,返回值为1
(integer) 1

redis 192.168.58.61:6379> hdel myhash field1			
###删除myhash键中字段名为field1的字段,删除成功返回1
(integer) 1

redis 192.168.58.61:6379> hdel myhash field1		
###再次删除myhash键中字段名为field1的字段,由于上一条命令已经将其删除,因为没有删除,返回0
(integer) 0

redis 192.168.58.61:6379> hexists myhash field1		
###判断myhash键中是否存在field1字段,由于上一条命令已经将其删除,因为返回0
(integer) 0

redis 192.168.58.61:6379> hsetnx myhash field1 zhang		
###通过hsetnx命令给myhash添加新字段field1,其值为zhang,因为该字段已经被删除,所以该命令添加成功并返回1
(integer) 1

redis 192.168.58.61:6379> hsetnx myhash field1 zhang		
###由于myhash的field1字段已经通过上一条命令添加成功,因为本条命令不做任何操作后返回0
(integer) 0

2. HINCRBY:
redis 192.168.58.61:6379> del myhash					
###删除该键,便于后面示例的测试
(integer) 1

redis 192.168.58.61:6379> hset myhash field 5			
###准备测试数据,该myhash的field字段设定值5
(integer) 1

redis 192.168.58.61:6379> hincrby myhash field 1		
###hincrby命令给myhash的field字段的值加1,返回加后的结果
(integer) 6

redis 192.168.58.61:6379> hincrby myhash field -1		
###hincrby命令给myhash的field字段的值加-1,返回加后的结果
(integer) 5

redis 192.168.58.61:6379> hincrby myhash field -10		
###hincrby命令给myhash的field字段的值加-10,返回加后的结果
(integer) -5  

3. HGETALL/HKEYS/HVALS/HMGET/HMSET:
redis 192.168.58.61:6379> del myhash					
###删除该键,便于后面示例测试
(integer) 1

redis 192.168.58.61:6379> hmset myhash field1 "hello" field2 "world"		###hmset命令为该键myhash,一次性设置多个字段,分别是field1="hello", field2="world"
OK

redis 192.168.58.61:6379> hmget myhash field1 field2 field3		
###hmget命令获取myhash键的多个字段,其中field3并不存在,因为在返回结果中与该字段对应的值为nil
1) "hello"
2) "world"
3) (nil)

redis 192.168.58.61:6379> hgetall myhash	
###hgetall命令返回myhash键的所有字段及其值,从结果中可以看出,他们是逐对列出的
1) "field1"
2) "hello"
3) "field2"
4) "world"

redis 192.168.58.61:6379> hkeys myhash			
###hkeys命令仅获取myhash键中所有字段的名字
1) "field1"
2) "field2"

redis 192.168.58.61:6379> hvals myhash			
###hvals命令仅获取myhash键中所有字段的值
1) "hello"
2) "world" 

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

4.Set数据类型(无序集合)

概述

无序集合,元素类型为String类型,元素具有唯一性,不允许存在重复的成员。多个集合类型之间可以进行并集、交集和差集运算

应用范围

1.可以使用Redis的Set数据类型跟踪一些唯一性数据,比如访问某一博客的唯一IP地址信息。对于此场景,我们仅需在每次访问该博客时将访问者的IP存入Redis中,Set数据类型会自动保证IP地址的唯一性

2.充分利用Set类型的服务端聚合操作方便、高效的特性,可以用于维护数据对象之间的关联关系。比如所有购买某一电子设备的客户ID被存储在一个指定的Set中,而购买另外一种电子产品的客户ID被存储在另外一个Set中,如果此时我们想获取有哪些客户同时购买了这两种商品时,Set的intersections命令就可以充分发挥它的方便和效率的优势了

1. SADD/SMEMBERS/SCARD/SISMEMBER:
redis 192.168.58.61:6379> sadd myset a b c		
###插入测试数据,由于该键myset之前并不存在,因此参数中的三个成员都被正常插入
(integer) 3

redis 192.168.58.61:6379> sadd myset a d e		
###由于参数中的a在myset中已经存在,因此本次操作仅仅插入了d和e两个新成员
(integer) 2

redis 192.168.58.61:6379> sismember myset a		
###判断a是否已经存在,返回值为1表示存在
(integer) 1

redis 192.168.58.61:6379> sismember myset f		
###判断f是否已经存在,返回值为0表示不存在
(integer) 0

redis 192.168.58.61:6379> smembers myset		
###通过smembers命令查看插入的结果,从结果可以看出,输出的顺序和插入顺序无关
1) "c"
2) "d"
3) "a"
4) "b"
5) "e"

redis 192.168.58.61:6379> scard myset			
###获取Set集合中元素的数量
(integer) 5

2. SPOP/SREM/SRANDMEMBER/SMOVE:
redis 192.168.58.61:6379> del myset				
###删除该键,便于后面的测试
(integer) 1

redis 192.168.58.61:6379> sadd myset a b c d	
###为后面的示例准备测试数据
(integer) 4

redis 192.168.58.61:6379> smembers myset		
###查看Set中成员的位置
1) "c"
2) "d"
3) "a"
4) "b"

redis 192.168.58.61:6379> srandmember myset		
###从结果可以看出,该命令确实是随机的返回了某一成员
"c"

redis 192.168.58.61:6379> spop myset			
###随机的移除并返回Set中的某一成员
"b"

redis 192.168.58.61:6379> smembers myset		
###查看移出后Set的成员信息
1) "c"
2) "d"
3) "a"

redis 192.168.58.61:6379> srem myset a d f	
###从Set中移出a、d和f三个成员,其中f并不存在,因此只有a和d两个成员被移出,返回为2
(integer) 2

redis 192.168.58.61:6379> smembers myset		
###查看移出后的输出结果
1) "c"

redis 192.168.58.61:6379> sadd myset a b		
###为后面的smove命令准备数据
(integer) 2

redis 192.168.58.61:6379> sadd myset2 c d
(integer) 2

redis 192.168.58.61:6379> smove myset myset2 a		
###将a从myset移到myset2,从结果可以看出移动成功
(integer) 1

redis 192.168.58.61:6379> smove myset myset2 a		
###再次将a从myset移到myset2,由于此时a已经不是myset的成员了,因此移动失败并返回0
(integer) 0

redis 192.168.58.61:6379> smembers myset			
###分别查看myset和myset2的成员,确认移动是否真的成功
1) "b"

redis 192.168.58.61:6379> smembers myset2
1) "c"
2) "d"
3) "a"

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

5.Sorted Set数据类型(zset、有序集合)

概述

a、有序集合,元素类型为Sting,元素具有唯一性,不能重复
b、每个元素都会关联一个double类型的分数score(表示权重),可以通过权重的大小排序,元素的score可以相同

应用范围

1)可以用于一个大型在线游戏的积分排行榜。每当玩家的分数发生变化时,可以执行ZADD命令更新玩家的分数,此后再通过ZRANGE命令获取积分TOP10的用户信息。当然我们也可以利用ZRANK命令通过username来获取玩家的排行信息。最后我们将组合使用ZRANGE和ZRANK命令快速的获取和某个玩家积分相近的其他用户的信息

2)Sorted-Set类型还可用于构建索引数据

1. ZADD/ZCARD/ZCOUNT/ZREM/ZINCRBY/ZSCORE/ZRANGE/ZRANK:
redis 192.168.58.61:6379> zadd myzset 1 "one"				
###添加一个分数为1的成员
(integer) 1

redis 192.168.58.61:6379> zadd myzset 2 "two" 3 "three"		
###添加两个分数分别是2和3的两个成员
(integer) 2

redis 192.168.58.61:6379> zrange myzset 0 -1 WITHSCORES		
###0表示第一个成员,-1表示最后一个成员。WITHSCORES选项表示返回的结果中包含每个成员及其分数,否则只返回成员
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "3"

redis 192.168.58.61:6379> zrank myzset one					
###获取成员one在Sorted-Set中的位置索引值。0表示第一个位置
(integer) 0

redis 192.168.58.61:6379> zrank myzset four					
###成员four并不存在,因此返回nil
(nil)

redis 192.168.58.61:6379> zcard myzset						
###获取myzset键中成员的数量
(integer) 3

redis 192.168.58.61:6379> zcount myzset 1 2					
###zcount key min max,分数满足表达式1 <= score <= 2的成员的数量
(integer) 2

redis 192.168.58.61:6379> zrem myzset one two				
###删除成员one和two,返回实际删除成员的数量
(integer) 2

redis 192.168.58.61:6379> zcard myzset						
###查看是否删除成功
(integer) 1

redis 192.168.58.61:6379> zscore myzset three				
###获取成员three的分数。返回值是字符串形式
"3"

redis 192.168.58.61:6379> zscore myzset two					
###由于成员two已经被删除,所以该命令返回nil
(nil)

redis 192.168.58.61:6379> zincrby myzset 2 one	
###成员one不存在,zincrby命令将添加该成员并假设其初始分数为0,将成员one的分数增加2,并返回该成员更新后的分数
"2"

redis 192.168.58.61:6379> zincrby myzset -1 one				
###将成员one的分数增加-1,并返回该成员更新后的分数
"1"

redis 192.168.58.61:6379> zrange myzset 0 -1 WITHSCORES		
###查看在更新了成员的分数后是否正确
1) "one"
2) "1"
3) "three"
4) "3"

2. ZRANGEBYSCORE/ZREMRANGEBYRANK/ZREMRANGEBYSCORE
redis 192.168.58.61:6379> del myzset
(integer) 1

redis 192.168.58.61:6379> zadd myzset 1 one 2 two 3 three 4 four
(integer) 4

redis 192.168.58.61:6379> zrangebyscore myzset 1 2		
###zrangebyscore key min max,获取分数满足表达式1 <= score <= 2的成员
1) "one"
2) "two"

redis 192.168.58.61:6379> zrangebyscore myzset (1 2			
###获取分数满足表达式1 < score <= 2的成员
1) "two"

redis 192.168.58.61:6379> zrangebyscore myzset -inf +inf limit 2 3		
###-inf表示第一个成员(位置索引值最低的,即0),+inf表示最后一个成员(位置索引值最高的),limit后面的参数用于限制返回成员的值,2表示从位置索引等于2的成员开始,取后面3个成员
1) "three"
2) "four"

redis 192.168.58.61:6379> zrangebyscore myzset 0 4 limit 2 3

redis 192.168.58.61:6379> zremrangebyscore myzset 1 2		
###删除分数满足表达式1 <= score <= 2的成员,并返回实际删除的数量
(integer) 2

redis 192.168.58.61:6379> zrange myzset 0 -1				
###看出一下上面的删除是否成功
1) "three"
2) "four"

redis 192.168.58.61:6379> zremrangebyrank myzset 0 1		
###删除位置索引满足表达式0 <= rank <= 1的成员
(integer) 2

redis 192.168.58.61:6379> zcard myzset						
###查看上一条命令是否删除成功
(integer) 0

3. ZREVRANGE/ZREVRANGEBYSCORE/ZREVRANK:
redis 192.168.58.61:6379> del myzset						
###为后面的示例准备测试数据
(integer) 0

redis 192.168.58.61:6379> zadd myzset 1 one 2 two 3 three 4 four
(integer) 4

redis 192.168.58.61:6379> zrevrange myzset 0 -1 WITHSCORES		
###以位置索引从高到低的方式获取并返回此区间内的成员
1) "four"
2) "4"
3) "three"
4) "3"wu
5) "two"
6) "2"
7) "one"
8) "1"

redis 192.168.58.61:6379> zrevrange myzset 1 3			
###由于是从高到低的排序,所以位置等于0的是four,1是three,并以此类推
1) "three"
2) "two"
3) "one"

redis 192.168.58.61:6379> zrevrank myzset one			
###由于是从高到低的排序,所以one的位置是3
(integer) 3

redis 192.168.58.61:6379> zrevrank myzset four			
###由于是从高到低的排序,所以four的位置是0
(integer) 0

redis 192.168.58.61:6379> zrevrangebyscore myzset 3 0	
###zrevrangebyscore key max min, 获取分数满足表达式3 >= score >= 0 的成员,并以从高到底的顺序输出
1) "three"
2) "two"
3) "one"

redis 192.168.58.61:6379> zrevrangebyscore myzset 4 0 limit 1 2		###zrevrangebyscore命令支持limit选项,其含义等同于zrangebyscore中的该选项,只是在计算位置时按照相反的顺序计算和获取
1) "three"
2) "two"

192.168.80.10:6379> zrevrangebyscore myzset +inf -inf limit 1 3

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

六、Redis 高可用

在web服务器中,高可用是指服务器可以正常访问的时间,衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999%等等)
但是在Redis语境中,高可用的含义似乎要宽泛一些,除了保证提供正常服务(如主从分离、快速容灾技术),还需要考虑数据容量的扩展、数据安全不会丢失等

在Redis中,实现高可用的技术主要包括持久化、主从复制、哨兵和 Cluster集群,下面分别说明它们的作用,以及解决了什么样的问题。

1.持久化

持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要作用是数据备份,即将数据存储在硬盘,保证数据不会因进程退出而丢失

2.主从复制

主从复制是高可用Redis的基础,哨兵和集群都是在主从复制基础上实现高可用的。主从复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。缺陷:故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制

3.哨兵

在主从复制的基础上,哨兵实现了自动化的故障恢复。缺陷:写操作无法负载均衡;存储能力受到单机的限制

4.Cluster集群

通过集群,Redis解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案

七、Redis 持久化

持久化的功能:Redis是内存数据库,数据都是存储在内存中,为了避免服务器断电等原因导致Redis进程异常退出后数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。

1.Redis 提供两种方式进行持久化
1)RDB 持久化

原理是将 Reids在内存中的数据库记录定时保存到磁盘上

2)AOF 持久化(append only file)

原理是将 Reids 的操作日志以追加的方式写入文件,类似于MySQL的binlog

由于AOF持久化的实时性更好,即当进程意外退出时丢失的数据更少,因此AOF是目前主流的持久化方式,不过RDB持久化仍然有其用武之地

2.RDB 持久化

RDB持久化是指在指定的时间间隔内将内存中当前进程中的数据生成快照保存到硬盘(因此也称作快照持久化),用二进制压缩存储,保存的文件后缀是rdb;当Redis重新启动时,可以读取快照文件恢复数据

1)触发条件

RDB持久化的触发分为手动触发和自动触发两种

1)手动触发
save命令bgsave命令都可以生成RDB文件
save命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在Redis服务器阻塞期间,服务器不能处理任何命令请求
bgsave命令会创建一个子进程,由子进程来负责创建RDB文件,父进程(即Redis主进程)则继续处理请求

bgsave命令执行过程中,只有fork子进程时会阻塞服务器,而对于save命令,整个过程都会阻塞服务器,因此save已基本被废弃,线上环境要杜绝save的使用

2)自动触发

在自动触发RDB持久化时,Redis也会选择bgsave而不是save来进行持久化
save m n
自动触发最常见的情况是在配置文件中通过save m n,指定当m秒内发生n次变化时,会触发bgsave进行快照

vim /usr/local/redis/conf/redis.conf
--433行--RDB默认保存策略
# save 3600 1 300 100 60 10000
###表示以下三个save条件满足任意一个时,都会引起bgsave的调用
save 3600 1 :当时间到3600秒时,如果redis数据发生了至少1次变化,则执行bgsave
save 300 10 :当时间到300秒时,如果redis数据发生了至少10次变化,则执行bgsave
save 60 10000 :当时间到60秒时,如果redis数据发生了至少10000次变化,则执行bgsave

--454行--是否开启RDB文件压缩
rdbcompression yes
--481行--指定RDB文件名
dbfilename dump.rdb
--504行--指定RDB文件和AOF文件所在目录
dir /usr/local/redis/data


###其他自动触发机制###
除了save m n 以外,还有一些其他情况会触发bgsave:
1)在主从复制场景下,如果从节点执行全量复制操作,则主节点会执行bgsave命令,并将rdb文件发送给从节点。
2)执行shutdown命令时,自动执行rdb持久化。
2)执行流程

1)Redis父进程首先判断当前是否在执行save,或bgsave/bgrewriteaof的子进程,如果在执行则bgsave命令直接返回。 bgsave/bgrewriteaof的子进程不能同时执行,主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题

2)父进程执行fork操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令

3)父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他命令

4)子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换

5)子进程发送信号给父进程表示完成,父进程更新统计信息

3)启动时加载

RDB文件的载入工作是在服务器启动时自动执行的,并没有专门的命令。但是由于AOF的优先级更高,因此当AOF开启时,Redis会优先载入 AOF文件来恢复数据;只有当AOF关闭时,才会在Redis服务器启动时检测RDB文件,并自动载入。服务器载入RDB文件期间处于阻塞状态,直到载入完成为止
Redis载入RDB文件时,会对RDB文件进行校验,如果文件损坏,则日志中会打印错误,Redis启动失败

3.AOF 持久化

RDB持久化是将进程数据写入文件,而AOF持久化,则是将Redis执行的每次写、删除命令记录到单独的日志文件中,查询操作不会记录; 当Redis重启时再次执行AOF文件中的命令来恢复数据
与RDB相比,AOF的实时性更好,因此已成为主流的持久化方案

1)开启AOF
Redis服务器默认开启RDB,关闭AOF;要开启AOF,需要在配置文件中配置:
vim /usr/local/redis/conf/redis.conf
--1380行--修改,开启AOF
appendonly yes
--1407行--指定AOF文件名称
appendfilename "appendonly.aof"
--1505行--是否忽略最后一条可能存在问题的指令
aof-load-truncated yes

systemctl restart redis-server.service
2)执行流程

由于需要记录Redis的每条写命令,因此AOF不需要触发,下面介绍AOF的执行流程

AOF的执行流程包括:
1)命令追加(append):将Redis的写命令追加到缓冲区aof_buf;

2)文件写入(write)和文件同步(sync):根据不同的同步策略将aof_buf中的内容同步到硬盘;

3)文件重写(rewrite):定期重写AOF文件,达到压缩的目的

1)命令追加(append)
Redis先将写命令追加到缓冲区,而不是直接写入文件,主要是为了避免每次有写命令都直接写入硬盘,导致硬盘IO成为Redis负载的瓶颈。
命令追加的格式是Redis命令请求的协议格式,它是一种纯文本格式,具有兼容性好、可读性强、容易处理、操作简单避免二次开销等优点。在AOF文件中,除了用于指定数据库的select命令(如select 0为选中0号数据库)是由Redis添加的,其他都是客户端发送来的写命令

2)文件写入(write)和文件同步(sync)
Redis提供了多种AOF缓存区的同步文件策略,策略涉及到操作系统的write函数和fsync函数,说明如下:
为了提高文件写入效率,在现代操作系统中,当用户调用write函数将数据写入文件时,操作系统通常会将数据暂存到一个内存缓冲区里,当缓冲区被填满或超过了指定时限后,才真正将缓冲区的数据写入到硬盘里。这样的操作虽然提高了效率,但也带来了安全问题:如果计算机停机,内存缓冲区中的数据会丢失;因此系统同时提供了fsync、fdatasync等同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保数据的安全性

AOF缓存区的同步文件策略存在三种同步方式,它们分别是:

vim /usr/local/redis/conf/redis.conf
--1439--
1)appendfsync always: 命令写入aof_buf后立即调用系统fsync操作同步到AOF文件,fsync完成后线程返回。这种情况下,每次有写命令都要同步到AOF文件,硬盘IO成为性能瓶颈,Redis只能支持大约几百TPS写入,严重降低了Redis的性能;即便是使用固态硬盘(SSD),每秒大约也只能处理几万个命令,而且会大大降低SSD的寿命

3)appendfsync no: 命令写入aof_buf后调用系统write操作,不对AOF文件做fsync同步;同步由操作系统负责,通常同步周期为30秒。这种情况下,文件同步的时间不可控,且缓冲区中堆积的数据会很多,数据安全性无法保证

3)appendfsync everysec: 命令写入aof_buf后调用系统write操作,write完成后线程返回;fsync同步文件操作由专门的线程每秒调用一次。everysec是前述两种策略的折中,是性能和数据安全性的平衡,因此是Redis的默认配置,也是我们推荐的配置

3)文件重写(rewrite)
随着时间流逝,Redis服务器执行的写命令越来越多,AOF文件也会越来越大;过大的AOF文件不仅会影响服务器的正常运行,也会导致数据恢复需要的时间过长

文件重写是指定期重写AOF文件,减小AOF文件的体积。需要注意的是,AOF重写是把Redis进程内的数据转化为写命令,同步到新的AOF文件;不会对旧的AOF文件进行任何读取、写入操作!

关于文件重写需要注意的另一点是:对于AOF持久化来说,文件重写虽然是强烈推荐的,但并不是必须的;即使没有文件重写,数据也可以被持久化并在Redis启动的时候导入;因此在一些现实中,会关闭自动的文件重写,然后通过定时任务在每天的某一时刻定时执行

#文件重写之所以能够压缩AOF文件,原因在于:
①过期的数据不再写入文件

②无效的命令不再写入文件:如有些数据被重复设值(set mykey v1, set mykey v2)、有些数据被删除了(set myset v1, del myset)等

③多条命令可以合并为一个:如sadd myset v1, sadd myset v2, sadd myset v3可以合并为sadd myset v1 v2 v3

通过上述内容可以看出,由于重写后AOF执行的命令减少了,文件重写既可以减少文件占用的空间,也可以加快恢复速度。

###文件重写的触发,分为手动触发和自动触发:
①手动触发:直接调用bgrewriteaof命令,该命令的执行与bgsave有些类似:都是fork子进程进行具体的工作,且都只有在fork时阻塞

②自动触发:通过设置auto-aof-rewrite-min-size选项和auto-aof-rewrite-percentage选项来自动执行BGREWRITEAOF。 只有当auto-aof-rewrite-min-size和auto-aof-rewrite-percentage两个选项同时满足时,才会自动触发AOF重写,即bgrewriteaof操作

vim /usr/local/redis/conf/redis.conf
--1480--
auto-aof-rewrite-percentage 100	:当前AOF文件大小(即aof_current_size)是上次日志重写时AOF文件大小(aof_base_size)两倍时,发生BGREWRITEAOF操作

auto-aof-rewrite-min-size 64mb :当前AOF文件执行BGREWRITEAOF命令的最小值,避免刚开始启动Reids时由于文件尺寸较小导致频繁的BGREWRITEAOF	

关于文件重写的流程,有两点需要特别注意:(1)重写由父进程fork子进程进行;(2)重写期间Redis执行的写命令,需要追加到新的AOF文件中,为此Redis引入了aof_rewrite_buf缓存

###文件重写的流程如下:
(1)Redis父进程首先判断当前是否存在正在执行bgsave/bgrewriteaof的子进程,如果存在则bgrewriteaof命令直接返回,如果存在 bgsave命令则等bgsave执行完成后再执行

(2)父进程执行fork操作创建子进程,这个过程中父进程是阻塞的

(3.1)父进程fork后,bgrewriteaof命令返回”Background append only file rewrite started”信息并不再阻塞父进程, 并可以响应其他命令。Redis的所有写命令依然写入AOF缓冲区,并根据appendfsync策略同步到硬盘,保证原有AOF机制的正确

(3.2)由于fork操作使用写时复制技术,子进程只能共享fork操作时的内存数据。由于父进程依然在响应命令,因此Redis使用AOF重写缓冲区(aof_rewrite_buf)保存这部分数据,防止新AOF文件生成期间丢失这部分数据。也就是说,bgrewriteaof执行期间,Redis的写命令同时追加到aof_buf和aof_rewirte_buf两个缓冲区

(4)子进程根据内存快照,按照命令合并规则写入到新的AOF文件

(5.1)子进程写完新的AOF文件后,向父进程发信号,父进程更新统计信息,具体可以通过info persistence查看

(5.2)父进程把AOF重写缓冲区的数据写入到新的AOF文件,这样就保证了新AOF文件所保存的数据库状态和服务器当前状态一致

(5.3)使用新的AOF文件替换老文件,完成AOF重写

3)启动时加载

当AOF开启时,Redis启动时会优先载入AOF文件来恢复数据;只有当AOF关闭时,才会载入RDB文件恢复数据
当AOF开启,但AOF文件不存在时,即使RDB文件存在也不会加载。
Redis载入AOF文件时,会对AOF文件进行校验,如果文件损坏,则日志中会打印错误,Redis启动失败。但如果是AOF文件结尾不完整(机器突然宕机等容易导致文件尾部不完整),且aof-load-truncated参数开启,则日志中会输出警告,Redis忽略掉AOF文件的尾部,启动成功。aof-load-truncated参数默认是开启的

4.RDB和AOF的优缺点
1)RDB持久化

优点
RDB文件紧凑,体积小,网络传输快,适合全量复制;恢复速度比AOF快很多。当然,与AOF相比,RDB最重要的优点之一是对性能的影响相对较小

缺点
RDB文件的致命缺点在于其数据快照的持久化方式决定了必然做不到实时持久化,而在数据越来越重要的今天,数据的大量丢失很多时候是无法接受的,因此AOF持久化成为主流。此外,RDB文件需要满足特定格式,兼容性差(如老版本的Redis不兼容新版本的RDB文件)。
对于RDB持久化,一方面是bgsave在进行fork操作时Redis主进程会阻塞,另一方面,子进程向硬盘写数据也会带来IO压力。

2)AOF持久化

与RDB持久化相对应,AOF的优点在于支持秒级持久化、兼容性好,缺点是文件大、恢复速度慢、对性能影响大
对于AOF持久化,向硬盘写数据的频率大大提高(everysec策略下为秒级),IO压力更大,甚至可能造成AOF追加阻塞问题
AOF文件的重写与RDB的bgsave类似,会有fork时的阻塞和子进程的IO压力问题。相对来说,由于AOF向硬盘中写数据的频率更高,因此对 Redis主进程性能的影响会更大

八、Redis 性能管理
1.查看Redis内存使用
192.168.58.61:6379> info memory

----- 内存碎片率 -----
mem_fragmentation_ratio:内存碎片率
mem_fragmentation_ratio = used_memory_rss / used_memory
used_memory_rss:是Redis向操作系统申请的内存
used_memory:是Redis中的数据占用的内存
used_memory_peak:redis内存使用的峰值

###内存碎片如何产生的
Redis内部有自己的内存管理器,为了提高内存使用的效率,来对内存的申请和释放进行管理
Redis中的值删除的时候,并没有把内存直接释放,交还给操作系统,而是交给了Redis内部有内存管理器
Redis中申请内存的时候,也是先看自己的内存管理器中是否有足够的内存可用
Redis的这种机制,提高了内存的使用率,但是会使Redis中有部分自己没在用,却不释放的内存,导致了内存碎片的发生

###跟踪内存碎片率对理解Redis实例的资源性能是非常重要的:
1)内存碎片率在11.5之间是正常的,这个值表示内存碎片率比较低,也说明 Redis 没有发生内存交换。
2)内存碎片率超过1.5,说明Redis消耗了实际需要物理内存的150%,其中50%是内存碎片率。
3)内存碎片率低于1的,说明Redis内存分配超出了物理内存,操作系统正在进行内存交换。需要增加可用物理内存或减少 Redis内存占用。

###解决碎片率大的问题:
如果你的Redis版本是4.0以下的,需要在 redis-cli 工具上输入 shutdown save 命令,让 Redis 数据库执行保存操作并关闭 Redis 服务,再重启服务器。Redis服务器重启后,Redis会将没用的内存归还给操作系统,碎片率会降下来

Redis4.0版本开始,可以在不重启的情况下,线上整理内存碎片
config set activedefrag yes     
###自动碎片清理,内存就会自动清理了
memory purge					
###手动碎片清理


----- 内存使用率 -----
redis实例的内存使用率超过可用最大内存,操作系统将开始进行内存与swap空间交换。

#避免内存交换发生的方法:
1)针对缓存数据大小选择安装 Redis 实例
2)尽可能的使用Hash数据结构存储
3)设置key的过期时间

----- 内回收key -----
内存数据淘汰策略,保证合理分配redis有限的内存资源

当达到设置的最大阀值时,需选择一种key的回收策略,默认情况下回收策略是禁止删除
配置文件中修改 maxmemory-policy 属性值:
vim /usr/local/redis/conf/redis.conf
--1149--
maxmemory-policy noenviction
●volatile-lru:使用LRU算法从已设置过期时间的数据集合中淘汰数据(移除最近最少使用的key,针对设置了TTL的key)
●volatile-ttl:从已设置过期时间的数据集合中挑选即将过期的数据淘汰(移除最近过期的key)
●volatile-random:从已设置过期时间的数据集合中随机挑选数据淘汰(在设置了TTL的key里随机移除)
●allkeys-lru:使用LRU算法从所有数据集合中淘汰数据(移除最少使用的key,针对所有的key)
●allkeys-random:从数据集合中任意选择数据淘汰(随机移除key)
●noenviction:禁止淘汰数据(不删除直到写满时报错)


###其它限制相关
●maxclients
设置redis同时可以与多少个客户端进行连接。
默认情况下为10000个客户端。
如果达到了此限制,redis则会拒绝新的连接请求,并且向这些连接请求方发出“max number of clients reached”以作回应。

●maxmemory
建议必须设置,否则,将内存占满,造成服务器宕机。
设置redis可以使用的内存量。一旦到达内存使用上限,redis将会试图移除内部数据,移除规则可以通过maxmemory-policy来指定。
如果redis无法根据移除规则来移除内存中的数据,或者设置了“不允许移除”,那么redis则会针对那些需要申请内存的指令返回错误信息,比如SET、LPUSH等。
但是对于无内存申请的指令,仍然会正常响应,比如GET等。如果你的redis是主redis(说明redis集群有主从),那么在设置内存使用上限时,需要在系统中留出一些内存空间给同步队列缓存,只有在你设置的是“不移除”的情况下,才不用考虑这个因素。

●maxmemory-samples
设置样本数量,LRU算法和最小TTL算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小,redis默认会检查这么多个key并选择其中LRU的那个。
一般设置37的数字,数值越小样本越不准确,但性能消耗越小
九、Redis 集群

主从复制
主从复制是高可用Redis的基础,哨兵和集群都是在主从复制基础上实现高可用的。主从复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。缺陷:故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制

哨兵
在主从复制的基础上,哨兵实现了自动化的故障恢复。缺陷:写操作无法负载均衡;存储能力受到单机的限制;哨兵无法对从节点进行自动故障转移,在读写分离场景下,从节点故障会导致读服务不可用,需要对从节点做额外的监控、切换操作

集群
通过集群,Redis解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案

1.Redis 主从复制

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

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

主从复制的作用
1)数据冗余:
主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式

2)故障恢复:
当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余

3)负载均衡:
在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量

4)高可用基石:
除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础

主从复制流程
1)若启动一个Slave机器进程,则它会向Master机器发送一个“sync command”命令,请求同步连接

2)无论是第一次连接还是重新连接,Master机器都会启动一个后台进程,将数据快照保存到数据文件中(执行rdb操作),同时Master还会记录修改数据的所有命令并缓存在数据文件中

3)后台进程完成缓存操作之后,Master机器就会向Slave机器发送数据文件,Slave端机器将数据文件保存到硬盘上,然后将其加载到内存中,接着Master机器就会将修改数据的所有操作一并发送给Slave端机器。若Slave出现故障导致宕机,则恢复正常后会自动重新连接

4)Master机器收到Slave端机器的连接后,将其完整的数据文件发送给Slave端机器,如果Master同时收到多个Slave发来的同步请求,则Master会在后台启动一个进程以保存数据文件,然后将其发送给所有的Slave端机器,确保所有的Slave端机器都正常

搭建Redis 主从复制
Master节点:192.168.58.61
Slave1节点:192.168.58.62
Slave2节点:192.168.58.63


-----安装 Redis-----
参考上面的redis部署


-----修改 Redis 配置文件(Master节点操作)-----
vim /usr/local/redis/conf/redis.conf
bind 0.0.0.0									#87行,修改监听地址为0.0.0.0
protected-mode no								#111行,将本机访问保护模式设置no
port 6379										#138行,Redis默认的监听6379端口
daemonize yes									#309行,设置为守护进程,后台启动
pidfile /usr/local/redis/log/redis_6379.pid		#341行,指定 PID 文件
logfile "/usr/local/redis/log/redis_6379.log"	#354行,指定日志文件
dir /usr/local/redis/data						#504行,指定持久化文件所在目录
requirepass 123								    #1037行,可选,设置redis密码
appendonly yes									#1380行,开启AOF

systemctl restart redis-server.service


-----修改 Redis 配置文件(Slave节点操作)-----
vim /usr/local/redis/conf/redis.conf
bind 0.0.0.0									#87行,修改监听地址为0.0.0.0
protected-mode no								#111行,将本机访问保护模式设置no
port 6379										#138行,Redis默认的监听6379端口
daemonize yes									#309行,设置为守护进程,后台启动
pidfile /usr/local/redis/log/redis_6379.pid		#341行,指定 PID 文件
logfile "/usr/local/redis/log/redis_6379.log"	#354行,指定日志文件
dir /usr/local/redis/data						#504行,指定持久化文件所在目录
requirepass 123				  				    #1037行,可选,设置redis密码
appendonly yes									#1380行,开启AOF
replicaof 192.168.68.61 6379					#528行,指定要同步的Master节点IP和端口
masterauth 123								    #535行,可选,指定Master节点的密码,仅在Master节点设置了requirepass


systemctl restart redis-server.service


-----验证主从效果-----
在Master节点上看日志:
tail -f /usr/local/redis/log/redis_6379.log 
Replica 192.168.58.62:6379 asks for synchronization
Replica 192.168.58.63:6379 asks for synchronization
Synchronization with replica 192.168.58.62:6379 succeeded
Synchronization with replica 192.168.58.63:6379 succeeded

在Master节点上验证从节点:
redis-cli -h 192.168.58.61 -p 6379 -a '123' info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.58.62,port=6379,state=online,offset=238,lag=0
slave1:ip=192.168.58.63,port=6379,state=online,offset=238,lag=1

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

2.Redis 哨兵模式

主从切换技术的方法是:当服务器宕机后,需要手动一台从机切换为主机,这需要人工干预,不仅费时费力而且还会造成一段时间内服务不可用。为了解决主从复制的缺点,就有了哨兵机制

哨兵的核心功能:在主从复制的基础上,哨兵引入了主节点的自动故障转移

哨兵模式的作用
1)监控:哨兵会不断地检查主节点和从节点是否运作正常

2)自动故障转移:当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并让其它从节点改为复制新的主节点

3)通知(提醒):哨兵可以将故障转移的结果发送给客户端

哨兵结构由两部分组成,哨兵节点和数据节点:
哨兵节点:哨兵系统由一个或多个哨兵节点组成,哨兵节点是特殊的redis节点,不存储数据
数据节点:主节点和从节点都是数据节点

故障转移机制
1)由哨兵节点定期监控发现主节点是否出现了故障
每个哨兵节点每隔1秒会向主节点、从节点及其它哨兵节点发送一次ping命令做一次心跳检测。如果主节点在一定时间范围内不回复或者是回复一个错误消息,那么这个哨兵就会认为这个主节点主观下线了(单方面的)。当超过半数哨兵节点认为该主节点主观下线了,这样就客观下线了

2)当主节点出现故障,此时哨兵节点会通过Raft算法(选举算法)实现选举机制共同选举出一个哨兵节点为leader,来负责处理主节点的故障转移和通知。所以整个运行哨兵的集群的数量不得少于3个节点

3)由leader哨兵节点执行故障转移,过程如下:
①将某一个从节点升级为新的主节点,让其它从节点指向新的主节点;
②若原主节点恢复也变成从节点,并指向新的主节点;
③通知客户端主节点已经更换

需要特别注意的是,客观下线是主节点才有的概念;如果从节点和哨兵节点发生故障,被哨兵主观下线后,不会再有后续的客观下线和故障转移操作

主节点的选举
1)过滤掉不健康的(已下线的),没有回复哨兵 ping 响应的从节点
2)选择配置文件中从节点优先级配置最高的。(replica-priority,默认值为100)
3)选择复制偏移量最大,也就是复制最完整的从节点

哨兵的启动依赖于主从模式,所以须把主从模式安装好的情况下再去做哨兵模式

搭建Redis 哨兵模式
Master节点:192.168.58.61
Slave1节点:192.168.58.62
Slave2节点:192.168.58.63

systemctl stop firewalld
setenforce 0

-----修改 Redis 哨兵模式的配置文件(所有节点操作)-----
cp /opt/redis-7.0.9/sentinel.conf /usr/local/redis/conf/
chown redis.redis /usr/local/redis/conf/sentinel.conf

vim /usr/local/redis/conf/sentinel.conf
protected-mode no									#6行,关闭保护模式
port 26379											#10行,Redis哨兵默认的监听端口
daemonize yes										#15行,指定sentinel为后台启动
pidfile /usr/local/redis/log/redis-sentinel.pid		#20行,指定 PID 文件
logfile "/usr/local/redis/log/sentinel.log"			#25行,指定日志存放路径
dir /usr/local/redis/data							#54行,指定数据库存放路径
sentinel monitor mymaster 192.168.58.61 6379 2		#73行,修改 指定该哨兵节点监控192.168.80.10:6379这个主节点,该主节点的名称是mymaster,最后的2的含义与主节点的故障判定有关:至少需要2个哨兵节点同意,才能判定主节点故障并进行故障转移
sentinel auth-pass mymaster 123					    #76行,可选,指定Master节点的密码,仅在Master节点设置了requirepass
sentinel down-after-milliseconds mymaster 3000		#114行,判定服务器down掉的时间周期,默认30000毫秒(30秒)
sentinel failover-timeout mymaster 180000			#214行,同一个sentinel对同一个master两次failover之间的间隔时间(180秒)


-----启动哨兵模式-----
先启master,再启slave
cd /usr/local/redis/conf/
redis-sentinel sentinel.conf &


-----查看哨兵信息-----
redis-cli -h 192.168.58.61 -p 26379 -a '123' info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.58.61:6379,slaves=2,sentinels=3

-----故障模拟-----
#查看redis-server进程号:
ps -ef | grep redis
root      57031      1  0 15:20 ?        00:00:07 /usr/local/bin/redis-server 0.0.0.0:6379
root      57742      1  1 16:05 ?        00:00:07 redis-sentinel *:26379 [sentinel]
root      57883  57462  0 16:17 pts/1    00:00:00 grep --color=auto redis

#杀死 Master 节点上redis-server的进程号
kill -9 57031			#Master节点上redis-server的进程号

#验证结果
tail -f /usr/local/redis/log/sentinel.log
20878:X 30 Jun 2023 16:55:22.575 # +sdown master mymaster 192.168.58.61 6379
20878:X 30 Jun 2023 16:55:22.629 # +odown master mymaster 192.168.58.61 6379 #quorum 3/2
20878:X 30 Jun 2023 16:55:22.629 # +new-epoch 1
20878:X 30 Jun 2023 16:55:22.630 # +try-failover master mymaster 192.168.58.61 6379
20878:X 30 Jun 2023 16:55:22.631 * Sentinel new configuration saved on disk
20878:X 30 Jun 2023 16:55:22.631 # +vote-for-leader 5fb961c0688f1e4716a9dccae5a40fba72a08cc1 1
20878:X 30 Jun 2023 16:55:22.639 # 3c83d6ead2ac682a93f2dd63ef587f9c1b8359a1 voted for 5fb961c0688f1e4716a9dccae5a40fba72a08cc1 1
20878:X 30 Jun 2023 16:55:22.642 # 962724e5097061d8ede3420a3a04ed738930b763 voted for 5fb961c0688f1e4716a9dccae5a40fba72a08cc1 1
20878:X 30 Jun 2023 16:55:22.691 # +elected-leader master mymaster 192.168.58.61 6379
20878:X 30 Jun 2023 16:55:22.691 # +failover-state-select-slave master mymaster 192.168.58.61 6379
20878:X 30 Jun 2023 16:55:22.758 # +selected-slave slave 192.168.58.62:6379 192.168.58.62 6379 @ mymaster 192.168.58.61 6379
20878:X 30 Jun 2023 16:55:22.758 * +failover-state-send-slaveof-noone slave 192.168.58.62:6379 192.168.58.62 6379 @ mymaster 192.168.58.61 6379
20878:X 30 Jun 2023 16:55:22.849 * +failover-state-wait-promotion slave 192.168.58.62:6379 192.168.58.62 6379 @ mymaster 192.168.58.61 6379
20878:X 30 Jun 2023 16:55:23.642 * Sentinel new configuration saved on disk
20878:X 30 Jun 2023 16:55:23.642 # +promoted-slave slave 192.168.58.62:6379 192.168.58.62 6379 @ mymaster 192.168.58.61 6379
20878:X 30 Jun 2023 16:55:23.642 # +failover-state-reconf-slaves master mymaster 192.168.58.61 6379
20878:X 30 Jun 2023 16:55:23.711 * +slave-reconf-sent slave 192.168.58.63:6379 192.168.58.63 6379 @ mymaster 192.168.58.61 6379
20878:X 30 Jun 2023 16:55:24.668 * +slave-reconf-inprog slave 192.168.58.63:6379 192.168.58.63 6379 @ mymaster 192.168.58.61 6379
20878:X 30 Jun 2023 16:55:24.668 * +slave-reconf-done slave 192.168.58.63:6379 192.168.58.63 6379 @ mymaster 192.168.58.61 6379
20878:X 30 Jun 2023 16:55:24.723 # +failover-end master mymaster 192.168.58.61 6379
20878:X 30 Jun 2023 16:55:24.723 # +switch-master mymaster 192.168.58.61 6379 192.168.58.62 6379
20878:X 30 Jun 2023 16:55:24.723 * +slave slave 192.168.58.63:6379 192.168.58.63 6379 @ mymaster 192.168.58.62 6379
20878:X 30 Jun 2023 16:55:24.723 * +slave slave 192.168.58.61:6379 192.168.58.61 6379 @ mymaster 192.168.58.62 6379
20878:X 30 Jun 2023 16:55:24.725 * Sentinel new configuration saved on disk
20878:X 30 Jun 2023 16:55:27.734 # +sdown slave 192.168.58.61:6379 192.168.58.61 6379 @ mymaster 192.168.58.62 6379



2.redis-cli -a '123' -p 26379 INFO Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_tilt_since_seconds:-1
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.58.62:6379,slaves=2,sentinels=3

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

3.Redis 群集模式

集群,即Redis Cluster,是Redis 3.0开始引入的分布式存储方案

集群由多组节点(Node)组成,Redis的数据分布在这些节点中。集群中的节点分为主节点和从节点:只有主节点负责读写请求和集群信息的维护;从节点只进行主节点数据和状态信息的复制

集群的作用,可以归纳为两点
1)数据分区
数据分区(或称数据分片)是集群最核心的功能
集群将数据分散到多个节点,一方面突破了Redis单机内存大小的限制,存储容量大大增加;另一方面每个主节点都可以对外提供读服务和写服务,大大提高了集群的响应能力
Redis单机内存大小受限问题,在介绍持久化和主从复制时都有提及;例如,如果单机内存太大,bgsave和bgrewriteaof的fork操作可能导致主进程阻塞,主从环境下主机切换时可能导致从节点长时间无法提供服务,全量复制阶段主节点的复制缓冲区可能溢出

2)高可用
集群支持主从复制和主节点的自动故障转移(与哨兵类似);当任一节点发生故障时,集群仍然可以对外提供服务

Redis集群的数据分片
Redis集群引入了哈希槽的概念
Redis集群有16384个哈希槽(编号0-16383)
集群的每组节点负责一部分哈希槽
每个Key通过CRC16校验后对16384取余来决定放置哪个哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作

#以3个节点组成的集群为例:
节点A包含0到5460号哈希槽
节点B包含5461到10922号哈希槽
节点C包含10923到16383号哈希槽

Redis集群的主从复制模型
集群中具有A、B、C三个节点,如果节点B失败了,整个集群就会因缺少5461-10922这个范围的槽而不可以用。
为每个节点添加一个从节点A1、B1、C1整个集群便有三个Master节点和三个slave节点组成,在节点B失败后,集群选举B1位为的主节点继续服务。当B和B1都失败后,集群将不可用

搭建Redis 群集模式
redis的集群一般需要6个节点,33从。方便起见,这里所有节点在同一台服务器上模拟:
以端口号进行区分:3个主节点端口号:6001/6002/6003,对应的从节点端口号:6004/6005/6006。

cd /usr/local/redis/
mkdir -p redis-cluster/redis600{1..6}

for i in {1..6}
do
cp /opt/redis-7.0.9/redis.conf /usr/local/redis/redis-cluster/redis600$i
cp /opt/redis-7.0.9/src/redis-cli /opt/redis-7.0.9/src/redis-server /usr/local/redis/redis-cluster/redis600$i
done

###开启群集功能:
###其他5个文件夹的配置文件以此类推修改,注意6个端口都要不一样
cd /usr/local/redis/redis-cluster/redis6001
vim redis.conf
#bind 127.0.0.1								    #87行,注释掉bind项,默认监听所有网卡
protected-mode no								#111行,关闭保护模式
port 6001										#138行,修改redis监听端口
daemonize yes									#309行,设置为守护进程,后台启动
pidfile /usr/local/redis/log/redis_6001.pid		#341行,指定 PID 文件
logfile "/usr/local/redis/log/redis_6001.log"	#354行,指定日志文件
dir /usr/local/redis/data						#504行,指定持久化文件所在目录
appendonly yes									#1379行,开启AOF
cluster-enabled yes								#1576行,取消注释,开启群集功能
cluster-config-file nodes-6001.conf				#1584行,取消注释,群集名称文件设置
cluster-node-timeout 15000						#1590行,取消注释群集超时时间设置


###启动redis节点
分别进入那六个文件夹,执行命令:redis-server redis.conf ,来启动redis节点
cd /usr/local/redis/redis-cluster/redis6001
redis-server redis.conf

for d in {1..6}
do
cd /usr/local/redis/redis-cluster/redis600$d
./redis-server redis.conf
done

ps -ef | grep redis

###启动集群
redis-cli --cluster create 127.0.0.1:6001 127.0.0.1:6002 127.0.0.1:6003 127.0.0.1:6004 127.0.0.1:6005 127.0.0.1:6006 --cluster-replicas 1

###六个实例分为三组,每组一主一从,前面的做主节点,后面的做从节点。下面交互的时候 需要输入 yes 才可以创建
--replicas 1 表示每个主节点有1个从节点

###测试群集
redis-cli -h 192.168.58.63 -p 6001 -a '123' -c					###加-c参数,节点之间就可以互相跳转
192.168.58.63:6001> cluster slots			
###查看节点的哈希槽编号范围
1) 1) (integer) 0
   2) (integer) 5460
#哈希槽编号范围
   3) 1) "127.0.0.1"
      2) (integer) 6001
#主节点IP和端口号
      3) "4da37ab91f60b2d33efa9fd70e6eb025994a0c62"
   4) 1) "127.0.0.1"
      2) (integer) 6005									#从节点IP和端口号
      3) "f736b2d373d4f54d2316ec3a21edbf71f6e5d27d"
2) 1) (integer) 5461
   2) (integer) 10922
   3) 1) "127.0.0.1"
      2) (integer) 6002
      3) "fef9ac37ca376a50c0e75f5c8232d570c2fc5ec0"
   4) 1) "127.0.0.1"
      2) (integer) 6006
      3) "a307275bfb31dd6b758ccdcef2e6ef5099047c54"
3) 1) (integer) 10923
   2) (integer) 16383
   3) 1) "127.0.0.1"
      2) (integer) 6003
      3) "baef7a62d8bb23e5edc89099a25bf9b28db9f653"
   4) 1) "127.0.0.1"
      2) (integer) 6004
      3) "5bfb619667507b2bbc703a22534e13c08ce62920"

192.168.58.63:6001> set name zhangsan
-> Redirected to slot [5798] located at 127.0.0.1:6003
OK

192.168.58.63:6001> cluster keyslot name					#查看name键的槽编号

redis-cli -h 192.168.58.63 -p 6004 -a '123' -c
192.168.58.63:6004> keys *							
###对应的slave节点也有这条数据,但是别的节点没有
1) "name"

redis-cli -h 192.168.58.63 -p 6001 -a '123' -c cluster nodes






Cluster 集群增加节点动态扩容

redis 5的集群支持在有负载的情况下增加节点动态扩容

已有集群为6个节点127.0.0.1:6001 - 127.0.0.1:60063组主从节点。现要增加第4组主从节点127.0.0.1:6007127.0.0.1:6008

1.创建一个新的主节点127.0.0.1:6007。命令里需要指定一个已有节点以便于获取集群信息,本例是指定的127.0.0.1:6001
redis-cli -p 6001 --cluster add-node 127.0.0.1:6007 127.0.0.1:6001
或
redis-cli -p 6001
cluster meet 127.0.0.1 6007
cluster meet 127.0.0.1 6008

2.127.0.0.1:6008创建为127.0.0.1:6007的从节点。命令里需要指定一个已有节点以便于获取集群信息和主节点的node ID
redis-cli -p 6001 --cluster add-node 127.0.0.1:6008 127.0.0.1:6001 --cluster-slave --cluster-master-id e44678abed249e22482559136bf45280fd3ac281
或
redis-cli -p 6008
cluster replicate e44678abed249e22482559136bf45280fd3ac281


3.新加入的主节点是没有槽数的,只有初始化集群的时候,才会根据主的数据分配好,如新增的主节点,需要手动分配
redis-cli -p 6007 --cluster reshard 127.0.0.1:6001 --cluster-from e1a033e07f0064e6400825b4ddbcd6680c032d10 --cluster-to e44678abed249e22482559136bf45280fd3ac281 --cluster-slots 1000 --cluster-yes
或
redis-cli -p 6007 --cluster reshard 127.0.0.1:6001
How many slots do you want to move (from 1 to 16384)? 1000                    #指定转移槽的数量
What is the receiving node ID? e44678abed249e22482559136bf45280fd3ac281       #指定接收槽数量的主节点node ID
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1: e1a033e07f0064e6400825b4ddbcd6680c032d10           #指定分配的主节点node ID
Source node #2: done                                               #输入完毕,开始转移


4.查看集群状态
redis-cli -p 6001 cluster nodes

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值