Redis:node
Redis是一款优秀的结构数据存储系统,因为出色的并发性能广为关注,可用做:数据库、缓存、消息队列;同类型的还有memcached,可是因为memcache支持的结构类型较少,而且不可以将数据持久化,慢慢的被redis所取代。redis
Redis支持的数据结构:字符串、列表(数组)、hashes(关联数组)、集合、有序集合、bitmaps、hyperloglogs、空间索引;本篇博客简单介绍redis对于实现高可用和持久化的四种redis工做模式,进入正题:算法
[TOC]数据库
进入正题简单介绍vim
单机模式下,对于数据的落地,根据业务数据的重要程度选择是RDB仍是AOF备份;数组
RDB:snapshotting, 二进制格式;按事先定制的策略,周期性地将数据从内存同步至磁盘;数据文件默认为dump.rdb; 客户端显式使用SAVE或BGSAVE命令来手动启动快照保存机制; SAVE:同步,即在主线程中保存快照,此时会阻塞全部客户端请求; BGSAVE:异步;backgroud AOF:Append Only File, fsync 记录每次写操做至指定的文件尾部实现的持久化;当redis重启时,可经过从新执行文件中的命令在内存中重建出数据库; BGREWRITEAOF:AOF文件重写; 不会读取正在使用AOF文件,而是经过将内存中的数据以命令的方式保存至临时文件中,完成以后替换原来的AOF文件;
注意:持久机制自己不能取代备份;应该制订备份策略,对redis库按期备份; RDB与AOF同时启用: (1) BGSAVE和BGREWRITEAOF不会同时进行; (2) Redis服务器启动时用持久化的数据文件恢复数据,会优先使用AOF;
RDB相关的配置: *save <seconds> <changes> save 900 1 save 300 10 save 60 10000 save 5 200000 表示:三个策略知足其中任意一个均会触发SNAPSHOTTING操做;900s内至少有一个key有变化,300s内至少有10个key有变化,60s内至少有1W个key发生变化; stop-writes-on-bgsave-error yes ## dump操做出现错误时,是否禁止新的写入操做请求; rdbcompression yes ##启用压缩 rdbchecksum yes ##检测完整性 dbfilename dump.rdb:指定rdb文件名 *dir /var/lib/redis:rdb文件的存储路径 AOF相关的配置 *appendonly no appendfilename "appendonly.aof" *appendfsync Redis supports three different modes: no:redis不执行主动同步操做,而是OS进行决定什么时候同步; everysec:每秒一次; ##推荐 always:每语句一次; no-appendfsync-on-rewrite no 是否在后台执行aof重写期间不调用fsync,默认为no,表示调用; auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb 上述两个条件同时知足时,方会触发重写AOF;与上次aof文件大小相比,其增加量超过100%,且大小很多于64MB; aof-load-truncated yes
实现原理:主从模式实现比较简单,相似于MySQL的主从,选定一个主机当作master,在从节点配置指向master的地址端口及口令信息便可实现;master开启主从模式后,会对当前自身进行快照RDB,而后基于网络发送给slave节点,在此以前用户能够选择直接基于网络发送给slave快照仍是先建立快照文件于磁盘中,然后将其发送给从节点;与此同时新接收的数据将放在repl-backlog-size中做为缓冲区,当slave接收RDB成功后,master将repl-backlog-size内容在陆续发送给slave缓存
开始以前全部Redis首先进行基本安全加固:安全
vim /etc/redis.conf ... bind 192.168.2.128 requirepass "ifan" ...
挑选一台Redis做为Master节点:ruby
Slave: 127.0.0.1:6379> SLAVEOF 192.168.2.128 6379 127.0.0.1:6379> CONFIG SET masterauth "ifan" 127.0.0.1:6379> CONFIG REWRITE
Master查看主从复制状态:bash
192.168.2.128:6379> INFO Replication # Replication role:master connected_slaves:1 slave0:ip=192.168.2.129,port=6379,state=online,offset=981,lag=1 master_repl_offset:981 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:2 repl_backlog_histlen:980 192.168.2.128:6379>
Redis的主从复制支持多slave,一样支持链式复制,只须要各级slave指定本身的master便可
主从复制调优参数:repl(主从复制)
slave-serve-stale-data yes # 代表slave会继续应答来自client的请求,但这些数据可能已通过期(由于链接中断致使没法从master同步)。若配置为no,则slave除正常应答"INFO"和"SLAVEOF"命令外,其他来自客户端的请求命令均会获得"SYNC with master in progress"的应答,直到该slave与master的链接重建成功或该slave被提高为master。 slave-read-only yes # 从状态下,不可写 repl-diskless-sync no # no,则是Disk-backend主节点新建立快照文件于磁盘中,然后将其发送给从节点; # yes,主节点新建立快照后直接经过网络套接字文件发送给从节点;为了实现并行复制,一般须要在复制启动前延迟一个时间段; repl-diskless-sync-delay 5 # 配置当收到第一个请求时,等待多个5个slave一块儿来请求之间的间隔时间。 repl-ping-slave-period 10 # 配置master与slave的心跳间隔10秒 repl-timeout 60 # 主从复制的超时时间 repl-disable-tcp-nodelay no # 禁止Nagle算法,容许小包的发送,不等待一有变化就发送。对于延时敏感型,同时数据传输量比较小的应用, repl-backlog-size 1mb # 设置备份的工做储备大小。工做储备是一个缓冲区,当从站断开一段时间的状况时,它替从站接收存储数据,所以当从站重连时,一般不须要彻底备份,只须要一个部分同步就能够,即把从站断开时错过的一部分数据接收。工做储备越大,从站能够断开并稍后执行部分同步的断开时间就越长。 slave-priority 100 # 复制集群中,主节点故障时,sentinel应用场景中的主节点选举时使用的优先级;数字越小优先级越高,但0表示不参与选举; min-slaves-to-write 3 # 主节点仅容许其可以通讯的从节点数量大于等于此处的值时接受写操做; min-slaves-max-lag 10 # 从节点延迟时长超出此处指定的时长时,主节点会拒绝写入操做;
原理实现:哨兵模式的实现是基于主从模式的一种补充,在主从模式下slave是不容许写入的,那么当master宕机后,整个业务将不可以继续写入,而哨兵则是为此作的了一个补充,当master出现故障后,每一个redis服务都会工做一个sentinel(哨兵)时刻监控redis6379端口,一旦发生故障,基于quorum机制投票决定进而实现故障转移,从新选举master提供写操做,sentinel拥有调用外部脚本实现报警的能力
注意:配置哨兵以前清先配置主从复制
[root@node1 ~]# vim /etc/redis-sentinel.conf ... port 26379 bind 0.0.0.0 ##绑定端口,不然会被进入安全模式,仅容许127.0.0.1连接 sentinel monitor mymaster 192.168.2.129 6379 2 ##集群名,master地址,端口,投票2票及以上才进行故障转移 sentinel down-after-milliseconds mymaster 10000 ##监控到指定的集群的主节点异常状态持续多久方才将标记为“故障”; sentinel failover-timeout mymaster 120000 ##sentinel必须在此指定的时长内完成故障转移操做,不然,将视为故障转移操做失败; sentinel auth-pass mymaster ifan ##哨兵集群,配置连接6379端口密码 # sentinel notification-script <master-name> <script-path> ##调用脚本报警功能 ...
哨兵模式管理指令
redis-cli -h SENTINEL_HOST -p SENTINEL_PORT redis-cli> SENTINEL masters ##查看当前master信息 SENTINEL slaves <MASTER_NAME> ##查看master下的从节点信息 SENTINEL failover <MASTER_NAME> ##手动进行故障转移,须要指定集群名 SENTINEL get-master-addr-by-name <MASTER_NAME> ##查看当前master的ip信息
实现原理:redis cluster是一种无中心的分布式服务,最小由3台便可实现集群(但不能保证高可用的能力)3台为master节点,3台为slave节点(master正常时不提供服务),当客户端的存请求到达redis集群时会将数据进行hash取模,根据redis集群创建划分的slot进行存储,取的时候根据取模的slot进行取值,cluster模式下不管客户端请求到达集群的任何节点都可以被提供服务,可是在非集群(-c选项)要求客户端是智能客户端,由于客户端到达节点存取数据进行取模获得数据可存放和读取的服务器位置,由客户端自行发起第二次访问
关于集群实现其余方案:Codis / cerberus.
这里咱们实现如下Redis的Cluster集群模式,不少博客在最后集群实现时采用了ruby的集群工具实现槽位自动划分,考虑到工具还要大费周折的布置环境,这里咱们所有手动来实现
地址 | 服务1 | 服务2 | 系统版本 |
---|---|---|---|
192.168.2.128(node1) | 6379(master) | 6380(slave) | CentOS7.2 |
192.168.2.129(node2) | 6379(master) | 6380(slave) | CentOS7.2 |
192.168.2.130(node3) | 6379(master) | 6380(slave) | CentOS7.2 |
三台机器分别安装redis:
[root@node1 ~]# ntpdate -u ntp.aliyun.com [root@node1 ~]# yum install redis -y [root@node1 ~]# cp /etc/redis.conf /etc/redis_6379.conf [root@node1 ~]# cp /etc/redis.conf /etc/redis_6380.conf [root@node1 ~]# egrep -v "^#|^$" /etc/redis_6379.conf ... bind 0.0.0.0 port 6379 pidfile /var/run/redis_6379.pid logfile /var/log/redis/redis_6379.log cluster-enabled yes cluster-config-file nodes-6379.conf cluster-node-timeout 15000 cluster-slave-validity-factor 10 cluster-migration-barrier 1 ... 注:为了完成多实例,请将/etc/redis_6380.conf和6379配置保持一致,端口除外
优化内存相关选项:
echo "vm.overcommit_memory=1" >> /etc/sysctl.conf ; sysctl -p echo "511" > /proc/sys/net/core/somaxconn echo never > /sys/kernel/mm/transparent_hugepage/enabled
启动服务:
[root@node1 ~]# redis-server /etc/redis_6379.conf & [root@node1 ~]# redis-server /etc/redis_6380.conf &
检查日志是否有异常信息:
[root@node1 ~]# tail -f /var/log/redis/redis_6379.log [root@node1 ~]# tail -f /var/log/redis/redis_6380.log
添加机器到集群:
redis-cli -h 192.168.2.128 -p 6379 -c 192.168.2.128:6379> CLUSTER MEET 192.168.2.129 6379 192.168.2.128:6379> CLUSTER MEET 192.168.2.130 6379
查看集群状态:
192.168.2.128:6379> CLUSTER INFO ##查看集群状态 cluster_state:fail ##集群状态,之因此会失败是由于咱们尚未对集群进行槽位的划分 cluster_slots_assigned:0 #分配的slot数 cluster_slots_ok:0 #正确的slot数 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:3 #当前的节点数 cluster_size:0 cluster_current_epoch:2 cluster_my_epoch:0 cluster_stats_messages_sent:171 cluster_stats_messages_received:171 192.168.2.128:6379> CLUSTER NODES 2a9f356a7362c535056f4311057d78269c7aa6d4 192.168.2.129:6379 master - 0 1551782791382 1 connected 5462-10922 af69cb43def99109d4e9a2e15b0fa41b1998bb02 192.168.2.130:6379 master - 0 1551782790373 0 connected 10923-16383 32db6806120a01ce90fa8641bd15abd3c6a55408 192.168.2.128:6379 myself,master - 0 0 2 connected 0-5461
划分槽位(根据master的数量进行划分,一共16384个槽位):
[root@node1 ~]# for k in {0..5461};do redis-cli -h 192.168.2.128 -p 6379 CLUSTER ADDSLOTS $k;done [root@node1 ~]# for k in {5462..10922};do redis-cli -h 192.168.2.129 -p 6379 CLUSTER ADDSLOTS $k;done [root@node1 ~]# for k in {10923..16383};do redis-cli -h 192.168.2.130 -p 6379 CLUSTER ADDSLOTS $k;done
再次查看集群状态:
192.168.2.128:6379> CLUSTER INFO ##查看集群状态 cluster_state:ok cluster_slots_assigned:16384 ##划分的总槽位 cluster_slots_ok:16384 ##成功划分的槽位 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:3 cluster_size:3 cluster_current_epoch:2 cluster_my_epoch:0 cluster_stats_messages_sent:3495 cluster_stats_messages_received:3495
添加从节点
redis-cli -h 192.168.2.128 -p 6379 -c 192.168.2.128:6379> CLUSTER MEET 192.168.2.128 6380 192.168.2.128:6379> CLUSTER MEET 192.168.2.129 6380 192.168.2.128:6379> CLUSTER MEET 192.168.2.130 6380
划分槽位(根据master的数量进行划分,一共16384个槽位):
[root@node1 ~]# for k in {0..5461};do redis-cli -h 192.168.2.128 -p 6380 CLUSTER ADDSLOTS $k;done [root@node1 ~]# for k in {5462..10922};do redis-cli -h 192.168.2.129 -p 6380 CLUSTER ADDSLOTS $k;done [root@node1 ~]# for k in {10923..16383};do redis-cli -h 192.168.2.130 -p 6380 CLUSTER ADDSLOTS $k;done
查看集群节点数:
192.168.2.128:6379> CLUSTER INFO ##查看集群状态 cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 ##全部节点数6个 cluster_size:3 ##主节点数3个 cluster_current_epoch:5 cluster_my_epoch:0 cluster_stats_messages_sent:4388 cluster_stats_messages_received:4388
查看状态:
192.168.2.128:6379> CLUSTER NODES 2a9f356a7362c535056f4311057d78269c7aa6d4 192.168.2.129:6379 master - 0 1551782791382 1 connected 5462-10922 af69cb43def99109d4e9a2e15b0fa41b1998bb02 192.168.2.130:6379 master - 0 1551782790373 0 connected 10923-16383 30744bd131bb249850d0f1ff5a91fae751b86fe3 192.168.2.130:6380 master - 0 1551782788357 5 connected 65d1453da3e1e3dc2a1e4bdefac375c4890e8555 192.168.2.128:6380 master - 0 1551782789362 3 connected 85461b05a18125b2301a615d217af4dacf35879c 192.168.2.129:6380 master - 0 1551782792388 4 connected 32db6806120a01ce90fa8641bd15abd3c6a55408 192.168.2.128:6379 myself,master - 0 0 2 connected 0-5461 注意:此时的六个节点所有是以master的身份,咱们须要将后需添加的3个变动为从节点,并基于master进行复制
修改从节点状态:
128(6380) -- 复制 --> 129 (6379) 129(6380) -- 复制 --> 130 (6379) 130(6380) -- 复制 --> 128 (6379) # redis-cli -h 192.168.2.128 -p 6380 -c CLUSTER REPLICATE 2a9f356a7362c535056f4311057d78269c7aa6d4 # redis-cli -h 192.168.2.129 -p 6380 -c CLUSTER REPLICATE af69cb43def99109d4e9a2e15b0fa41b1998bb02 # redis-cli -h 192.168.2.130 -p 6380 -c CLUSTER REPLICATE 32db6806120a01ce90fa8641bd15abd3c6a55408
再次查看节点状态(此时的主从关系已经造成):
192.168.2.128:6379> CLUSTER NODES 2a9f356a7362c535056f4311057d78269c7aa6d4 192.168.2.129:6379 master - 0 1551786306436 1 connected 5462-10922 af69cb43def99109d4e9a2e15b0fa41b1998bb02 192.168.2.130:6379 master - 0 1551786305391 0 connected 10923-16383 30744bd131bb249850d0f1ff5a91fae751b86fe3 192.168.2.130:6380 slave 32db6806120a01ce90fa8641bd15abd3c6a55408 0 1551786308514 5 connected 65d1453da3e1e3dc2a1e4bdefac375c4890e8555 192.168.2.128:6380 slave 2a9f356a7362c535056f4311057d78269c7aa6d4 0 1551786307471 3 connected 85461b05a18125b2301a615d217af4dacf35879c 192.168.2.129:6380 slave af69cb43def99109d4e9a2e15b0fa41b1998bb02 0 1551786303303 4 connected 32db6806120a01ce90fa8641bd15abd3c6a55408 192.168.2.128:6379 myself,master - 0 0 2 connected 0-5461
到此Redis3.2版本的集群已经搭建完成,redis-cluster须要智能客户端,在写入时须要智能判断
# for k in {1..10};do redis-cli -h 192.168.2.128 -p 6379 SET key$k val$k;done ##咱们先不以集群模式来寸数据,因为redis的分片机制,会提醒你本次数据要交给xx主机存 (error) MOVED 9189 192.168.2.129:6379 OK OK (error) MOVED 13120 192.168.2.130:6379 (error) MOVED 9057 192.168.2.129:6379 OK OK (error) MOVED 13004 192.168.2.130:6379 (error) MOVED 8941 192.168.2.129:6379 (error) MOVED 5850 192.168.2.129:6379 # for k in {11..20};do redis-cli -c -h 192.168.2.128 -p 6379 SET key$k val$k;done ##咱们以集群模式去存,则所有存成功 OK OK OK OK OK OK OK OK OK OK # for k in {11..20};do redis-cli -h 192.168.2.128 -p 6379 GET key$k;done ##不以集群模式读取 "val11" (error) MOVED 13976 192.168.2.130:6379 (error) MOVED 9913 192.168.2.129:6379 (error) MOVED 5726 192.168.2.129:6379 "val15" (error) MOVED 13852 192.168.2.130:6379 (error) MOVED 9789 192.168.2.129:6379 (error) MOVED 6098 192.168.2.129:6379 "val19" "val20" # redis-cli -h 192.168.2.130 -p 6379 GET key12 ##根据上面提示的数据存储位置去读取,则读取成功 "val12"
模拟故障:
# ps -ef|grep redis root 2805 1 0 18:31 ? 00:00:09 redis-server 0.0.0.0:6379 [cluster] root 2809 1 0 18:31 ? 00:00:06 redis-server 0.0.0.0:6380 [cluster] # kill -9 2805 杀掉进程后先是先是集群状态fail稍后slave上线对外提供服务 192.168.2.128:6380> cluster nodes 2a9f356a7362c535056f4311057d78269c7aa6d4 192.168.2.129:6379 master - 0 1551787432472 1 connected 5462-10922 32db6806120a01ce90fa8641bd15abd3c6a55408 192.168.2.128:6379 master,fail - 1551787261748 1551787257954 2 disconnected 85461b05a18125b2301a615d217af4dacf35879c 192.168.2.129:6380 slave af69cb43def99109d4e9a2e15b0fa41b1998bb02 0 1551787433495 4 connected 30744bd131bb249850d0f1ff5a91fae751b86fe3 192.168.2.130:6380 master - 0 1551787431446 7 connected 0-5461 65d1453da3e1e3dc2a1e4bdefac375c4890e8555 192.168.2.128:6380 myself,slave 2a9f356a7362c535056f4311057d78269c7aa6d4 0 0 3 connected af69cb43def99109d4e9a2e15b0fa41b1998bb02 192.168.2.130:6379 master - 0 1551787427638 0 connected 10923-16383
注:当集群中master出现宕机时,slave才会对外提供服务,不然slave接收请求时会提示该数据可以提供服务的位置,当6台机器的master的所有宕机,slave上线,除非宕机超过一半,集群再也不成立,机器下线后,从新上线以slave的身份工做
宕机前: # redis-cli -h 192.168.2.128 -p 6380 GET key13 ##宕机前129的备节128(6380)点不提供服务,提示该数据能提供服务的服务器位置 (error) MOVED 9913 192.168.2.129:6379 # redis-cli -h 192.168.2.129 -p 6379 GET key13 ##根据提示,129的6379可以提供服务 "val13" 模拟宕机后: # redis-cli -h 192.168.2.129 -p 6379 GET key13 ##129的6379不可以提供服务 Could not connect to Redis at 192.168.2.129:6379: Connection refused # redis-cli -h 192.168.2.128 -p 6380 GET key13 ##129的备节点128(6380)提供服务 "val13"