目录
一、单机部署
1、软件准备
Redis:redis-5.0.5
gcc-c++编译器(其他的C语言编译器也可以):直接使用yum拉取就可以了,最近能用的yum源不是很多,可以去替换一下yum:可用yum源
yum -y install gcc-c++
2、安装配置
cd /usr/redis/redis-5.0.5/
ls
发现没有configure 就需要⾃⼰指定安装⽬录
编译安装(从源⽂件中抽取指令过程)
make
ls /usr/redis
make install PREFIX=/usr/redis
#再次查看
ls /usr/redis
发现多了⼀个bin⽬录 说明安装成功
3、启动Redis
直接演示后台运行方式启动吧
#复制配置⽂件到bin下
cp /usr/redis/redis-5.0.5/redis.conf /usr/redis/bin/
#修改配置⽂件 :136行 daemonize yes
vim /usr/redis/bin/redis.conf
#使⽤配置⽂件启动服务(脚本+参数启动):
/usr/redis/bin/redis-server /usr/redis/bin/redis.conf
#回⻋启动,发现不再占⽤窗⼝
#检查是否启动成功:
ps -ef |grep redis |grep -v grep
#可以看到⼀个redis-server进程在运⾏,标识后台启动成功
#在本窗⼝就可以使⽤redis客户端连接
/usr/redis/bin/redis-cli
#退出客户端
quit
#停止redis-server服务,在客户端里输入
shutdown
就不会出现下面的情况了
二、Redis集群
2.1、主从模式
2.1.1、作用
- 防止单点故障(一台redis宕机,整个redis不能用)
- 可以进行读写分离(一台redis主机进行写操作,其它机子进行读操作),提高使用效率
2.1.2、规划图
2.1.3、具体配置
准备工作
利用之前单机部署的机子,再克隆两台虚拟机,克隆之后记住修改ip地址
主从配置
在redis-1上操作:
cp /usr/redis/redis-5.0.5/redis.conf /usr/redis/bin/master.conf
vim /usr/redis/bin/master.conf
修改master.conf配置文件
:69 bind 192.168.37.161 绑定IP
:92 port 6379 实例端口号
:136 daemonize yes 守护方式运行
在redis-2和redis-3上操作
cp /usr/redis/redis-5.0.5/redis.conf /usr/redis/bin/slave1.conf
vi /usr/redis/bin/slave1.conf
分别修改slave1.conf和slave2.conf
:69 bind 192.168.37.161 绑定IP
:92 port 6379 实例端口号
:136 daemonize yes 守护方式运行
#最后一行添加 slaveof 主节点ip 主节点redis端口号
slaveof 192.168.37.161 6379
启动测试
三台redis服务都启动
#redis-1上执行
/usr/redis/bin/redis-server /usr/redis/bin/master.conf
/usr/redis/bin/redis-cli -h 192.168.37.161 -p 6379
#redis-2上执行
/usr/redis/bin/redis-server /usr/redis/bin/slave1.conf
/usr/redis/bin/redis-cli -h 192.168.37.162 -p 6379
#redis-3上执行
/usr/redis/bin/redis-server /usr/redis/bin/slave2.conf
/usr/redis/bin/redis-cli -h 192.168.37.163 -p 6379
在mater主机(redis-1)上执行写命令。没问题,说明master节点可写,可读
在slave从机(redis-2和redis-3)上执行写命令。有问题,说明slave从节点不能写,只能读
2.1.4、主从复制原理
主从复制
单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。
通过执⾏slaveof命令或设置slaveof选项(在Redis 5.0及以后的版本中改名为replicaof),让⼀个服务器去复制另⼀个服务器的数据。主数据库可以进行读写操作,当写操作导致数据变化时会⾃动将数据同步给从数据库。⽽从数据库⼀般是只读的,并接受主数据库同步过来的数据。⼀个主数据库可以拥有多个从数据库,而一个从数据库只能拥有⼀个主数据库。
主从全量复制
第8步之前都是正常的,但是还是不能确定数据是否同步成功,就不如master在生成RDB文件的时候,又接收到了客户端的其它请求,那么刚刚发送过去的RD文件就不是最新的了,其实master在记录RDB文件的过程中,会把新的请求记录在一个日志文件中(repl_baklog),然后再把这个日志文件发送个slave,slave执行完日志文件后,就实现了主从数据的完全同步了
有几个问题:master是怎么判断slave是不是第一次请求?另一个就是日志文件,假如我们后期再次同步的时候,基本上都是从日志文件中记录命令,然后发送给从节点去执行,那么他们是如何确保master和slave同步数据的时候每次都不多不少就是slave需要的那部分数据呐。再引申两个概念Replication Id(简称:replid;数据集的标记)和offset(偏移量),每一个master都会有一个唯一的replid,他的从节点会继承他的replid,当从节点发送数据同步请求的时候,会把自己的replid发送给master,master拿slave的replid和自己的replid进行比较,如果不一样的话,证明这个slave是第一次进行数据同步,然后master把自己的replid发送给slave,然后slave就把这个信息记录到了本地,这个时候他俩的replid就一致了,下面就去执行bgsave进行数据同步,这样我们就清楚slave是不是第一次同步数据了;;;;;;;;如果不是第一次进行数据同步,那么两者的replid是一样的,说明之前是同步过数据的,这个时候master就不会生成RDB文件了,就会根据日志文件去同步数据
那么第23456次去日志文件同步数据的时候,怎么知道同步多少数据呐这就需要第二个概念:offset(自增的整数值)了,,日志文件记录的数据越多,这个offset的值就越大,再主从进行数据同步的时候会携带上offset这个值,假如现在offset记录的值是50,主节点的offset的值是80,这就是这50-80的数据还没有完成同步,同步的时候,master就会把这50-80的部分数据同步给slave就可以了。
主从增量同步(slave重启或后期数据变化)
2.1.5、缺点
无法真正高可用(master一旦宕机,整个集群就无法进行写操作)
所有的写操作都是master进行,写操作无法负载均衡
2.2、哨兵模式
2.2.1、作用
监控:Sentinel会不断检查您的master和slave是否按预期工作
自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主
通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新消息推给Redis的客户端
2.2.2、规划图
2.2.3、具体配置
修改配置
在三台redis上操作
cp /usr/redis/redis-5.0.5/sentinel.conf /usr/redis/bin/sentinel1.conf
cp /usr/redis/redis-5.0.5/sentinel.conf /usr/redis/bin/sentinel2.conf
cp /usr/redis/redis-5.0.5/sentinel.conf /usr/redis/bin/sentinel3.conf
#分别修改redis1,redis2,redis3哨兵配置文件
vim /usr/redis/bin/sentinel1.conf
vim /usr/redis/bin/sentinel2.conf
vim /usr/redis/bin/sentinel3.conf
#关闭保护模式 当开启保护模式的时候默认只能本机连
protected-mode no #17
#哨兵端口
port 26666 #21
#添加守护进程模式
daemonize yes #26
#添加指明日志文件名
logfile "./temp.log" #36
#修改工作目录
dir "/tmp" #65
#哨兵监控的master名称可以随便起,ip和端口固定 quorum 当哨兵是集群时,有多少个哨兵认为master失效(客观下线),master才算失效。 防止脑裂 配置计算最好是 哨兵总数量/2+1
sentinel monitor mymaster 192.168.37.162 6379 2 #84
#设置master和slaves验证密码
sentinel auth-pass mymaster 123456 #103
#master(默认30秒)不能使用后标记为主观down状态。
sentinel down-after-milliseconds mymaster 30000 #113
启动测试
/usr/redis/bin/redis-sentinel /usr/redis/bin/sentinel1.conf
/usr/redis/bin/redis-sentinel /usr/redis/bin/sentinel2.conf
/usr/redis/bin/redis-sentinel /usr/redis/bin/sentinel3.conf
ps -ef |grep redis|grep -v grep
演示自动故障切换
使用客户端连接3台服务器
/usr/redis/bin/redis-cli -h 192.168.37.161 -p 6379
/usr/redis/bin/redis-cli -h 192.168.37.162 -p 6379
/usr/redis/bin/redis-cli -h 192.168.37.163 -p 6379
显示3台主机的主从状态 ,发现redis1是master ,2和3是slave
info replication
让redis1(master)的实例宕机
等大约30秒 其中两个哨兵都在30连不上,分别认为是主观下线,当3个哨兵相互沟通后,就确定客观下线
shutdown 关闭服务器并保存数据
ps -ef |grep redis|grep -v grep
在redis2和redis3中执行
会发现redis3变成了master,redis2变成了redis3的slave了
info replication
让redis1恢复实例
redis1自动变成redis3的slave
/usr/redis/bin/redis-server /usr/redis/bin/master.conf
/usr/redis/bin/redis-cli -h 192.168.37.161 -p 6379
info replication
当你在redis1宕机期间,在redis3中执行写操作,当redis1实例恢复之后,发现redis1中有宕机期间redis3中写的数据
2.2.4、哨兵模式原理
哨兵发现节点未响应,标记为主观下线;哨兵系统继续监控该节点,等待更多的证据来确认节点的状态;如果多个哨兵也认为节点下线,达到预设的确认阈值,则节点被标记为客观下线;节点被标记为客观下线后,系统会采取进一步的故障处理措施,如故障转移或通知管理员等
服务状态监控
哨兵选主规则
2.2.5、优点
1、保证高可用(除了切换主从的一瞬间,集群是高可用)
2、哨兵监控各个节点
3、自动故障迁移
2.2.6、缺点
主从模式下切换需要时间,切换期间,不能对外提供服务
没有解决master写的压力
2.3、分片集群
2.3.1、作用
主从和哨兵可以解决高可用、高并发的问题。但是依然有两个问题没有解决:
- 海量数据存储问题
- 高并发写得问题
使用分片集群可以解决上述问题,分片集群特征
- 集群中有多个master,每个master保存不同数据
- 每个master都可以有多个slave节点
- master之间通过ping检测彼此健康状态
- 客户端请求可以访问集群任意节点,最终都会被转发到正确节点
2.3.2、规划图
2.3.3、具体配置
在3台redis服务器创建集群目录
mkdir /usr/redis/cluster
ls /usr/redis/
分别在redis1,redis2,redis3执行
mkdir /usr/redis/cluster/8001 /usr/redis/cluster/8002 #redis1执行
mkdir /usr/redis/cluster/8003 /usr/redis/cluster/8004 #redis2执行
mkdir /usr/redis/cluster/8005 /usr/redis/cluster/8006 #redis3执行
redis1上操作,redis2和redis3一样(2和3需要改ip和端口)
#复制配置文件到集群节点目录下
cp /usr/redis/redis-5.0.5/redis.conf /usr/redis/cluster/8001/
#修改配置文件,配置集群 8001和8002都需要修改
vim /usr/redis/cluster/8001/redis.conf
#需要改为其他节点机器可访问的ip 否则创建集群时无法访问对应的端口,无法创建集群 69行
bind 192.168.37.161
#92行
port 8001
#redis后台运行 136行
daemonize yes
#pidfile文件 158行
pidfile /var/run/redis_8001.pid
#aof日志开启 有需要就开启,它会每次写操作都记录一条日志 699行
appendonly yes
#AOF 持久化使用的文件名称 703行
appendfilename "appendonly-8001.aof"
#开启集群 把注释#去掉 832行
cluster-enabled yes
#集群的配置 配置文件首次启动自动生成 840行
cluster-config-file nodes_8001.conf
#请求超时 默认15秒,可自行设置 846行
cluster-node-timeout 15000
启动所有节点
redis1上执行
/usr/redis/bin/redis-server /usr/redis/cluster/8001/redis.conf
/usr/redis/bin/redis-server /usr/redis/cluster/8002/redis.conf
ps -ef |grep redis |grep -v grep #查看是否启动成功
redis2上执行
/usr/redis/bin/redis-server /usr/redis/cluster/8003/redis.conf
/usr/redis/bin/redis-server /usr/redis/cluster/8004/redis.conf
ps -ef |grep redis |grep -v grep #查看是否启动成功
redis3上执行
/usr/redis/bin/redis-server /usr/redis/cluster/8005/redis.conf
/usr/redis/bin/redis-server /usr/redis/cluster/8006/redis.conf
ps -ef |grep redis |grep -v grep #查看是否启动成功
创建集群
/usr/redis/bin/redis-cli --cluster create 192.168.37.161:8001 192.168.37.161:8002 192.168.37.162:8003 192.168.37.162.32:8004 192.168.37.163:8005 192.168.37.163:8006 --cluster-replicas 1
看到这个说明就成功了
测试
/usr/redis/bin/redis-cli -c -h 192.168.37.161 -p 8001
#提供当前集群节点状态信息
cluster info
#获取集群节点配置(显示主从配置信息)
cluster nodes
2.3.4、分片集群原理
Redis分片集群引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽
槽的总数量是16384 在创建集群会按照配置的主节点数量,大概平均分配在不同的节点上
咱的例子是配置的一主一从,主从关系如下myself,master指的是当前登录的节点,咱当前是8001窗口下查看的
主节点 从节点 槽
8001 8004 0-5460
8003 8006 5461-10922
8005 8002 10923-16383
槽和值的关系:
redis中存放数据都是键值对方式进行存储,所有的数据类型存入redis时都有对应的key, 每次向redis写入数据时,redis会根据key的值使用CRC16算法进行计算得到一个数字,再使用该数字对16384取余,余数是几就会把该数据放入该槽中
比如:数据(key-value) 槽 节点
CRC16('aaa')%16384=10439 10439 8003[5461-10922]
CRC16('bbb')%16384=5287 5287 8001[ 0-5460 ]
CRC16('ddd')%16384=11367 5287 8005[ 10923-16383 ]
每次添加数据都会在节点间跳转
集群的自动故障切换
在创建集群时,为了保证集群的高可用,设置了1主1从,实际工作使用过程中,可以设置1主多从,让集群具有更高可用性。平时进行数据读写时都是主节点来执行,从节点使用主从同步,备份主节点数据,当主机点意外宕机,从节点就会自动转换为主节点,当前主节点保存的数据就可以正常的读写。如果主节点恢复,自动变为从节点。
集群的失败
当没有从节点的主节点宕机,集群状态会失败
当超过半数的主节点同时宕机,集群状态会失败
2.3.5、优点
1、无中心架构(不存在哪个节点影响性能瓶颈),少了 proxy 层。
2、数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布。
3、可扩展性,可线性扩展到 1000 个节点,节点可动态添加或删除。 5461=1G
假如1000个节点 16384 一个节点上只放 16.3 槽 数据更均匀的存放不同的服务器,假如该服务器的内存全用来缓存,能缓存的数据会更多
4、高可用性,部分节点不可用时,集群仍可用。通过增加 Slave 做备份数据副本
5、实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave到 Master 的角色提升。
2.3.6、缺点
1、资源隔离性较差,容易出现相互影响的情况(多个应用同时使用一个redis集群时,可能会出现)。
2、数据通过异步复制,不保证数据的强一致性
3、自动故障切换时,集群状态fail 无法对外提供服务
2.4、总结
Redis提供了三种集群策略:
1. 主从模式:这种模式⽐较简单,主库可以读写,并且会和从库进⾏数据同步,这种模式下,客户端 直接连主库或某个从库,但是但主库或从库宕机后,客户端需要⼿动修改IP,另外,这种模式也⽐ 较难进⾏扩容,整个集群所能存储的数据受到某台机器的内存容量,所以不可能⽀持特⼤数据量
2. 哨兵模式:这种模式在主从的基础上新增了哨兵节点,但主库节点宕机后,哨兵会发现主库节点宕 机,然后在从库中选择⼀个库作为进的主库,另外哨兵也可以做集群,从⽽可以保证但某⼀个哨兵 节点宕机后,还有其他哨兵节点可以继续⼯作,这种模式可以⽐较好的保证Redis集群的⾼可⽤,但 是仍然不能很好的解决Redis的容量上限问题。
3. Cluster模式:Cluster模式是⽤得⽐较多的模式,它⽀持多主多从,这种模式会按照key进⾏槽位的 分配,可以使得不同的key分散到不同的主节点上,利⽤这种模式可以使得整个集群⽀持更⼤的数据容量,同时每个主节点可以拥有⾃⼰的多个从节点,如果该主节点宕机,会从它的从节点中选举⼀ 个新的主节点。
对于这三种模式,如果Redis要存的数据量不⼤,可以选择哨兵模式,如果Redis要存的数据量⼤,并且 需要持续的扩容,那么选择Cluster模式。
三、RDB与AOF
1、数据的持久化是怎么实现的?
1.1、RDB
RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据
手动生成RDB文件
save #由Redis主进程来执行RDB,会阻塞所有命令
bgsave #开启子进程执行RDB,避免主进程受到影响
简单说一下save为什么不好
立即将当前数据库的内容保存到磁盘,在主线程中同步执行。这意味着 Redis 在执行save 命令时,会暂停所有的其他操作,直到数据写入完成.由于save命令会阻塞主线程,这可能会导致 Redis 在保存期间无法处理其他请求,从而影响性能
自动生成RDB文件
Redis内部有触发RDB的机制,可以在redis.conf文件中找到
RDB的执行原理
bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入RDB文件。(是异步的,对主进程几乎是没有阻塞的,但是开启子进程的时候会产生阻塞,纳秒级的忽略不计)
fork采用的是copy-write技术:
- 当主进程执行读操作时,访问共享内存;
- 当主进程执行写操作时,则会拷贝一份数据,执行写操作。
首先有个物理内存,你可以理解为计算机的内存条,这个时候有个redis的主进程,要实现对数据的读写肯定是在内存中实现的,是吧!但是在Linux系统中,所有的进程都不能直接操作物理内存,但是操作系统给所有的进程都分配了一块虚拟内存,那主进程只能操作虚拟内存,当然操作系统会维护虚拟内存和物理内存之间的映射关系表,这个表被称为页表;这个时候主进程去操作虚拟内存,而虚拟内存基于页表的映射关联到物理内存真正的存储数据的位置,这样就可以实现对物理内存数据的读和写的操作了,现在我们就知道页表的作用了。
那现在我们重点来看一下主进程和子进程是如何进行数据同步的。首先我们知道,当我们执行bgsave的时候,它会开一个子进程去执行RDB对不对,其实内部啊,它会fork一个子进程(fork的话相当于克隆了一个子进程),当有了子进程之后,它并不是针对物理内存的数据做拷贝,仅仅是对页表数据进行拷贝,也就是说把映射关系拷贝给了子进程,这个时候子进程就有了和主进程一样的映射关系了,那子进程在操作自己的虚拟内存时,因为映射关系和主进程是一样的,是不是最终一定能映射到相同的物理内存区域,这样就是实现了子进程和主进程内存空间的共享,这样我们就不需要拷贝内存中的数据了,直接实现内存共享,这个速度是非常的快的,这样阻塞的时间就会尽可能的缩短了,因为它只需要拷贝页表的数据就可以了这个时间是非常的快的(纳秒级别,可以忽略不计);当子进程有了页表之后,就可以读取物理内存数据了,然后就可以把读到的数据写入磁盘中,这是一个新的RDB文件,写完之后,就会替换旧的RDB文件,其实到这里我们的异步持久化就完成了,但是要注意,子进程在写RDB文件的过程中,主进程是不是会接受用户的请求,去修改内存中的数据啊,当然可以啦,这个时候主进程在修改数据,而子进程在读和写数据,是不是会有冲突啊,甚至会有一些脏数据,该怎么办呐,所以为了避免这个问题的发生,在fork底层采用了copy-on-write的技术,什么意思呐,其实就是:当主进程要去写的时候,去做一次拷贝,现在我们的内存是没有拷贝的,是贡献的个,主进程和子进程都是可以操作这个数据的,在fork的时候,会把共享的这个内存标记为read-only(它是个只读模式),任何一个进程只能来读数据,不能写数据,假设现在用户真的接收了一个写的请求,主进程必须先拷贝一份数据出来,也就是把数据B完整拷贝一份出来,然后再去完成他的写操作,一旦完成拷贝之后,我们的主进程再去读操作的时候也会往这边去读了,页表的映射关系也会映射到新的数据来,这个就是copy-on-write技术,他只要每次有写操作都可以拷贝,这样就避免了脏写的问题,这个就是RDB使用bgsave命令执行的底层原理了
优点
1. 整个Redis数据库将只包含⼀个⽂件 dump.rdb,⽅便持久化。
2. 容灾性好,⽅便备份。
3. 性能最⼤化,fork ⼦进程来完成写操作,让主进程继续处理命令,所以是 IO 最⼤化。使⽤单独⼦
进程来进⾏持久化,主进程不会进⾏任何 IO 操作,保证了 redis 的⾼性能
4. 相对于数据集⼤时,⽐ AOF 的启动效率更⾼。
缺点
1. 数据安全性低。RDB 是间隔⼀段时间进⾏持久化,如果持久化之间 redis 发⽣故障,会发⽣数据丢
失。所以这种⽅式更适合数据要求不严谨的时候)
2. 由于RDB是通过fork⼦进程来协助完成数据持久化⼯作的,因此,如果当数据集较⼤时,可能会导
致整个服务器停⽌服务⼏百毫秒,甚⾄是1秒钟
1.2、AOF
AOF全称Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看作是命令日志文件。
优点
1. 数据安全,Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也
是异步完成的,其效率也是⾮常⾼的,所差的是⼀旦系统出现宕机现象,那么这⼀秒钟之内修改的
数据将会丢失。⽽每修改同步,我们可以将其视为同步持久化,即每次发⽣的数据变化都会被⽴即
记录到磁盘中。。
2. 通过 append 模式写⽂件,即使中途服务器宕机也不会破坏已经存在的内容,可以通过 redis
check-aof ⼯具解决数据⼀致性问题。
3. AOF 机制的 rewrite 模式。定期对AOF⽂件进⾏重写,以达到压缩的⽬的
缺点
1、 因为是记录命令,AOF文件会比RDB文件大得多。而且AOF会记录对同一个key的多次写操作,单只有最后一次写操作才有意义。通过执行bgrewriteaof命令,可以让AOF文件执行重写功能,用最少的命令达到相同的效果。
2、 数据集⼤的时候,⽐ rdb 启动效率低。
3、运⾏效率没有RDB⾼
Redis也会在触发阈值时自动去重写AOF文件。阈值也可以在redis.conf中配置