redis环境搭建
redis环境搭建
1. 安装linux虚拟机
- CentOS-6.5-i386-minimal.iso
- 查看虚拟机中分配的ip
ifconfig
- 配置
/etc/sysconfig/network-scripts/ifcfg-eth0
文件,将ip地址设置为静态分配.
DEVICE=eth0
TYPE=Ethernet
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.1.8
NETMASK=255.255.255.0
GATEWAY=192.168.1.1
- 重启network
service network restart
- 关闭防火墙
service iptables stop
service ip6tables stop
chkconfig iptables off
chkconfig ip6tables off
- 配置yum
yum clean all
yum makecache
yum install wget
- 安装Java
scp /Users/serenityma/jdk-8u241-linux-i586.rpm root@192.168.1.8:/root/jdk1.8.rpm
rpm -ivh jdk1.8.rpm
配置jdk相关的环境变量
vi ~/.bashrc
export JAVA_HOME=/usr/java/latest
export PATH=$PATH:$JAVA_HOME/bin
source .bashrc
- 安装perl
yum install -y gcc
wget http://www.cpan.org/src/5.0/perl-5.16.1.tar.gz
tar -xzf perl-5.16.1.tar.gz
cd perl-5.16.1
./Configure -des -Dprefix=/usr/local/perl
make && make test && make install
perl -v
- 复制多个linux
先修改 /etc/udev/rules.d/70-persistent-net.rules文件,将eth1的mac地址复制到eth0上,并删除eth1,
再修改/etc/sysconfig/network-scripts/ifcfg-eth0文件,设置静态ip
重启
- 配置hosts文件及免密通信
vi /etc/hosts
添加
192.168.1.7 vm01
193.168.1.8 vm02
194.168.1.9 vm03
195.168.1.11 vm04
- 配置4台CentOS为ssh免密码互相通信
首先在三台机器上配置对本机的ssh免密码登录
ssh-keygen -t rsa
生成本机的公钥,过程中不断敲回车即可,ssh-keygen命令默认会将公钥放在/root/.ssh目录下
cd /root/.ssh
ssh-keygen -t rsa
cp id_rsa.pub authorized_keys
将公钥复制为authorized_keys文件,此时使用ssh连接本机就不需要输入密码了
接着配置三台机器互相之间的ssh免密码登录
使用ssh-copy-id -i hostname命令将本机的公钥拷贝到指定机器的authorized_keys文件中
2. 安装redis
wget http://downloads.sourceforge.net/tcl/tcl8.6.1-src.tar.gz
tar -xzvf tcl8.6.1-src.tar.gz
cd /usr/local/tcl8.6.1/unix/
./configure
make && make install
tar -zxvf redis-3.2.8.tar.gz
cd redis-3.2.8
make && make test && make install
3. redis生产环境启动方案
redis/utils目录下,有个redis_init_script
脚本
将redis_init_script脚本拷贝到linux的/etc/init.d目录中,将redis_init_script重命名为redis_6379,6379是我们希望这个redis实例监听的端口号
修改redis_6379脚本的第6行的REDISPORT,设置为相同的端口号(默认就是6379)
创建两个目录:/etc/redis(存放redis的配置文件),/var/redis/6379(存放redis的持久化文件)
修改redis配置文件(默认在根目录下,redis.conf),拷贝到/etc/redis目录中,修改名称为6379.conf
修改redis.conf中的部分配置为生产环境
daemonize yes
让redis以daemon进程运行
bin 192.169.1.7
绑定本机的ip地址host
pidfile /var/run/redis_6379.pid
设置redis的pid文件位置
port 6379
设置redis的监听端口号
dir /var/redis/6379
设置持久化文件的存储位置
启动redis,执行cd /etc/init.d
, chmod 777 redis_6379
,
./redis_6379 start
确认redis进程是否启动,ps -ef | grep redis
让redis跟随系统启动自动启动
在redis_6379脚本中,最上面,加入两行注释
# chkconfig: 2345 90 10
# description: Redis is a persistent key-value database
chkconfig redis_6379 on
cp /root/redis-3.2.8/utils/redis_init_script /etc/init.d/redis_6379
mkdir /etc/redis
mkdir /var/redis
mkdir /var/redis/6379
cp /root/redis-3.2.8/redis.conf /etc/redis/6379.conf
chmod 777 /etc/init.d/redis_6379
vi /etc/redis/6379.conf
daemonize yes
pidfile /var/run/redis_6379.pid
port 6379
dir /var/redis/6379
requirepass 123456
masterauth 123456
vi /etc/init.d/redis_6379
# chkconfig: 2345 90 10
# description: Redis is a persistent key-value database
chkconfig redis_6379 on
3-1. redis.conf文件
# include /etc/redis/redis_common.conf
################ NETWORK ###################
bind 127.0.0.1
protected-mode yes
port 6379
tcp-backlog 511
# unixsocket /tmp/redis.sock
# unixsocketperm 700
timeout 0
tcp-keepalive 300
############### GENERAL ####################
daemonzie no
supervised no
pidfile /var/run/redis_6379.pidfile
logfile ""
# syslog-enabled no
# syslog-ident redis
# syslog-facility local0
databases 16
############### SNAPSHOTTING ################
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir ./
############## REPLICATION ###################
# slaveof <masterip> <masterport>
# masterauth <master-password>
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
# relp-timeout 60
repl-disable-tcp-nodelay no
# repl-backlog-size 1mb
# repl-backlog-ttl 3600
slave-priority 100
# min-slaves-to-write 3
# min-slaves-max-lag 10
# slave-announce-ip 5.5.5.5
# slave-announce-port 1234
############## SECURITY #######################
# requirepass foobared
# rename-command CONFIG ""
############## LIMITS #########################
# maxclients 10000
# maxmemory <bytes>
# maxmemory-policy noeviction
# maxmemory-samples 5
############## APPEND ONLY MODE ###############
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
# appendfsync always
# appendfsync no
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
############## LUA SCRIPTING ##################
lua-time-limit 5000
############## REDIS CLUSTER ##################
# cluster-enabled yes
# cluster-config-file nodes-6379.conf
# cluster-node-timeout 15000
# cluster-slave-validity-factor 10
# cluster-migration-barrier 1
# cluster-require-full-coverage yes
############## SLOW LOG #######################
slowlog-log-slower-than 10000
slowlog-max-len 128
############## LATENCY MONITOR #################
latency-monitor-threshold 0
############## EVENT NOTIFICATION ##############
# notify-keyspace-events Elg
# notify-keyspace-events Ex
notify-keyspace-events ""
############## ADVANCED CONFIG ###################
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
4. redis的持久化机制
4-1. RDB
- copy-on-write
快照操作会fork一个进程
来专门生成快照,生成快照保存在一个临时的rdb文件
中,待快照操作完成后会替换
原先的rdb文件.生成快照的时候,如果有数据发生变化
,都会以副本
的方式存放在另一个新的内存区域
,待快照操作结束后才会同步到原先的内存区域中.
如果写快照期间,服务崩溃,则这个写快照操作不算成功,需用上一次完整的rdb文件恢复数据 - RDB相关配置
save <seconds> <changes>
seconds秒内有changes个改变则生成rdb,默认的设置为
save 900 1
save 300 10
save 60 10000
可使用save ""
关闭rdb快照功能stop-writes-on-bgsave-error
默认为yes,如果快照操作出现异常(权限不足,磁盘空间写满等),则redis就会禁止写操作
rdbcompression
使用LZF压缩算法将字符串
类型在快照时进行压缩,默认为yesrdbchecksum
验证rdb文件的完整性,默认为yesdbfilename
rdb文件名,默认为dump.rdb
dir
rdb文件的生成位置,默认位置为./
- 手动生成快照
save
bgsave
4-2. AOF
append only file,将所有数据库修改语句记录到文件中,恢复时可以根据记录文件重新执行所有修改的语句.
- AOF相关配置信息
appendonly
:AOF开关,默认是no,改为yes用以支持AOF功能appendfilename
文件名,默认为appendonly.aof
appendfsync
aof文件写入磁盘策略
fsync()
方法告诉操作系统将数据真实的写入磁盘,而不是等待buffer中有更多的数据才写入磁盘always
:每次写入append only log的修改都会调用fsync()方法,将修改写入到磁盘中,效率低
,安全
everysec
:每秒调用一次fsync()方法,将修改写入到磁盘中,默认
的配置.no
:不会调用fsync()方法,操作系统自己决定什么时候刷入数据到磁盘.快速
no-appendfsync-on-rewrite
:在bgsave或者bgrewriteaof操作时不刷盘,默认为no
,因为有可能会丢失数据.auto-aof-rewrite-percentage
redis会记录上一次rewrite之后aof文件的大小,当前aof文件超过上次rewrite大小一定比例之后,就会调用rewrite方法,默认为100
,表示超过一倍.auto-aof-rewrite-min-size
触发rewrite的aof文件大小,默认为64mb
4-3. RDB和AOF的优缺点
- 数据的安全性
- rdb备份策略导致生成rdb文件时间间隔可能很大,如果期间服务崩溃,丢失数据
- aof默认设置每秒同步一次数据,最多丢失一秒内的数据
- 数据恢复的效率
- rdb:保存一份数据库的快照,恢复的时候直接加载到内存中即可
- aof:存放的指令日志,做数据恢复时,需要回放和执行所有的指令日志,速度慢
5. 数据备份方案
RDB非常适合做冷备,数据备份方案:
- 写crontab定时调度脚本去做数据备份
- 每小时都copy一份rdb的备份到指定目录中,仅仅保留最近48小时的备份
- 每天都保留一份当日的rdb备份,仅仅保留最近一个月的备份
- 每次copy备份时,删除太旧的备份
- 每天晚上将当前服务器上所有的数据备份发送到云服务器上.
- 每小时copy一次备份,删除48小时前的数据
创建redis_rdb_copy_hourly.sh文件,内容如下
#!/bin/sh
cur_date=`date +%Y%m%d%H`
rm -rf /usr/local/redis/snapshotting/$cur_date
mkdir /usr/local/redis/snapshotting/$cur_date
cp /var/redis/6379/dump.rdb /usr/local/redis/snapshotting/$cur_date
del_date=`date -d -48hour +%Y%m%d%H`
rm -rf /usr/local/redis/snapshotting/$del_date
设置定时任务:
crontab -e
0 * * * * sh /usr/local/redis/copy/redis_rdb_copy_hourly.sh
- 每天copy一次备份
创建redis_rdb_copy_daily.sh
#!/bin/sh
cur_date=`date +%Y%m%d`
rm -rf /usr/local/redis/snapshotting/$cur_date
mkdir /usr/local/redis/snapshotting/$cur_date
cp /var/redis/6379/dump.rdb /usr/local/redis/snapshotting/$cur_date
del_date=`date -d -1month +%Y%m%d`
rm -rf /usr/local/redis/snapshotting/$del_date
设置定时任务
crontab -e
0 0 * * * sh /usr/local/redis/copy/redis_rdb_copy_daily.sh
- crontab命令
crontab用来设定固定的时间间隔执行某项任务.
crontab {-l | -r | -e}
-e:编辑任务内容
-r:删除目前的时程表
-l:列出目前的时程表
编辑内容格式:f1 f2 f3 f4 f5 program
f1:分钟,表示一小时中的哪些分钟需要执行任务,*
表示每分钟都要执行
f2:小时,表示当天几点中需要执行任务,0
表示凌晨0点
f3:日期,表示当月的第几日需要执行任务
f4:月份
f5:星期
* * * * *
- - - - -
| | | | |
| | | | +----- 星期中星期几 (0 - 7) (星期天 为0)
| | | +---------- 月份 (1 - 12)
| | +--------------- 一个月中的第几天 (1 - 31)
| +-------------------- 小时 (0 - 23)
+------------------------- 分钟 (0 - 59)
6. redis数据恢复
- 如果是redis
进程挂掉
,那么重启redis进程即可,直接基于AOF日志文件恢复数据 - 如果是redis所在
机器挂掉
,那么重启机器后,尝试重启redis进程,基于AOF日志文件进行数据恢复- 如果AOF没有破损,可以直接恢复
- 如果AOF文件破损,那么用redis-check-aof fix修复文件
- 如果AOF和RDB文件出现丢失或损坏,尝试基于该机器某个最新的
RDB副本
进行恢复 - 如果该机器的RDB副本全部损坏,从
远程拉取云服务
上最新的RDB快照来恢复数据 - 如果是发现有重大的数据错误,比如新上线的功能有bug,导致前一个小时数据全部被污染了,那么可以选择更早的时间点,对数据进行恢复.
- RDB数据恢复的步骤
- redis服务挂掉,dump.rdb和appendonly.aof文件皆被损坏
- 关掉redis的aof功能,修改redis配置文件中appendonly yes为appendonly no
如果不关闭,redis启动时发现aof开着,而aof文件不存在,则会创建一个空的aof文件并启动,而不会使用rdb文件. - 从其他备份中复制一份完好的dump.rdb文件到redis数据文件目录下
- 启动redis
热修改
打开aofconfig set appendonly yes
如果不热修改打开aof的话,redis还是不会生成aof文件.
热修改只会影响当前实例状态,不会修改配置文件- 在配置文件中重新打开aof
7. redis的主从架构原理
- slave node启动时,从配置文件中读取master的host和ip
- slave node内部有个定时任务,每秒检查是否有新的master node要连接和复制,如果有,则跟master node建立socket网络连接
- slave node每10秒发送ping命令给master node,检查是否能够通信
- 如果master node设置了
requirepass
,那么slave node必须发送masterauth
的口令去进行认证 - slave node第一次连接master时,会触发full resynchronization,全量复制.此时master会启动一个后台线程,生成一份RDB快照,同时将所有的写请求缓存在内存中,rdb快照生成后,master会将这个rdb发送给salve,slave会先写入本地磁盘,然后再从本地磁盘加载到内存中,然后master会将内存中缓存的写命令发送给slave完成同步操作.
- master和slave都会维护一个自身当前数据的offset,同时master还会维护所有的slave节点的offset,每次slave节点发送psync请求同步时,master将该节点的offset之后的数据同步给slave.
- 过期key的处理
slave节点不会过期key,在master节点某个key过期或者通过lru淘汰了一个key后,那么master会发送一条del命令给slave节点.
8. 配置redis主从架构
- 修改配置文件,所有的redis的
bind
都要设置为自己的ip,如bind 192.168.1.7
- 所有的slave几点都要设置
slaveof masterip masterport
,如slaveof 192.168.1.7 6379
9. redis性能测试
redis-benchmark
在redis/src下有个redis-benchmark脚本,可以用来测试redis性能./redis-benchmark -h 192.168.1.7
10. redis哨兵集群
quorum
:多少个哨兵认为master主观宕机了,才进行主备切换majority
:选举出来的哨兵需要得到多少个哨兵的授权才可以做主备切换操作
每次哨兵要做主备切换,都需要quorum数量的哨兵认为sdown,然后选举出一个哨兵来做切换,这个哨兵还需要得到majority个哨兵的授权,才能正式进行主备切换主观宕机sdown
:如果一个哨兵ping一个master,超过了is_master_down_after_milliseconds指定的毫秒数之后,那么这个哨兵就认为master宕机了客观宕机odown
:如果quorum个哨兵都认为master宕机了,那么就是客观宕机.- 哨兵集群的自动发现策略
哨兵之间的发现是通过redis的pub/sub
系统实现的,每个哨兵都会往__sentinel__:hello
这个channel里发送一个消息,所有的哨兵都订阅这个消息,感知到其他哨兵的存在.每隔两秒,哨兵都会往自己监控的某个master+salves对应的__sentinel__:hello
channel里发送一个消息,内容是自己的host,ip和runid以及对master的监控配置 - 主备切换的slave选择策略
- 如果一个slave和master断开连接的时间过长,那就不会被选为master
(down-after-millisecends * 10) + milliseconds_since_master_is_in_sdown_state
- 先根据
slave priority
排序,slave priority越小,优先级越高 - 如果priority一样,根据replica offset排序,offset越大,优先级越高,offset越大,说明数据越完整
- 如果offset也相同,那就选择run id较小的那个
- 如果一个slave和master断开连接的时间过长,那就不会被选为master
10-1. 哨兵的配置
mkdir -p /var/sentinel/26379
mkdir -p /var/log/sentinel/26379
mkdir /etc/sentinel
vi /etc/sentinel/26379.conf
daemonize yes
logfile /var/log/sentinel/26379/sentinel.log
port 26379
bind 192.168.1.7
dir /var/sentinel/26379
sentinel monitor mymaster 192.168.1.7 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
- sentinal启动
redis-sentinal /etc/sentinal/26379.conf
- 登录sentinel命令行
redis-cli -h 192.168.1.7 -p 26379
sentinel master mymaster
查看sentinel中配置的mymaster关联的master节点
sentinel slaves mymaster
查看mymaster中slave的节点
sentinel sentinels mymaster
查看所有哨兵
sentinel get-master-addr-by-name mymaster
查看master节点ip和port
11. redis数据丢失问题
11-1. 异步复制导致的数据丢失
- 原因:
master数据同步到slave的过程是异步的,可能部分数据还没有同步完,然后master就宕机了,此时这些数据就丢失了
11-2. 脑裂导致的数据丢失
master正常提供写服务但是无法和其他的slave通信,这时哨兵就会认为master宕机了,然后重新选举,将一个slave切换成master,这时,这个节点下就会有两个master在同时提供写服务,如果有client还未切换到新的master上去,仍然想原先的master写数据,在旧的master恢复正常后,会被作为一个slave挂到新的master上去,自己的数据会清空,重新从新的master上同步数据,这样就会造成数据丢失
11-3. 解决方案:
redis配置文件
min-slaves-to-write 1
至少有1个slave可以写
min-slaves-max-lag 10
master与slaves之间ping返回时间最小值是10秒
如果上面两个条件有一个不满足,那么master就停止向外提供write操作
12. redis集群架构
12-1. 数据分布算法
12-1-1. 最原始的hash算法
类似hashmap原理,对key按节点个数取模,放到对应的节点下
缺点:当某个节点变成不可用,节点个数减少,所有数据不可用了.
12-1-2. 一致性hash算法
构造一个环,将节点根据hash算法分布在环上,每次需要保存数据时,根据数据的key做hash运算出来的hash值,取环上比这个hash值大的下一个节点,获取节点信息然后访问该节点存储数据.
1. 使用TreeMap保存所有节点信息,key为hash(ip),value为ip
2. 为了保证负载均衡,可以设置多个虚拟节点,比如对于同一个节点,多设置几个key不同,但是value都为ip的虚拟节点,比如key为hash(ip+"1"),value为ip,key为hash(ip+"2"),value为ip
3. 每次需要判断一个数据应该存到那个节点上时,先根据数据的key进行hash运算得出一个hash值,然后到treemap中找到第一个key比hash大的节点,获取它的value
int hash = hash(key);
int nodeKey = treeMap.higherKey(hash);
if(nodeKey == null)
nodeKey = treeMap.firstKey();//环状处理
String ip = treeMap.get(nodeKey);
return ip;
12-1-3. redis cluster的hash slot算法
redis cluster有固定的16384个hash slot,每个key值hash后对16384取模,可以获得key对应的hash slot.
redis cluster中的每个master都会持有一些slot,增加master时,从现有master节点的slot中移动一部分过去
12-2. redis cluster集群
12-2-1. 配置cluster集群
# 每个cluster节点的配置文件中修改
cluster-enabled yes
cluster-config-file /etc/redis/cluster/node-7001.conf
cluster-node-timeout 15000
# 安装ruby,redis5.0之后就不用ruby了,使用redis-cli就可以搭建集群
yum install -y ruby
yum install -y rubygems
gem install redis
cp /root/redis-3.2.8/src/redis-trib.rb /usr/local/bin
redis-trib.rb create --replicas 1 192.168.1.7:7000 192.168.1.7:7001 192.168.1.8:7002 192.168.1.8:7003 192.168.1.9:7004 192.168.1.9:7005
如果遇到create时卡在了Waiting for the cluster to join.......,那么可能需要检查下所有的配置文件中的bind,只绑定本机的外网ip
gem install redis报错解决方法
带有密码的集群,需要修改gems中client.rb文件的password字段
12-2-2. cluster扩容
12-2-2-1. 添加master
- 将192.168.1.11:7006添加到192.168.1.7:7000所在的集群上去,作为一个master
redis-trib.rb add-node 192.168.1.11:7006 192.168.1.7:7000
- 192.168.1.11:7006分配一些slot
redis-trib.rb reshard 192.168.1.11:7006
12-2-2-2. 添加slave
redis-trib.rb add-node --slave --master-id 28927912ea0d59f6b790a50cf606602a5ee48108 192.168.1.11:7006 192.168.1.7:7000
12-2-2-3. 删除节点
- 要保证删除的节点上没有slot之后才能删除
redis-trib.rb del-node 192.168.1.11:7006 ce6a8fce762d84a9c60b5a323d8a7
12-2-3. cluster清空
需要到每一个cluster节点上,删除自动生成的dump.rdb和cluster node的conf文件
/etc/redis/redis-cluster.sh stop
rm -rf /etc/redis/cluster/*
rm -rf /var/redis/7000/*
12-2-4. redis cluster内部通信机制gossip协议
- redis cluster节点间采用gossip协议进行通信
元数据的更新比较分散,更新请求会陆陆续续,有一定的延时,降低了压力,
缺点:元数据更新有延时,可能导致集群的一些操作有滞后. - 每个节点的服务端口+10000用于节点间通信的端口
- 通信内容包括故障信息,节点的增加和移除,hash slot信息等
- gossip协议包含的消息体,包括ping,pong,meet,fail等
meet
:某个节点发送meet消息给新加入的节点,让新节点加入到集群中ping
:每个节点会频繁的发送ping给其他节点,包含自己的状态以及维护的集群元数据,节点间通过ping互相交换元数据pong
:返回ping和meet,包含自己的状态和其他信息,也可以用于信息广播和更新fail
:某个节点判断另一个节点fail之后,就会发送fail给其他节点,通知其他节点,指定的节点宕机了
13. redis实践中的优化
13-1. fork耗时导致高并发请求延时
生成rdb快照以及aof文件的rewrite过程都会fork一个子进程来进行操作.
fork的时候,子进程需要拷贝父进程的空间内存页表,会耗费一定的时间,导致用户并发请求变慢
- 优化思路:
fork的耗时和redis主进程的内存大小有关,redis内存不要设置过大,一般10GB以内.
13-2. AOF的fsync阻塞问题
redis将数据写入AOF缓冲区,单独开一个线程做fsync操作,每秒一次
redis的主线程会检查两次fsync的时间,如果距离上次fsync时间超过了2秒,那么写请求就会阻塞.
- 优化思路:
优化硬盘写入的速度,使用SSD硬盘
13-3. 主从复制延迟问题
主从复制可能会超时,这时候需要有监控和报警机制
用info replication命令可以查看到master和slave的offset,它们之间的差值就是对应的延迟量,如果延迟过多,进行报警.
13-4. 主从复制风暴问题
如果多个slave都从master去执行全量复制,一份大的rdb文件同时发送到多个slave上,会导致网络宽带被严重占用
解决思路:使用树状结构,master同步到某些slave,然后其他slave从该slave上去同步数据.