首页 » redis » 正文

浅谈小白如何读懂Redis高速缓存与持久化并存及主从高可用集群

一、简介    

Redis是一个基于键值(K-V)的高速缓存软件,和他具有相同功能的软件有memcached,但其支持更为复杂的数据结构,例如:List,set,sorted set,同时redis具有持久性功能。redis究竟是什么?对于不同的应用场合,对redis的理解也不相同,如下有三种不同的理解。

①key value store(键值存储),是一个以键值形式存储的数据库,用来作为唯一的存储系统,同时借助于sentinel实现一定意义上的高可用。

②memory cached(内存缓存),是一个把数据存储在内存中的高速缓存,在应用中用来实现高效的响应用户请求。

③data structrue server(数据结构服务),支持对复杂数据库结构的高速操作例如:list,string,hash,set,stored等,提供某特殊业务操作。

redis的优势:

①丰富的操作,例如:hash,list,set,stored,sets.

②内建复制(replication)及其集群(cluster)

③就地更新操作,而无需停机重启生效

④支持持久缓存,常用的有RDB和AOF

    基本架构图:

wKiom1hRB6mSHMJYAAKXoJpwRZ8104.png

原理:redis工作时,将启动一个fork函数创建一个子进程,复制当前进程,存为副本,父进程任然接受并处理客服端请求,而子进程则将内存中的数据文件写入磁盘中的临时文件,当子进程完成所有的写入操作时会将原来的件替换成最新生成的临时文件。

   二、redis持久性介绍

redis持久性分为两种:RDB(Redis datebase)和AOF(append only file),同时,RDB和AOF可同时使用,但BGSAVE和BGWRITEAOF不会同时执行,在redis服务器启动用于恢复数据时将会优先使用AOF。持久的功能是用于恢复,但持久本身不能取代备份,还应该制定备份策略,对redis进行数据库备份,保证数据的完整性。

RDB:此方式基于快照实现,该持久化方式是在redis内部有一个定时器,每隔固定时间去检查当前数据发生改变的次数与时间是否满足配置的持久性触发的条件,如果满足则通过操作系统启动一个fork函数调用来创建出一个字进程,这个子进程默认会与父进程共享相同的地址空间,这时就可以通过子进程来遍历整个内存来进行存储操作,而主进程则仍然可以提供服务,当有写入时由操作系统按照内存页(page)为单位来进行copy-on-write保证父子进程之间不会互相影响。该持久化的主要缺点是定时快照只是代表一段时间内的内存映像,所以系统重启会丢失上次快照与重启之间所有的数据。

 

编辑/etc/redis.conf可查看相应的配置参数及其意义

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
################################ SNAPSHOTTING  ################################
#
# Save the DB on disk:
#
#   save <seconds> <changes>
#
#   Will save the DB if both the given number of seconds and the given
#   number of write operations against the DB occurred.
#
#   In the example below the behaviour will be to save:
#   after 900 sec (15 min) if at least 1 key changed
#   after 300 sec (5 min) if at least 10 keys changed
#   after 60 sec if at least 10000 keys changed
#
#   Note: you can disable saving completely by commenting out all "save" lines.
#
#   It is also possible to remove all the previously configured save
#   points by adding a save directive with a single empty string argument
#   like in the following example:
#
#   save ""
save 900 1  #表示900秒内有一个值发生改变则就会触发存储
save 300 10 #表示300秒内有10个值发生改变则就会触发存储   
save 60 10000 #表示60内有10000个值发生改变则就会触发储存  
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbcheksum yes
dbfilename dump.rdb  #数据库文件名
dir /var/lib/redis   #redis安装路径

    AOF:redis主进程通过fork创建子进程,子进程根据redis内存中的数据库重构后将此存储于临时文件中,父进程继承客服端的请求,并会把这些请求中的操作继续追加至原来的AOF文件,额外的这些新的写请求还会被放置于一个缓冲队列中,父进程把缓冲中的命令写到临时文件中,子进程重写完成会通知父进程,父进程用临时文件替换原来的AOF老文件。

AOF方式实际类似mysql的基于语句的binlog方式,即每条会使Redis内存数据发生改变的命令都会追加到一个log文件中,也就是说这个log文件就是Redis的持久化数据。

AOF的方式的主要缺点是追加log文件可能导致体积过大,当系统重启恢复数据时如果是aof的方式则加载数据会非常慢,几十G的数据可能需要几小 时才能加载完,当然这个耗时并不是因为磁盘文件读取速度慢,而是由于读取的所有命令都要在内存中执行一遍。另外由于每条命令都要写log,所以使用aof 的方式,Redis的读写性能也会有所下降。

AOF对日志文件的写入操作时采用追加的模式进行,因此写入的过程中如果发生断电,机器宕机等情况发生,也不会对已存在数据文件造成破坏。

1
2
3
4
5
6
7
#aof相关参数:
auto-aof-rewrite-percentage 100 #当目前的AOF文件大小超过上一次重写时的AOF文件大小的百分之多少时会再次进行重写,如果之前没有重写过,则以启动时的AOF文件大小为依据
auto-aof-rewrite-min-size 64mb  #允许重写的最小AOF文件大小
appendonly yes   #开启AOF 
appendfilename "appendonly.aof"
appendfsync everysec  #每秒执行同步
no-appendfsync-on-rewrite no

    Redis持久化磁盘IO方式及其带来的问题

有Redis线上运维经验的人会发现Redis在物理内存使用比较多,但还没有超过实际物理内存总容量时就会发生不稳定甚至崩溃的问题,有人认为是基于快照方式持久化的fork系统调用造成内存占用加倍而导致的,这种观点是不准确的,因为fork用的copy-on-write机制是基于操作系统页这个单位的,也就是只有有写入的脏页会被复制,但是一般你的系统不会在短时间内所有的页都发生了写入而导致复制,那么是什么原因导致Redis崩溃的呢?

答案是Redis的持久化使用了Buffer IO造成的,所谓Buffer IO是指Redis对持久化文件的写入和读取操作都会使用物理内存的Page Cache,而大多数数据库系统会使用Direct IO来绕过这层Page Cache并自行维护一个数据的Cache,而当Redis的持久化文件过大(尤其是快照文件),并对其进行读写时,磁盘文件中的数据都会被加载到物理内 存中作为操作系统对该文件的一层Cache,而这层Cache的数据与Redis内存中管理的数据实际是重复存储的,虽然内核在物理内存紧张时会做 Page Cache的剔除工作,但内核很可能认为某块Page Cache更重要,而让你的进程开始Swap ,这时你的系统就会开始出现不稳定或者崩溃了。我们的经验是当你的Redis物理内存使用超过内存总容量的3/5时就会开始比较危险了。

    三、redis主从复制

redis主从复制和大部分主从类似,一个master可以有多个slave,支持链式复制,master以非阻塞的方式同步数据至slave。启动一个slave后,slave会向主发送同步命令,请求同步主库上的数据,master将启动一个后台的子进程,将数据快照保存至在数据文件中,把数据文件发送给slave,slave将数据文件保存至本地中,在本地重建数据库后载入内存,同步完成。

redis主从的特点:

1、redis使用异步复制,从服务器会以每秒一次的频率向主服务器报告复制流的处理进度

2、一个主服务器可以有多个从服务器,从服务器也可以有自己的从服务器(级联复制)

3、复制功能不会阻塞主服务器,即使一个或多个从服务器正在进行初次同步,主服务器也可以继续处理命令请求

4、复制功能可以用于数据冗余,也可以通过让多个从服务器处理只读命令请求来提升扩展性

5、Redis从节点默认为只读,无须手动配置,redis的主从集群可以实现分担压力的效果,但是无法做到高可用,如果master宕掉,服务就不可用了,所以使用redis的sentinel可以实现HA的功能。

主从实战配置:

编辑/etc/redis.conf配置文件,将bind改为本机IP地址,重启服务即可。

主机名 IP地址 节点标注
node1.alren.com 10.1.10.65 master
node2.alren.com 10.1.10.66 slave

主节点相关配置参数如下:

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
[root@node1 ~]# redis-cli -h 10.1.10.65 -p 6379
10.1.10.65:6379> INFO
# Server
redis_version:3.0.2
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:6be7fc9e6b88f79
redis_mode:standalone
os:Linux 3.10.0-327.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.4.7
process_id:2540
run_id:05cd9584be6ad13262aeb42965f486744f7d10c4
tcp_port:6379
uptime_in_seconds:103
uptime_in_days:0
hz:10
lru_clock:5327264
config_file:/etc/redis.conf
# Clients
connected_clients:1
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0
# Memory
used_memory:1922496
used_memory_human:1.83M
used_memory_rss:7675904
used_memory_peak:1922496
used_memory_peak_human:1.83M
used_memory_lua:36864
mem_fragmentation_ratio:3.99
mem_allocator:jemalloc-3.6.0
# Persistence
loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1481722242
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
# Stats
total_connections_received:2
total_commands_processed:33
instantaneous_ops_per_sec:0
total_net_input_bytes:1103
total_net_output_bytes:100
instantaneous_input_kbps:0.02
instantaneous_output_kbps:0.01
rejected_connections:0
sync_full:1
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:1946
migrate_cached_sockets:0
# Replication
role:master  #当前节点的角色
connected_slaves:1  #从节点的个数
slave0:ip=10.1.10.66,port=6379,state=online,offset=43,lag=0 #从节点IP地址及端口、状态信息,偏移量等
master_repl_offset:43
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:42
# CPU
used_cpu_sys:0.17
used_cpu_user:0.03
used_cpu_sys_children:0.01
used_cpu_user_children:0.00
# Cluster
cluster_enabled:0
# Keyspace
db0:keys=2,expires=0,avg_ttl=0
10.1.10.65:6379>

配置node2为从节点,并打印相关信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@node2 ~]# redis-cli  -h 10.1.10.66 -p 6379
10.1.10.66:6379> slaveof 10.1.10.65 6379  #表示主节点IP地址和端口
OK
10.1.10.66:6379> INFO replication
# Replication
role:slave  #当前角色
master_host:10.1.10.65 #主节点IP地址
master_port:6379  #主节点端口号
master_link_status:up #主节点状态信息
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_repl_offset:1093
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
10.1.10.66:6379> config get slaveof #查询当前从的主节点IP地址及端口信息
1) "slaveof"
2) "10.1.10.65 6379"
10.1.10.66:6379>

在主中插入数据,查看从中是否已经同步完成

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
#在主中插入键值对,查看其是否插入成功
10.1.10.65:6379> set port 8080
OK
10.1.10.65:6379> get port
"8080"
10.1.10.65:6379> set ip 10.1.1.1
OK
10.1.10.65:6379> keys *
1) "ip"
2) "port"
10.1.10.65:6379> get port
"8080"
10.1.10.65:6379> get ip
"10.1.1.1"
10.1.10.65:6379>
#在从中查看是否已同步完成,如下结果显示在主中插入数据从节点立刻能get到数据,说明配置成功
10.1.10.66:6379> keys *
1) "port"
2) "ip"
10.1.10.66:6379> get ip
"10.1.1.1"
10.1.10.66:6379> get port
"8080"
10.1.10.66:6379>

    四、redis高可用管理工具sentinel

Sentinel是一个管理redis实例的工具,它可以对现有的redis进行监控、通知、故障自动转移,sentinel不断的检测redis实例是否可以正常的工作,通过API向其他程序报告redis的转台,如redis master不能工作,则会自动启动故障转移进程,将其中一个slave提升为master,其他slave将从新设置新的master服务器,而故障的master再次启动后会被sentinel自动降级为slave。

基本架构图:

wKioL1hR_9bz8uCcAALNCO3KDcc596.png

Sentinel作用如下:

1、监控:sentinel会不断的检查你的主服务器和从服务器是否运行正常

2、当被监控的某个redis服务器出现问题时,sentinel可以通过API向管理员或者其他应用程序发送通知

3、故障自动转移:当一个主服务器不能正常工作时,sentinel会开始一次自动故障转移操作,他会将其中一个从服务器升级为新的主服务器,并将其他从服务器改为复制新的主服务器;当客户端试图连接失效的主服务器时,集群也会向客户端返回新主服务器的地址,使得集群可以使用新主服务器代替失效服务器。

redis sentinel在监控redis实例时有两种redis宕机状态S_DOWN和O_DOWN:

S_DOWN:当sentinel在指定的超时时间内没有收到一个正确的ping回复值,则认为是S_DOWN

O_DOWN:O_DOWN的条件是有足够多的sentinel认为该redis实例是S_DOWN。

注意:O_DOWN只能是发生在主服务器,sentinel和其他从服务器不会发生O_DOWN

Sentinel监控管理redis实战配置:

本实验在一台服务器上可完成实验,本实验使用node1附加上面配合的主从完成sentinel高可用测试实验。

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
#创建数据目录并修改相应的配置文件,如下所示:
mkdir /redis/db/{1,2,3}
chown -R redis.redis /redis/db*   
#修改配置文件主要有如下:
配置文件实例一:
bind 0.0.0.0 
pidfile "/var/run/redis.pid"
port 6379   
logfile "/var/log/redis/redis.log"
dir    "/redis/db1"
配置文件实例二:
bind 0.0.0.0 
pidfile "/var/run/redis2.pid"
port 6380 
logfile "/var/log/redis/redis2.log"
dir    "/redis/db2"
配置文件实例三:
bind 0.0.0.0 
pidfile "/var/run/redis3.pid"
port 6380 
logfile "/var/log/redis/redis3.log"
dir    "/redis/db3"
#配置sentinel监控配置文件如下  
port 26379
logfile "/var/log/redis/sentinel.log"
dir "/tmp"
sentinel monitor mymaster 10.1.10.65 6381 1
sentinel down-after-milliseconds mymaster 50000
sentinel failover-timeout mymaster 60000
sentinel config-epoch mymaster 1
sentinel leader-epoch mymaster 1
sentinel known-slave mymaster 10.1.10.65 6379
sentinel current-epoch 1

分别使用不同的配置文件启动redis服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@node1 redis]# redis-server /etc/redis/redis.conf
[root@node1 redis]# redis-server /etc/redis/redis.conf.2
[root@node1 redis]# redis-server /etc/redis/redis.conf.3
[root@node1 redis]# ss -tnl #其端口全部监听
State       Recv-Q Send-Q Local Address:Port               Peer Address:Port
LISTEN      0      128          *:6379                     *:*
LISTEN      0      128          *:6380                     *:*
LISTEN      0      128          *:6381                     *:*
LISTEN      0      5      192.168.122.1:53                       *:*
LISTEN      0      128          *:22                       *:*
LISTEN      0      128    127.0.0.1:631                      *:*
LISTEN      0      100    127.0.0.1:25                       *:*
LISTEN      0      32          :::21                      :::*
LISTEN      0      128         :::22                      :::*
LISTEN      0      128        ::1:631                     :::*
LISTEN      0      100        ::1:25                      :::*
[root@node1 redis]#

使用slaveof命令把主节点设置为本机的6381端口

1
2
3
4
5
6
7
8
9
10
11
12
13
10.1.10.65:6381> INFO replication
# Replication
role:master
connected_slaves:3
slave0:ip=10.1.10.65,port=6380,state=online,offset=10551,lag=1 
slave1:ip=10.1.10.65,port=6379,state=online,offset=10551,lag=1
slave2:ip=10.1.10.66,port=6379,state=online,offset=10551,lag=1
master_repl_offset:10551
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:10550
10.1.10.65:6381>

启动sentinel监控器节点状态

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
[root@node1 ~]# redis-server /etc/redis/redis-sentinel.conf
[root@node1 ~]# redis-cli -h 10.1.10.65 -p 26379
10.1.10.65:26379> INFO  #如下信息表明了个节点的信息
# Server
redis_version:3.0.2
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:6be7fc9e6b88f79
redis_mode:sentinel
os:Linux 3.10.0-327.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.4.7
process_id:3769
run_id:93704fa35c2fa79d20fe4fed732a1dc8a257a01f
tcp_port:26379
uptime_in_seconds:46
uptime_in_days:0
hz:10
lru_clock:5332425
config_file:/etc/redis/redis-sentinel.conf
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=10.1.10.65:6381,slaves=3,sentinels=1
10.1.10.65:26379> sentinel masters
1)  1) "name"
    2) "mymaster"
    3) "ip"
    4) "10.1.10.65"
    5) "port"
    6) "6381"
    7) "runid"
    8) "ea1bbae43911ec703e0bad35053a99e88c6d71ce"
    9) "flags"
   10) "master"
   11) "pending-commands"
   12) "0"
   13) "last-ping-sent"
   14) "0"
   15) "last-ok-ping-reply"
   16) "650"
   17) "last-ping-reply"
   18) "650"
   19) "down-after-milliseconds"
   20) "50000"
   21) "info-refresh"
   22) "6391"
   23) "role-reported"
   24) "master"
   25) "role-reported-time"
   26) "106722"
   27) "config-epoch"
   28) "1"
   29) "num-slaves"
   30) "3"
   31) "num-other-sentinels"
   32) "0"
   33) "quorum"
   34) "1"
   35) "failover-timeout"
   36) "60000"
   37) "parallel-syncs"
   38) "1"
10.1.10.65:26379> sentinel masters #查看主节点信息状态
1)  1) "name"
    2) "mymaster"
    3) "ip"
    4) "10.1.10.65"
    5) "port"
    6) "6381"
    7) "runid"
    8) "ea1bbae43911ec703e0bad35053a99e88c6d71ce"
    9) "flags"
   10) "master"
   11) "pending-commands"
   12) "0"
   13) "last-ping-sent"
   14) "0"
   15) "last-ok-ping-reply"
   16) "239"
   17) "last-ping-reply"
   18) "239"
   19) "down-after-milliseconds"
   20) "50000"
   21) "info-refresh"
   22) "6176"
   23) "role-reported"
   24) "master"
   25) "role-reported-time"
   26) "257144"
   27) "config-epoch"
   28) "1"
   29) "num-slaves"
   30) "3"
   31) "num-other-sentinels"
   32) "0"
   33) "quorum"
   34) "1"
   35) "failover-timeout"
   36) "60000"
   37) "parallel-syncs"
   38) "1"
10.1.10.65:26379>

模拟redis-server 6381除故障可以将此进程kill掉,查看主节点是否转移

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
10.1.10.65:26379> info
# Server
redis_version:3.0.2
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:6be7fc9e6b88f79
redis_mode:sentinel
os:Linux 3.10.0-327.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.4.7
process_id:3769
run_id:93704fa35c2fa79d20fe4fed732a1dc8a257a01f
tcp_port:26379
uptime_in_seconds:435
uptime_in_days:0
hz:12
lru_clock:5332814
config_file:/etc/redis/redis-sentinel.conf
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=10.1.10.65:6380,slaves=3,sentinels=1 #主节点已经自动转移至本机的6380上
10.1.10.65:26379>

查看redis-server的6380端口是否成为主节点

1
2
3
4
5
6
7
8
9
10
11
12
13
10.1.10.65:6380> INFO replication
# Replication
role:master
connected_slaves:3
slave0:ip=10.1.10.65,port=6379,state=online,offset=35484,lag=1
slave1:ip=10.1.10.66,port=6379,state=online,offset=35484,lag=1
slave2:ip=10.1.10.65,port=6381,state=online,offset=35484,lag=1
master_repl_offset:35484
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:35483
10.1.10.65:6380>


总结:Redis持久性中的RDB是基于快照方式,意外重启会丢失数据,而AOF对日志文件的写入操作时采用追加的模式进行,因此写入的过程中如果发生断电,机器宕机等情况发生,也不会对已存在数据文件造成破坏。在考虑数据的完整性可根据自己的业务可同时使用AOF和RDB,保证了数据的完整性,但是redis持久性并不代表备份,还需制定相关的备份方案,对redis已有的数据进行备份。重新启动Redis,在redis服务器启动用于恢复数据时,会优先考虑使用AOF。

发表评论