redis以其高性能而闻名,它最大程度地利用了 单线程,非阻塞,多路复用的I/O模型 来快速地处理请求。
Redis基本上就是一个接受并处理来自客户端请求的非阻塞、I/O复用的TCP服务器。我们可以使用各种编程语言通过TCP协议与Redis进行通信。对Redis来说,这种通信协议叫做Redis Serialization Protocal(RESP,Redis序列化协议)。
例如我们利用nc向redis服务器发送命令:
echo -e "*3\r\n\$3\r\nset\r\n\$5\r\nmykey\r\n\$1\r\n1\r\n" | nc 127.0.0.1 6379
返回+OK
解析方式:
—pipe
选项通过管道发送命令;将 1 中使用的RESP原始报文直接写入txt文件中也有同样效果。set mykey myvalue
asdd myset value1 value2
get mykey
scard myset
为了让文本文件中的每一行都必须以\r\n,而不是\n结束,可以使用命令unix2dos实现:
unix2dos pipeline.txt
cat pipeline.txt | redis-cli -- pipe
redis会根据存储的字符串自动匹配相应的编码方式
int :用于能够使用64位有符号整数表示的字符串
embstr :用于长度小于或等于44字节的字符串,这种类型的编码在内存使用和性能方面更有效率
raw :用于长度大于44字节的字符串
127.0.0.1:6379> set mykey 123456
OK
127.0.0.1:6379> object encoding mykey
"int"
127.0.0.1:6379> set mykey "string"
OK
127.0.0.1:6379> object encoding mykey
"embstr"
ziplist :对于长度小于配置中hash-max-ziplist-entries选项配置的值(默认512b),且所有元素的大小都小于配置中hash-max-ziplist-value选项配置的值(默认64b)的哈希,采用此编码。对于较小的哈希而言可以节省占用空间。
ziplist :对于长度小于配置中zset-max-ziplist-entries选项配置的值(默认128b),且所有元素的大小都小于配置中zset-max-ziplist-value选项配置的值(默认64b)的有序集合,采用此编码。对于较小的集合而言可以节省占用空间。
skiplist :当ziplist不适用时有序集合使用的默认编码。
intset :对于元素都是整数,且长度小于配置中set-max-intset-entries选项配置的值(默认512b)的集合,采用此编码。对于较小的集合而言可以节省占用空间。
hashtable :当ziplist,intset不适用时使用的默认编码。
Sparse(稀疏) :对于长度小于配置中hill-space-max-bytes选项设置的值(默认3000)的HLL对象,采用此编码。稀疏表示方式存储效率更高,但可能会消耗更多的CPU资源。
Dense(稠密) :当稀疏方式不适用时的默认编码。
geohash :基于一种52位整数的表示(实现了低于一米的精度)。当需要一个标准的geohash字符串是,可以使用geohash命令来获取一个长度为11的字符串。性能取决于中心点和半径所决定的圆形区域的外接矩阵中成员的个数。
用expire(指定过期时间(s))和expireat(指定绝对UNIX时间戳) 指定键的过期时间,ttl命令获取过期时间。
Redis是否宕机不影响一个设置了过期时间键的过期时间。宕机时过期时间的绝对时间戳会被持久化到RDB文件中,再次启动时并不会改变。
过期键redis会定期运行一个基于概率的算法来进行主动删除。如果客户端尝试访问已过期的键, redis会立即将其从内存中删除。因为redis对已过期的键的删除是随机不可预期的,所以有些已过期的键可能永远不会被删除,太多过期键我们可以通过执行SCAN命令主动触发删除操作。
清除一个键的过期时间:
对于秒杀类应用而言,超卖问题发生的原因在于我们获取计数器值的时候涉及竞争状态,计数器的值也许在业务场景中是有效的(即大于0),但计数器的值在减少之前可能已经被其他请求修改了。我们可以使用redis的事务来避免这种情况发生。
redis事务虽然也是将一组操作原子化执行,但其与关系数据库事务不同,redis事务没有回滚功能。
在一个redis事务中可能会出现以下两种类型的错误:
发布订阅是一种历史悠久的消息传递模式。简单的说,想要发布事件(event)的发布者(publisher)会把消息(message)发送到一个PubSub频道(channel),这个频道会把时间投递(deliver)给对这个频道感兴趣的每一个订阅者(subscriber)。许多流行的消息中间件都是利用这个模式来构建消息投递系统,Redis也是如此。
订阅者1订阅A消息:
127.0.0.1:6379> subscribe A
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "A"
3) (integer) 1
订阅者2订阅A, B消息:
127.0.0.1:6379> subscribe A B
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "A"
3) (integer) 1
1) "subscribe"
2) "B"
3) (integer) 2
发布者发布 A 消息:
127.0.0.1:6379> publish A "A xxx"
(integer) 2
订阅者1和订阅者2都收到消息:
1) "message"
2) "A"
3) "A xxx"
发布者发布 B 消息:
127.0.0.1:6379> publish B "B xxx"
(integer) 1
只有订阅者2收到消息:
1) "message"
2) "B"
3) "B xxx"
pubsub 命令用于进行频道管理。如pubsub channels
可以获取当前活跃的频道。
Redis中基于PubSub的键空间通知(keyspace notification)功能允许客户端订阅一个Redis频道来接受命令发布或者数据改变的事件。
订阅匹配__key*__:*的频道
127.0.0.1:6379> psubscribe __key*__:*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "__key*__:*"
3) (integer) 1
1) "pmessage"
2) "__key*__:*"
3) "__keyspace@0__:foo"
4) "set"
1) "pmessage"
2) "__key*__:*"
3) "__keyevent@0__:set"
4) "foo"
redis发送命令(此时会向keyspace和keyevent两个频道发布消息)
127.0.0.1:6379> set foo 1
OK
Lua作为服务器端一种轻量级的脚本语言,Redis可以使用Lua脚本将一组操作原子化执行,比redis事务具有更强大的功能和程序逻辑,能够处理涉及复杂逻辑判断或循环处理的业务场景。
拷贝一份默认的配置文件redis.conf命名为redis-slave.conf,修改以下字段成为从实例的配置文件:
port 6380
pidfile /var/run/redis_6380.pid
dir ./slave
#要复制的主实例ip及端口
replicaof 127.0.0.1 6379
#masterauth 主实例有密码的在这里设
#默认从实例是只读的,这里取消只读
replica-read-only no
也可以在redis-cli中使用slaveof命令,将动态地当前的redis实例变为另一个实例的从实例。
启动主实例redis-server conf/redis-slave.conf
,启动从实例 redis-server conf/redis-slave.conf
连接主:
redis-cli -p 6379
info replication #查看复制关系信息
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=112,lag=0
master_replid:2f807e3b31be4f6af413af163b45963a7176a158
master_replid2:0000000000000000000000000000000000000000
…
连接从:
redis-cli -p 6380
info replication #查看复制关系信息
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
…
正确操作结果:在主实例上分别设置键值,在从实例可以取到;在从实例上设置键值,在主实例上可以取到;从实例shutduwn之后再连接依旧可以获取主实例的值。
master_replid:标识主实例,主实例启动时生成;
master_repl_offset:主实例复制流中的偏移标记。
两者通常用来表示与主实例同步的最后一个快照。
完全重新同步需要主实例建立线程去存储RDB文件,并将文件发送给从实例。
部分重新同步从实例读取RDB文件存放的偏移量发送给主实例,主实例只需要根据的偏移量将缓冲区里的命令发送给从实例。
在Redis4.0以后才有的部分重新同步,以前一直是完全重新同步。当一个从实例被提升为主实例,新的主实例能够接受其他从实例的部分重新同步。
1.启动两个实例
ps: mac使用127.0.0.2地址要执行下这句sudo ifconfig lo0 alias 127.0.0.2 netmask 0xFFFFFFFF
$ M_IP=127.0.0.1
$ M_PORT=6379
$ M_RDB_NAME=master.rdb
$ M_OUT=master.out
$ S1_IP=127.0.0.2
$ S1_PORT=6380
$ S1_OUT=slave_1.out
$ S1_RDB_NAME=slave_1.rdb
$ nohup /usr/local/redis/bin/redis-server --port $M_PORT --bind $M_IP --dbfilename $M_RDB_NAME > $M_OUT &
$ nohup /usr/local/redis/bin/redis-server --port $S1_PORT --bind $S1_IP --dbfilename $S1_RDB_NAME > $S1_OUT &
2.实例启动完成后向一个实例中写入数据
echo set redis hello | nc $M_IP $M_PORT
echo lpush num 1 2 3 | nc $M_IP $M_PORT
3.配置主从复制
echo slaveof 127.0.0.1 6379 | nc $S1_IP $S1_PORT
4.数据同步完成后,中断主实例和从实例之间的网络连接
# linux
echo “modify iptables”
iptables -I INPUT -s 127.0.0.1 -d 127.0.0.2 -j DROP
Iptabels -I OUTPUT -s 127.0.0.2 -d 127.0.0.1 -j DROP
# mac
sudo cp /etc/pf.conf /etc/pf-redis.conf
sudo vim /etc/pf-redis.conf
# 在anchor "com.apple/*" 后加以下两行
block drop in inet from 127.0.0.1 to 127.0.0.2
block drop out inet from 127.0.0.2 to 127.0.0.1
sudo pfctl -d
sudo pfctl -ef /etc/pf-redis.conf
5.网络中断期间,向主实例导入测试数据
sh prepdata.sh 1000
cd /Users/bijiayang/Desktop/file/my/study/redis
rm -rf repl.data
touch repl.data
for i in $(seq 1 $1)
do
echo $i
echo "set redis$i hello$i" >> repl.data
done
du -sh repl.data
cat repl.data | redis-cli --pipe $M_IP -p $M_PORT
1000条数据du 结果24KB
40000条数据du 结果1M
6.数据导入结束后,恢复主从实例间的网络连接
# linux
echo “restore iptables”
iptables -D INPUT -s 127.0.0.1 -d 127.0.0.2 -j DROP
iptables -D OUTPUT -s 127.0.0.2 -d 127.0.0.1 -j DROP
# mac
sudo pfctl -d
sudo pfctl -ef /etc/pf.conf
iptables文档: http://ipset.netfilter.org/iptables.man.html
pfctl文档:http://man.openbsd.org/pfctl
7.恢复网络连接后,等待主从实例进行数据重新同步完成后,同时关闭主从实例
echo "shutdown" | nc $M_IP $M_PORT
echo "shutdown" | nc $S1_IP $S1_PORT
Possible Problem :
Error reply to PING from master: '-DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the loopback interface.
Solution:
config set protected-mode no
protected-mode no
,重启redisbind 127.0.0.1 127.0.0.2
主实例配置
# vi redis_master.conf
port 6379
requirepass redis
从实例配置
# vi redis_slave.conf
port 6379
slaveof 192.168.56.10 6379
masterauth redis
requirepass redis # 当前实例如果还有从服务器就需要,否则不需要
从实例第一次连接主实例进行同步时,从实例没有主实例的复制ID信息,会进行完全重新同步。
master.out
13334:M 16 Jul 2019 17:53:23.723 * Replica 127.0.0.2:6380 asks for synchronization
13334:M 16 Jul 2019 17:53:23.723 * Partial resynchronization not accepted: Replication ID mismatch (Replica asked for '033eba642ec0e3269c81e408ab9085db28c19962', my replication IDs are '9e4db2221538f2706e57c3fb6e2fcba888d15bcb' and '0000000000000000000000000000000000000000')
13334:M 16 Jul 2019 17:53:23.724 * Starting BGSAVE for SYNC with target: disk
13334:M 16 Jul 2019 17:53:23.725 * Background saving started by pid 13352
13352:C 16 Jul 2019 17:53:23.728 * DB saved on disk
13334:M 16 Jul 2019 17:53:23.823 * Background saving terminated with success
13334:M 16 Jul 2019 17:53:23.824 * Synchronization with replica 127.0.0.2:6380 succeeded
slave_1.out
Before turning into a replica, using my master parameters to synthesize a cached master: I may be able to synchronize with the new master with just a partial transfer.
13335:S 16 Jul 2019 17:53:23.721 * REPLICAOF 127.0.0.1:6379 enabled (user request from 'id=3 addr=127.0.0.2:59312 fd=7 name= age=0 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=23 qbuf-free=32745 obl=0 oll=0 omem=0 events=r cmd=slaveof')
13335:S 16 Jul 2019 17:53:23.721 * Connecting to MASTER 127.0.0.1:6379
13335:S 16 Jul 2019 17:53:23.721 * MASTER <-> REPLICA sync started
13335:S 16 Jul 2019 17:53:23.722 * Non blocking connect for SYNC fired the event.
13335:S 16 Jul 2019 17:53:23.723 * Master replied to PING, replication can continue...
13335:S 16 Jul 2019 17:53:23.723 * Trying a partial resynchronization (request 033eba642ec0e3269c81e408ab9085db28c19962:1).
13335:S 16 Jul 2019 17:53:23.725 * Full resync from master: 05465ab144737b75b5f9df409efba72a9d008f50:0
13335:S 16 Jul 2019 17:53:23.725 * Discarding previously cached master state.
13335:S 16 Jul 2019 17:53:23.824 * MASTER <-> REPLICA sync: receiving 226 bytes from master
13335:S 16 Jul 2019 17:53:23.824 * MASTER <-> REPLICA sync: Flushing old data
13335:S 16 Jul 2019 17:53:23.824 * MASTER <-> REPLICA sync: Loading DB in memory
13335:S 16 Jul 2019 17:53:23.825 * MASTER <-> REPLICA sync: Finished with success
24KB数据的同步,部分重新同步。
master.out
13334:M 16 Jul 2019 17:58:09.932 * Replica 127.0.0.2:6380 asks for synchronization
13334:M 16 Jul 2019 17:58:09.938 * Partial resynchronization request from 127.0.0.2:6380 accepted. Sending 40893 bytes of backlog starting from offset 113.
slave_1.out
13335:S 16 Jul 2019 17:58:09.931 * Non blocking connect for SYNC fired the event.
13335:S 16 Jul 2019 17:58:09.931 * Master replied to PING, replication can continue...
13335:S 16 Jul 2019 17:58:09.932 * Trying a partial resynchronization (request 05465ab144737b75b5f9df409efba72a9d008f50:113).
13335:S 16 Jul 2019 17:58:09.938 * Successful partial resynchronization with master.
13335:S 16 Jul 2019 17:58:09.938 * MASTER <-> REPLICA sync: Master accepted a Partial Resynchronization.
1M数据的同步,完全重新同步。
master.out
14422:M 16 Jul 2019 18:30:02.236 * Replica 127.0.0.2:6380 asks for synchronization
14422:M 16 Jul 2019 18:30:02.236 * Unable to partial resync with replica 127.0.0.2:6380 for lack of backlog (Replica request was: 29).
14422:M 16 Jul 2019 18:30:02.236 * Starting BGSAVE for SYNC with target: disk
14422:M 16 Jul 2019 18:30:02.236 * Background saving started by pid 14445
14445:C 16 Jul 2019 18:30:02.269 * DB saved on disk
14422:M 16 Jul 2019 18:30:02.342 * Background saving terminated with success
14422:M 16 Jul 2019 18:30:02.344 * Synchronization with replica 127.0.0.2:6380 succeeded
slave_1.out
14423:S 16 Jul 2019 18:29:43.191 * MASTER <-> REPLICA sync started
14423:S 16 Jul 2019 18:30:02.235 * Non blocking connect for SYNC fired the event.
14423:S 16 Jul 2019 18:30:02.235 * Master replied to PING, replication can continue...
14423:S 16 Jul 2019 18:30:02.236 * Trying a partial resynchronization (request 627249f633a6947b54de20d99737c54e2943a53f:29).
14423:S 16 Jul 2019 18:30:02.237 * Full resync from master: 627249f633a6947b54de20d99737c54e2943a53f:1837925
14423:S 16 Jul 2019 18:30:02.237 * Discarding previously cached master state.
14423:S 16 Jul 2019 18:30:02.342 * MASTER <-> REPLICA sync: receiving 898012 bytes from master
14423:S 16 Jul 2019 18:30:02.347 * MASTER <-> REPLICA sync: Flushing old data
14423:S 16 Jul 2019 18:30:02.347 * MASTER <-> REPLICA sync: Loading DB in memory
14423:S 16 Jul 2019 18:30:02.390 * MASTER <-> REPLICA sync: Finished with success
为什么上面两次24KB和1M数据的同步方式不一样呢?主实例上的一段内存(环形缓冲区,称为replication backlog)会跟踪最近所有写入命令,这个缓冲区实际上是一个固定长度的列表。redis使用这个backlog缓冲区来决定同步方式(主实例接收从实例请求中的offset和master_replid,判断master_replid是否匹配,再判断最后一个offset是否能从backlog缓冲区取到),默认的缓冲区大小是1M。
有时在数据写入量较大的时候,超过了缓冲区容量,部分重新同步会被拒绝,而启动完全重新同步。所以我们一般要根据自己的实际情况,将缓冲区大小repl_backlog_size
设为流量写入峰值期间的合适大小。我们可以在峰值期间利用info replication查看master_repl_offset的变化量从而进行估算。
其他优化参数:
Repl-backlog-ttl
:如果所有的从实例与主实例全部断开,主实例释放backlog占用的内存的等待时间,默认3600s;
Repl-disable-tcp-nodelay
:设为yes,redis会尝试将几个小包合并成一个包来减少带宽,这在主从实例位置较远的时候有些作用,可能会造成约40ms的复制延迟;
Repl-disless-sync
:设为yes来将复制机制从默认的disk-backed切换为dissless,就是使用无盘复制,直接将rdb文件内容发送给从实例无需在磁盘上创建RDB文件,可以节省磁盘I/O和内存,在磁盘速度不快或内存使用率很高的情况下可以使用此选项。
Repl-ping-slave-period
:主实例默认每隔10s(可通过Repl-ping-slave-period配置)向从实例发送一个PING命令判断从实例是否正常运行,而从实例每秒会向主实例发送RELICONF ACK {offset}来报告它的复制偏移量。
Repl-timeout
:主从实例间没有数据传输流量即ACK传输,或两次PING的时间间隔时间比超时时间长,那么主从实例之间的复制连接将被断开,下次要进行重新的复制连接。我们应尽量避免长时间的阻塞操作。
Client-output-buffer-limit
:主从复制建立时,主实例加载rdb文件到缓冲区(Slave client buffer),并将缓冲区内容发送给从实例,默认slave 256 MB 64 MB 60
,含义是当缓冲区大小超过64MB持续60时,主实例会关闭连接。
持久化文件通常用于存储Redis数据,以防意外可用与恢复Redis数据。
Redis通过持久化将内存中的数据存储到rdb文件中,相当于保存了当时的数据库快照。
启用RDB持久化config set save “900 1”
,表示 900 秒内有 1 个键发生了改变就会进行一次RDB快照;禁用RDB持久化config set save “”
。永久设置就去redis.conf中设置。
save/bgsave
:手动执行RDB转储,bgsave是非阻塞的。
info persistence
获取持久化相关指标和状态
127.0.0.1:6379> hset hs k1 v1 k2 v2
(integer) 2
127.0.0.1:6379> lpush listkey v1 v2
(integer) 2
127.0.0.1:6379> zadd zset 1 v1 2 v2
(integer) 2
127.0.0.1:6379> save
OK
安装bvi(yum install bvi
或 brew install bvi
)
打开redis数据目录中的文件bvi dump.rdb
REDIS0009:rdb文件版本
redis-ver:Redis实例的版本
redis-bits:运行redis实例的主机的架构
ctime:RDB创建时的Unix时间戳
used-mem:转储时使用的内存大小
repl_stream_db:复制链中数据库的索引,启用了复制机制才存在
aof-preamble:是否在AOF文件的开头放置RDB快照(即开启混合持久化)
repl-id:主实例的ID
repl-offset:主实例的偏移
FA 为辅助操作码,其后总跟一个键值对。
RDB文件中,保存了数据库的元数据之后,还将保存数据库索引(FE 00,索引为0)、哈希大小调整代码、数据库大小调整操作代码,以及过期大小调整操作代码。
RDB文件以EOF和CRC64校验结束。
一个RDB数据转储文件仅包含某个时间点上的数据快照,虽然我们可以定期地将数据转储到RDB文件中,但当Redis进程崩溃或出现故障时,两次RDB转储之间的数据将会永久丢失。
AOF(append-only file)是一种只记录Redis写入命令的追加式日志文件。因为每个写入命令都会被追加到文件中,所以AOF的数据一致性比RDB更高。
启用AOF持久化config set appendonly yes
,禁用RDB持久化config set appendonly yes
。永久设置就去redis.conf中设置。
info persistence
获取持久化相关指标和状态。
查看redis数据目录中的是否存在文件appendonly.aof
,这是默认文件名,可修改。
AOF文件写入:操作系统维护一个缓冲区,Redis的命令先写入缓冲区,操作系统会等缓冲区被填满或超过指定时限后再将缓冲区中的数据写入磁盘,如果计算机发生停机,那么缓冲区中的数据将会丢失。Redis可以通过系统调用 fsync() 立即将缓冲区数据刷新到磁盘中,这是一个阻塞调用,只有磁盘设备报告缓冲区中的数据写入结束后才会返回。
可以通过配置参数appendfsync调整调用fsync()的频率:
always
:每个写入命令都调用fsync()。服务器崩溃或故障时只会丢失最后一个命令,但阻塞调用会降低redis的写入性能。
everysec
:每秒调用一次fsync()。在意外灾难事件中只有一秒的数据会丢失。
no
:永不调用fsync()。
当redis服务器关闭时,fsync()会被显示调用,以确保写入缓冲区的所有数据都会被刷新到磁盘中。
文件的格式就是Redis通信协议所使用的RESP格式。
开头的SELECT 0
:表示选择索引为0的数据库。Redis支持多个数据库,这些数据库由0到15的数字指定,默认的数据库索引是0。
当文件越来越大,通过 AOF write (bgrewriteaof)来压缩AOF文件。压缩规则例如:已经删除的键的相关命令会被删除,多个set
命令会用一个mset
命令替代等。
有两种使用方式
aof-use-rdb-preamble
设为yes,导入一些测试数据,然后调用save命令创建RDB文件,bgrewriteaof命令重写AOF文件,RDB文件会正常保存,但在重写AOF文件时,会把当前数据集以RDB的格式转储到AOF文件的开始部分,在重写之后,Redis会继续使用传统的AOF格式在AOF文件中记录写入命令。前面说的主从复制和持久化都是只是做到数据备份,但当redis主实例宕机,却无法自动恢复。我们需要用redis集群来实现高可用。Sentinel机制是redis提供的Redis Cluster方案。
Sentinel(哨兵)充当主从实例的守卫者,单个哨兵也可能失效,且对主实例的故障迁移的决策是基于冲裁系统的,所以至少需要3个哨兵才能组成一个健壮的分布式系统来持续监控Redis主实例的状态。
如果有多个哨兵进程检测到主实例下线,其中的一个哨兵进程会被选举出来负责推选一个实例替代原有实例。
启动一主两从实例,三个Sentinel实例
启动实例方式不再赘述。
redis-server --port 6379 --bind 127.0.0.1 &
redis-server --port 6380 --bind 127.0.0.2 &
redis-server --port 6381 --bind 127.0.0.3 &
echo slaveof 127.0.0.1 6379 | nc 127.0.0.2 6380
echo slaveof 127.0.0.1 6379 | nc 127.0.0.3 6381
下面说下启动Sentinel方式:
Sentinel.conf原文件可从源码中复制出来进行修改,我的 sentinel1.conf 配置如下:
bind 127.0.0.1 127.0.0.2 127.0.0.3
port 26379
daemonize no
pidfile /var/run/redis-sentinel.pid
logfile ""
dir /tmp
# 至少2个sentinel确认master实例已下线才可判断该master实例已下线,可增加行监听新的主实例
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
# 最多有1个slave同时对新的master进行同步,设为1确保不会有多个salve因replication不可用
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
启动sentinel
redis-server /usr/local/redis/etc/sentinel1.conf --sentinel
redis-server /usr/local/redis/etc/sentinel2.conf --sentinel
redis-server /usr/local/redis/etc/sentinel3.conf --sentinel
启动好之后再查看 sentinel1.conf 的内容,可以看出被添加了几行信息。在之后我们对Sentinel配置的更改也会同步更改到这个文件里并且带上版本号,方便我们下次可以使用最新的配置来启动:
cat sentinel1.conf
bind 127.0.0.1 127.0.0.2 127.0.0.3
port 26379
daemonize no
pidfile "/var/run/redis-sentinel.pid"
sentinel myid a468bd3204a7c709a38111d1c6643a5dee774434
sentinel deny-scripts-reconfig yes
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
# Generated by CONFIG REWRITE
dir "/Users/bijiayang/Desktop/file/my/study/redis"
sentinel known-replica mymaster 127.0.0.2 6380
sentinel known-sentinel mymaster 127.0.0.1 26381 021970dffc4f7723164b94878230075d96a46f2d
sentinel known-sentinel mymaster 127.0.0.1 26380 c31eda3f098ef5cb48b5e72f5f551c880b0cbcec
sentinel current-epoch 0
查看哨兵信息,最后一行可以看出检测到了主实例mymaster,2个从实例和3个哨兵:
redis-cli -h 127.0.0.1 -p 26379
127.0.0.1:26379> info sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
127.0.0.2:26380> sentinel failover mymaster
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:127.0.0.2
master_port:6380
...
查看Sentinel1.conf,可以看出主实例的地址变成了127.0.0.2 6380
cat sentinel1.conf
bind 127.0.0.1 127.0.0.2 127.0.0.3
port 26379
daemonize no
pidfile "/var/run/redis-sentinel.pid"
sentinel myid a468bd3204a7c709a38111d1c6643a5dee774434
sentinel deny-scripts-reconfig yes
sentinel monitor mymaster 127.0.0.2 6380 2
sentinel config-epoch mymaster 1
sentinel leader-epoch mymaster 0
# Generated by CONFIG REWRITE
dir "/Users/bijiayang/Desktop/file/my/study/redis"
sentinel known-replica mymaster 127.0.0.3 6381
sentinel known-replica mymaster 127.0.0.1 6379
sentinel known-sentinel mymaster 127.0.0.1 26381 021970dffc4f7723164b94878230075d96a46f2d
sentinel known-sentinel mymaster 127.0.0.1 26380 c31eda3f098ef5cb48b5e72f5f551c880b0cbcec
sentinel current-epoch 1
故障迁移过程
127.0.0.2 6380
,我们用SHUTDOWN
关闭这个主实例。127.0.0.3:6380
已经被推举为了主实例:127.0.0.2:6380> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.3,port=6381,state=online,offset=759564,lag=1
...
127.0.0.3:6381> info replication
# Replication
role:slave
master_host:127.0.0.2
master_port:6380
...
min-slave-to-write
为 1 ,即我们写入数据的主实例至少要有一个从实例存在才允许写入,然后关闭两个从实例,只剩一个主实例在运行,我们是无法对主实例进行写入操作的。使用redis-cli连接到其中一个哨兵,对其进行管理。
| 工作项目 | 状态 |
|–|–|–|
|sentinel get-master-addr-by-name |获取当前主实例信息|
|sentinel masters|获取所有被监控的主实例的状态|
|sentinel slaves |获取一个被监控主实例的所有从实例的信息|
|sentinel set mymaster down-after-milliseconds 1000|更新哨兵的配置|
|sentinel set mymaster notification-script mymaster notify.py|每当有Sentinel事件发生时,就会触发脚本,脚本可用于发送邮件等|
|sentinel set mymaster client-reconfig-script reconfig.bash|发生故障迁移时,就会触发脚本,脚本可用于一些配置的更新等|
当Redis中的数量急剧增长时,必须对其进行分区,我可以利用Redis Cluster(Redis集群)实现自动的数据分片(sharding)和高可用。
配置以上集群步骤如下:
daemonize yes
pidfile “/redis/run/redis-6379.pid”
port 6379
bind 127.0.0.1 127.0.0.2 127.0.0.3
logfile “/redis/log/redis-6379.log”
dbfilename “dump-6379.rdb”
dir “/redis/data”
...
cluster enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 10000
cluster-config-file
配置实例需要覆写的配置文件路径,实例启动好后,会发现配置的文件被修改了。
2. 确认所有实例启动好,redis-cli 连接某一个实例,执行cluster meet
命令让每个redis实例发现彼此。
127.0.0.1:6379> cluster meet 127.0.0.1 6379
127.0.0.1:6379> cluster meet 127.0.0.1 6380
127.0.0.1:6379> cluster meet 127.0.0.2 6379
127.0.0.1:6379> cluster meet 127.0.0.2 6380
127.0.0.1:6379> cluster meet 127.0.0.3 6379
127.0.0.1:6379> cluster meet 127.0.0.3 6380
HASH_SLOT = CRC16(key) mod
的算法分布到16384个哈希槽中,当前节点只能处理当前节点分配到的槽,数据key所属槽不是当前节点的,会返回错误并指引用户去负责此槽的节点。for i in {0..5400}; do redis-cli -h 127.0.0.1 -p 6379 cluster addslots $i; done
for i in {5401..11000}; do redis-cli -h 127.0.0.2 -p 6380 cluster addslots $i; done
for i in {11001..16383}; do redis-cli -h 127.0.0.3 -p 6381 cluster addslots $i; done
cluster replicate node-id
以上四步就完成了Redis集群的配置,我们可以通过cluster nodes
查看主从复制状态,cluster info
获取集群相关信息。