redis环境搭建

redis环境搭建

1. 安装linux虚拟机

  • CentOS-6.5-i386-minimal.iso
  • 查看虚拟机中分配的ipifconfig
  • 配置/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
  • 重启networkservice 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压缩算法将字符串类型在快照时进行压缩,默认为yes
    • rdbchecksum
      验证rdb文件的完整性,默认为yes
    • dbfilename
      rdb文件名,默认为dump.rdb
    • dir
      rdb文件的生成位置,默认位置为./
  • 手动生成快照
    • save
    • bgsave

4-2. AOF

append only file,将所有数据库修改语句记录到文件中,恢复时可以根据记录文件重新执行所有修改的语句.

  • AOF相关配置信息
    • appendonly:AOF开关,默认是no,改为yes用以支持AOF功能
    • appendfilename文件名,默认为appendonly.aof
    • appendfsyncaof文件写入磁盘策略
      fsync()方法告诉操作系统将数据真实的写入磁盘,而不是等待buffer中有更多的数据才写入磁盘
      • always:每次写入append only log的修改都会调用fsync()方法,将修改写入到磁盘中,效率低,安全
      • everysec:每秒调用一次fsync()方法,将修改写入到磁盘中,默认的配置.
      • no:不会调用fsync()方法,操作系统自己决定什么时候刷入数据到磁盘.快速
    • no-appendfsync-on-rewrite:在bgsave或者bgrewriteaof操作时不刷盘,默认为no,因为有可能会丢失数据.
    • auto-aof-rewrite-percentageredis会记录上一次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非常适合做冷备,数据备份方案:

  1. 写crontab定时调度脚本去做数据备份
  2. 每小时都copy一份rdb的备份到指定目录中,仅仅保留最近48小时的备份
  3. 每天都保留一份当日的rdb备份,仅仅保留最近一个月的备份
  4. 每次copy备份时,删除太旧的备份
  5. 每天晚上将当前服务器上所有的数据备份发送到云服务器上.
  • 每小时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数据恢复

  1. 如果是redis进程挂掉,那么重启redis进程即可,直接基于AOF日志文件恢复数据
  2. 如果是redis所在机器挂掉,那么重启机器后,尝试重启redis进程,基于AOF日志文件进行数据恢复
    • 如果AOF没有破损,可以直接恢复
    • 如果AOF文件破损,那么用redis-check-aof fix修复文件
  3. 如果AOF和RDB文件出现丢失或损坏,尝试基于该机器某个最新的RDB副本进行恢复
  4. 如果该机器的RDB副本全部损坏,从远程拉取云服务上最新的RDB快照来恢复数据
  5. 如果是发现有重大的数据错误,比如新上线的功能有bug,导致前一个小时数据全部被污染了,那么可以选择更早的时间点,对数据进行恢复.
  • RDB数据恢复的步骤
    1. redis服务挂掉,dump.rdb和appendonly.aof文件皆被损坏
    2. 关掉redis的aof功能,修改redis配置文件中appendonly yes为appendonly no
      如果不关闭,redis启动时发现aof开着,而aof文件不存在,则会创建一个空的aof文件并启动,而不会使用rdb文件.
    3. 从其他备份中复制一份完好的dump.rdb文件到redis数据文件目录下
    4. 启动redis
    5. 热修改打开aof config set appendonly yes
      如果不热修改打开aof的话,redis还是不会生成aof文件.
      热修改只会影响当前实例状态,不会修改配置文件
    6. 在配置文件中重新打开aof

7. redis的主从架构原理

  1. slave node启动时,从配置文件中读取master的host和ip
  2. slave node内部有个定时任务,每秒检查是否有新的master node要连接和复制,如果有,则跟master node建立socket网络连接
  3. slave node每10秒发送ping命令给master node,检查是否能够通信
  4. 如果master node设置了requirepass,那么slave node必须发送masterauth的口令去进行认证
  5. slave node第一次连接master时,会触发full resynchronization,全量复制.此时master会启动一个后台线程,生成一份RDB快照,同时将所有的写请求缓存在内存中,rdb快照生成后,master会将这个rdb发送给salve,slave会先写入本地磁盘,然后再从本地磁盘加载到内存中,然后master会将内存中缓存的写命令发送给slave完成同步操作.
  6. master和slave都会维护一个自身当前数据的offset,同时master还会维护所有的slave节点的offset,每次slave节点发送psync请求同步时,master将该节点的offset之后的数据同步给slave.
  7. 过期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__:hellochannel里发送一个消息,内容是自己的host,ip和runid以及对master的监控配置
  • 主备切换的slave选择策略
    1. 如果一个slave和master断开连接的时间过长,那就不会被选为master
      (down-after-millisecends * 10) + milliseconds_since_master_is_in_sdown_state
    2. 先根据slave priority排序,slave priority越小,优先级越高
    3. 如果priority一样,根据replica offset排序,offset越大,优先级越高,offset越大,说明数据越完整
    4. 如果offset也相同,那就选择run id较小的那个

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 10master与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;

一致性hash算法

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上去同步数据.

posted on 2020-04-12 04:20  Serenity1994  阅读(191)  评论(0编辑  收藏  举报