Redis 缓存数据库

[TOC]

前言

缓存数据库是一种位于应用程序与主要后端数据库之间的中间层,用于提高数据访问速度的技术

image-20240513185433565

以下是一些关于缓存数据库的详细介绍:

  1. 作用:缓存数据库主要用于存储那些频繁访问或计算成本较高的数据副本。这些数据通常被放置在高速、低延迟的存储介质上,如内存中,以便快速访问。加快访问速度 ,缓解关系型数据库的读压力
  2. 使用场景:当应用程序需要数据时,首先会从缓存中查询。如果所需数据在缓存中存在,则可以直接获取,从而避免了直接访问主数据库的步骤,这可以显著减少数据检索的时间,并减轻主数据库的负载
  3. 常见类型
    • Redis:它是一种非关系型(NoSQL)内存键值存储数据库,支持多种数据结构,包括字符串、哈希、列表等。Redis以其快速的读写能力和丰富的功能而闻名,适用于缓存、消息传递、会话存储等多种场景。
    • Memcached:同样是键值对形式的内存缓存系统,设计相对简单,主要用于缓存常用数据,特别适应于分布式环境中的数据缓存需求。
  4. 优势:缓存数据库的使用可以减少数据库的读取次数,加快数据的处理速度,改善用户体验,并在高流量环境下保持系统的响应性和稳定性。

注意事项:缓存数据库固然提供了许多好处,但也要注意数据一致性和缓存失效的问题。必须确保在适当的时候更新或清除缓存,以避免过时或错误的数据被使用。

1、简介

NoSQL产品: RedisMongoDBMemcached

  • MongoDB:
    • 基于文档的数据存储:MongoDB使用BSON(类似JSON)格式存储数据,这使得数据结构灵活且易于扩展。

NOSQL名词解释:非关系型数据库;通常是以键值对的方式存储数据(Key-Value)的形式。

2、NoSQL的优点/缺点

优点:

  • 数据模型灵活性
    • NoSQL数据库不需要预先定义表结构,可以根据实际需求动态调整数据类型
  • 高可扩展性
    • NoSQL数据库采用分布式架构,可以通过水平扩展来处理大规模数据和高并发读写。通过在集群中添加更多的节点,可以提高数据库的性能和容量。这种高可扩展性使得NoSQL数据库适用于大规模数据处理和高并发场景,可以满足业务的快速增长需求。
  • 高性能
    • NoSQL数据库通常采用内存存储,以及并行计算和分布式计算技术,可以提供高性能的数据存储和查询能力。在对数据进行读取和写入操作时,NoSQL数据库可以快速响应,提供低延迟的数据访问。

缺点:

  • 一致性问题
    • NoSQL数据库通常采用最终一致性的策略,即在一段时间内达到一致状态,可以容忍一定的数据不一致性。在数据更新和复制过程中,可能会出现数据不一致的情况。
  • 查询能力限制
    • NoSQL数据库的查询能力相对较弱,通常只支持基本的查询操作。与传统关系型数据库相比,NoSQL数据库缺少复杂的查询操作和聚合函数。在需要进行复杂的数据查询和分析的场景中,NoSQL数据库的查询能力可能无法满足需求。
  • 缺乏标准化
    • NoSQL数据库的种类繁多,没有一个统一的标准化规范。不同的NoSQL数据库具有不同的查询语言。

3、关系型数据库与非关系型数据库的区别:———面试高频率问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1.首先了解一下 什么是关系型数据库?
关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织。


优点:
1、易于维护:都是使用表结构,格式一致;
2、使用方便:SQL语言通用,可用于复杂查询;
3、复杂操作:支持SQL,可用于一个表以及多个表之间非常复杂的查询。
缺点:
1、读写性能比较差,尤其是海量数据的高效率读写;
2、固定的表结构,灵活度稍欠;
3、高并发读写需求,传统关系型数据库来说,硬盘I/O是一个很大的瓶颈;

二 非关系型数据库
什么非关系型数据库呢?

非关系型数据是一种数据结构化存储方法的集合,可以是文档或者键值对等

优点:
1、格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型。
2、速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;
3、高扩展性;
4、成本低:nosql数据库部署简单,基本都是开源软件。

缺点:
1、不提供sql支持,学习和使用成本较高;
2、无事务处理;
3、数据结构相对复杂,复杂查询方面稍欠。

2、Memcached

2.1 特点

  1. 分布式缓存
    Memcached是一个分布式的缓存系统,可以将数据存储在多个服务器上,从而提供更高的可用性可扩展性。
  2. 内存存储
    Memcached将缓存数据存储在内存中,因此读取速度非常快,适用于需要快速访问的数据。由于数据存储在内存中,读取速度非常快,适用于缓存热点数据。但是服务器重启后,数据会丢失。
  3. 键值存储
    Memcached采用键值对存储数据,通过键(key)快速定位和检索数据值(value)。
  4. 自动过期
    缓存数据可以设置过期时间,过期后自动从缓存中移除,避免数据过时或脏数据的问题。
  5. 缓存逐出策略
    当内存不足时,Memcached会根据一定的策略逐出部分数据,为新数据腾出空间。

2.2 服务框架

原理

1、检查客户端的请求数据是否在memcached中,如有,直接把请求2数据返回,不再对数据库进行任何操作,路径操作为①②③⑦。
2、如果请求的数据不在memcached中,就去查数据库,把从数据库中获取的数据返回给客户端,同时把数据缓存一份到memcached中(memcached客户端不负责,需要程序明确实现),路径操作为①②④⑤⑦⑥。

3、保持缓存的新鲜性,每当数据发生变化的时候(比如,数据有被修改,或被删除的情况下),要同步更新的缓存信息,确保用户不会在缓存取到旧的数据。

2.3 配置安装Memcached

memcache能存放多少数据,取决于服务器本身的内存有多大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
1.安装----准备一台服务器
[root@memcached ~]# yum install memcached -y
[root@memcached ~]# systemctl start memcached #启动
2.修改配置文件
[root@memcached ~]# vim /etc/sysconfig/memcached
PORT="11211" ---监听的端口,默认11211可以修改
USER="memcached" -----用户
MAXCONN="1024" -----默认并发,可以修改
CACHESIZE="64" ------给的内存。默认是M
OPTIONS="" ----监听的网络地址
然后把ip地址发给开发人员,开发的会使用api接口连接memcached.


#>>> 安装telent,Telnet是一种用于远程登录的协议。传输数据是明文的,存在安全性问题。
[root@memcached ~]# yum install -y telnet

#>>> 测试
[root@memcached ~]# telnet 192.168.246.188 11211
Trying 192.168.246.188...
Connected to 192.168.246.188.
Escape character is '^]'.
set name 0 60 10 # key名 标记位(id号) 过期时间 大小
helloworld # name的key值
STORED # 出现stoped表示已经存储成功。
get name #查询key值
VALUE name 0 10
helloword
END
quit ---退出
参数解释:
# name:key的名字 自己定义
# 0:key的id号,需要和其他的key不一样
# 60:缓存过期时间,单位为秒,0为永远
# 9:字符串最大长度

不用它的原因:存储的数据类型单一,而且数据只能存储在内存中。无法实现数据的持久化,服务器重启,数据将消失。
========================================================================
扩展:安装php支持memcached的扩展模块: 安装php7.0

#>>> 安装PHP依赖的epel源
[root@memcached ~]# rpm -Uvh https://mirror.webtatic.com/yum/el7/epel-release.rpm

#>>> 安装PHP yum源
[root@memcached ~]# rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm

#>>> 安装PHP
[root@memcached ~]# yum install php70w.x86_64 php70w-cli.x86_64 php70w-common.x86_64 php70w-gd.x86_64 php70w-ldap.x86_64 php70w-mbstring.x86_64 php70w-mcrypt.x86_64 php70w-mysql.x86_64 php70w-pdo.x86_64 php70w-devel zlib-devel make gcc zlib-devel libmemcached-devel git php70w-fpm -y

#>>> 下载PHP Memcache 扩展包
[root@memcached ~]# yum install libmemcached php70w-pecl-memcached -y
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#>>> 安装nginx
[root@memcached ~]# yum -y install nginx

#>>> 配置默认界面
[root@memcached ~]# vim /etc/nginx/conf.d/default.conf
server {
listen 80;
server_name localhost;

location ~ \.php$ {
root /usr/share/nginx/html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}

#>>> 编辑php页面
[root@memcached ~]# cd /usr/share/nginx/html/index.php
<?php
phpinfo();
?>

#>>> 启动服务
[root@memcached ~]# systemctl enable --now nginx php-fpm

游览器访问:http://192.168.174.38/index.php

image-20240813144523867

3、Redis服务

3.1 介绍

redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库,它支持多种数据结构,如字符串(strings)、散列(hashes)、列表

img

redis的官网:www.redis.io

3.1.1 redis的特点:

  • 高性能:由于数据存储在内存中,Redis能够实现微秒级的读写速度。
  • 持久化:虽然数据主要存储内存中,但Redis提供了数据持久化的功能,可以将内存中的数据定期保存到磁盘中,以防止数据丢失
  • 支持丰富数据类型:除了基本的键值对存储,Redis还支持列表、集合、有序集合等多种数据结构,这使得它能够适应更多样的应用场景。
  • 原子性操作:Redis的所有操作都是原子性的,这意味着每一个操作都将完整地执行,不会被其他客户端的命令所干扰,确保了数据的一致性和完整性。

3.1.2 Redis和memcached区别(面试题)

  • 数据结构:Redis提供了更丰富的数据类型,包括字符串列表集合有序集合散列等,而Memcached主要支持简单的键值对结构。这使得Redis能够支持更复杂的数据操作,减少网络IO次数和数据体积。
  • 数据持久化:Memcached不支持数据持久化,服务器重启后数据会丢失,但这使得它在运行时拥有更高的性能。相比之下,Redis支持数据持久化到磁盘,提供了数据的恢复能力,但这也意味着它需要承担额外的性能开销
  • 性能考虑:Memcached在存储大数据时性能更高,因为它的内存管理机制简单高效。而Redis虽然在处理大量数据时性能有所下降,但它提供了更多的数据结构和操作。
  • 应用场景:Memcached通常用于缓存系统中,以减轻数据库的读负载,适合多读少写的场景。而Redis不仅适用于缓存,还适用于对读写效率要求高、数据处理复杂和对安全性要求较高的系统。

img

4、安装Redis

4.1 安装单机版redis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#>>> 下载redis安装包
[root@redis-master ~]# wget https://download.redis.io/releases/redis-6.2.7.tar.gz

#>>> 解压安装包到指定目录
[root@redis-master ~]# tar xzf redis-6.2.7.tar.gz -C /usr/local/ && cd /usr/local

#>>> 创建软链接
[root@localhost local]# ln -s redis-6.2.7 redis

#>>> 切换redis家目录
[root@localhost local]# cd redis/

#>>> 安装编译环境
[root@redis-master redis]# yum install -y gcc make

#>>> 编译
[root@redis-master redis]# make

#>>> 备份文件
[root@redis-master redis]# cp redis.conf redis.conf.bak

#>>> 修改配置文件
[root@redis-master redis]# vim redis.conf
bind 192.168.246.202 # 只监听内网IP
daemonize yes # 开启后台模式将no改为yes
port 6379 # 端口号
dir /data/redis/data # 本地数据库存放持久化数据的目录该目录需要存在创建存放数据的目录

#>>> 配置systemctl的redis启动脚本
[root@redis-master redis]# cat >> /lib/systemd/system/redis.service <<-EOF
[Unit]
Description=Redis
After=network.target

[Service]
ExecStart=/usr/local/redis/src/redis-server /usr/local/redis/redis.conf --daemonize no
ExecStop=/usr/local/redis/src/redis-cli -h 127.0.0.1 -p 6379 shutdown

[Install]
WantedBy=multi-user.target
E

#>>> 重新加载并且启动服务
[root@redis-master system]# systemctl daemon-reload
[root@redis-master system]# systemctl enable --now redis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#>>> 登陆redis
[root@redis-master system]# cd /usr/local/redis/src/
[root@redis-master src]# ./redis-cli -h 192.168.246.202 -p 6379

#>>> 测试redis是否可以用
192.168.246.202:6379> ping
PONG

#>>> 设置key--name,并设置值
192.168.246.202:6379> set name xiaoming
OK

#>>> 获取到key
192.168.246.202:6379> get name
"xiaoming"

========================================================================

192.168.246.202:6379> set key value [EX seconds] [PX milliseconds] [NX|XX]
# 参数解释:
# EX seconds : 将键的过期时间设置为 seconds 秒。默认为秒;
# PX milliseconds : 将键的过期时间设置为 milliseconds 毫秒。默认为毫秒;
# NX : 只在键不存在时, 才对键进行设置操作。
# XX : 只在键已经存在时, 才对键进行设置操作。会覆盖原有的values值

#>>> 使用 EX 选项:
[root@localhost src]# ./redis-cli -h 192.168.62.231 -p 6379
192.168.62.231:6379> set name1 xiaohong EX 10
OK
192.168.62.231:6379> get name1
"xiaohong"

#>>> 等待10s,再次查看
192.168.62.231:6379> get name1
(nil)

#>>> 使用 PX 选项:
192.168.62.231:6379> set name2 xiaohong PX 3233
OK
192.168.62.231:6379> get name2
"xiaohong"

#>>> 等待3s,再次查看
192.168.62.231:6379> get name2
(nil)

#>>> NX 选项:
192.168.62.231:6379> set class 2204 NX
OK # 键不存在,设置成功
192.168.62.231:6379> get class
"2204"
192.168.62.231:6379> set class 2205 NX
(nil) # 键已经存在,设置失败
192.168.62.231:6379> get class
"2204" # 维持原值不变

#>>> XX 选项:
192.168.62.231:6379> set home taikang XX
(nil) # 因为键不存在,设置失败
192.168.62.231:6379> set home taikang
OK # 先给键设置一个值
192.168.62.231:6379> set home zhengzhou XX
OK # 设置新值成功
192.168.62.231:6379> get home
"zhengzhou"

#>>> 删除key
192.168.62.231:6379> del class
(integer) 1
192.168.62.231:6379> get class
(nil)
1
2
3
4
5
6
7
#>>> 切换数据库
127.0.0.1:6379[1]> SELECT 2
OK
127.0.0.1:6379[2]>

#>>> 查看当前库所有的数据
127.0.0.1:6379> keys *

4.2 redis的相关工具

1
2
3
4
./redis-cli           # redis的客户端
./redis-server # redis的服务端
./redis-check-aof # 用于修复出问题的AOF文件
./redis-sentinel # 用于集群管理

5、Redis设置密码的两种方式

Redis修改密码的方式主要有两种:使用redis-cli命令行工具和通过配置文件。

5.1 命令行临时修改密码

  1. 登录Redis:首先,你需要使用当前没有密码的Redis客户端登录到Redis服务器。
  2. 设置新密码:使用CONFIG SET命令来设置新的密码。例如,要设置密码为qfyyds,你可以执行:
1
2
3
4
5
6
7
8
9
127.0.0.1:6379> CONFIG SET requirepass "qfyyds"

#>>> 密码使用方式
#>>> 方式一:
192.168.174.48:6379> AUTH qfyyds
OK

#>>> 方式二:
[root@localhost ~]# redis-cli -h 192.168.174.48 -p 6379 -a qfyyds

注意:这种方式设置的密码只会临时生效,重启Redis服务后密码会失效。

5.2 配置文件永久修改密码

  1. 找到Redis配置文件:通常,Redis的配置文件名为redis.conf,它位于Redis安装目录或数据目录中。
  2. 编辑配置文件:编辑redis.conf
  3. 设置密码:在配置文件中找到# requirepass foobared这一行(没有#注释符号),将foobared替换为你想要设置的新密码。例如,设置为afyyds
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@localhost redis]# vim redis.conf
···
requirepass qfyyds
···

#>>> 重启服务
[root@localhost redis]# systemctl restart redis

#>>> 查看服务状态
[root@localhost redis]# systemctl status redis

#>>> 测试
[root@localhost src]# ./redis-cli
127.0.0.1:6379> get name
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth qfyyds
OK
127.0.0.1:6379> get name
"zhangsan"

保存并重启Redis:保存配置文件并重启Redis服务,新的密码设置就会生效。

注意事项:在修改密码后,确保所有客户端都已更新为使用新密码进行连接,否则可能会出现认证失败的问题。


5.3 Redis配置文件详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
[root@localhost redis]# egrep -v "^#|^$" redis.conf
# 设置Redis服务器监听所有IP地址,即允许任何客户端连接。
bind 0.0.0.0
# 关闭保护模式,允许非本机客户端连接。
protected-mode no
# 设置Redis服务器监听的端口号为6379。
port 6379
# 设置TCP连接队列的大小为511,即允许最多有511个连接等待被处理。
tcp-backlog 511
# 设置超时时间
timeout 0
# 设置TCP心跳间隔为300秒。
tcp-keepalive 300
# 以守护进程方式运行Redis服务器。
daemonize yes
# 指定Redis服务器的进程ID文件路径。
pidfile /var/run/redis_6379.pid
# 设置日志级别为notice,只记录警告和错误信息。
loglevel notice
# 指定日志文件的路径。
logfile "/var/log/redis.log"
# 设置Redis支持的数据库数量为16个。
databases 16
# 在后台保存出错时停止写入操作。
stop-writes-on-bgsave-error yes
# rdbcompression yes:启用RDB文件压缩。
rdbcompression yes
# 在RDB文件中包含CRC64校验和
rdbchecksum yes
# 指定RDB文件的名称。
dbfilename dump.rdb
# 不删除同步生成的RDB文件。
rdb-del-sync-files no
# 指定RDB文件和AOF文件的存储目录。
dir /data/redis/data
# 设置副本节点为只读模式。
replica-read-only yes
# 设置密码为"qfyyds",用于验证客户端连接。
requirepass qfyyds
# 禁用AOF持久化。
appendonly no
# 指定AOF文件的名称。
appendfilename "appendonly.aof"
# 每秒执行一次fsync操作,将缓冲区的数据写入磁盘。
appendfsync everysec

6、数据持久化

Redis是一个内存数据库,一旦断电或服务器进程退出,内存数据库中的数据将全部丢失,所以需要redis持久化;Redis持久化就是把数据保存在磁盘上,利用永久性存储介质将数据保存,在特定的时间将保存的数据进行恢复的工作机制。

6.1 redis持久化 – 两种方式

6.1.1 RDB

​ 在指定的时间间隔内将内存中的数据集写入磁盘,也就是快照(Snapshot),数据恢复是将快照文件直接读到内存中redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入一个到一个临时文件(dump.rdb)中,待持久化过程结束后,再用本次的临时文件替换上次持久化后的文件。

fork函数的作用是复制一个与当前进程一样的进程,新进程的所有数据数值都和原进程一致,但是一个全新的进程,并作为原进程的子进程

redis服务器在处理bgsave采用子线程进行IO写入,而主进程仍然可以接收其他请求,但创建子进程是同步阻塞的,此时不接受其他请求。

img

save: 该命令会阻塞当前redis服务器,执行save命令期间,redis不能处理其他命令,直到RDB过程结束为止(会造成长时间阻塞,不建议使用)

bgsave:该命令执行后,redis会在后台异步进行快照操作,快照同时还可以响应客户端的请求,阻塞只发生在fork阶段,基本上redis内部的所有RDB操作都是采用bgsave命令。

1. RDB持久化配置

​ RDB持久化默认开启,但是需要配置触发规则。如下列代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@redis-master redis]# vim /usr/local/redis/redis.conf
···
# dbfilename:持久化数据存储在本地的文件
dbfilename dump.rdb

#dir:持久化数据存储在本地的路径,可自定义
dir /data/redis/data

##snapshot触发的时机,save <seconds> <changes>
##对于此值的设置,需要谨慎,评估系统的变更操作密集程度
##可以通过save “”来关闭snapshot功能
# 900秒内如果至少有一个key进行了修改则进行持久化操作
save 900 1
# 300秒内如果至少有10个key进行了修改则进行持久化操作
save 300 10
# 60秒内,如果至少有10000个key进行了修改则进行持久化操作
save 60 10000

##yes代表当使用bgsave命令持久化出错时候停止写RDB快照文件,no表明忽略错误继续写文件,“错误”可能因为磁盘已满/磁盘故障/OS级别异常等
stop-writes-on-bgsave-error yes

##是否启用rdb文件压缩,默认为“yes”,压缩往往意味着“额外的cpu消耗”,同时也意味着较短的网络传输时间
rdbcompression yes
2. RDB持久化数据恢复

​ 将备份文件(dump.rdb)移动到redis路径下(可以配置文件的存放路径)启动服务即可,redis启动会将文件数据加载到内存,在此期间redis会处于阻塞状态,直到全部数据存入内存。

3. RDB持久化的优缺点
  • 优点:

    • 数据恢复快;

    • 体积小;

    • 数据备份使用子进程,对redis服务性能影响小。

  • 缺点:

    • 在一定时间间隔进行备份,当redis意外宕机,将会丢失最后一次修改的数据,无法做到秒级持久化;
    • fork进程时,会占用一定的内存空间;
    • RDB文件是二进制的没有可读性。

6.1.2 AOF

​ 将客户端的每一个写操作命令以日志的形式记录下来,追加到appendonly.aof的文件末尾,在redis服务器重启时,会加载aof文件中的所有命令,来达到数据恢复的目的。

当有写命令请求时,会追加到AOF缓冲区内,AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作同步到磁盘的AOF文件中,当AOF文件大小超过重写策略或手动重写时,会对AOF文件进行重写来压缩AOF文件容量,redis服务重启时,会重新加载AOF文件中的写操作来进行数据恢复。

img
1. AOF持久化策略
  • always: 把每个写命令立即同步到AOF文件,很慢但安全;
  • everysec: 每秒同步一次,默认配置;
  • no: redis不执行写入磁盘。
2. AOF的触发方式
  1. 手动触发bgrewriteaof
1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started


[root@localhost data]# stat appendonly.aof
文件:"appendonly.aof"
大小:112 块:8 IO 块:4096 普通文件
设备:fd00h/64768d Inode:17874728 硬链接:1
权限:(0644/-rw-r--r--) Uid:( 0/ root) Gid:( 0/ root)
最近访问:2024-05-14 22:03:51.707774066 +0800
最近更改:2024-05-14 22:03:51.707774066 +0800
最近改动:2024-05-14 22:03:51.818774763 +0800

默认情况,redis是没有开启AOF(默认使用RDB持久化),需要通过配置文件开启。

1
2
3
4
5
6
7
8
9
10
#>>> 是否开启 Redis AOF持久化,默认为no
appendonly no

#>>> AOF持久化文件名
appendfilename "appendonly.aof"

#>>> AOF持久化策略,默认为eveysec,每秒同步一次
# appendfsync always
appendfsync everysec
# appendfsync no
  • Always:每次执行写入操作后,都会立即调用fsync将数据同步到磁盘。这确保了极高的数据持久性,因为即使在系统崩溃的情况下,也最多只会丢失一次写入操作的数据。然而,这种模式会对性能产生较大影响,因为在每次写入后都进行fsync会导致较高的I/O开销。
  • Everysec:这是默认设置,每秒执行一次fsync。它在性能和持久性之间取得了平衡,既保证了较好的数据安全性,又避免了频繁的I/O操作对性能的影响。
  • No:不做持久化
3. AOF的重写机制

​ AOF持久化,会把每次写命令都追加appendonly.aof文件中,当文件过大,redis的数据恢复时间就会变长,因此加入重写策略对aof文件进行重写,生成一个恢复当前数据的最少命令集。通过压缩AOF文件里面的相同指令保留最新的一个数据操作指令,即将存储了某个key的多次变更记录。只是存储最新的变更记录即可,丢弃历史变更记录 。

img

1
2
3
4
5
6
# Redis 重写机制配置
[root@localhost redis]# vim redis.conf
···
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
···
  1. auto-aof-rewrite-percentage:当前 AOF 文件大小超过上次重写后 AOF 文件大小的百分比时,触发 AOF 重写机制,默认值为 100 。
  2. auto-aof-rewrite-min-size:表示当当前 AOF 文件大小超过指定值时,才可能触发 AOF 重写机制,默认值为 64 MB 。
  • 系统自动触发 AOF 重写机制还需要满足以下条件 :
    • 当前没有正在执行 BGSAVE 或 BGREWRITEAOF 的子进程
    • 当前没有正在执行 SAVE 的主进程
    • 当前没有正在进行集群切换或故障转移
3.1 Redis AOF 重写机制原理

Redis AOF(Append Only File)重写流程是一个用于优化压缩AOF文件的过程,以减少存储空间和提高写入效率。以下是AOF重写的流程:

  1. 触发条件:当满足一定条件时,Redis会自动触发AOF重写。常见的触发条件包括文件大小超过阈值、系统负载较低等。

  2. 创建子进程:Redis启动一个子进程进行AOF重写操作。这个子进程与主进程并行运行,不会阻塞主进程的处理

  3. 遍历数据库:子进程开始遍历Redis数据库中的所有键值对。这个过程中,子进程会记录下每个键值对的操作命令,但不会执行这些命令。

  4. 生成重写缓冲区:在遍历数据库的过程中,子进程会将记录的操作命令写入一个临时文件,即重写缓冲区。这个临时文件最终会成为新的AOF文件。

  5. 同步命令到主进程:在重写过程中,子进程会将部分命令同步回主进程,以确保主进程和子进程的数据一致性。这是通过Redis的内部机制实现的,确保在重写过程中主进程的数据不会被破坏。

  6. 重写AOF文件:当子进程遍历完整个数据库并生成重写缓冲区后,它会根据一定的规则对缓冲区中的命令进行优化和压缩,生成新的AOF文件。这个新文件会替换原有的AOF文件,具有更小的体积和更高的写入效率

  7. 更新配置:一旦新的AOF文件生成并替换原有的文件,Redis会更新其配置信息,将新的AOF文件名写入配置文件中。这样,在下次启动时,Redis就会使用新的AOF文件作为持久化存储。

  8. AOF重写机制带来优点

通过AOF重写流程,Redis可以有效地优化和压缩AOF文件,减少存储空间的使用,并提高写入效率。同时,由于重写过程是在子进程中进行的,不会阻塞主进程的处理,因此对Redis的性能影响较小。

白话文解释:

  • 旧AOF文件过大触发重写机制
  • 创建子进程构建一个新的aof文件
  • 子进程读取当前redis里面的数据,写入到新的aof文件里面
  • 读取redis数据期间,主进程如果有其他新的操作指令则写入重写缓存中
  • 重写完成以后,将重写缓存追加到新的aof文件中
  • 用新的aof文件覆盖现有的aof文件
4. AOF的优点和缺点

​ 优点:

  • 数据安全性高,不易丢数据;

  • AOF文件有序保存了所有写操作,可读性强。

    缺点:

  • AOF方式生成文件体积大;

  • 数据恢复速度比RDB慢。

持久化配置

1
2
3
4
3、AOF默认关闭--开启
[root@redis-master src]# cd ..
[root@redis-master redis]# vim redis.conf
修改如下:

1
2
3
4
5
6
7
8
9
10
1、此选项为aof功能的开关,默认为“no”,可以通过“yes”来开启aof功能,只有在“yes”下,aof重写/文件同步等特性才会生效
====================================
2、指定aof文件名称
appendfilename appendonly.aof
====================================
3、指定aof操作中文件同步策略,有三个合法值:always everysec no,默认为everysec
appendfsync everysec
always #每次有数据修改发生时都会写入AOF文件
everysec #每秒钟同步一次,该策略为AOF的缺省策略/默认策略
no #从不同步。高效但是数据不会被持久化

开启持久化功能后,重启redis后,数据会自动通过持久化文件恢复。RDB是默认持久化方式,但 Redis 允许 RDB 与 AOF 两种持久化技术同时开启。不过如果同时存在两种持久化方式,会默认采取AOF的方式,AOF持久化方式的优先级更高。

拓展RDB快照备份恢复:

redis数据库备份与恢复(dump.rdb快照方式),两台机器

备份Redis实例配置

  1. 备份redis实例操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#>>> 备份机器的redis.conf配置文件内容
[root@localhost ~]# vim /usr/local/redis/redis.conf
···
bind 0.0.0.0
dbfilename dump.rdb
dir /data/redis/data
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
···

[root@localhost ~]# ./src/redis-cli
127.0.0.1:6379> set name zhangsan
OK

127.0.0.1:6379> BGSAVE

[root@localhost ~]# ls /data/redis/data
dump.rdb

[root@localhost ~]# scp /data/redis/data/dump.rdb 192.168.174.49:/data/redis/data/
  1. 恢复Redis实例配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#>>> 配置文件修改
[root@redis-backup ~]# vim /usr/local/redis/redis.conf
···
bind 0.0.0.0
dbfilename dump.rdb
dir /data/redis/data/
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
daemonize yes
···

#>启动实例
[root@redis-backup ~]# cd /usr/local/redis/src/
[root@redis-backup redis]# ./src/redis-server redis.conf
[root@redis-backup redis]# ./src/redis-cli
127.0.0.1:6379> keys *
1) "name"

7、Redis主从配置

7.1 主从简介

image-20240418113140326

持久化技术只是解决了Redis服务故障之后,快速数据恢复的问题。宕机和数据恢复的过程中整个业务系统来说,还是有损失的,并没有根本上提升可用性问题,而且持久化技术对于Redis服务性能来说是有损的。我们需要的是保障Redis的高可用,减少甚至避免Redis服务发生宕机的可能。

目前实现Redis高可用的模式主要有三种: 主从模式哨兵模式集群模式。我们先来聊一下主从模式。
Redis 提供的主从模式,是通过复制的方式,将主服务器上的Redis的数据同步复制一份到从 Redis 服务器,这种做法很常见,MySQL通过binlog进行的主从复制也是这么做的。
主节点的Redis我们称之为master,从节点的Redis我们称之为slave,主从复制为单向复制,只能由主到从,不能由从到主。可以有多个从节点,比如1主3从甚至n从,从节点的多少根据实际的业务需求来判断。

主从结构,一是为了纯粹的冗余备份,二是为了提升读性能,比如很消耗性能的操作就可以由从服务器来处理。
redis的主从同步是异步进行的,这意味着主从同步不会影响主逻辑,也不会降低redis的处理性能。
主从架构中,可以考虑关闭主服务器的数据持久化功能,只让从服务器进行持久化,这样可以提高主服务器的处理性能。

7.2 主从复制的意义

  • 故障隔离和恢复

    无论主节点或者从节点宕机,其他节点依然可以保证服务的正常运行,并可以手动或自动切换主从。

    • 如果Slave库故障,则读写操作全部走到Master库中
    • 如果Master库故障,则将Slave转成Master库,仅丢失Master库来不及同步到Slave的小部分数据
  • 读写隔离:Master 节点提供写服务,Slave 节点提供读服务,分摊流量压力,均衡流量的负载。

  • 提供高可用保障:主从模式是高可用的最基础版本,也是 sentinel 哨兵模式cluster 集群模式实施的前置条件。

7.3 主从同步原理偏移

  1. 从服务器会向主服务器发出SYNC指令
  2. 当主服务器接到此命令后,就会调用BGSAVE指令来fork一个子进程专门进行数据持久化工作,也就是将主服务器的数据写入RDB文件中。在数据持久化期间,主服务器将执行的写指令都缓存在内存中;
  3. 在BGSAVE指令执行完成后,主服务器会将持久化好的RDB文件发送给从服务器,
  4. 从服务器接到此文件后会将其存储到磁盘上,然后再将其读取到内存中。
  5. 这个动作完成后,主服务器会将这段时间缓存的写指令再以redis协议的格式发送给从服务器。

另外,要说的一点是,即使有多个从服务器同时发来SYNC指令,主服务器也只会执行一次BGSAVE,然后把持久化好的RDB文件发给多个从服务器。

image-20240418113526905

2.8版本之后,redis支持了效率更高的增量同步策略,这大大降低了连接断开的恢复成本。主服务器会在内存中维护一个缓冲区,缓冲区中存储着将要发给从服务器的内容。从服务器在与主服务器出现网络瞬断之后,从服务器会尝试再次与主服务器连接,一旦连接成功,主服务器就会向从服务器发送增量内容。增量同步功能,需要服务器端支持全新的PSYNC指令。这个指令,只有在redis-2.8之后才具有。image-20240418114706094

扩展内容

1. 首次配置完成主从库之后的全量复制:在从库第一次连接到主库时,将采用psync复制方式进行全量复制。 这意味着从库会从头开始复制主库中的全部数据。
2. 主从正常运行期间,准实时同步:在正常运行状态下,从库通过读取主库的缓冲区来进行增量复制。 这个过程涉及复制主库上发生的新的数据变更。
3. 从库第二次启动(异常或主从网络断开后恢复): Append增量数据 + 准实时同步将通过读取主库的缓冲区进行部分复制。 这种方式能够快速同步中断期间发生的数据变更,而不会对主库造成重大影响。

image

PSYNC 命令是Redis中用于从节点与主节点之间数据同步的关键命令。它的工作原理包括以下几个步骤:

1. 启动或重连判断:
当从节点(Slave)启动或与主节点(Master)的连接断开后重连时,从节点需要确定是否曾经同步过。如果从节点没有保存任何主节点的运行ID(runid),它将视为第一次连接到主节点。

2. 首次同步处理:
如果是第一次同步的情况下,从节点会发送 PSYNC -1 命令给主节点,代表请求全量数据同步。 全量同步是指主节点将其所有数据完整地Copy一份给从节点。

3. 主从重连后的处理:
对于之前已经同步过的从节点,它会发送 PSYNC runid offset 命令,其中runid是主节点的唯一标识符,offset是从节点上次同步数据的偏移量。这样本质就是增量同步。

4. 主节点响应:
主节点接收到PSYNC命令后,会检查runid是否匹配,以及offset是否在复制积压缓冲区的范围内。如果匹配且offset有效,主节点将回复CONTINUE,并发送自从节点上次断开连接以来的所有写命令。

5. 触发全量同步的条件:
如果runid不匹配,或offset超出了积压缓冲区的范围,主节点将通知从节点执行全量同步,回复FULLRESYNC runid offset

6. 积压缓冲区的作用:
主节点会在处理写命令的同时,将这些命令存入复制积压队列(缓冲池),同时记录队列中存放命令的全局offset。
这样做法是保证了效率。当从节点断线重连,且条件允许时(runid和offset都具备),它可以通过offset从积压队列中进行增量复制,而不是全量复制,这样复制的成本就低很多。

7. 保障数据一致性:
PSYNC机制允许从节点在网络不稳定或其他意外断开连接的情况下,能够以增量方式重新同步数据。这也是它的一大优势,那就是保持主从节点数据的一致性。

8. 什么时候启动重连工作
判断是否进行全量同步,需要考虑两个关键因素:首先,确认这是否是第一次进行数据同步;其次,检查缓存区是否已经达到或超过其容量上限。只有在是第一次同步,或者缓存区已溢出的情况下,才会执行全量同步。

7.4 1主n从的同步说明

如果你有多个从库,则在每次连接的时候需要注意一些细节,如下:

  • 多个从库情况下,每个从库都会记录自己的 slave_repl_offset,各自复制的进度也不相同。

  • 重连主库进行恢复时,从库会通过psync命令将 slave_repl_offset 告知主库,主库判断从库的状态,来决定进行增量复制,还是全量复制。

  • replication buffer(复制缓冲区) 和 repl_backlog 的说明

    • replication buffer: 与每个从节点(slave)相关联的缓冲区,存在于主节点(master)上。当主从连接稳定时,主节点会将其接收到的所有写命令放入这个缓冲区中,并异步地发送给从节点。这样,从节点就可以通过执行这些写命令来更新自己的数据集。可以通过client-output-buffer-limit配置来设定其大小限制,以防止因从节点处理速度慢而导致主节点内存溢出。

    • repl_backlog_buffer:是为了解决从库断连后找不到主从差异数据而设立的环形缓冲区,从而避免全量同步带来的性能开销。在redis.conf配置文件中可以设置大小,如果从库断开时间过长,repl_backlog_buffer环形缓冲区会被主库的写命令覆盖,那么从库重连后只能全量同步,所以repl_backlog_size配置尽量大一点可以降低从库连接后全量同步的频率。

  • 主库和从库会各自记录自己的复制进度,所以,不同的从库在进行恢复时,需要将自己的复制进度(slave_repl_offset)发给主库,主库才可以按照偏移量取数据跟它同步。

image-20240422111235791

7.5 部署三台机器Redis—主从同步

  1. 集群环境准备
1
2
3
4
5
6
7
8
9
#>>> 三台服务器关闭防火墙
$ systemctl disable --now firewalld && setenforce 0

#>>> 三台服务器添加本地解析
$ cat >> /etc/hosts <<-EOF
redis-master 192.168.174.38
redis-slave-1 192.168.174.39
redis-slave-2 192.168.174.40
EOF
  1. redis-master节点配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#>>> 修改配置文件
[root@redis-master ~]# vim /usr/local/redis/redis.conf
# 设置Redis监听的IP地址和端口号,默认监听所有IP地址和6379端口
bind 0.0.0.0

# 关闭保护模式,允许远程访问
protected-mode no

# 指定Redis监听的端口号
port 6379

# 增加Redis的最大内存限制,以容纳更多数据
#maxmemory 16GB 增加内存限制,根据您的服务器实际内存调整
maxmemory 20480mb
...

#>>> 启动主节点redis服务
[root@redis-master ~]# cd /usr/local/redis/src
[root@redis-master src]# ./redis-server ../redis.conf & 会加载此文件中的配置信息

#>>> 查看端口
[root@redis-master src]# ss -tunlp | grep 6379
tcp LISTEN 0 128 *:6379 *:* users:(("redis-server",pid=1360,fd=6))

关闭protected-mode模式,此时外部网络可以直接访问

开启protected-mode保护模式,需配置bind ip或者设置访问密码

  1. redis-slave01配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#>>> 修改配置文件
[root@redis-slave01 ~]# vim /usr/local/redis/redis.conf
...
# 设置Redis监听的IP地址和端口号,默认监听所有IP地址和6379端口
bind 0.0.0.0

# 添加需要同步的主库信息
replicaof 192.168.174.39 6379

# 关闭保护模式,允许远程访问
protected-mode no

# 自定义数据目录
dir /data/redis
...

#>>> 启动redis服务
[root@redis-slave-1 ~]# cd /usr/local/redis/src/
[root@redis-slave-1 src]# ./redis-server ../redis.conf &

#>>> 查看端口
[root@redis-slave01 src]# ss -tunlp | grep 6379
tcp LISTEN 0 128 *:6379 *:* users:(("redis-server",pid=1360,fd=6))

可以通过 replicaof(Redis 5.0 之前使用 slaveof)命令形成主库和从库的关系。

  1. redis-slave02配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#>>> 修改配置文件
[root@redis-slave01 ~]# vim /usr/local/redis/redis.conf
...
# 设置Redis监听的IP地址和端口号,默认监听所有IP地址和6379端口
bind 0.0.0.0

# 添加需要同步的主库信息
replicaof 192.168.174.48 6379

# 关闭保护模式,允许远程访问
protected-mode no

# 自定义数据目录
dir /data/redis
...

#>>> 启动redis服务
[root@redis-slave-1 ~]# cd /usr/local/redis/src/
[root@redis-slave-1 src]# ./redis-server ../redis.conf &

#>>> 查看端口
[root@redis-slave01 src]# ss -tunlp | grep 6379
tcp LISTEN 0 128 *:6379 *:* users:(("redis-server",pid=1360,fd=6))
  1. 测试主从复制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#>>> redis-master执行
[root@redis-master redis]# cd src/
[root@redis-master src]# ./redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set name jack
OK
127.0.0.1:6379> get name
"jack"

#>>> 分别在slave-1和slave-2上面执行
[root@redis-slave01 redis]# cd src/
[root@redis-slave01 src]# ./redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> get name
"jack"
127.0.0.1:6379>
[root@redis-slave-2 src]# ./redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> get name
"jack"
127.0.0.1:6379>

#>>> redis-master查看复制状态
127.0.0.1:6379> info replication
role:master
connected_slaves:2
slave0:ip=192.168.246.203,port=6379,state=online,offset=490,lag=0
slave1:ip=192.168.246.204,port=6379,state=online,offset=490,lag=1


#>>> redis-slave执行
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:192.168.246.202
master_port:6379
master_link_status:up

注意:从服务器一般默认禁止写入操作:slave-read-only yes

redis-master执行info repliaction参数解释

image-20240813233259506

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 表示当前节点的角色是主节点
role:master
# 表示当前主节点连接了两个从节点。
connected_slaves:2
# 表示第一个从节点的IP地址为192.168.174.49,端口号为6379,状态为在线(online),复制偏移量为3276,与主节点的延迟为1。
slave0:ip=192.168.174.49,port=6379,state=online,offset=3276,lag=1
# 表示第二个从节点的IP地址为192.168.174.50,端口号为6379,状态为在线(online),复制偏移量为3276,与主节点的延迟为1。
slave1:ip=192.168.174.50,port=6379,state=online,offset=3276,lag=1
# 表示当前没有进行故障转移。
master_failover_state:no-failover
# 表示主节点的复制ID。
master_replid:169726e22cc9736afd05f50b7fef4d8b6e48b47a
# 表示主节点的第二个复制ID,这里为全零。
master_replid2:0000000000000000000000000000000000000000
# 表示主节点当前的复制偏移量。
master_repl_offset:3276
# 表示第二个从节点的复制偏移量,这里为-1,表示没有设置。
second_repl_offset:-1
# 表示复制积压缓冲区是否处于活动状态,1表示活动。
repl_backlog_active:1
# 表示复制积压缓冲区的大小,单位为字节。
repl_backlog_size:1048576
# 表示复制积压缓冲区中第一个字节的偏移量。
repl_backlog_first_byte_offset:1
# 表示复制积压缓冲区的历史长度。
repl_backlog_histlen:3276

redis-slave执行info replication参数解释

image-20240813233652087

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# slave 节点执行 info replication 命令回显参数解释
# 表示当前节点的角色是从节点。
role:slave
# 表示主节点的IP地址为192.168.174.48。
master_host:192.168.174.48
# 表示主节点的端口号为6379。
master_port:6379
# 表示与主节点的连接状态为正常(up)。
master_link_status:up
# 表示距离上一次与主节点进行IO操作的时间过去了8秒。
master_last_io_seconds_ago:8
# 表示当前没有正在进行的主从同步操作。
master_sync_in_progress:0
# 表示从节点读取复制数据时的偏移量。
slave_read_repl_offset:3794
# 表示从节点当前的复制偏移量。
slave_repl_offset:3794
# 表示从节点的优先级为100。
slave_priority:100
# 表示从节点以只读模式运行。
slave_read_only:1
# 表示从节点已经向其他节点宣告自己是复制节点。
replica_announced:1
# 表示当前从节点没有连接其他从节点。
connected_slaves:0
# 表示当前没有进行故障转移。
master_failover_state:no-failover
# 表示主节点的复制ID。
master_replid:169726e22cc9736afd05f50b7fef4d8b6e48b47a
# 表示主节点的第二个复制ID,这里为全零。
master_replid2:0000000000000000000000000000000000000000
# 表示主节点当前的复制偏移量。
master_repl_offset:3794
# 表示第二个从节点的复制偏移量,这里为-1,表示没有设置。
second_repl_offset:-1
# 表示复制积压缓冲区是否处于活动状态,1表示活动。
repl_backlog_active:1
# 表示复制积压缓冲区的大小,单位为字节。
repl_backlog_size:1048576
# 表示复制积压缓冲区中第一个字节的偏移量。
repl_backlog_first_byte_offset:1
# 表示复制积压缓冲区的历史长度。
repl_backlog_histlen:3794

7.6 主从同步保证数据一致性

​ 为了保证主服务器Redis的数据和从服务器Redis的数据的一致性,也为了分担访问压力,均衡负载,应用层面一般采取读写分离的模式。读操作:主、从库都可以执行,一般是在从库上读数据,对实时性和准确性有100%高真要求的部分业务,在谨慎评估之后也可以读主库,前提是不能给Master带来高压力和风险。写操作:只在主库上写数据,写完之后将写操作指令同步到从库。

7.7 总结

  • 主从复制的作用一个是为分担读写压力,均衡负载,另一个是为了保证部分实例宕机之后服务的持续可用性,所以Redis演变出主从架构和读写分离。
  • 主从复制的步骤包括:建立连接的阶段、数据同步的阶段、基于长连接的命令传播阶段。
  • 数据同步可以分为全量复制部分复制,全量复制一般为第一次全量或者长时间主从连接断开。
  • 主从模式是比较低级的可用性优化,要做到故障自动转移,异常预警,高保活,还需要更为复杂的哨兵或者集群模式,这个后面我们继续介绍。

8、Redis-sentinel—哨兵模式

img

8.1 哨兵简介:Redis Sentinel

Sentinel(哨兵)进程是用于监控redis集群中Master主服务器工作的状态,在Master主服务器发生故障的时候,可以实现Master和Slave服务器的切换,保证系统的高可用。

哨兵(Sentinel) 是一个分布式系统,你可以在一个架构中运行多个哨兵(sentinel) 进程,这些进程使用流言协议来接收关于Master主服务器是否下线的信息,并使用投票协议来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master。

流言协议:

​ 流言协议是一种去中心化的信息传播方式,其工作原理类似于人们在日常生活中传播流言:每个节点都将自己所知的信息告诉给它所连接的其他节点,这些节点再将信息传播给它们所连接的节点,以此类推,直到所有节点都获得了这条信息。

1、Redis哨兵集群的角色划分
  • 主节点(Master): 处理客户端的读写请求。
  • 从节点(Slave): 复制主节点的数据,用于提供读取服务和备份。
  • 哨兵节点(Sentinel): 监控集群中各节点的健康状态,负责选举和故障转移。
2、作用
  • 监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master和Slave是否运作正常。
  • 提醒(Notification):当被 监控的某个Redis节点出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automatic failover):当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作,它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其他Slave改为复制新的Master;当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用现在的Master替换失效Master。

哨兵之间如何通信

image-20240815082655417

哨兵实例之间可以相互发现,要归功于 Redis 提供的 pub/sub 机制,也就是发布 / 订阅机制。

哨兵只要和主库建立起了连接,就可以在主库上发布消息了,比如说发布它自己的连接信息(IP 和端口)。同时,它也可以从主库上订阅消息,获得其他哨兵发布的连接信息。当多个哨兵实例都在主库上做了发布和订阅操作后,它们之间就能知道彼此的 IP 地址和端口。

为了区分不同应用的消息,Redis 会以频道的形式,对这些消息进行分门别类的管理。所谓的频道,实际上就是消息的类别。当消息类别相同时,它们就属于同一个频道。反之,就属于不同的频道。只有订阅了同一个频道的应用,才能通过发布的消息进行信息交换

在主从集群中,主库上有一个名为“sentinel:hello”的频道,不同哨兵就是通过它来相互发现,实现互相通信的。

在上图图中,哨兵 1 把自己的 IP(172.16.19.3)和端口(26579)发布到“sentinel:hello”频道上,哨兵 2 和 3 订阅了该频道。那么此时,哨兵 2 和 3 就可以从这个频道直接获取哨兵 1 的 IP 地址和端口号。

然后,哨兵 2、3 可以和哨兵 1 建立网络连接。通过这个方式,哨兵 2 和 3 也可以建立网络连接,这样一来,哨兵集群就形成了。它们相互间可以通过网络连接进行通信,比如说对主库有没有下线这件事儿进行判断和协商。

哨兵如何知道从库的ip地址和端口呢?

image-20240815083231411

由哨兵向主库发送 INFO 命令来完成的。就像上图所示,哨兵 2 给主库发送 INFO 命令,主库接受到这个命令后,就会把从库列表返回给哨兵。接着,哨兵就可以根据从库列表中的连接信息,和每个从库建立连接,并在这个连接上持续地对从库进行监控。哨兵 1 和 3 可以通过相同的方法和从库建立连接。

3、工作模式
  • 每个Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的Master主服务器,Slave从服务器以及其他Sentinel(哨兵)进程发送一个 PING 命令。并通过实例返回的结果来判断实例是否在线。
  • 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel(哨兵)进程标记为主观下线(SDOWN)。
  • 如果一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的所有 Sentinel(哨兵)进程要以每秒一次的频率确认Master主服务器的确进入了主观下线状态
  • 当有足够数量的 Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认Master主服务器进入了主观下线状态(SDOWN), 则Master主服务器会被标记为客观下线(ODOWN)。
  • 在一般情况下, 每个 Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的所有Master主服务器、Slave从服务器发送 INFO 命令。
  • 当Master主服务器被 Sentinel(哨兵)进程标记为客观下线(ODOWN)时,Sentinel(哨兵)进程向下线的 Master主服务器的所有 Slave从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次
  • 若没有足够数量的 Sentinel(哨兵)进程同意 Master主服务器下线, Master主服务器的客观下线操作就会被移除。若 Master主服务器重新向 Sentinel(哨兵)进程发送 PING 命令返回有效回复,Master主服务器的主观下线状态就会被移除。
4、主观下线和客观下线

主观下线:Subjectively Down,简称 SDOWN,指的是当前一个Sentinel 实例对某个redis服务器做出的下线判断。
客观下线:Objectively Down, 简称ODOWN,指的是多个 Sentinel 实例在对Master Server做出 SDOWN 判断,并且通过SENTINEL is-master-down-by-addr 命令互相交流之后,得出的Master Server下线判断,然后开启failover/故障转移

5、配置哨兵模式
  • 每台机器上修改redis主配置文件redis.conf文件设置:bind 0.0.0.0
  • 每台机器上修改sentinel.conf配置文件:修改如下配置
1
2
3
4
5
6
7
8
9
10
11
12
13
[root@redis-master ~]#  cd /usr/local/redis/

[root@redis-master redis]# vim sentinel.conf
...
sentinel monitor mymaster 192.168.174.48 6379 2
sentinel down-after-milliseconds mymaster 3000
sentinel failover-timeout mymaster 10000
protected-mode no
...

# 每台机器启动哨兵服务:
[root@redis-master redis]# ./src/redis-sentinel sentinel.conf
注意:在生产环境下将哨兵模式启动放到后台执行: ./src/redis-sentinel sentinel.conf &

参数解释:

sentinel down-after-milliseconds mymaster 3000

​ 当集群中有2个sentinel认为master死了时,才能真正认为该master已经不可用了。 (slave上面写的是master的ip,master写自己ip)

sentinel down-after-milliseconds mymaster 3000

​ 表示如果名为 mymaster 的主节点在3秒(3000毫秒)内未对 Sentinel 的 PING 命令做出有效响应,那么 Sentinel 会开始考虑该主节点可能已经出现故障,并做好相应的故障转移准备。

sentinel failover-timeout mymaster 10000

​ 表示在进行名为 mymaster 的主节点的故障转移操作时,Sentinel 最多允许花费10秒(10000毫秒)的时间来完成整个操作。

protected-mode no

​ 关闭加密保护模式

将master的哨兵模式退出(Crtl+c),再将redis服务stop了,在两台slave上面查看其中一台是否切换为master:(没有优先级,为随机切换)

1
2
3
4
5
^C4854:signal-handler (1564349039) Received SIGINT scheduling shutdown...
4854:X 29 Jul 05:23:59.592 # User requested shutdown...
4854:X 29 Jul 05:23:59.592 # Sentinel is now ready to exit, bye bye...
[root@redis-master redis]# systemctl stop redis.service
或者用kill命令杀死

在slave机器上面查看:

6、Redis故障Master选举算法(了解)
  • 优先级: 每个节点都有一个优先级,选择优先级最高的节点作为新的主节点。
  • 复制偏移量: 选择复制偏移量最大的从节点,确保数据同步性。
  • 运行ID: 选择运行ID最大的节点,确保节点唯一性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.exceptions.JedisException;

import java.util.Set;

public class RedisClusterElection {

public static void main(String[] args) {
String masterName = "mymaster";
Set<String> sentinels = Set.of("sentinel1:26379", "sentinel2:26379", "sentinel3:26379");

try (JedisSentinelPool sentinelPool = new JedisSentinelPool(masterName, sentinels)) {
Jedis jedis = sentinelPool.getResource();

// 获取当前主节点
String currentMaster = jedis.sentinelGetMasterAddrByName(masterName).getHost();
System.out.println("Current Master: " + currentMaster);

// 模拟主节点故障
simulateMasterFailure(jedis, masterName);

// 等待哨兵节点进行选举
Thread.sleep(5000);

// 获取新的主节点
String newMaster = jedis.sentinelGetMasterAddrByName(masterName).getHost();
System.out.println("New Master: " + newMaster);

} catch (JedisException | InterruptedException e) {
e.printStackTrace();
}
}

private static void simulateMasterFailure(Jedis jedis, String masterName) {
// 模拟主节点故障,停止主节点
jedis.sentinelFailover(masterName);
}
}

面试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
redis有哪些优点
(1) 速度快,因为数据存在内存中。
(2) 支持丰富数据类型,支持string,list,set,hash等
(3) 支持事务,操作都是原子性,就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

redis相比memcached有哪些优势
(1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型
(2) redis可以持久化其数据

redis常见性能问题和解决方案
(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用树状结构,用单向链表结构更为稳定,即:Master(写) <- Slave1(读) <- Slave2(读) <- Slave3(读)...
这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
redis集群的工作原理
主多从+哨兵模式

了解:

redis–快照

1
2
3
4
5
6
7
8
9
10
11
12
13
14
快照,主要涉及的是redis的RDB持久化相关的配置

用如下的指令来让数据保存到磁盘上,即控制RDB快照功能:

save <seconds> <changes>

举例
save 900 1 //表示每15分钟且至少有1个key改变,就触发一次持久化
save 300 10 //表示每5分钟且至少有10个key改变,就触发一次持久化
save 60 10000 //表示每60秒至少有10000个key改变,就触发一次持久化

如果想禁用RDB持久化的策略,只要不设置任何save指令就可以,或者给save传入一个空字符串参数也可以达到相同效果,就像这样:

save ""

安全:为redis加密:

1
2
3
4
5
可以要求redis客户端在向redis-server发送请求之前,先进行密码验证。当你的redis-server处于一个不太可信的网络环境中时,相信你会用上这个功能。由于redis性能非常高,所以每秒钟可以完成多达15万次的密码尝试,所以你最好设置一个足够复杂的密码,否则很容易被黑客破解。

requirepass 123456

这里我们通过requirepass将密码设置成“123456”。

修改了Redis的哨兵配置文件后,先把文件拷贝到从机再启动,否则会出现myid冲突的情况。

解决方法:将哨兵停掉,哨兵配置文件删除后,重新弄一份新的,然后再启动即可。

+slave :取消主观下线
-slave :标记为主观下线