无常是常

分享技术见解、学习心得和生活感悟

最新文章

Redis复制

Replication

Redis使用主从复制非常简单,它允许从实例成为主实例的副本,每当链接断开时,从节点将自动重新连接到主服务器上。并且无论主服务器发生什么情况,从节点都尝试完全复制所有数据。

Redis主从复制使用以下三种机制:

  • 当主从实例连接良好时,主节点向从节点发送命令流来进行更新,是为了复制由于Key过期或回收等其他操作对数据产生影响。
  • 当主从实例断开时,从节点重新连接后只会尝试获取链接断开期间错开的命令流。
  • 如果无法进行部分重新同步,则会要求完全重新同步。

默认情况下,Redis会使用异步复制,Redis从节点会异步地确认接收的数据。因此,主节点不会等待从节点处理完成。但是,如果需要知道从节点都处理了那些命令,也可以选择同步复制。

Redis 复制的几个特征:

  1. Redis是使用异步复制
  2. 一个主节点可以有多个从节点
  3. 从节点还可以有子节点,从Redis 4.0开始,所有子节点将从主节点接收完全相同的复制流。
  4. Redis主从复制是无阻塞的,意味着在进行复制或同步是仍然可以进行数据查询
  5. 复制既可以用于可伸缩性,也可以用于只读查询的多个副本(读写分离)
  6. 可以配置主节点不进行数据保存或只启用AOF,然后将数据保存到副本,但是注意:当服务器重启后,从节点服务器重新同步数据时,同时会清空从节点数据。

Redis 复制的原理:

每个Redis Master 节点都有一个replication ID:这是一个较大的伪随机字符串,用于标记当前数据集。每个Master 还有一个偏移量,该偏移量会针对复制流中要发送到副本的每个字节的增量而增加。
以便更新副本的状态。即使未连接任何副本,复制偏移也会增加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Replication
role:master
connected_slaves:1
slave0:ip=39.105.157.176,port=6379,state=online,offset=61238,lag=0
# 主 Replication ID
master_replid:649255ffb2183786d00203aa51715169b06f4f47
# 副 Replication ID
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:61238
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:61238

当从节点连接主节点时,会使用PSYNC命令来发送当前旧的主节点的Replication ID和offset,这样主节点只会同步相差的数据量,如果主节点缓冲区的副本引用(Replication ID)不存在,则会进行全量同步。

全量同步的工作流程如下:

主节点开始后台生成RDB文件。同时,它开始缓冲从客户端收到的所有新写入命令。后台保存完成后,主数据库将数据库文件传输到从节点,从节点将其保存在磁盘上,然后将其加载到内存中。然后,主服务器将所有缓冲的命令发送到副本。

Replication ID 说明:

如果两个实例具有相同的Replication ID和offset,则它们具有完全相同的数据。但是为什么会有两个Replication ID(主和副)?

因为两个实例A和B具有相同的Replication ID,但一个实例的offset为1000,另一个实例的offset为1023,则意味着第一个实例缺少应用于数据集的某些命令。这也意味着,仅通过应用一些命令,A即可达到与B完全相同的状态。

Redis实例具有两个Replication ID的原因是由于副本被提升为主副本。故障转移后,升级后的副本仍需要记住其过去的Replication ID,因为该Replication ID是以前的主副本之一。这样,当其他副本将与新的主副本同步时,它们将尝试使用旧的主Replication ID执行部分重新同步。因为将副本提升为主副本时,它会将其辅助ID设置为其主ID,并记住发生此ID切换时的offset。稍后它将选择一个新的随机Replication ID,因为新的历史记录开始了。处理新的副本连接时,主机将其ID和offset与当前ID和辅助ID相匹配(为安全起见,直到给定的偏移量)。简而言之,这意味着在故障转移后,连接到新提升的主服务器的副本不必执行完全同步。

无盘复制

通常,完全重新同步需要在磁盘上创建RDB文件,然后从磁盘重新加载相同的RDB,以便为副本提供数据。
但是对于硬盘速度慢,但是在内网环境下,可以采用无盘复制,这样可以直接通过Socket将RDB发送给从节点,而无需使用硬盘作为中间存储。

配置

1
replicaof 192.168.1.1 6379

也可以使用REPLICAOF命令进行同步。
可以使用repl-diskless-sync配置参数启用无盘复制。在repl-diskless-sync-delay 参数控制第一个副本之后,为了等待更多副本到达而开始传输的延迟。

1
masterauth <password>

设置主节点的密码,也可以使用redis-cli输入config set masterauth <password>进行设置。

1
2
3
4
# 在当前至少有N个副本连接到主服务器时才接受写查询
min-replicas-to-write <number of replicas>
# 在有N个副本延迟少于M秒时,则接受写入
min-replicas-max-lag <number of seconds>

Replication

Redis使用主从复制非常简单,它允许从实例成为主实例的副本,每当链接断开时,从节点将自动重新连接到主服务器上。并且无论主服务器发生什么情况,从节点都尝试完全复制所有数据。

Redis主从复制使用以下三种机制:

  • 当主从实例连接良好时,主节点向从节点发送命令流来进行更新,是为了复制由于Key过期或回收等其他操作对数据产生影响。
  • 当主从实例断开时,从节点重新连接后只会尝试获取链接断开期间错开的命令流。
  • 如果无法进行部分重新同步,则会要求完全重新同步。

默认情况下,Redis会使用异步复制,Redis从节点会异步地确认接收的数据。因此,主节点不会等待从节点处理完成。但是,如果需要知道从节点都处理了那些命令,也可以选择同步复制。

Redis 复制的几个特征:

  1. Redis是使用异步复制
  2. 一个主节点可以有多个从节点
  3. 从节点还可以有子节点,从Redis 4.0开始,所有子节点将从主节点接收完全相同的复制流。
  4. Redis主从复制是无阻塞的,意味着在进行复制或同步是仍然可以进行数据查询
  5. 复制既可以用于可伸缩性,也可以用于只读查询的多个副本(读写分离)
  6. 可以配置主节点不进行数据保存或只启用AOF,然后将数据保存到副本,但是注意:当服务器重启后,从节点服务器重新同步数据时,同时会清空从节点数据。

Redis 复制的原理:

每个Redis Master 节点都有一个replication ID:这是一个较大的伪随机字符串,用于标记当前数据集。每个Master 还有一个偏移量,该偏移量会针对复制流中要发送到副本的每个字节的增量而增加。
以便更新副本的状态。即使未连接任何副本,复制偏移也会增加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Replication
role:master
connected_slaves:1
slave0:ip=39.105.157.176,port=6379,state=online,offset=61238,lag=0
# 主 Replication ID
master_replid:649255ffb2183786d00203aa51715169b06f4f47
# 副 Replication ID
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:61238
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:61238

当从节点连接主节点时,会使用PSYNC命令来发送当前旧的主节点的Replication ID和offset,这样主节点只会同步相差的数据量,如果主节点缓冲区的副本引用(Replication ID)不存在,则会进行全量同步。

全量同步的工作流程如下:

主节点开始后台生成RDB文件。同时,它开始缓冲从客户端收到的所有新写入命令。后台保存完成后,主数据库将数据库文件传输到从节点,从节点将其保存在磁盘上,然后将其加载到内存中。然后,主服务器将所有缓冲的命令发送到副本。

Replication ID 说明:

如果两个实例具有相同的Replication ID和offset,则它们具有完全相同的数据。但是为什么会有两个Replication ID(主和副)?

因为两个实例A和B具有相同的Replication ID,但一个实例的offset为1000,另一个实例的offset为1023,则意味着第一个实例缺少应用于数据集的某些命令。这也意味着,仅通过应用一些命令,A即可达到与B完全相同的状态。

Redis实例具有两个Replication ID的原因是由于副本被提升为主副本。故障转移后,升级后的副本仍需要记住其过去的Replication ID,因为该Replication ID是以前的主副本之一。这样,当其他副本将与新的主副本同步时,它们将尝试使用旧的主Replication ID执行部分重新同步。因为将副本提升为主副本时,它会将其辅助ID设置为其主ID,并记住发生此ID切换时的offset。稍后它将选择一个新的随机Replication ID,因为新的历史记录开始了。处理新的副本连接时,主机将其ID和offset与当前ID和辅助ID相匹配(为安全起见,直到给定的偏移量)。简而言之,这意味着在故障转移后,连接到新提升的主服务器的副本不必执行完全同步。

无盘复制

通常,完全重新同步需要在磁盘上创建RDB文件,然后从磁盘重新加载相同的RDB,以便为副本提供数据。
但是对于硬盘速度慢,但是在内网环境下,可以采用无盘复制,这样可以直接通过Socket将RDB发送给从节点,而无需使用硬盘作为中间存储。

配置

1
replicaof 192.168.1.1 6379

也可以使用REPLICAOF命令进行同步。
可以使用repl-diskless-sync配置参数启用无盘复制。在repl-diskless-sync-delay 参数控制第一个副本之后,为了等待更多副本到达而开始传输的延迟。

1
masterauth <password>

设置主节点的密码,也可以使用redis-cli输入config set masterauth <password>进行设置。

1
2
3
4
# 在当前至少有N个副本连接到主服务器时才接受写查询
min-replicas-to-write <number of replicas>
# 在有N个副本延迟少于M秒时,则接受写入
min-replicas-max-lag <number of seconds>

Redis持久化

Redis的持久化机制-RDB

1.什么是RDB

The RDB persistence performs point-in-time snapshots of your dataset at specified intervals.(RDB持久化是以指定的时间间隔执行数据集的时间点快照。)
简单来说,RDB是每隔一段时间,会把内存中的数据写入磁盘的临时文件作为快照,恢复时把快照文件读取到内存中。如果机器宕机,那么内存中的数据就会丢失,使用RDB机制后,重启后数据会恢复。

2.备份与恢复

内存备份 --> 磁盘临时文件
临时文件 --> 恢复到内存

3.RDB优劣势

  • 优势

    1. 每隔一段时间全量备份
    2. 容灾简单,适合进行远程冷备份
    3. 子进程备份的时候,主进程不会有任何IO操作(不会有写入或修改),保证备份数据的完整性
    4. 相对AOF来说,当有更大的文件时候可以快速重启恢复
  • 劣势

    1. 发生故障时,可能会丢失最后一次备份数据
    2. 子进程所占用的内存会和父进程一模一样,会造成CPU负担
    3. 由于定时全量备份是重量级操作,所以对于实时备份,就无法处理了

4.RDB的配置

  1. 保存位置可以自定义配置:
1
dir /var/redis/6379/dump.rdb
  1. 保存机制
1
2
3
4
5
6
7
8
#   save <seconds> <changes>
# 900秒(15分钟)后,如果至少有 1 个Key发生改变
# 300秒(5分钟)后,如果至少有 10 个Key发生改变
# 60秒(1分钟)后,如果至少有 10000 个Key发生改变

save 900 1
save 300 10
save 60 10000

Redis的持久化机制-AOF

1.什么是AOF

The AOF persistence logs every write operation received by the server, that will be played again at server startup, reconstructing the original dataset. Commands are logged using the same format as the Redis protocol itself, in an append-only fashion. Redis is able to rewrite the log in the background when it gets too big.

AOF持久化会记录服务器接收到的每个写入操作,这些操作将在服务器启动时再次执行,以重新构建原始的数据。命令记录的格式与Redis协议本身的格式相同,采用追加方式。当日志文件过大时,Redis可以在后台进行rewrite

2.备份与恢复

日志备份 --> 命令追加方式
日志文件 --> 根据命令日志文件重新构建

3.AOF优劣势

  • 优势
    1. 使用AOF数据持久化更加完整
    2. 可以使用不同的fsync策略,使用默认策略fsync时,每秒的写入性能仍然很好(fsync是使用后台线程执行的,并且在没有进行fsync的情况下,主线程将尽力执行写入操作)但是会损失一秒的写入时间
    3. AOF是使用日志追加的方式,如果断电或其他原因导致日志只写了一半,可以使用redis-check-aof工具进行修复
    4. Redis太大时,Redis可以在后台自动重写AOF。 Redis继续追加到旧文件时,会生成一个新的文件,其中包含创建当前数据集所需的最少操作集,一旦准备好第二个文件,Redis会切换这两个文件并开始追加到新的那一个
    5. 即使使用FLUSHALL命令清空了所有数据,也可以通过修改AOF文件进行数据恢复
  • 劣势
    1. AOF相比RDB占用空间更大
    2. 使用fsync策略后,相比RDB性能差一些
    3. 当服务器宕机后,使用AOF恢复的数据可能不完整

4.AOF的配置

  1. 开启AOF
1
appendonly yes
  1. 配置fsync策略
1
2
3
4
5
6
# If unsure, use "everysec".
# 如果不确定,推荐使用 "everysec"

# appendfsync always
appendfsync everysec
# appendfsync no
  1. AOF rewrite配置
1
2
3
4
# 设置一个百分比
auto-aof-rewrite-percentage 100
# 设置AOF 的最小大小
auto-aof-rewrite-min-size 64mb
  1. Redis 启动加载配置
1
2
# 优先加载AOF 文件
aof-use-rdb-preamble yes

两种持久化方式改如何选择

  • 如果对数据可靠性要求很高,则应同时使用两种持久性方式。
  • 如果在灾难情况下仍然可以承受几分钟的数据丢失,则可以仅使用RDB。
  • 不推荐单独使用AOF,因为AOF在数据恢复时,有时候会出现Bugs。

Redis的持久化机制-RDB

1.什么是RDB

The RDB persistence performs point-in-time snapshots of your dataset at specified intervals.(RDB持久化是以指定的时间间隔执行数据集的时间点快照。)
简单来说,RDB是每隔一段时间,会把内存中的数据写入磁盘的临时文件作为快照,恢复时把快照文件读取到内存中。如果机器宕机,那么内存中的数据就会丢失,使用RDB机制后,重启后数据会恢复。

2.备份与恢复

内存备份 --> 磁盘临时文件
临时文件 --> 恢复到内存

3.RDB优劣势

  • 优势

    1. 每隔一段时间全量备份
    2. 容灾简单,适合进行远程冷备份
    3. 子进程备份的时候,主进程不会有任何IO操作(不会有写入或修改),保证备份数据的完整性
    4. 相对AOF来说,当有更大的文件时候可以快速重启恢复
  • 劣势

    1. 发生故障时,可能会丢失最后一次备份数据
    2. 子进程所占用的内存会和父进程一模一样,会造成CPU负担
    3. 由于定时全量备份是重量级操作,所以对于实时备份,就无法处理了

4.RDB的配置

  1. 保存位置可以自定义配置:
1
dir /var/redis/6379/dump.rdb
  1. 保存机制
1
2
3
4
5
6
7
8
#   save <seconds> <changes>
# 900秒(15分钟)后,如果至少有 1 个Key发生改变
# 300秒(5分钟)后,如果至少有 10 个Key发生改变
# 60秒(1分钟)后,如果至少有 10000 个Key发生改变

save 900 1
save 300 10
save 60 10000

Redis的持久化机制-AOF

1.什么是AOF

The AOF persistence logs every write operation received by the server, that will be played again at server startup, reconstructing the original dataset. Commands are logged using the same format as the Redis protocol itself, in an append-only fashion. Redis is able to rewrite the log in the background when it gets too big.

AOF持久化会记录服务器接收到的每个写入操作,这些操作将在服务器启动时再次执行,以重新构建原始的数据。命令记录的格式与Redis协议本身的格式相同,采用追加方式。当日志文件过大时,Redis可以在后台进行rewrite

2.备份与恢复

日志备份 --> 命令追加方式
日志文件 --> 根据命令日志文件重新构建

3.AOF优劣势

  • 优势
    1. 使用AOF数据持久化更加完整
    2. 可以使用不同的fsync策略,使用默认策略fsync时,每秒的写入性能仍然很好(fsync是使用后台线程执行的,并且在没有进行fsync的情况下,主线程将尽力执行写入操作)但是会损失一秒的写入时间
    3. AOF是使用日志追加的方式,如果断电或其他原因导致日志只写了一半,可以使用redis-check-aof工具进行修复
    4. Redis太大时,Redis可以在后台自动重写AOF。 Redis继续追加到旧文件时,会生成一个新的文件,其中包含创建当前数据集所需的最少操作集,一旦准备好第二个文件,Redis会切换这两个文件并开始追加到新的那一个
    5. 即使使用FLUSHALL命令清空了所有数据,也可以通过修改AOF文件进行数据恢复
  • 劣势
    1. AOF相比RDB占用空间更大
    2. 使用fsync策略后,相比RDB性能差一些
    3. 当服务器宕机后,使用AOF恢复的数据可能不完整

4.AOF的配置

  1. 开启AOF
1
appendonly yes
  1. 配置fsync策略
1
2
3
4
5
6
# If unsure, use "everysec".
# 如果不确定,推荐使用 "everysec"

# appendfsync always
appendfsync everysec
# appendfsync no
  1. AOF rewrite配置
1
2
3
4
# 设置一个百分比
auto-aof-rewrite-percentage 100
# 设置AOF 的最小大小
auto-aof-rewrite-min-size 64mb
  1. Redis 启动加载配置
1
2
# 优先加载AOF 文件
aof-use-rdb-preamble yes

两种持久化方式改如何选择

  • 如果对数据可靠性要求很高,则应同时使用两种持久性方式。
  • 如果在灾难情况下仍然可以承受几分钟的数据丢失,则可以仅使用RDB。
  • 不推荐单独使用AOF,因为AOF在数据恢复时,有时候会出现Bugs。

Redis安装与配置

下载

  1. Redis官网下载redis 最新稳定版本
  2. 解压到 /usr/local/ 目录下

安装

1
make && make test && make install

make时报如下错误:

1
2
3
4
5
zmalloc.h:50:31: error: jemalloc/jemalloc.h: No such file or directory
zmalloc.h:55:2: error: #error "Newer version of jemalloc required"
make[1]: *** [adlist.o] Error 1
make[1]: Leaving directory `/data0/src/redis-2.6.2/src'
make: *** [all] Error 2

原因是jemalloc重载了Linux下的ANSI C的malloc和free函数。
解决办法:

1
make MALLOC=libc

redis 生产环境启动方案

  1. redis utils 目录下,有个redis_init_script脚本
  2. 将redis_init_script脚本拷贝到linux的/etc/init.d 目录中,将redis_init_script重命名为redis_6379, 6379是我们希望redis 的实例端口号
  3. 创建两个目录 /etc/redis (存放redis 配置的目录), /var/redis/6379 (存放redis的持久文件)
  4. 修改redis.conf配置文件(默认在根目录下),拷贝到/etc/redis目录下,修改名称为6379.conf
  5. 修改redis.conf中的部分配置为生产环境
参数 说明
daemonize yes 让redis以daemon进程运行
pidfile /var/run/redis_6379.pid 设置redis的pid文件位置
port 6379 设置redis的监听端口号
dir /var/redis/6379 设置持久化文件的存储位置
  1. 启动redis:
1
2
3
cd /etc/init.d 
chmod 777 redis_6379
./redis_6379 start
  1. 确认redis 进程是否启动: ps -ef | grep redis
  2. 让redis跟随系统启动自动启动,在redis_6379 脚本中,最上面加入两行注释
1
2
3
# chkconfig:    2345 90 10
# description: Redis is a persistent key-value database
chkconfig redis_6379 on

主从架构(主节点和从节点最好保证一致的版本)

  1. 在 slave node 上配置:
1
slaveof 192.168.1.1 6379
  1. 强制读写分离

基于主从复制架构,实现读写分离
redis slave node 只读,默认开启,slave-read-only
开启了只读的redis slave node,会拒绝所有的写操作,这样可以强制搭建成读写分离架构

  1. 集成安全认证
    master 上启用安全认证策略,requirepass
    master 连接口令 masterauth

  2. 读写分离架构的测试
    先启动主节点,再启动从节点,在搭建生产环境的时候,不要忘记修改一个配置:

1
bind 127.0.0.1

每个redis.conf 中的bind 127.0.0.1 -> bind自己的ip,并且防火墙打开6379端口

redis-cli的使用

命令 说明
redis-cli shutdown 连接本机的6379端口停止redis进程
redis-cli -h 127.0.0.1 -p 6379 指定要连接的ip和端口号
redis-cli ping ping redis的端口,看是否正常
redis-cli 进入交互式命令行
set k1 v1 设置一个key:value
get k1 获取key的值

下载

  1. Redis官网下载redis 最新稳定版本
  2. 解压到 /usr/local/ 目录下

安装

1
make && make test && make install

make时报如下错误:

1
2
3
4
5
zmalloc.h:50:31: error: jemalloc/jemalloc.h: No such file or directory
zmalloc.h:55:2: error: #error "Newer version of jemalloc required"
make[1]: *** [adlist.o] Error 1
make[1]: Leaving directory `/data0/src/redis-2.6.2/src'
make: *** [all] Error 2

原因是jemalloc重载了Linux下的ANSI C的malloc和free函数。
解决办法:

1
make MALLOC=libc

redis 生产环境启动方案

  1. redis utils 目录下,有个redis_init_script脚本
  2. 将redis_init_script脚本拷贝到linux的/etc/init.d 目录中,将redis_init_script重命名为redis_6379, 6379是我们希望redis 的实例端口号
  3. 创建两个目录 /etc/redis (存放redis 配置的目录), /var/redis/6379 (存放redis的持久文件)
  4. 修改redis.conf配置文件(默认在根目录下),拷贝到/etc/redis目录下,修改名称为6379.conf
  5. 修改redis.conf中的部分配置为生产环境
参数 说明
daemonize yes 让redis以daemon进程运行
pidfile /var/run/redis_6379.pid 设置redis的pid文件位置
port 6379 设置redis的监听端口号
dir /var/redis/6379 设置持久化文件的存储位置
  1. 启动redis:
1
2
3
cd /etc/init.d 
chmod 777 redis_6379
./redis_6379 start
  1. 确认redis 进程是否启动: ps -ef | grep redis
  2. 让redis跟随系统启动自动启动,在redis_6379 脚本中,最上面加入两行注释
1
2
3
# chkconfig:    2345 90 10
# description: Redis is a persistent key-value database
chkconfig redis_6379 on

主从架构(主节点和从节点最好保证一致的版本)

  1. 在 slave node 上配置:
1
slaveof 192.168.1.1 6379
  1. 强制读写分离

基于主从复制架构,实现读写分离
redis slave node 只读,默认开启,slave-read-only
开启了只读的redis slave node,会拒绝所有的写操作,这样可以强制搭建成读写分离架构

  1. 集成安全认证
    master 上启用安全认证策略,requirepass
    master 连接口令 masterauth

  2. 读写分离架构的测试
    先启动主节点,再启动从节点,在搭建生产环境的时候,不要忘记修改一个配置:

1
bind 127.0.0.1

每个redis.conf 中的bind 127.0.0.1 -> bind自己的ip,并且防火墙打开6379端口

redis-cli的使用

命令 说明
redis-cli shutdown 连接本机的6379端口停止redis进程
redis-cli -h 127.0.0.1 -p 6379 指定要连接的ip和端口号
redis-cli ping ping redis的端口,看是否正常
redis-cli 进入交互式命令行
set k1 v1 设置一个key:value
get k1 获取key的值