一、主从复制概念
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点
(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。
Master以写为主,Slave 以读为主。
默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只 能有一个主节点。
二、主从复制的作用:
1、数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
2、故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务
的冗余。
3、负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务
(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写
少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
4、高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复
制是Redis高可用的基础。
三、主从复制配置文件的修改和启动Redis服务
[root@VM-4-10-centos redis-6.2.4]# ls
00-RELEASENOTES deps redis-6379.conf runtest sentinel_26379.log sentinel-26382.conf src
BUGS INSTALL redis-6380.conf runtest-cluster sentinel-26380.conf sentinel_26382.log tests
CONDUCT Makefile redis-6381.conf runtest-moduleapi sentinel_26380.log sentinel-26383.conf TLS.md
CONTRIBUTING MANIFESTO redis-6382.conf runtest-sentinel sentinel-26381.conf sentinel_26383.log
COPYING README.md redis-6383.conf sentinel-26379.conf sentinel_26381.log sentinel.conf
配置多个redis.conf用来启动多个redis服务,加端口号来命名更容易区分。配置了五个配置文件redis-6379.conf、redis-6380.conf、redis-6381.conf、redis-6382.conf、redis-6383.conf。
其中端口6379是主节点其余都是从节点。
默认情况下,每台Redis服务器都是主节点; 一般情况下只用配置从机就好了,在各个从节点的配置文件中配置:
daemonize yes #表示后台启动
port 6379 #修改为自己的端口号
slaveof 127.0.0.1 6379 #主节点IP地址 :主节点端口号 表示是该节点的从节点
配置文件配置好后就是启动redis服务
使用命令:./redis-server …/redis-6379.conf 来启动一个redis服务
[root@VM-4-10-centos src]# ./redis-server ../redis-6379.conf
[root@VM-4-10-centos src]# ps -ef|grep redis
root 3298 1 0 14:28 ? 00:00:29 ./redis-server 127.0.0.1:6379
root 3323 1 0 14:28 ? 00:00:26 ./redis-server 127.0.0.1:6380
root 3340 1 0 14:28 ? 00:00:25 ./redis-server 127.0.0.1:6381
root 3353 1 0 14:28 ? 00:00:26 ./redis-server 127.0.0.1:6382
root 3365 1 0 14:28 ? 00:00:26 ./redis-server 127.0.0.1:6383
[root@VM-4-10-centos src]# ./redis-cli -p 6379 #启动主节点的客户端
127.0.0.1:6379> info replication
# Replication
role:master #表示该节点为主节点
connected_slaves:4 #表示该节点有4个从节点
slave0:ip=127.0.0.1,port=6380,state=online,offset=1809829,lag=0 #从节点的信息
slave1:ip=127.0.0.1,port=6381,state=online,offset=1809829,lag=0
slave2:ip=127.0.0.1,port=6382,state=online,offset=1809829,lag=0
slave3:ip=127.0.0.1,port=6383,state=online,offset=1809829,lag=0
master_failover_state:no-failover
master_replid:415619cc6e479b710c68b353d1ebbad28d368ea1
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1809962
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:761387
repl_backlog_histlen:1048576
127.0.0.1:6379>
[root@VM-4-10-centos src]# ./redis-cli -p 6380 #启动端口号为6380的从节点的客户端
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:1824900
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:415619cc6e479b710c68b353d1ebbad28d368ea1
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1824900
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:776325
repl_backlog_histlen:1048576
127.0.0.1:6380>
127.0.0.1:6379> set key "value" #在主节点中设置一个键值对,尝试在从节点中取出
OK
127.0.0.1:6379>
[root@VM-4-10-centos src]# ./redis-cli -p 6380 #启动端口号为6380的从节点的客户端
127.0.0.1:6380> get key #在从节点中成功取值
"value"
127.0.0.1:6380> set key2 "value2" #尝试往从节点中写入,提示为只读。
(error) READONLY You can't write against a read only replica.
127.0.0.1:6380>
主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。
四、哨兵模式概念
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是***哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。***
[root@VM-4-10-centos redis-6.2.4]# ./src/redis-sentinel ./sentinel-26379.conf #启动哨兵
[root@VM-4-10-centos redis-6.2.4]# ps -ef|grep redis
root 3298 1 0 14:28 ? 00:00:33 ./redis-server 127.0.0.1:6379
root 3323 1 0 14:28 ? 00:00:30 ./redis-server 127.0.0.1:6380
root 3340 1 0 14:28 ? 00:00:28 ./redis-server 127.0.0.1:6381
root 3353 1 0 14:28 ? 00:00:30 ./redis-server 127.0.0.1:6382
root 3365 1 0 14:28 ? 00:00:30 ./redis-server 127.0.0.1:6383
root 9050 1 0 15:03 ? 00:00:55 ./src/redis-sentinel *:26379 [sentinel]
root 12160 1 0 15:23 ? 00:00:42 ./src/redis-sentinel *:26380 [sentinel]
root 12196 1 0 15:24 ? 00:00:42 ./src/redis-sentinel *:26382 [sentinel]
root 12213 1 0 15:24 ? 00:00:41 ./src/redis-sentinel *:26383 [sentinel]
root 14032 9428 0 22:35 pts/0 00:00:00 ./redis-cli -p 6379
root 14626 9484 0 22:39 pts/1 00:00:00 ./redis-cli -p 6380
root 24367 23851 0 23:42 pts/2 00:00:00 grep --color=auto redis
[root@VM-4-10-centos redis-6.2.4]#
哨兵模式的其他配置项
配置项 | 参数类型 | 作用 |
---|---|---|
port | 整数 | 启动哨兵进程端口 |
dir | 文件夹目录 | 哨兵进程服务临时文件夹,默认为/tmp,要保证有可写入的权限 |
sentinel down-after-milliseconds | <服务名称><毫秒数(整数)> | 指定哨兵在监控Redis服务时,当Redis服务在一个默认毫秒数内都无法回答时,单个哨兵认为的主观下线时间,默认为30000(30秒) |
sentinel parallel-syncs | <服务名称><服务器数(整数)> | 指定可以有多少个Redis服务同步新的主机,一般而言,这个数字越小同步时间越长,而越大,则对网络资源要求越高 |
sentinel failover-timeout | <服务名称><毫秒数(整数)> | 指定故障切换允许的毫秒数,超过这个时间,就认为故障切换失败,默认为3分钟 |
sentinel notification-script | <服务名称><脚本路径> | 指定sentinel检测到该监控的redis实例指向的实例异常时,调用的报警脚本。该配置项可选,比较常用 |
sentinel down-after-milliseconds配置项只是一个哨兵在超过规定时间依旧没有得到响应后,会自己认为主机不可用。对于其他哨兵而言,并不是这样认为。哨兵会记录这个消息,当拥有认为主观下线的哨兵达到sentinel monitor所配置的数量时,就会发起一次投票,进行failover,此时哨兵会重写Redis的哨兵配置文件,以适应新场景的需要。
修改redis.conf文件如下:
# 使得Redis服务器可以跨网络访问
bind 0.0.0.0
# 设置密码
requirepass "123456"
# 指定主服务器,注意:有关slaveof的配置只是配置从服务器,主服务器不需要配置
slaveof 192.168.11.128 6379
# 主服务器密码,注意:有关slaveof的配置只是配置从服务器,主服务器不需要配置
masterauth 123456
上述内容主要是配置Redis服务器,从服务器比主服务器多一个slaveof的配置和密码。
配置3个哨兵,每个哨兵的配置都是一样的。在Redis安装目录下有一个sentinel.conf文件,copy一份进行修改
# 禁止保护模式
protected-mode no
# 配置监听的主服务器,这里sentinel monitor代表监控,mymaster代表服务器的名称,可以自定义,192.168.11.128代表监控的主服务器,6379代表端口,2代表只有两个或两个以上的哨兵认为主服务器不可用的时候,才会进行failover操作。
sentinel monitor mymaster 192.168.11.128 6379 2
# sentinel author-pass定义服务的密码,mymaster是服务名称,123456是Redis服务器密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster 123456
Redis缓存穿透,缓存击穿,缓存雪崩原因+解决方案
-
缓存穿透:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。
解决:
一个一定不存在缓存及查询不到的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。
有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
-
缓存击穿:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
解决:
key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题。
使用互斥锁(mutex key)
业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。
public String get(key) { String value = redis.get(key); if (value == null) { //代表缓存值过期 //设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表设置成功 value = db.get(key); redis.set(key, value, expire_secs); redis.del(key_mutex); } else { //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可 sleep(50); get(key); //重试 } } else { return value; } }
-
缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
解决:
缓存失效时的雪崩效应对底层系统的冲击非常可怕!大多数系统设计者考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
正常:
雪崩: