Redis

Redis

1. 架构的演变

1.1****单体/库架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yGfg28Yu-1680569199501)(D:\word\Typora\image\1680182192105.png)]

90年代开始,当时主流的技术是JSP/HTML+Servlet+JDBC,那个时候网站上基本的网站访问量都不会太大,所以单机数据库就已经完全胜任。那个时代互联网刚刚发展! select * select 列名 where 条件要精准

架构的瓶颈

  1. 数据量太大怎么办?

  2. 数据访问量太大怎么办? --(读写混合)

  3. 当数据库单表数据超出千万级别,就必须需要创建索引,内存放不下怎么办?

1.2 缓存+读写分离+垂直拆分

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FyZh2u5W-1680569199502)(D:\word\Typora\image\1680182213067.png)]

单纯的实现了读写分离确实也解决了服务器压力,但是不足以胜任日益增长的访问量,然后我们将矛头指向了架构优化,如何优化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8iGlCSIN-1680569199502)(D:\word\Typora\image\1680182229363.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uw5Z6Mfd-1680569199503)(D:\word\Typora\image\1680182243905.png)]

瓶颈:

用户量和数据量越来越大,即使我们通过读写分离和分库分表也不足以支撑的时候,单表数据量已经爆炸了。

1.3 分库分表+水平拆分(MySQL集群)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jDW6yrxO-1680569199503)(D:\word\Typora\image\1680182260757.png)]

瓶颈:

近10年,是历史上科技发展最迅速的10年,我们的数据从简单的文字、图片也已经转变为各式各样的数据类型,那么传统的关系型数据库MySQL已经不满足我们现在发展的需求。图片、热点数据、大篇幅文章、定位信息等各式各样的数据扑面而来!,那么就需要我们挨个解决,如果有一个数据库能帮助我们分担这种特殊的数据,那么关系型数据库(MySQL、Oracle)的压力也就会变小了。

同时数据的存储也从IO 1+1 变成了 IO 1+N

1.4 当下互联网公司架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kslRkiFA-1680569199504)(D:\word\Typora\image\1680182275403.png)]

那么我们对架构的分析,大家也了解到,目前互联网项目对于各式各样的数据我们MySQL这种关系型数据库存储数据就显得特别疲惫,那么这个时候

NoSQL(非关系型)数据库就随之而来。

**2.**关于NoSQL

NoSQL ----错误翻译 没有SQL

正确翻译:Not Only SQL (不仅仅是SQL) 泛指我们非关系数据库,主要适用于web2.0 时代。

NoSQL,泛指非关系型的数据库。随着互联网web2.0网站的兴起,传统的关系数据库在处理web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,出现了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,特别是大数据应用难题

2.1 区别

关系型:

表-行-列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tr5ouamP-1680569199505)(D:\word\Typora\image\1680182290479.png)]

典型的就是key-value形式进行存储数据,而且存储的类型也变得多样化,不再是单一的文字结构。

2.2 NoSQL****特点

  1. 扩展方便,数据与数据之间没有必然联系,0耦合。

  2. 大数据量和高性能(redis => 单秒写10万次 读取11万次) QPS

  3. 数据类型的多样化(5+3) String、List、Set、Hash、Zset | geo(地理位置信息) 、hyperloglog(访客信息)、bitmap 位图(常用计算活跃粉丝和不活跃粉丝、登录和未登录、是否打卡等)

  4. 不需要提前设计数据库,随取随用。

2.3 淘宝架构分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TOtZ4Uzk-1680569199506)(D:\word\Typora\image\1680182305780.png)]

商品基本信息 id、名称、分类基本信息

- MySQL | Oracle 淘宝是讲MySQL进行了开发,虽然都叫MySQL但是内容完全不同

2012年5月,淘宝做了一个艰难的决定,实行去IOE运动。这里的I是指IBM小型机,O指oracle数据库,E是指EMC2是数据库的存储设备。而这一次的去IOE运动却恰恰是将亚洲亚洲最成功也是最大的oracle RAC应用的典范转换为MYSQL +PC server,当然需要mysql的hadoop集群的架构。

商品的描述信息、评论等文字量较多的信息

- MongoDB 文档型数据库

图片

- 淘宝 - TFS 自主研发

- 分布式系统 FastDFS

- Google - GFS 自主研发- 

-Hadoop – HDFS

- 阿里云 – OSS

商品的关键字(搜索功能)

- 搜索引擎 solr elasticsearch 

-比较火- 淘宝自己使用 ISearch

热门的波段信息(秒杀)

- Redis Tair memacached

订单交易

- 第三方应用 MQ消息中间件 – RabbitMQ

阿里的框架发展史(强烈建议阅读)

https://max.book118.com/html/2019/0407/6144150224002021.shtm

2.4 NoSQL****的四大类

# KV类型
 - 新浪 redis  
 - 美团 Redis + Tair  
 - 阿里、百度 redis + memcached
# 文档类型数据库(BSON 和 JSON一样)
 - MongoDB (目前企业需求也是比较大的) 是一个给予分布式存储的数据库,主要用于处理大量的文档,是一个介于关系型和非关系型数据库的中间产品,本身属于非关系型。
# 列存储的数据库
 - 大数据的HBase
 - 分布式文件系统(一个业务分拆多个子业务,部署在不同的服务器上)
# 图形关系数据库
 - 图示 - 存储的是图形关系,类似与朋友圈或社交平台
 - Neo4J InfoGrid

3. redis****介绍及安装

3.1 redis****介绍

Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。

redis能做什么?

  1. 内存存储,持久化,断电即丢失,所以持久化很重要,redis采用两种机制(RDB和AOF)

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

  3. 发布订阅系统

  4. 地图信息分析

  5. 计数器等,网站浏览量

redis特点/特性

  1. 多样的数据类型

  2. 持久化

  3. 集群

  4. 事务等…

3.2 Install redis in centos7

3.2.1 Install gcc

linux中的gcc是由GNU推出的一款功能强大的、性能优越的多平台编译器。gcc编译器能将C、C++语言源程序和目标程序编译、连接成可执行文件。

因为redis依赖c++环境 所以我们需要安装c++

yum install gcc-c++

可以通过gcc -v 检查版本

gcc -v

升级版本 因为redis7.* 需要gcc高版本的支持 4个分别执行

yum -y install centos-release-scl

yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils

#修改使用版本

scl enable devtool

set-9 bashecho "source /opt/rh/devtoolset-9/enable" >>/etc/profile

继续查看gcc版本 升级到9.* 完成安装

3.2.2 下载与安装redis

redis可以在windows和linux下安装使用,但是windows系统版本已经停更很长时间,不建议使用。

官网 https://redis.io/ 中文网 http://www.redis.cn/

通过工具将安装文件放在opt文件夹下:

注意:如果是系统应用如Java我们会安装到/usr/local 下, 如果是用户级的应用,我们就放在opt下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8RHCuBuW-1680569199507)(D:\word\Typora\image\1680182330826.png)]

解压文件夹:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rjn6XnE0-1680569199507)(D:\word\Typora\image\1680182345746.png)]

解压后进入redis文件夹查看文件:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1C9Czpdc-1680569199509)(D:\word\Typora\image\1680182356933.png)]

编译:会将需要的配置帮助我们配置完成

make #注意需要在解压后的redis文件夹中执行

这一步就是编译,大多数的源代码包都经过这一步进行编译(当然有些perl或python编写的软件需要调用perl或python来进行编译)。如果 在 make 过程中出现 error ,就要记下错误代码(注意不仅仅是最后一行),然后可以向开发者提交 bugreport(一般在 INSTALL 里有提交地址)。或者系统少了一些依赖库等,这些需要自己仔细研究错误代码。make 的作用是开始进行源代码编译,以及一些功能的提供,这些功能由他的 Makefile 设置文件提供相关的功能。比如 make install 一般表示进行安装,make uninstall 是卸载,不加参数就是默认的进行源代码编译。make 是 Linux 开发套件里面自动化编译的一个控制程序,他通过借助 Makefile 里面编写的编译规范进行自动化的调用 gcc 、ld 以及运行某些需要的程序进行编译的程序。

安装redis

make install

这条命令来进行安装(当然有些软件需要先运行 make check 或 make test来进行一些测试),这一步一般需要你有 root 权限(因为要向系统写入文件)。

命令拓展 - 软件安装流程

1、解zhi包软件

tar zxf xxxx.tgz

2、配置

cd xxxx./configure

3、编译make

4、安装make install

5、卸载make uninstall

检查redis环境服务所在的位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t9zv96wr-1680569199509)(D:\word\Typora\image\1680182386457.png)]

启动测试

redis-server

redis-server redis-config  #后面是配置文件路径

2.2.3 配置redis

安装vim编辑器

yum -y install vim*

备份文件

在redis配置文件的同级目录创建一个文件夹并且cp一个redis.conf文件到新的文件夹,以后我们使用这个cp的文件进行操作,原生原件不变动。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RRzxQmid-1680569199510)(D:\word\Typora\image\1680182398396.png)]

修改后台启动

vim redis-conf

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Sjsi8ZF-1680569199511)(D:\word\Typora\image\1680182412284.png)]

daemonize 默认值是no 修改yes 意思是以守护进程方式启动(后台运行)

重新启动redis 并查看测试

[root@it-sunwz ~]# redis-server /opt/redis-6.0.6/it-sunwz/redis.conf

查看进程

ps -aux | grep redis

ps -ef | grep redis

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d90jwSzc-1680569199511)(D:\word\Typora\image\1680182431880.png)]

客户端连接测试

[root@it-sunwz ~]# redis-cli -h 127.0.0.1 -p 6379

[root@it-sunwz ~]# redis-cli -h 127.0.0.1 -p 6379 --raw #支持中文显示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sfZ3W1nq-1680569199512)(D:\word\Typora\image\1680182444274.png)]

客户端IO测试

set name 张三

get name

keys *

退出客户端和结束服务

\

# 退出客户端

Exit

# 结束服务方式1 客户端内直接输入 shutdown

Shutdown

# 结束服务方式2 客户端外直接输入命令

redis-cli shutdown

2.2.4 redis开关

第一种:退出客户端之后

[root@sunwz ~]# redis-cli shutdown

第二种:直接在客户端内(建议)

127.0.0.1:6379>shutdown

3.3 redis****可视化工具安装

Redis Desktop Manager(Redis可视化工具)安装及使用

默认无法连接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bc4tYV9e-1680569199512)(D:\word\Typora\image\1680182458196.png)]

配置防火墙开放端口

firewall-cmd --add-port=6379/tcp –permanent

firewall-cmd --remove-port=8888/tcp –permanent

firewall-cmd --list-ports

redis服务默认有保护机制,机制规则默认是只允许当前安装redis服务的电脑访问127.0.0.1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UScxPGnK-1680569199513)(D:\word\Typora\image\1680182476246.png)]

redis默认在配置文件中有一个Ip地址 绑定的就是127.0.0.1 ,需要我们注释掉这个地址才能支持远程访问。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wp59sYGt-1680569199513)(D:\word\Typora\image\1680182487789.png)]

重新启动redis 服务

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iz1yX3W0-1680569199513)(D:\word\Typora\image\1680182498226.png)]

4 redis入门

4.1 关于redis线程问题

redis的单线程的,那么单线程为什么还这么快?

Redis 的数据结构并不全是简单的 Key-Value,还有 list,hash 等复杂的结构。这些结构有可能会进行很细粒度的操作,比如在很长的列表后面添加一个元素,在 hash 当中添加或者删除一个对象。这些操作可能就需要加非常多的锁,导致的结果是同步开销大大增加。

总之,在单线程的情况下,代码更清晰,处理逻辑更简单,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗,不存在多进程或者多线程导致的切换而消耗 CPU。

单线程多进程的集群方案

单线程的威力实际上非常强大,每核效率也非常高。多线程自然是可以比单线程有更高的性能上限,但是在今天的计算环境中,即使是单机多线程的上限也往往不能满足需要了,需要进一步摸索的是多服务器集群化的方案,这些方案中多线程的技术照样是用不上的。所以单线程、多进程的集群不失为一个时髦的解决方案。

CPU 消耗

采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU。但是如果 CPU 成为 Redis 瓶颈,或者不想让服务器其他 CPU 核闲置,那怎么办?

可以考虑多起几个 Redis 进程,Redis 是 key-value 数据库,不是关系数据库,数据之间没有约束。只要客户端分清哪些 key 放在哪个 Redis 进程上就可以了。

4.2 redis 基础命令

4.2.1 数据库切换操作

127.0.0.1:6379> PING   #检查连接
PONG
127.0.0.1:6379> SELECT 15
OK
127.0.0.1:6379[15]> SELECT 16  #一共16个库 0-15
(error) ERR DB index is out of range
127.0.0.1:6379[15]> SELECT 0 #默认是0 不显示索引
OK
127.0.0.1:6379>

4.2.2 查看当前数据库

127.0.0.1:6379> SET student zhangsan  #增加一个数据
OK
127.0.0.1:6379> SET student lisi #相同key增加一个数据 会覆盖掉
OK
127.0.0.1:6379> GET student # 取值
"lisi"
127.0.0.1:6379> KEYS * # 查看所有的key
1) "list11"
2) "student"
3) "user:1:age"
4) "user:1:name"
127.0.0.1:6379> 

4.2.3 清除数据库中的数据

flushdb 删除当前库 – 前提进入当前数据库中

flushall 删除所有库

127.0.0.1:6379> SET student zhangsan  #在索引0库加信息
OK
127.0.0.1:6379> SET teacher lisi #在索引0库加信息
OK
127.0.0.1:6379> select 5  #切换索引为5的库
OK
127.0.0.1:6379[5]> SET user wangwu #在索引5库加信息
OK
127.0.0.1:6379[5]> KEYS * 
1) "user"
127.0.0.1:6379[5]> FLUSHDB  #清除当前库
OK
127.0.0.1:6379[5]> keys *
(empty array)
127.0.0.1:6379[5]> SELECT 0
OK
127.0.0.1:6379> KEYS *
1) "teacher"
2) "student"
127.0.0.1:6379> SELECT 5
OK
127.0.0.1:6379[5]> FLUSHALL  #清空所有的库信息
OK
127.0.0.1:6379[5]> SELECT 0
OK
127.0.0.1:6379> KEYS *
(empty array)
127.0.0.1:6379> 

4.2.4 设置过期时间

127.0.0.1:6379> set student jiazong
OK
127.0.0.1:6379> EXPIRE student 30 #设置过期 秒数
(integer) 1
127.0.0.1:6379> TTL student
(integer) 18
127.0.0.1:6379> TTL student
(integer) 16
127.0.0.1:6379> TTL student #查看过期时间
(integer) 14
127.0.0.1:6379> TTL student
(integer) 7
127.0.0.1:6379> TTL student
(integer) -2
127.0.0.1:6379> get student
(nil)
127.0.0.1:6379>

4.2.5 查看key类型

type key

查看key是否存在

EXISTS key

5. redis数据类型

redis存在5种基本数据类型和三种特殊类型

5.1 String类型

例举:分布式锁、Session相关、验证码相关、计数相关(点击量/阅读量/关注)等单一的值。

5.1.1 取值赋值

关键词:set get append strlen

127.0.0.1:6379> set stu1 zhangsan  ##基本使用设置值
OK
127.0.0.1:6379> get stu1
"zhangsan"
127.0.0.1:6379> APPEND stu1 shigehaoren ##追加值
(integer) 19
127.0.0.1:6379> get stu1
"zhangsanshigehaoren"
127.0.0.1:6379> STRLEN stu1  ##获取字符串长度
(integer) 19
127.0.0.1:6379>
5.1.2 加减操作

increment 自增1 decrement 自减

incrby 固定增量 自定义

decrby 固定减量 自定义

##值
++incr key
##值--
decr key
##增长指定的长度
incrby key number
##减少指定的长度
decrby key number
127.0.0.1:6379> set fans 0
OK
127.0.0.1:6379> INCR fans ##值++
(integer) 1
127.0.0.1:6379> INCR fans
(integer) 2
127.0.0.1:6379> INCR fans
(integer) 3
127.0.0.1:6379> get fans
"3"
127.0.0.1:6379> DECR fans ##值--
(integer) 2
127.0.0.1:6379> DECR fans
(integer) 1
127.0.0.1:6379> DECR fans
(integer) 0
127.0.0.1:6379> DECR fans
(integer) -1
127.0.0.1:6379> INCRBY stu1 100  ##值固定+N
(integer) 100
127.0.0.1:6379> INCRBY stu1 100
(integer) 200
127.0.0.1:6379> INCRBY stu1 100
(integer) 300
127.0.0.1:6379> DECRBY stu1 50 ##值固定-N
(integer) 250
127.0.0.1:6379> DECRBY stu1 50
(integer) 200
127.0.0.1:6379> 
5.1.3 范围操作 range

getrange key index1 index2 获取当前指定范围 如果最大长度 index2 可替为-1

127.0.0.1:6379> set content hellojiazongnihao
OK
127.0.0.1:6379> get content
"hellojiazongnihao"
127.0.0.1:6379> STRLEN content  
(integer) 17
127.0.0.1:6379> GETRANGE content 5 11
"jiazong"
127.0.0.1:6379> get content
"hellojiazongnihao"
127.0.0.1:6379> GETRANGE content 0 -1 # 获取全部字符串
"hellojiazongnihao"
127.0.0.1:6379> 
5.1.4 替换操作

setrange key offset index 内容 ##offset 偏移量 指定位置开始替换

127.0.0.1:6379> GETRANGE content 0 -1
"hellojiazongnihao"
127.0.0.1:6379> SETRANGE content 5 sunzong #将sunzong覆盖jiazong
(integer) 17
127.0.0.1:6379> GETRANGE content 0 -1
"hellosunzongnihao"
127.0.0.1:6379> SETRANGE content 5 wang #将wang覆盖jiaz
(integer) 17
127.0.0.1:6379> GETRANGE content 0 -1
"hellowangongnihao"
127.0.0.1:6379> 
5.1.5 判断是否存在
EXISTS t6 判断值是否存在
#set with expire #如果存在设置消失时间及信息值 -- 订单
setex key time v 既能增加过期时间还能够重新指定新的值,如果当前key不存在创建一个新的key和值并指定过期时间
#如果一个值不想改变只想重新设定时间
127.0.0.1:6379> set message 1089
127.0.0.1:6379> expire message 300
#如果既想刷新时间又想改变值
127.0.0.1:6379> setex message 300 8888
#set if not expire #如果不存在 默认会创建一个新的,存在不操作 / 及判断存在不存在同时也能根据结果进行进一步操作
setnx key v
# 如果值存在 ,不作处理
127.0.0.1:6379> setnx message ooooooooooooooo
(integer) 0
# 如果值不存在, 执行创建
127.0.0.1:6379> setnx mess ooooooooooooooo
(integer) 1
5.1.6 批量值操作

more set

mset k1 v1 k2 v2

mget k1 k2

127.0.0.1:6379> mset b 123 c 123 d 123
OK
127.0.0.1:6379> get c
"123"
127.0.0.1:6379> mget a b c d 
1) "123"
2) "123"
3) "123"
4) "123"127.0.0.1:6379> 
5.1.7 关于对象的存储
#普通的json对象
{id:1,name:zhangsan,age:40}
#redis存储的对象
{class:[{},{}]}
{id:1,name:zhangsan,age:40}
{id:2,name:lisi,age:14}

将这个对象存储到redis中, key user 注意:字符串需要去掉引号

127.0.0.1:6379> set user {id:1,name:"zhangsan",age:40}
Invalid argument(s)
127.0.0.1:6379> set user {id:1,name:zhangsan,age:40}
OK
127.0.0.1:6379> get user
"{id:1,name:zhangsan,age:40}"
127.0.0.1:6379>

希望再存储一个李四,key 希望叫user 但是直接用会被覆盖,取值也不方便。

建议: 组合键(这个组合键目的就是将对象中的属性和值打散,通过组合不同的key到redis中读取不同的值)

方式1 : 可能后面根据ID读取某一个对象的情况

127.0.0.1:6379> set user:1 {id:1,name:zhangsan,age:40}
OK
127.0.0.1:6379> set user:2 {id:2,name:lisi,age:14}
OK
127.0.0.1:6379> keys user*
1) "user:2"
2) "user:1"
127.0.0.1:6379> get user:2
"{id:2,name:lisi,age:14}"
127.0.0.1:6379> 

方式2:存在通过对象-id-属性,查询某些数据

set user:1:id 1
set user:1:name zhangsan
set user:1:age 14
set user:2:id 2
set user:2:name lisi
set user:2:age 14
 #查询所有
127.0.0.1:6379> keys user*
1) "user:2:age"
2) "user:1:age"
3) "user:1:id"
4) "user:2:id"
5) "user:1:name"
6) "user:2:name"
# 根据id查询
127.0.0.1:6379> keys user:1:*
1) "user:1:age"
2) "user:1:id"
3) "user:1:name"
# 根据具体条件查询
127.0.0.1:6379> get user:2:name
"lisi"
127.0.0.1:6379> 
5.1.8 取值赋值操作

getset 先取值,再赋值

127.0.0.1:6379> getset student zhangsan
(nil)
127.0.0.1:6379> getset student lisi
"zhangsan"
127.0.0.1:6379> getset student wangwu
"lisi"
127.0.0.1:6379> get student
"wangwu"
127.0.0.1:6379> 
5.1.9 关于浮点类型的增减操作

incr decr 直接使用会报错

127.0.0.1:6379> incr price
(error) ERR value is not an integer or out of range

关键词: incrbyfloat 不支持直接+1-1操作 支持固定增减量操作

127.0.0.1:6379> incrbyfloat price 9.9
"19.8"
5.1.10 删除数据

del key …

127.0.0.1:6379> del ww
127.0.0.1:6379> del nn oo

5.2 List列表类型

list列表类型,特点:所有的命令操作都是使用l开始。 和链表/队列比较相似,可以通过首尾进行操作。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YRhlh7Cp-1680569199514)(D:\word\Typora\image\1680185841475.png)]

工作中主要实现一些列表数据(导航、数据集List 队列消息,主要列表相关都可以存储)

5.2.1 基本赋值和取值操作

保留案例:导航信息存储

默认左侧插入值: lpush l意思 list

右侧插入值:rpush r意思 right

查询数据:lrange key start end

获取长度 llen key

127.0.0.1:6379> lpush stu zhangsan lisi
(integer) 2  #返回list长度
127.0.0.1:6379> lrange stu 0 -1  #取值
1) "lisi"
2) "zhangsan"
127.0.0.1:6379> lpush stu wangwu 
(integer) 3
127.0.0.1:6379> lrange stu 0 -1
1) "wangwu"
2) "lisi"
3) "zhangsan"
127.0.0.1:6379> rpush stu zhaoliu  # 结尾插入值
(integer) 4
127.0.0.1:6379> lrange stu 0 -1
1) "wangwu"
2) "lisi"
3) "zhangsan"
4) "zhaoliu"
127.0.0.1:6379> llen stu  # 获取当前列表长度
5.2.2 list结构的删除数据操作

删除整个list列表

del key  #这种是直接将整个列表都删除 del删除删除任意类型

删除list列表中的某一些数据

关键词:pop 弹出 抛出

语法:

lpop count 左侧弹出

 #没给count值,默认左 弹出1 返回的信息是弹出的值
127.0.0.1:6379> lpop stu
"lisi"
#count 提供值,可以弹出多个,返回值就是弹出的值得列表
127.0.0.1:6379> lpop stu 10
1) "zhangsan"

rpop count 右侧弹出

127.0.0.1:6379> rpop stu
 "zhangsan"
127.0.0.1:6379> rpop stu 1
1) "lisi"
127.0.0.1:6379> 

删除指定值 list remove

关键词:lrem key count value ---- 默认从左侧开始查询并删除

127.0.0.1:6379> lpush stu wangwu zhaoliu zhangsan lisi wangwu tianqi zhangsan 
zhangsan
127.0.0.1:6379> lrem stu 2 zhangsan
#count正数 从左向右删除 count负数, 从右向左删除
127.0.0.1:6379> lrange stu 0 -1
5.2.3索引相关 index

通过索引获取数据

lindex key index

127.0.0.1:6379> lindex stu 1

截取列表中一部分值

trim 在Java和脚本中是去掉前后空格,本身意思 修剪、整理。 在redis中作用域截取部分值,截取完成只保留获取的内容。

ltrim key start end

127.0.0.1:6379> ltrim stu 1 4

列表中替换值

lset

127.0.0.1:6379> lset stu 1 zhaoliu
5.2.4 其他

在指定的一个位置前面插入值(不是索引)

在指定的一个位置后面插入值(不是索引)

linsert key before|after 原始值位置 新的值

127.0.0.1:6379> linsert stu before zhaoliu lisi
127.0.0.1:6379> linsert stu after lisi wangwu

两个列表值交换 需要两个列表, 第一个列表中的最后一个值插入到第二个列表中的第一位

rpoplpush r pop l push (右弹出左推进) 如果list1和list2为同一个,最后一个值置顶

rpoplpush list1[第一个列表] list2[第二个列表]

127.0.0.1:6379> rpoplpush stu teacher
#置顶数据
127.0.0.1:6379> rpoplpush teacherteacher

5.3 Set集合类型

特点: 无序、不能重复 所有命令 s 开头

用途:点赞,签到,like等功能、抽奖功能

5.3.1 基本操作

通过测试:重复的值无法添加成功,并且值得顺序也不是固定的。

添加值

sadd key v…

127.0.0.1:6379> sadd book sanguoyanyi jinpingmei hongloumeng

查询值

smembers members 成员 组成

127.0.0.1:6379> smembers book

通用删除 del key 将整个集合直接删除

删除指定指定的元素 srem key value

127.0.0.1:6379> srem book jinpingmei
5.3.2 其他操作

判断当前集合中是否存在指定的值 s is member 是否是成员

语法 sismember key v

127.0.0.1:6379> sismember book jinpingmei
(integer) 1
127.0.0.1:6379> sismember book jinpingmei2
(integer) 0

查看当前集合中值个数 s card

语法:sacrd key

127.0.0.1:6379> scard book

(integer) 4

随机获取和随机删除

随机获取: srandmember [ s random member ]

语法:srandmember key count

127.0.0.1:6379> srandmember book
"jinpingmei"
127.0.0.1:6379> srandmember book 2
1) "hongloumeng"
2) "jinpingmei"

随机删除: spop

语法: spop key count

127.0.0.1:6379> spop book 
"jinpingmei"
127.0.0.1:6379> spop book 10
1) "hongloumeng"
2) "sanguoyanyi"
5.3.3 特殊操作

解决中文问题

[root@sunwz ~]# redis-cli -h 127.0.0.1 -p 6379 --raw

差集 diff 差异 只返回第一个集合的差值

127.0.0.1:6379> sadd zb1 xiaozhi dasima caixukun lijiaqi
4
127.0.0.1:6379> sadd zb2 liziqi xiaozhi dasima wangdaxian xuxubaobao
5
127.0.0.1:6379> sdiff zb1 zb2
caixukun
lijiaqi
127.0.0.1:6379> sdiff zb2 zb1
wangdaxian
liziqi
xuxubaobao
127.0.0.1:6379> 

交集 sinter 交集

127.0.0.1:6379> sinter zb2 zb1
dasima
xiaozhi

并集 s union 联盟 工会 ---- 去重复

127.0.0.1:6379> sunion zb1 zb2
lijiaqi
dasima
xuxubaobao
xiaozhi
caixukun
wangdaxian
liziqi

5.4 Hash类型(map结构)

key-value(map) 比较适合对象类型的数据存储 key -(key-value)

5.4.1 基础语法

存值(key (key - value))

语法:

hset key k1 v1 k2 v2 k3 v3

hget key k – 获取单个值

#book redis的key name 值得key pingandeshijie 值中的值 (key key value)
127.0.0.1:6379> hset book name pingandeshijie
127.0.0.1:6379> hget book name
pingandeshijie
127.0.0.1:6379> hset book name pingandeshijie price 30 author luyao
#如果存储对象
127.0.0.1:6379> hset student name zhangsan age 19 hobbies chouyanhejiutangtou
#如果存储对象集合
{id:1,name:zhangsan}
{id:2,name:lisi}
127.0.0.1:6379> hset student 1 {id:1,name:zhangsan} 2 {id:2,name:lisi}
127.0.0.1:6379> hget student 2

hmget 名称k k k #获取多个key

127.0.0.1:6379> hmget student 1 2 

获取全部的值 hgetall 名称k

127.0.0.1:6379> hgetall student
1
{id:1,name:zhangsan}
2
{id:2,name:lisi}

通过key删除所有数据 – 通用

127.0.0.1:6379> del student

通过值的key删除指定的数据

127.0.0.1:6379> hdel student 2
1

读取当前hash表(map集合)中数据的个数

127.0.0.1:6379> hlen student
5.4.2 其他用法

判断是否存在

127.0.0.1:6379> hexists student 10
0
127.0.0.1:6379> hexists student 3
1

读取所有的key ----- 1w条数据 取某一些特定的数据,取到所有key 循环 取值 判断 筛选。

127.0.0.1:6379> hkeys student
1
3
2

取所有的value — 案例:Java中想将redis中的hash结构转化为set结构

127.0.0.1:6379> hvals student
{id:1,name:zhangsan}
{id:1,name:zhangsan}
{id:2,name:lisi}

5.5 Zset

有序不重复集合,在set的基础上多增加一个值 set k1 * v1 (zset k1 排序的值(score) v1)

有序不可重复的set集合

主要使用方向:工资、班级成绩等等之类的数据。 或者 权重处理 0 普通 1 重要

5.5.1 基本语法

增加和查询

#emps k
{id:1,name:zhangsan,sal:8000}
{id:2,name:lisi,sal:5000}
{id:3,name:wangwu,sal:12000}

#增加数据
127.0.0.1:6379> zadd emps 8000 {id:1,name:zhangsan,sal:8000}
1
127.0.0.1:6379> zadd emps 5000 {id:2,name:lisi,sal:5000}
1
127.0.0.1:6379> zadd emps 12000 {id:3,name:wangwu,sal:12000}
1
127.0.0.1:6379> zrange emps 0 -1   #查询 - 默认升序
{id:2,name:lisi,sal:5000}
{id:1,name:zhangsan,sal:8000}
{id:3,name:wangwu,sal:12000}
127.0.0.1:6379> zrandmember emps 1  #随机读取
{id:1,name:zhangsan,sal:8000}

删除数据

删除整个key del key

zrem k   v
127.0.0.1:6379> zrem emps {id:2,name:lisi,sal:5000}
1
5.5.2 zset 复杂查询

#显示所有信息,从小到大 (范围) -inf 负无穷 +inf正无穷

语法:** z range by score** key min max — min max使用的是排序字段的值

127.0.0.1:6379> zrangebyscore emps 8000 10000
{id:1,name:zhangsan,sal:8000}
127.0.0.1:6379> zrangebyscore emps -inf +inf
{id:2,name:lisi,sal:5000}
{id:1,name:zhangsan,sal:8000}
{id:3,name:wangwu,sal:12000}

升序操作

#默认取所有数据
127.0.0.1:6379> zrange emps 0 -1
{id:2,name:lisi,sal:5000}
{id:1,name:zhangsan,sal:8000}
{id:3,name:wangwu,sal:12000}
# 根据当前排序结果取所有数据
127.0.0.1:6379> zrangebyscore emps -inf +inf
{id:2,name:lisi,sal:5000}
{id:1,name:zhangsan,sal:8000}
{id:3,name:wangwu,sal:12000}
# 根据当前排序结果取所有数据 并显示排序字段
127.0.0.1:6379> zrangebyscore emps -inf +inf withscores
{id:2,name:lisi,sal:5000}
5000
{id:1,name:zhangsan,sal:8000}
8000
{id:3,name:wangwu,sal:12000}
12000
# 根据当前排序结果取所有数据 并显示排序字段 并截取某一部分值
127.0.0.1:6379> zrangebyscore emps -inf +inf withscores limit 0 1
{id:2,name:lisi,sal:5000}
5000
# 取排序字段值大于**的数据
127.0.0.1:6379> zrangebyscore emps 8000 +inf 
{id:1,name:zhangsan,sal:8000}
{id:3,name:wangwu,sal:12000}
# 取排序字段值小于**的数据
127.0.0.1:6379> zrangebyscore emps -inf 8000 
{id:2,name:lisi,sal:5000}
{id:1,name:zhangsan,sal:8000}
127.0.0.1:6379>

降序操作

z rev range k start stop start stop取索引的范围。

127.0.0.1:6379> zrange emps 0 -1
{id:2,name:lisi,sal:5000}
{id:1,name:zhangsan,sal:8000}
{id:3,name:wangwu,sal:12000}
127.0.0.1:6379> zrangebyscore emps -inf +inf
{id:2,name:lisi,sal:5000}
{id:1,name:zhangsan,sal:8000}
{id:3,name:wangwu,sal:12000}
127.0.0.1:6379> zrevrange emps 0 -1
{id:3,name:wangwu,sal:12000}
{id:1,name:zhangsan,sal:8000}
{id:2,name:lisi,sal:5000}
127.0.0.1:6379> zrevrangebyscore emps  +inf -inf
{id:3,name:wangwu,sal:12000}
{id:1,name:zhangsan,sal:8000}
{id:2,name:lisi,sal:5000}
127.0.0.1:6379> zrevrangebyscore emps  +inf -inf withscores
{id:3,name:wangwu,sal:12000}
12000
{id:1,name:zhangsan,sal:8000}
8000
{id:2,name:lisi,sal:5000}
5000
127.0.0.1:6379> zrevrangebyscore emps  +inf -inf withscores limit 0 1
{id:3,name:wangwu,sal:12000}
12000
127.0.0.1:6379> zrevrangebyscore emps  +inf 8000
{id:3,name:wangwu,sal:12000}
{id:1,name:zhangsan,sal:8000}
127.0.0.1:6379> zrevrangebyscore emps  8000 -inf
{id:1,name:zhangsan,sal:8000}
{id:2,name:lisi,sal:5000}
127.0.0.1:6379> 

6. redis事务

前言: 事务自从出现就是高危面试题,无论MySQL还是redis都需要做到必会且熟练对答。

6.1 理论

【夺命三段斩】

redis 事务可以一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。redis事务没有隔离级别的概念,所以不会出现脏读、幻读、不可重复读,因为队列就有序列化的概念,你可以认为默认就是MySQL序列化级别。

redis采用了乐观锁方式进行事务控制,它使用watch命令监视给定的key,当exec(提交事务)时候如果监视的key从调用watch后发生过变化,则整个事务会失败。也可以调用watch多次监视多个key。注意watch的key是对整个连接有效的,如果连接断开,监视和事务都会被自动清除。当然exec,discard,unwatch命令都会清除连接中的所有监视。

redis保证一个事务中的所有命令要么都执行,要么都不执行。如果在发送EXEC命令前客户端断线了,则Redis会清空事务队列,事务中的所有命令都不会执行。而一旦客户端发送了EXEC命令,所有的命令就都会被执行,即使此后客户端断线也没关系,因为Redis中已经记录了所有要执行的命令。 -------前面话述中看似redis是能够保证原子性,但是保证原子性是有特殊的前提的,首先单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。可能有人问redis因为使用watch命令监视再加上乐观锁应该能保证原子性了,但实际事务执行过程中因为编译(命令)导致的错误可以保证原子性,但是运行的错误(命令正确,但是结果出错误)就不能够保证原子性了。实操部分会给大家演示。

6.2 实践

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ySKg8P11-1680569199516)(D:\word\Typora\image\1680429025071.png)]

事务从开始到执行会经历以下三个阶段:

  1. 开始事务(NUITI)
  2. 命令入队(输出queued)
  3. 执行事务(EXEC) / 取消事务(DISCARD)
6.2.1 标准事务执行 EXEC

组执行的命令绑定成一个执行的事务单元

127.0.0.1:6379> MULTI  #开启事务
OK
127.0.0.1:6379> set zhangsan 100 #增加信息
QUEUED #入列但不执行
127.0.0.1:6379> set lisi 200 #增加信息
QUEUED
127.0.0.1:6379> get zhangsan #读取信息
QUEUED
127.0.0.1:6379> set wangwu 50000 #增加信息
QUEUED
127.0.0.1:6379> EXEC #提交事务
1) OK
2) OK
3) "100"
4) OK
6.2.2 放弃事务执行DISCARD

在关系型数据库中,之所以有回滚的方法是因为在事务执行的过程中(增加)在commit之前已经操作了数据库,那么一旦出现问题,我们必须提供一个回滚的方法来回到最初的状态。 — 占ID

reids中,我们执行的命令都在队列中(没有执行),所以就没有回滚的概念,此时你只要放弃提交(取消执行),那么数据就是最初状态了。

127.0.0.1:6379> MULTI #开启事务

OK

127.0.0.1:6379> set k1 v1 #执行增加

QUEUED

127.0.0.1:6379> set k2 v2

QUEUED

127.0.0.1:6379> DISCARD #在出现一些特殊情况的时候 例如set k2的代码(java)通过if判断当前值不可用,那么当前事务就没有意义了,那么可以直接取消事务

OK

127.0.0.1:6379> get k1

(nil)

127.0.0.1:6379>

6.2.3 redis事务保证原子性测试

redis事务中执行编译错误(命令错误)是保证原子性的,

但是redis运行错误(命令正确,但是结果出错误),事务是不保证原子性的。

相当于Java中两种异常状态 编译异常 运行异常1/0

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> sss 2  #命令错误
ERR unknown command `sss`, with args beginning with: `2`,
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> EXEC
EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k2
6.2.4 redis事务不保证原子性测试
127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379> set k1 v1  #设置值
QUEUED
127.0.0.1:6379> set k2 v2 #设置值
QUEUED
127.0.0.1:6379> set k3 "jiazong" #设置字符串
QUEUED
127.0.0.1:6379> INCRBY k3 100 #自增一个字符串
QUEUED
127.0.0.1:6379> set k4 v4 #设置值
QUEUED
127.0.0.1:6379> EXEC #提交事务
1) OK
2) OK
3) OK
4) (error) ERR value is not an integer or out of range #报错的位置,但是其他的命令都执行
5) OK
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k4
"v4"
127.0.0.1:6379> get k3
"jiazong"
127.0.0.1:6379> 

6.3 测试redis乐观锁

redis默认使用的是乐观锁保证事务原子性。

悲观锁: 认为什么时候都会出问题,无论做什么都需要加锁。synchronized – 特别消耗性能

乐观锁: 认为什么时候都不会出现问题,所以不会上锁,但是执行的时候会经过判断是否有修改,MySQL中使用version判断,正确提交,错误不提交。

redis 中通过 Watch监视器实现乐观锁功能!

6.3.1 正常执行
127.0.0.1:6379> set money 100 #设定100元
OK
127.0.0.1:6379> set out 0 #花销 0
OK
127.0.0.1:6379> WATCH money #设定监控 钱的变化
OK
127.0.0.1:6379> MULTI #开启事物
OK
127.0.0.1:6379> DECRBY money 20 #我自己花销20
QUEUED
127.0.0.1:6379> INCRBY out 20 #花销记录中增加20
QUEUED
127.0.0.1:6379> EXEC #提交事务
1) (integer) 80
2) (integer) 20
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> get out
"20"
127.0.0.1:6379> 
6.3.2 异常执行案例

模拟有其他人干扰 - 开两个客户端代表多线程。

客户端1

127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> WATCH money
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY out 20
QUEUED
127.0.0.1:6379> 
#没有提交事务

客户端2 – 去操作钱

127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379>

提交客户端1

127.0.0.1:6379> EXEC  #提交失败!原因就是watch启动了监视功能,一旦发生变化,放弃事务
6.3.3 关于废弃锁处理

旧的监视因为已经出错,需要解锁否则事务处理会出问题。

127.0.0.1:6379> UNWATCH  #放弃原来的锁
OK
127.0.0.1:6379> WATCH money  #重新启动监视,重新加锁
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY money 200
QUEUED
127.0.0.1:6379> INCRBY out 200
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 800
2) (integer) 200
127.0.0.1:6379>

9. redis持久化

9.1 理论

前面我们说过,Redis 相对于 Memcache 等其他的缓存产品,有一个比较明显的优势就是 Redis 不仅仅支持简单的key-value类型的数据,

同时还提供list,set,zset,hash等数据结构的存储。这几种丰富的数据类型我们

接下来我们要介绍 Redis 的另外一大优势——持久化。

由于 Redis 是一个内存数据库,所谓内存数据库,就是将数据库中的内容保存在内存中,

这与传统的MySQL,Oracle等关系型数据库直接将内容保存到硬盘中相比,内存数据库的读写效率比传统数

据库要快的多(内存的读写效率远远大于硬盘的读写效率)。

但是保存在内存中也随之带来了一个缺点,一旦断电或者宕机,那么内存数据库中的数据将会全部丢失。

为了解决这个缺点,Redis提供了将内存数据持久化到硬盘,以及用持久化文件来恢复数据库数据的功能。Redis 支持两种形式的持久化,一种是RDB快照(snapshotting),另外一种是AOF(append-only-file)

数据库存储方式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OlFlARVX-1680569199516)(D:\word\Typora\image\1680430383363.png)]

NoSQL存储方式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4sKvFTsB-1680569199517)(D:\word\Typora\image\1680430419109.png)]

9.2 RDB(redis DateBase)

9.2.1 概述

RDB是Redis用来进行持久化的一种方式,是把当前内存中的数据集快照写入磁盘,也就是 Snapshot 快照(数据库中所有键值对数据)。恢复时是将快照文件直接读到内存里。

触发方式

RDB 有两种触发方式,分别是自动触发和手动触发。

9.2.2 自动触发

查看redis配置文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-owPbNt51-1680569199517)(D:\word\Typora\image\1680430562818.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IbaIC3Vn-1680569199518)(D:\word\Typora\image\1680430611389.png)]

save: 这里是用来配置触发 Redis的 RDB 持久化条件,也就是什么时候将内存中的数据保存到硬盘。比如“save m n”。表示m秒内数据集存在n次修改时,自动触发bgsave(这个命令下面会介绍,手动触发RDB持久化的命令)。

rdbcompression :默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩。如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能,但是存储在磁盘上的快照会比较大。

stop-writes-on-bgsave-error :默认值为yes。当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了。

rdbchecksum :默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。

dbfilename :设置快照的文件名,默认是 dump.rdb

dir:设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。默认是和当前配置文件保存在同一目录。

9.2.3 测试自动触发

1 添加数据测试, 查找数据库文件

2 改变文件名称

3 查看快照路径

9.2.4 手动触发

手动触发Redis进行RDB持久化的命令有两种:

save:该命令会阻塞当前Redis服务器,执行save命令期间,redis不能处理其他命令,直到RDB过程完成为止。显然该命令对于内存比较大的实例会造成长时间阻塞,这是致命的缺陷,为了解决此问题,redis提供了第二种方式。(不建议使用)

bgsave:执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。具体操作是Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在创建子进程fork阶段,一般时间很短。

基本上 Redis 内部所有的RDB操作都是采用 bgsave 命令。

还有两种特殊的操作也能触发RDB的持久化,但是因为情况特殊,所以不作为手动触发条件

#执行执行 flushall 命令,也会产生dump.rdb文件,但里面是空的,无意义

#关闭redis 服务同样会生成 — 规则使用 bgsave 保存数据

9.2.5 测试手动触发
9.2.6 RDB数据恢复(企业管理者经常用的手段)

将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可,redis就会自动加载文件数据至内存了。Redis 服务器在载入 RDB 文件期间,会一直处于阻塞状态,直到载入工作完成为止。

启动服务器的当前目录一定是redis-*** 否则快照生成的路径就会发生错误!

127.0.0.1:6379> config get dir
dir
/opt/redis-7.0.4

测试文件恢复

9.2.7 停止 RDB 持久化

有些情况下,我们只想利用Redis的缓存功能,并不像使用 Redis 的持久化功能,那么这时候我们最好停掉 RDB 持久化。可以通过上面讲的在配置文件 redis.conf 中,可以注释掉所有的 save 行来停用保存功能或者直接一个空字符串来实现停用:save “”

#save 60 10000
save ""

也可以执行命令

redis-cli config set save ""
9.2.8 RDB 的优势和劣势(高危面试题)

优势

  1. RDB是一个非常紧凑的文件(默认压缩),它保存redis 在某个时间点上的数据集。这种文件非常适合用于备份和灾难恢复。
  2. 生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
  3. RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

劣势

  1. RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作(内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑),频繁执行成本过高(影响性能)
  2. RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题(版本不兼容)
  3. 在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改(数据有丢失)
9.2.9 RDB 自动保存的原理(高低薪差异题)

redis有个服务器状态结构:

struct redisService{
	//1、记录保存save条件的数组
	struct saveparam *saveparams;
	 //2、修改计数器
	long long dirty;
	 //3、上一次执行保存的时间
	 time_t lastsave;

}

首先看记录保存save条件的数组 saveparam,里面每个元素都是一个 saveparams 结构:

struct saveparam{    
 	//秒数
    time_t seconds;
    //修改数
     int changes;
};

前面我们在 redis.conf 配置文件中进行了关于save 的配置:

save 3600 1 :表示3600 秒内如果至少有 1 个 key 的值变化,则保存
save 300 100:表示300 秒内如果至少有 10 个 key 的值变化,则保存
save 60 10000:表示60 秒内如果至少有 10000 个 key 的值变化,则保存

那么服务器状态中的saveparam 数组将会是如下的样子:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w8lRXS9m-1680569199519)(D:\word\Typora\image\1680431271084.png)]

dirty 计数器和lastsave 属性

dirty 计数器记录距离上一次成功执行 save 命令或者 bgsave 命令之后,Redis服务器进行了多少次修改(包括写入、删除、更新等操作)。

lastsave 属性是一个时间戳,记录上一次成功执行 save 命令或者 bgsave 命令的时间。

执行原理:

通过这两个命令,当服务器成功执行一次修改操作,那么dirty 计数器就会加 1,而lastsave 属性记录上一次执行save或bgsave的时间,Redis 服务器还有一个周期性操作函数 severCron ,默认每隔 100 毫秒就会执行一次,该函数会遍历并检查 saveparams 数组中的所有保存条件,只要有一个条件被满足,那么就会执行 bgsave 命令。

执行完成之后,dirty 计数器更新为 0 ,lastsave 也更新为执行命令的完成时间。

9.3 AOF

9.3.1 概述

Redis的持久化方式之一RDB是通过保存数据库中的键值对来记录数据库的状态。而另一种持久化方式 AOF 则是通过保存Redis服务器所执行的写命令来记录数据库状态。

AOF以协议文本的方式,将所有对数据库进行写入的命令(及其参数)记录到 AOF 文件,以此达到记录数据库状态的目的。

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

比如对于如下命令:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FhKfr0HT-1680569199519)(D:\word\Typora\image\1680521197781.png)]

RDB 持久化方式就是将 str1,str2,str3 这三个键值对保存到 RDB文件中,而 AOF 持久化则是将执行的 set,sadd,lpush 三个命令保存到 AOF 文件中。

9.3.2 AOF配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OZwDetiR-1680569199520)(D:\word\Typora\image\1680521256212.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QoGQEPI2-1680569199520)(D:\word\Typora\image\1680521284907.png)]

appendonly:默认值为no,也就是说redis 默认使用的是rdb方式持久化,如果想要开启 AOF 持久化方式,需要将appendonly 修改为 yes。

appendfilename :aof文件名,默认是"appendonly.aof"

appendfsync**:**aof持久化策略的配置;

  1. no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快,但是不太安全;
  2. always表示每次写入都执行fsync,以保证数据同步到磁盘,效率很低;
  3. everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。通常选择 everysec ,兼顾安全性和效率。(默认)

no-appendfsync-on-rewrite:在aof重写或者写入aof文件的时候,会执行大量IO,此时对于everysec和always的aof模式来说,执行fsync会造成阻塞过长时间,no-appendfsync-on-rewrite字段设置为默认设置为no。如果对延迟要求很高的应用,这个字段可以设置为yes,否则还是设置为no,这样对持久化特性来说这是更安全的选择。 设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yes。Linux的默认fsync策略是30秒。可能丢失30秒数据。默认值为no。

auto-aof-rewrite-percentage:默认值为100。aof自动重写配置,当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文件增长到一定大小的时候,Redis能够调用bgrewriteaof对日志文件进行重写。当前AOF文件大小是上次日志重写得到AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程。 64M - 40M - 80M(55M) - 110M(70M)

auto-aof-rewrite-min-size:64mb。设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写。

aof-load-truncated:aof文件可能在尾部是不完整的,当redis启动的时候,aof文件的数据被载入内存。重启可能发生在redis所在的主机操作系统宕机后,出现这种现象 redis宕机或者异常终止不会造成尾部不完整现象,可以选择让redis退出,或者导入尽可能多的数据。如果选择的是yes,当截断的aof文件被导入的时候,会自动发布一个log给客户端然后load。如果是no,用户必须手动redis-check-aof修复AOF文件才可以。默认值为 yes。

9.3.3 开启AOF

默认不开启,需要手动开启 - AOF和RDB同时开启 ,默认走AOF策略

appendonly yes

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ApN8vK3h-1680569199521)(D:\word\Typora\image\1680521524424.png)]

注意配置完成后重启redis,并set几个值,# vim查看aof文件

#7.04 版本 aof文件存储已经更改到appendonlydir文件夹下,对应去查找 appendonly.aof.1.incr.aof

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qS5DNhNF-1680569199521)(D:\word\Typora\image\1680521559102.png)]

所以这里就是以日志的形式默认记录所有的写操作,如果这个文件遭到破坏,可以通过以下这个命令进行恢复

9.3.4 AOF文件故障修复
#关闭redis
#删除dump.rdb
#随便修改点aof文件
#重新启动redis

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yzoNpNJg-1680569199522)(D:\word\Typora\image\1680521680179.png)]

通过redis-check-aof --fix 工具 修复文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v2DxiAwH-1680569199522)(D:\word\Typora\image\1680521703494.png)]

修复后重新启动。

9.3.5 AOF文件重写机制

AOF 文件包含三类文件:基本文件、增量文件与清单文件。其中基本文件一般为 rdb 格式,就是 rdb 持久化的数据文件。

由于AOF持久化是Redis不断将写命令记录到 AOF 文件中,随着Redis不断的进行,AOF 的文件会越来越大,文件越大,占用服务器内存越大以及 AOF 恢复要求时间越长。为了解决这个问题,Redis新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。可以使用命令 bgrewriteaof来重写。

比如对于如下命令:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3PHaDdrC-1680569199522)(D:\word\Typora\image\1680521762774.png)]

如果不进行 AOF 文件重写,那么 AOF 文件将保存四条 SADD 命令,如果使用AOF 重写,那么AOF 文件中将只会保留下面一条命令:

sadd animals “dog” “tiger” “panda” “lion” “cat”

也就是说 AOF 文件重写并不是对原文件进行重新整理,而是直接读取服务器现有的键值对,然后用一条命令去代替之前记录这个键值对的多条命令,生成一个新的文件后去替换原来的 AOF 文件。

AOF 重写触发机制:

通过 redis.conf 配置文件中的 auto-aof-rewrite-percentage:默认值为100,以及auto-aof-rewrite-minsize:64mb 配置,也就是说默认Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发。

这里再提一下,我们知道 Redis 是单线程工作,如果重写 AOF 需要比较长的时间,那么在重写 AOF 期间,Redis将长时间无法处理其他的命令,这显然是不能忍受的。Redis为了克服这个问题,解决办法是将 AOF 重写程序放到子程序中进行,这样有两个好处:

  1. 子进程进行 AOF 重写期间,服务器进程(父进程)可以继续处理其他命令。
  2. 子进程带有父进程的数据副本,使用子进程而不是线程,可以在避免使用锁的情况下,保证数据的安全性。

使用子进程解决了上面的问题,但是新问题也产生了:因为子进程在进行 AOF 重写期间,服务器进程依然在处理其它命令,这新的命令有可能也对数据库进行了修改操作,使得当前数据库状态和重写后的 AOF 文件状态不一致。

为了解决这个数据状态不一致的问题,Redis 服务器设置了一个 AOF 重写缓冲区,这个缓冲区是在创建子进程后开始使用,当Redis服务器执行一个写命令之后,就会将这个写命令也发送到 AOF 重写缓冲区。当子进程完成 AOF 重写之后,就会给父进程发送一个信号,父进程接收此信号后,就会调用函数将 AOF 重写缓冲区的内容都写到新的 AOF 文件中。

这样将 AOF 重写对服务器造成的影响降到了最低。

9.3.6 AOF的优缺点

优点:

  1. AOF 持久化的方法提供了多种的同步频率,即使使用默认的同步频率每秒同步一次,最多也就丢失1秒的数据而已。
  2. AOF 文件使用 Redis 命令追加的形式来构造,因此,即使 Redis 只能向 AOF 文件写入命令的片断,使用 redis-check-aof 工具也很容易修正 AOF 文件。
  3. AOF 文件的格式可读性较强,这也为使用者提供了更灵活的处理方式。例如,如果我们不小心错用了 FLUSHALL 命令,在重写还没进行时,我们可以手工将最后的 FLUSHALL 命令去掉,然后再使用 AOF 来恢复数据。

缺点:

  1. 对于具有相同数据的的 Redis,AOF 文件通常会比 RDB 文件体积更大。
  2. 虽然 AOF 提供了多种同步的频率,默认情况下,每秒同步一次的频率也具有较高的性能。但在 Redis 的负载较高时,RDB 比 AOF 具好更好的性能保证。
  3. RDB 使用快照的形式来持久化整个 Redis 数据,而 AOF 只是将每次执行的命令追加到 AOF 文件中,因此从理论上说,RDB 比 AOF 方式更健壮。官方文档也指出,AOF 的确也存在一些 BUG,这些 BUG 在 RDB 没有存在。
9.3.7 关于RDB和AOF的思考和选择

如果可以忍受一小段时间内数据的丢失,毫无疑问使用 RDB 是最好的,定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快,而且使用 RDB 还可以避免 AOF 一些隐藏的 bug;否则就使用 AOF 重写。但是一般情况下建议不要单独使用某一种持久化机制,而是应该两种一起用,在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。Redis后期官方可能都有将两种持久化方式整合为一种持久化模型。

RDB VS AOF

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-quwDAVCe-1680569199523)(D:\word\Typora\image\1680521996836.png)]

10. redis发布订阅

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

Redis 的 subscribe 命令可以让客户端订阅任意数量的频道, 每当有新信息发送到被订阅的频道时, 信息就会被发送给所有订阅指定频道的客户端。

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5o89XvbP-1680569199523)(D:\word\Typora\image\1680522065967.png)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CWhB19Kj-1680569199524)(D:\word\Typora\image\1680522096810.png)]

为什么要用发布订阅?

熟悉消息中间件的同学都知道,针对消息订阅发布功能,市面上很多大厂使用的是kafka、RabbitMQ、ActiveMQ, RocketMQ等这几种,redis的订阅发布功能跟这三者相比,相对轻量,针对数据准确和安全性要求没有那么高可以直接使用,适用于小公司。

Redis有两种发布/订阅模式:

基于频道(Channel)的发布/订阅

基于模式(pattern)的发布/订阅(可以自学)

基于频道(Channel)的发布/订阅 操作命令如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h8rH2289-1680569199524)(D:\word\Typora\image\1680522149584.png)]

“发布/订阅” 包含2种角色:发布者和订阅者。发布者可以向指定的频道(channel)发送消息;订阅者可以订阅一个或者多个频道(channel),所有订阅此频道的订阅者都会收到此消息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Hal8ICL-1680569199525)(D:\word\Typora\image\1680522173312.png)]

订阅者订阅频道 subscribe channel [channel …]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ik34RrOk-1680569199525)(D:\word\Typora\image\1680522197365.png)]

发布者发布消息 publish channel message

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Px9mv5li-1680569199525)(D:\word\Typora\image\1680522225558.png)]

客户端2 (发布者) 发布消息给频道后,此时我们再来观察 客户端1 (订阅者) 的客户端窗口变化:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KnMbVAwE-1680569199526)(D:\word\Typora\image\1680522256691.png)]

https://blog.csdn.net/w15558056319/article/details/121490953

11. redis集群

11.1 概念

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iaMx21v0-1680569199527)(D:\word\Typora\image\1680522410822.png)]

使用redis的复制功能创建主机和从机(一对多) 主从机支持多个数据库之间的数据同步。一类是主数据库(master主机)一类是从数据库(slave从机)(主从复制) 读写分离

主数据库可以进行读写操作,当发生写操作的时候自动将数据同步到从数据库,而从数据库一般是只读的(读写分离)

从机接收主数据库同步过来的数据,一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库(只有一个老大)

通过redis的复制功能可以很好的实现数据库的读写分离,提高服务器的负载能力。主数据库主要进行写操作,而从数据库负责读操作。

这样大部分80%的操作都是读取数据,所以在之前给大家介绍的架构图中,读写分离的方式,从而减轻服务器压力,这样也就是集群的环境了。

#一般推荐搭建方式为1主2从为最低配。

主从复制(redis集群)的作用

#1 数据冗余: 主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式,在大数据领域,冗余一般是指一模一样的数据存储多于一份的情况.

#2 数据灾备(故障恢复): 当主节点出现问题时,可以由从节点提供服务,实现快速的故障服务

3 负载均衡:主从复制的基础之上,可以实现读写分离,提高并发了量。

4 高可用(集群)基础:主从复制是哨兵和集群实施的基础,因此说redis的主从复制是高可用的基础(集群环境的基础)

所以在真实的项目中,我们不可能是单机模式,基本都是搭建redis集群,实现高可用和高并发。

11.2 集群基础搭建(单机多集群)

11.2.1 基础命令
#info 查看所有配置信息 -- 信息太多。
#info server 服务器信息
#info clients 表示已连接客户端信息 
#info cpu   CPU 计算量统计信息
#info replication 主从复制信息 **************************

首先查看当前环境信息

#Replication
role:master  #表示当前环境为主机
connected_slaves:0  #集群从机连接的数量
master_failover_state:no-failover
master_replid:a38595b7159c4f304a57e43c6352259afd396799
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
11.2.2 准备工作

将存储方式改为rdb

搭建1主2从集群 6379 6380 6381

多复制2份 redis-config 文件 并修改对应的端口号和dump6379.rdb dump6380.rdb dump6381.rdb

修改pidfile记录文件

修改启动日志文件名

#主机配置 - 6379
#主机端口port --> 6379 不用修改
#pidfile --> 守护进程产生的文件 默认redis_6379 主机也不用改
#日志logfile --> 改成"6379.log"
#数据库文件dbfilename --> dump6379.rdb

#从机配置-1 6380# 主机端口port --> 6380
#pidfile --> 守护进程产生的文件 默认redis_6380
#日志logfile --> 改成"6380.log"
#数据库文件dbfilename --> dump6380.rdb

#从机配置-2 6381
#主机端口port --> 6381
#pidfile --> 守护进程产生的文件 默认redis_6381
#日志logfile --> 改成"6381.log"
#数据库文件dbfilename --> dump6381.rdb
11.2.3 启动集群

启动6379

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yBP7ixgB-1680569199527)(D:\word\Typora\image\1680522965927.png)]

[root@sunwz redis-7.0.4]# redis-server sunwz/redis6379.conf
[root@sunwz redis-7.0.4]# ps -ef | grep redis
root      66509      1  0 00:06 ?        00:00:00 redis-server *:6379
root      66841  65483  0 00:06 pts/0    00:00:00 grep --color=auto redis
[root@sunwz redis-7.0.4]# redis-cli -p 6379 --raw
127.0.0.1:6379> info replication
#Replication
role:master  # 主机
connected_slaves:0
master_failover_state:nofailover
master_replid:a283a0746770b978c00c1768146261d82fe5038c
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
127.0.0.1:6379> 

启动6380

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HB41kqqz-1680569199527)(D:\word\Typora\image\1680523199446.png)]

[root@sunwz redis-7.0.4]# redis-server sunwz/redis6380.conf
[root@sunwz redis-7.0.4]# ps -ef | grep redis
root      66509      1  0 00:06 ?        00:00:00 redis-server *:6379
root      68472  65483  0 00:07 pts/0    00:00:00 redis-cli -p 6379 --raw
root      75357      1  0 00:11 ?        00:00:00 redis-server *:6380
root      66841  65483  0 00:06 pts/0    00:00:00 grep --color=auto redis
[root@sunwz redis-7.0.4]# redis-cli -p 6380 --raw
127.0.0.1:6380> info replication
#Replication
role:master  # 主机
connected_slaves:0
master_failover_state:nofailover
master_replid:a283a0746770b978c00c1768146261d82fe5038c
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
127.0.0.1:6380> 

启动6381

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5F7r3WQl-1680569199528)(D:\word\Typora\image\1680523333739.png)]

[root@sunwz redis-7.0.4]# redis-server sunwz/redis6380.conf
[root@sunwz redis-7.0.4]# ps -ef | grep redis
root      66509      1  0 00:06 ?        00:00:00 redis-server *:6379
root      68472  65483  0 00:07 pts/0    00:00:00 redis-cli -p 6379 --raw
root      75357      1  0 00:11 ?        00:00:00 redis-server *:6380
root      77552  71194  0 00:12 pts/2    00:00:00 redis-cli -p 6380 --raw
root      83211      1  0 00:14 ?        00:00:00 redis-server *:6381
root      66841  65483  0 00:06 pts/0    00:00:00 grep --color=auto redis
[root@sunwz redis-7.0.4]# redis-cli -p 6381 --raw
127.0.0.1:6381> info replication
#Replication
role:master  # 主机
connected_slaves:0
master_failover_state:nofailover
master_replid:a283a0746770b978c00c1768146261d82fe5038c
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
127.0.0.1:6381>
11.2.4 一主二从配置

默认情况下,每一台redis服务器都是主节点 - 所以我们只要配置从机就可以了!!

分别连接客户端(对应端口登录) 通过 info replication查看情况 — 默认都是主机

配置策略 – 小黄人找大哥(认老大,认主)

主(6379) 从(6380,6381)

配置命令

slaveof ip port

#===========6380认老大==============
127.0.0.1:6380> info replication
#Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
#=============6381认老大===============
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
# ==============查看主机==============
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=168,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=168,lag=1

测试:数据是否同步。

注意:我们这里使用的是命令配置的,如果服务器重启就会消失配置,真是的企业环境都配置到配置文件中,这样就是永久配置了。

11.2.5 读写分离(默认)

redis实现了主从复制之后默认就是读写分离的。如果想set数据,那么只能在master主机中才能进行存储。

#主机--写
# 从机--读
# 并且主机中所有的数据都会被从机保存

测试从机写数据

127.0.0.1:6380> set k110 ceshi

(error) READONLY You can’t write against a read only replica.

127.0.0.1:6380>

测试主机宕机从机是否还能正常读取

127.0.0.1:6380> keys *
1) "k1"   #值存在
127.0.0.1:6380> info replication
#Replicationrole: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

测试恢复主机,测试是否还能同步(可以的)

总结:主机断开,从机依然能正常获取数据,但是没有写操作了,当主机恢复后,依然能够同步数据(高可用)

测试从机宕机,主机继续增加数据,查看从机是否获取,从新连接之后继续增加数据查看是否能获取

127.0.0.1:6380> shutdown
not connected> exit
[root@sunwz redis-7.0.4]# redis-server sunwz/redis6380.conf 
[root@sunwz redis-7.0.4]# redis-cli -p 6380 --raw
127.0.0.1:6380> get u
127.0.0.1:6380> info replication
#Replication
role:master
connected_slaves:0
master_failover_state:nofailover
master_replid:d7cead93f6383df180b3c1de6750d425049c8612
master_replid2:c84b86fcd5b2f7d723431edbfe33d1d4abf28152
master_repl_offset:7661
second_repl_offset:7662
repl_backlog_active:1repl_backlog_size:1048576
repl_backlog_first_byte_offset:7662
repl_backlog_histlen:0
127.0.0.1:6380> slaveof 127.0.0.1 6379
OK
127.0.0.1:6380> get u
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:2
master_sync_in_progress:0
slave_read_repl_offset:7928
slave_repl_offset:7928
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:c84b86fcd5b2f7d723431edbfe33d1d4abf28152
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:7928
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:7929
repl_backlog_histlen:0
127.0.0.1:6380> keys *
stu
u
ww
name
127.0.0.1:6380> get u
123
127.0.0.1:6380>

从机宕机后,此时剩余的一主一从可以正常工作,从机6380恢复之后,不能直接同步数据,原因是通过命令进行挂载的主机,重新启动后默认当前6380是主机,如果继续保持这个集群环境,需要我们再次通过 slaveof host port 指定老大,指定之后所有的数据会同步到从机中。

如果我们这个集群通过配置文件搭建的,是否能够避免上述问题。

修改配置文件

#replicaof <masterip> <masterport>
#replicaof 127.0.0.1 6379   #将当前6381 挂载到6379上

重新启动从机

127.0.0.1:6381> 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_read_repl_offset:8754
slave_repl_offset:8754
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:c84b86fcd5b2f7d723431edbfe33d1d4abf28152
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:8754
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:8754
repl_backlog_histlen:476
127.0.0.1:6380> keys *
ww
name
stu
u
11.2.6 主从复制原理(面试题)

全量复制(第一次挂载<手动命令>或从机启动<配置文件>)

1 Slave启动成功连接到master后会发送一个sync(同步)命令;(从机发送sync的同步命令)

2 Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令(收集数据),在后台进程执行完毕之后,

#master将传送整个数据文件到slave,以完成一次完全同步

增量复制(挂载之后主节点再次新增数据)

1 Master继续将新的所有收集到的修改命令依次传给slave,完成同步

注意:但是只要是重新连接master,一次完全同步(全量复制)自动执行。

_repl_offset:7661
second_repl_offset:7662
repl_backlog_active:1repl_backlog_size:1048576
repl_backlog_first_byte_offset:7662
repl_backlog_histlen:0
127.0.0.1:6380> slaveof 127.0.0.1 6379
OK
127.0.0.1:6380> get u
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:2
master_sync_in_progress:0
slave_read_repl_offset:7928
slave_repl_offset:7928
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:c84b86fcd5b2f7d723431edbfe33d1d4abf28152
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:7928
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:7929
repl_backlog_histlen:0
127.0.0.1:6380> keys *
stu
u
ww
name
127.0.0.1:6380> get u
123
127.0.0.1:6380>


从机宕机后,此时剩余的一主一从可以正常工作,从机6380恢复之后,不能直接同步数据,原因是通过命令进行挂载的主机,重新启动后默认当前6380是主机,如果继续保持这个集群环境,需要我们再次通过 slaveof host port 指定老大,指定之后所有的数据会同步到从机中。

如果我们这个集群通过配置文件搭建的,是否能够避免上述问题。

修改配置文件

```shell
#replicaof <masterip> <masterport>
#replicaof 127.0.0.1 6379   #将当前6381 挂载到6379上

重新启动从机

127.0.0.1:6381> 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_read_repl_offset:8754
slave_repl_offset:8754
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:c84b86fcd5b2f7d723431edbfe33d1d4abf28152
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:8754
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:8754
repl_backlog_histlen:476
127.0.0.1:6380> keys *
ww
name
stu
u
11.2.6 主从复制原理(面试题)

全量复制(第一次挂载<手动命令>或从机启动<配置文件>)

1 Slave启动成功连接到master后会发送一个sync(同步)命令;(从机发送sync的同步命令)

2 Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令(收集数据),在后台进程执行完毕之后,

#master将传送整个数据文件到slave,以完成一次完全同步

增量复制(挂载之后主节点再次新增数据)

1 Master继续将新的所有收集到的修改命令依次传给slave,完成同步

注意:但是只要是重新连接master,一次完全同步(全量复制)自动执行。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值