一,什么是redis?
- Redis是一种key-value形式的nosql数据库。
1.1 redis的历史。
- 2008年,意大利的一家创业公司Merzia推出了一款基于MySQL的网站实时统计系统LLOOGG,然而没过多久该公司的创始人 Salvatore Sanfilippo便 对MySQL的性能感到失望,于是他决定亲自为LLOOGG量身定做一个数据库,并于2009年开发完成,这个数据库就是Redis。 不过Salvatore Sanfilippo并不满足只将Redis用于LLOOGG这一款产品,而是希望更多的人使用它,于是在同一年Salvatore Sanfilippo将Redis开源发布,并开始和Redis的另一名主要的代码贡献者Pieter Noordhuis一起继续着Redis的开发,直到今天。Salvatore Sanfilippo自己也没有想到,短短的几年时间,Redis就拥有了庞大的用户群体。HackerNews在2012年发布了一份数据库的使用情况调查,结果显示有近12%的公司在使用Redis。国内如新浪微博、街旁网、知乎网,国外如GitHub、Stack Overflow、Flickr等都是Redis的用户。VMware公司从2010年开始赞助Redis的开发, Salvatore Sanfilippo和Pieter Noordhuis也分别在3月和5月加入VMware,全职开发Redis。
1.2 什么是nosql。
-
NoSQL,泛指非关系型的数据库,NoSQL即Not-Only SQL,它可以作为关系型数据库的良好补充。随着互联网web2.0网站的兴起,非关系型的数据库现在成了一个极其热门的新领域,非关系数据库产品的发展非常迅速。而传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,例如:
1、High performance - 对数据库高并发读写的需求
web2.0网站要根据用户个性化信息来实时生成动态页面和提供动态信息,所以基本上无法使用动态页面静态化技术,因此数据库并发负载非常高,往往要达到每秒上万次读写请求。关系数据库应付上万次SQL查询还勉强顶得住,但是应付上万次SQL写数据请求,硬盘IO就已经无法承受了。其实对于普通的BBS网站,往往也存在对高并发写请求的需求,例如网站的实时统计在线用户状态,记录热门帖子的点击次数,投票计数等,因此这是一个相当普遍的需求。
2、Huge Storage - 对海量数据的高效率存储和访问的需求
类似Facebook,twitter,Friendfeed这样的SNS网站,每天用户产生海量的用户动态,以Friendfeed为例,一个月就达到了2.5亿条用户动态,对于关系数据库来说,在一张2.5亿条记录的表里面进行SQL查询,效率是极其低下乃至不可忍受的。再例如大型web网站的用户登录系统,例如腾讯,盛大,动辄数以亿计的帐号,关系数据库也很难应付。
3、High Scalability && High Availability- 对数据库的高可扩展性和高可用性的需求在基于web的架构当中,数据库是最难进行横向扩展的,当一个应用系统的用户量和访问量与日俱增的时候,你的数据库却没有办法像web server和app server那样简单的通过添加更多的硬件和服务节点来扩展性能和负载能力。对于很多需要提供24小时不间断服务的网站来说,对数据库系统进行升级和扩展是非常痛苦的事情,往往需要停机维护和数据迁移,为什么数据库不能通过不断的添加服务器节点来实现扩展呢?
NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难
题。
NoSQL数据库的四大分类如下:
键值(Key-Value)存储数据库
相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
典型应用: 内容缓存,主要用于处理大量数据的高访问负载。
数据模型: 一系列键值对列存储数据库
相关产品:Cassandra, HBase, Riak
典型应用:分布式的文件系统
数据模型:以列簇式存储,将同一列数据存在一起文档型数据库
相关产品:CouchDB、MongoDB
典型应用:Web应用(与Key-Value类似,Value是结构化的)
数据模型: 一系列键值对图形(Graph)数据库
相关数据库:Neo4J、InfoGrid、Infinite Graph
典型应用:社交网络
数据模型:图结构
1.3 redis的应用场景
- 缓存(数据查询、短连接、新闻内容、商品内容等等)。(最多使用)
- 分布式集群架构中的session分离。
- 聊天室的在线好友列表。
- 任务队列。(秒杀、抢购、12306等等)
- 应用排行榜。
- 网站访问统计。
- 数据过期处理(可以精确到毫秒)
二、Redis的安装
2.1.版本说明
- Redis是C语言开发的,官方并不提供windows版本,所有的版本都是基于linux的。但是微软的一个团
队将redis移植到了windows,所以在Windows上也可以使用redis。 - redis官方下载地址:
https://redis.io/download
Windows版下载地址:
https://github.com/MicrosoftArchive/redis/releases - 由于redis的官方并不提供Windows版所以生产环境中的redis推荐使用Linux版,Windows版可以供测
试和学习使用。本教程中的redis是基于Linux的。
2.2.编译环境安装
redis是C语言开发,并且建议在linux上运行,本教程使用Centos7作为安装环境。安装redis需要先将官
网下载的源码进行编译,编译依赖gcc环境,如果没有gcc环境,需要安装gcc。
可以使用命令来安装编译环境:
yum install gcc-c++ -y
2.3.安装步骤
- 下载redis的源码
wget http://download.redis.io/releases/redis-5.0.7.tar.gz
- 源码解压缩
tar zxvf redis-5.0.7.tar.gz -C /opt/module/
- 编译源代码
进入目录 redis-5.0.7 ,然后执行make命令编译源代码
- 编译源代码
>cd redis-5.0.7
>make
源码编译后会得到redis的可执行文件。
- 安装
可以将编译后的文件安装到指定位置。
- 安装
> make install PREFIX=/usr/local/redis
bin里面全部都是可执行文件。
三、启动和关闭
1.启动
3.1. 前端启动模式
直接运行bin/redis-server将以前端模式启动,前端模式启动的缺点是在redis的旧版本中ssh命令
窗口关闭则redis-server程序结束,最新版的redis并不会因为ssh连接的关闭而关闭,但是还是会
占用ssh连接,故此不推荐使用此方法。如下图:
3.2. 守护进程模式
以守护进程模式启动的话需要 redis.conf 配置文件。在配置文件中设置redis 的启动模式。从redis源码目录 redis-5.0.7 中复制 redis.conf 文件到 /usr/local/redis/bin 目录下,然后修改其中的配置信息:
daemonize yes
将 no 改为 yes
启动redis时,指定使用的配置文件:
> ./redis-server redis.conf
这样redis就以守护进程模式启动了。
运行之后可以查找进程,确认是否运行redis
ps aux|grep redis
2.关闭
- 前端启动模式可以使用 ctrl+c 快捷键关闭进程
- 守护模式可以使用 kill 进程id 关闭(强制关闭是kill -9 进程id)
- 守护模式也可以使用客户 ./redis-cli shutdown 命令关闭
3.客户端
在redis的安装目录中有redis的客户端,即redis-cli(Redis Command Line Interface),它是Redis自
带的基于命令行的Redis客户端。
使用方法:
/redis-cli -h [IP地址] -p [端口号]
- 直接启动 redis-cli 将默认连接运行在本机的6379端口的redis服务。可以使用 -h 参数连接指定IP地址
的服务, -p 参数指定服务运行的端口号。
[root@bigdata111 bin]# ./redis-cli
127.0.0.1:6379>
[root@bigdata111 bin]# ./redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>
也可以安装Redis Desktop Manager 客户端来方便操作Redis.
连接客户端需要注意两个问题:
- 防火墙需要关闭,systemctl status firewalld 查看状态。
- 修改bin下面的redis.conf文件,使得bind 改成虚拟机的ip。
bind 192.168.11.1
再指定新的ip 地址:
[root@bigdata111 bin]# ./redis-cli -h 192.168.11.111
192.168.11.111:6379>
然后重启redis即可。
默认有16 个数据库,且每个相对独立的。
四、数据类型
-
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
如上图所以,数据的结构关系。
list 的value 是一个双向链表,有序,可以随意从左边或右边访问。
set 的value 是set集合,数据是不可重复且无序的。
sorted set value是有序的,不能重复。具备List和set的特点。
4.1.String
string 是 redis 最基本的类型,你可以理解成与Map中的一模一样的数据类型,一个 key 对应一个value。
string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。
string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。
- 赋值
set key value
192.168.11.111:6379> set key1 1000
OK
- 取值
赋值与取值:
GET key
192.168.11.111:6379> get key1
"1000"
192.168.11.111:6379> get key2
(nil)
当键不存在时返回空结果。
- 取值时同时对key进行赋值操作。
GETSET key value
192.168.11.111:6379> GETSET key1 333
"222"
192.168.11.111:6379> get key1
"333"
4.删除
Del key
192.168.11.111:6379> del key2
(integer) 1
192.168.11.111:6379> get key2
(nil)
- 查看当前数据库有多少key
keys *
192.168.11.111:6379> keys *
1) "key1"
切换数据库:select 1
192.168.11.111:6379> select 1
OK
192.168.11.111:6379[1]> set a 1234
OK
- 数值加一
INCR key
当存储的字符串是整数时,Redis提供了一个实用的命令INCR,其作用是让当前键值递增,并返回
递增后的值。
192.168.11.111:6379> get c
"123"
192.168.11.111:6379> incr c
(integer) 124
192.168.11.111:6379> incr c
(integer) 125
192.168.11.111:6379> incr c
(integer) 126
192.168.11.111:6379> get c
"126"
- 增加指定的整数
INCRBY key increment
192.168.11.111:6379> get c
"126"
192.168.11.111:6379> INCRBY c 100
(integer) 226
- 数值减一/减少指定的整数
DECR key/DECRBY key increment
192.168.11.111:6379> get c
"126"
192.168.11.111:6379> INCRBY c 100
(integer) 226
192.168.11.111:6379> decr c
(integer) 225
192.168.11.111:6379> decrby c 100
(integer) 125
4.2.Hash
hash叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。相当于一个key对应一个map.
- 创建hash并添加值
HSET [key] [field] [value]
192.168.11.111:6379> hset hash1 a1 b1
(integer) 1
192.168.11.111:6379> hset hash2 a2 b2
(integer) 1
192.168.11.111:6379> hset hash3 a3 b3
(integer) 1
192.168.11.111:6379> keys *
1) "hash2"
2) "a"
3) "hash1"
4) "hash3"
5) "c"
6) "key1"
7) "b"
一个集合添加多组数据:
192.168.11.111:6379> hmset hash4 a1 1 a2 2 c2 3 d4 4
OK
- 从hash中取值
HGET [key] [field]
192.168.11.111:6379> hget hash4 a1
"1"
一个hash 里面有多少Key, value或者一起查看key和value
192.168.11.111:6379> hkeys hash4
1) "a1"
2) "a2"
3) "c2"
4) "d4"
192.168.11.111:6379> hvals hash4
1) "1"
2) "2"
3) "3"
4) "4"
192.168.11.111:6379> hgetall hash4
1) "a1"
2) "1"
3) "a2"
4) "2"
5) "c2"
6) "3"
7) "d4"
8) "4"
- 从hash中删除
HDEL [key] [field]
192.168.11.111:6379> hdel hash4 a1
(integer) 1
192.168.11.111:6379> hgetall hash4
1) "a2"
2) "2"
3) "c2"
4) "3"
5) "d4"
6) "4"
- 判断filed是否存在
HEXISTS [key] [field]
如果存在返回“1”,如果不存在返回“0”
192.168.11.111:6379> hexists hash4 a2
(integer) 1
192.168.11.111:6379> hexists hash4 a1
(integer) 0
4.3.List
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边) 一个列表最多可以包含 2 ^32 - 1个元素 (4294967295, 每个列表超过40亿个元素)。
- 添加元素
左边添加:lpush [key] [element...]
右边添加:rpush [key] [element...] - 取元素
左边取元素:lpop [key]
右边去元素:rpop [key] - 查看列表中元素
lrange [key] [start] [end]
需要设置列表中起始的元素的索引和结束元素的索引,如果结束元素的索引是 -1 则代表取到列表
结尾。 - 查看列表的长度
llen [key]
192.168.11.111:6379> lpush list1 a b c d e f g
(integer) 7
192.168.11.111:6379> rpush list1 g h j
(integer) 10
注意这里的取元素,是取完就不存在了,不是查看。
查看元素(0 -1是查看所有的元素)
192.168.11.111:6379> lrange list1 0 -1
1) "f"
2) "e"
3) "d"
4) "c"
5) "b"
6) "a"
7) "g"
8) "h"
9) "j"
192.168.11.111:6379> lrange list1 0 4
1) "f"
2) "e"
3) "d"
4) "c"
5) "b"
192.168.11.111:6379> llen list1
(integer) 9
4.4.Set
Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。集合中最大的成员数为 2 ^32 - 1 (4294967295, 每个集合可存储40多亿个成员)。
- 添加元素:
sadd [key] [member...]
192.168.11.111:6379> sadd set1 a a a b c d f g
(integer) 6
Set集合会去除重复的元素,并且元素的无序的
2.取集合元素成员
smembers [key]
192.168.11.111:6379> smembers set1
1) "d"
2) "c"
3) "g"
4) "a"
5) "b"
6) "f"
- 取集合元素个数
scard [key]
192.168.11.111:6379> scard set1
(integer) 6
- 删除元素
删除指定元素:srem [key] [member]
- 删除元素
从集合中取移除随机元素:spop [key]
192.168.11.111:6379> srem set1 a
(integer) 1
192.168.11.111:6379> spop set1
"c"
192.168.11.111:6379> smembers set1
1) "g"
2) "b"
3) "d"
4) "f"
4. 5.SortedSet
Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 2^32 - 1
(4294967295, 每个集合可存储40多亿个成员)。
由于SortedSet所有的命令都是以 Z 开头所以SortedSet也叫做 zset 。
- 添加元素
zadd [key] [score] [member] - 删除元素
zrem [key] [member] - 元素数量
zcard [key] - 获取集合成员
zrange [key] [start] [stop] [withscroes]
192.168.11.111:6379> zadd zset1 1 a 2 s 3 d 4 f 5 g
(integer) 5
可以看到结果是根据得分进行排序的。
192.168.11.111:6379> zrange zset1 0 -1
1) "a"
2) "s"
3) "d"
4) "f"
5) "g"
192.168.11.111:6379> zrange zset1 0 -1 withscores
1) "a"
2) "1"
3) "s"
4) "2"
5) "d"
6) "3"
7) "f"
8) "4"
9) "g"
10) "5"
降序:
192.168.11.111:6379> zrevrange zset1 0 -1
1) "g"
2) "f"
3) "d"
4) "s"
5) "a"
192.168.11.111:6379> zrevrange zset1 0 -1 withscores
1) "g"
2) "5"
3) "f"
4) "4"
5) "d"
6) "3"
7) "s"
8) "2"
9) "a"
10) "1"
4.6.服务器命令
切换数据库:select [index]
查看当前数据库中的key:keys [partten]
删除key:del [key]
查看key的数据类型:type [key]
查看key是否存在:exists [key]
设置key的生存时间:expire [key]
查看key多长时间过期:ttl [key]
清空当前数据库中的数据:flushdb
删除所有数据库中的数据:flushall
192.168.11.111:6379> expire hash2 10
(integer) 1
192.168.11.111:6379> ttl hash2
(integer) 5
192.168.11.111:6379>
192.168.11.111:6379> ttl hash2
(integer) 3
192.168.11.111:6379> ttl hash2
(integer) 1
192.168.11.111:6379> ttl hash2
(integer) 1
192.168.11.111:6379> ttl hash2
(integer) -2
五、持久化
Redis的高性能是由于其将所有数据都存储在了内存中,为了使Redis在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘中,这一过程就是持久化。Redis支持两种方式的持久化,一种是RDB方式,一种是AOF方式。可以单独使用其中一种或将二者结合使用。
5.1 RDB模式
RDB方式的持久化是通过快照(snapshotting)完成的,当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘。RDB是Redis默认采用的持久化方式,在redis.conf配置文件中默认有此下
配置:
save 900 1
save 300 10
save 60 10000
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
save 开头的一行就是持久化配置,可以配置多个条件(每行配置一个条件),每个条件之间是“或”的关系,“save 900 1”表示15分钟(900秒钟)内至少1个键被更改则进行快照,“save 300 10”表示5分钟(300秒)内至少10个键被更改则进行快照。在redis.conf中配置dbfilenam指定rdb快照文件的名称Redis启动后会读取RDB快照文件,将数据从硬盘载入到内存。根据数据量大小与结构和服务器性能不同,这个时间也不同。通常将记录一千万个字符串类型键、大小为1GB的快照文件载入到内存中需要花费20~30秒钟。
问题总结:
通过RDB方式实现持久化,一旦Redis异常退出,就会丢失最后一次快照以后更改的所有数据。这就需要开发者根据具体的应用场合,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受的范围。如果数据很重要以至于无法承受任何损失,则可以考虑使用AOF方式进行持久化。
我们新建一个key a 然后关闭reds再重启,可以看到a 时一直存在的:
192.168.11.111:6379> set a 100
OK
192.168.11.111:6379> shutdown
not connected> exit
[root@bigdata111 bin]# ps aux|grep redis
root 7517 0.0 0.2 24672 7108 pts/0 T 02:58 0:00 ./redis-cli -h 192.168.11.1
root 9995 0.0 0.0 112644 952 pts/0 S+ 06:53 0:00 grep --color=auto redis
[root@bigdata111 bin]# ./redis-server redis.conf
10008:C 08 Feb 2020 06:54:08.585 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
10008:C 08 Feb 2020 06:54:08.585 # Redis version=5.0.7, bits=64, commit=00000000, modified=0, pid=10008, just started
10008:C 08 Feb 2020 06:54:08.585 # Configuration loaded
[root@bigdata111 bin]# ./redis-cli -h 192.168.11.111
192.168.11.111:6379> keys *
1) "zset1"
2) "hash4"
3) "set1"
4) "a"
5) "hash3"
6) "hash1"
7) "key1"
8) "b"
9) "c"
10) "list1"
5.2 .AOF模式
默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数开启:
appendonly yes
# By default Redis asynchronously dumps the dataset on disk. This mode is
# good enough in many applications, but an issue with the Redis process or
# a power outage may result into a few minutes of writes lost (depending on
# the configured save points).
#
# The Append Only File is an alternative persistence mode that provides
# much better durability. For instance using the default data fsync policy
# (see later in the config file) Redis can lose just one second of writes in a
# dramatic event like a server power outage, or a single write if something
# wrong with the Redis process itself happens, but the operating system is
# still running correctly.
#
# AOF and RDB persistence can be enabled at the same time without problems.
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.
- 开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof,可以通过appendfilename参数修改:appendfilename appendonly.aof
一旦开启了AOF模式,就会以AOF模式为主
[root@bigdata111 bin]# cat appendonly.aof
*2
$6
SELECT
$1
0
*3
$3
set
$1
a
$3
100
六、主从复制
6.1.主从复制原理
-
持久化保证了即使redis服务重启也不会丢失数据,因为redis服务重启后会将硬盘上持久化的数据恢复到内存中,但是当redis服务器的硬盘损坏了可能会导致数据丢失,如果通过redis的主从复制机制就可以避免这种单点故障,如下图:
说明:
- 主redis中的数据有两个副本(replication)即从redis1和从redis2,即使一台redis服务器宕机其它两台redis服务也可以继续提供服务。
- 主redis中的数据和从redis上的数据保持实时同步,当主redis写入数据时通过主从复制机制会复制到两个从redis服务上。
- 只有一个主redis,可以有多个从redis。主从复制不会阻塞master,在同步数据时,master 可以继续处理client 请求,一个redis可以即是主又是从。
6.2.主从配置
之前版本的redis,配置为slave of,现在改为REPLICATION。主从模式,可以是树状的,从服务属于多台主服务,且从服务也可以有从服务。主从模式,可实现读写分离;高可用模式下,主服务出现问题,也可以通过哨兵切换从服务为主服务;可实现主服务不用数据持久化,从服务进行持久化工作,减轻主服务负担等等。
- 创建两个redis实例,一个master 两个slave。
- master实例不做任何特殊配置(或者根据需求修改端口)。
- 修改slave的配置文件
复制三个 redis:
[root@bigdata111 redis]# cp -r bin redis-master
[root@bigdata111 redis]# cp -r bin redis-slave1
[root@bigdata111 redis]# cp -r bin redis-slave2
一个主,两个从。
master 的redis.conf 修改端口号为:6380
slave1 的redis.conf 修改端口号为:6381,并修改如下配置
replicaof 192.168.11.111 6380
同样的,slave2 的redis.conf 修改端口号为:6382,并修改如下配置
replicaof 192.168.11.111 6380
分别启动三个redis
root 10476 0.2 0.2 156396 7912 ? Ssl 07:28 0:02 ./redis-server 192.168.11.111:6380
root 10554 0.2 0.2 153836 7740 ? Ssl 07:33 0:01 ./redis-server 192.168.11.111:6382
root 10621 0.2 0.4 160492 11984 ? Ssl 07:36 0:01 ./redis-server 192.168.11.111:6381
我们分别连这三个redis
192.168.11.111:6380> keys *
1) "a"
192.168.11.111:6380>
192.168.11.111:6382> keys *
1) "a"
192.168.11.111:6382>
192.168.11.111:6381> keys *
1) "a"
192.168.11.111:6381>
在主redis里写入数据,从redis是可以get 到的值,但是从redis不能set 值。
192.168.11.111:6380> set hello1 word1
OK
192.168.11.111:6381> get hello1
"word1"
192.168.11.111:6382> get hello1
"word1"
192.168.11.111:6382> set aaa bbb
(error) READONLY You can't write against a read only replica.
七、哨兵模式
Redis的主从复制模式下, 一旦主节点由于故障不能提供服务, 需要人工将从节点晋升为主节点, 同时还要通知应用方更新主节点地址, 对于很多应用场景这种故障处理的方式是无法接受的。 可喜的是Redis从2.8开始正式提供了Redis Sentinel(哨兵) 架构来解决这个问题。
7.1哨兵模式介绍
-
Sentinel(哨兵)进程是用于监控redis集群中Master主服务器工作的状态,在Master主服务器发生故障的时候,可以实现Master和Slave服务器的切换,保证系统的高可用(HA),其已经被集成在redis2.6+的版本中,Redis的哨兵模式到了2.8版本之后就稳定了下来。哨兵(sentinel) 会不断地检查你的Master和Slave是否运作正常。当被监控的某个Redis节点出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知。当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作。
哨兵是redis集群架构中非常重要的一个组件,主要功能如下:
- 集群监控:负责监控redis master和slave进程是否正常工作
- 消息通知:如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
- 故障转移:如果master node挂掉了,会自动转移到slave node上
- 配置中心:如果故障转移发生了,通知client客户端新的master地址
7.2 哨兵进程的工作方式
7.2.1监控过程
- 每个Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的Master主服务器,Slave从服务器以及其他Sentinel(哨兵)进程发送一个 PING 命令。
- 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值,则这个实例会被 Sentinel(哨兵)进程标记为主观下线(SDOWN)。
- 如果一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的所有Sentinel(哨兵)进程要以每秒一次的频率确认Master主服务器的确进入了主观下线状态。
- 当有足够数量的 Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认Master主服务器进入了主观下线状态(SDOWN), 则Master主服务器会被标记为客观下线(ODOWN)。
- 在一般情况下, 每个Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的所有Master主服务器、Slave从服务器发送 INFO 命令。
- 当Master主服务器被 Sentinel(哨兵)进程标记为客观下线(ODOWN)时,Sentinel(哨兵)进程向下线的 Master主服务器的所有 Slave从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
- 若没有足够数量的 Sentinel(哨兵)进程同意 Master主服务器下线, Master主服务器的客观下线状态就会被移除。若 Master主服务器重新向 Sentinel(哨兵)进程发送 PING 命令返回有效回复,Master主服务器的主观下线状态就会被移除。
7.2.2 sdown和odown
- sdown和odown两种失败状态
- sdown是主观宕机,就一个哨兵如果自己觉得一个master宕机了,那么就是主观宕机
- odown是客观宕机,如果quorum数量的哨兵都觉得一个master宕机了,那么就是客观宕机
- sdown达成的条件:如果一个哨兵ping一个master,超过了is-master-down-after-milliseconds指定的毫秒数之后,就主观认为master宕机
- odown达成的条件:如果一个哨兵在指定时间内,收到了quorum指定数量的其他哨兵也认为那个master是sdown了,那么就认为是odown了,客观认为master宕机
7.2.3 quorum和majority
- quorum:确认odown的最少的哨兵数量
- majority:授权进行主从切换的最少的哨兵数量
- 每次一个哨兵要做主备切换,首先需要quorum数量的哨兵认为odown,然后选举出一个哨兵来做切换,这个哨兵还得得到majority哨兵的授权,才能正式执行切换
- 如果quorum < majority,比如5个哨兵,majority就是3,quorum设置为2,那么就3个哨兵授权就可以执行切换,但是如果quorum >= majority,那么必须quorum数量的哨兵都授权,比如5个哨兵,quorum是5,那么必须5个哨兵都同意授权,才能执行切换
7.2.4为什么哨兵至少3个节点
哨兵集群必须部署2个以上节点。如果哨兵集群仅仅部署了个2个哨兵实例,那么它的majority就是2(2的majority=2,3的majority=2,5的majority=3,4的majority=2),如果其中一个哨兵宕机了,就无法满足majority>=2这个条件,那么在master发生故障的时候也就无法进行主从切换。
7.3.哨兵配置
sentinel.conf
# 设置端口
port 26379
# 是否守护进程启动
daemonize no
# 守护进程运行的时候需要保留pidfile
pidfile /var/run/redis-sentinel.pid
# 日志文件
logfile "/root/log/sentinel.log"
## sentinel monitor master-group-name hostname port quorum
## quorum的解释如下:
##(1)至少多少个哨兵要一致同意,master进程挂掉了,或者slave进程挂掉了,或者要启动一个故障
转移操作
## (2)quorum是用来识别故障的,真正执行故障转移的时候,还是要在哨兵集群执行选举,选举一个哨
兵进程出来执行故障转移操作
## (3)假设有5个哨兵,quorum设置了2,那么如果5个哨兵中的2个都认为master挂掉了; 2个哨兵中
的一个就会做一个选举,选举一个哨兵出来,执行故障转移; 如果5个哨兵中有3个哨兵都是运行的,那么
故障转移才会被允许执行。
# 原文是:Note that whatever is the ODOWN quorum, a Sentinel will require to
# be selected by the majority of the known Sentinels in order to
# start a failover, so no failover can be performed in minority.
sentinel monitor mymaster 127.0.0.1 6379 3
# down-after-milliseconds,超过多少毫秒跟一个redis实例断了连接(ping不通),哨兵就可能认
为这个redis实例挂了
sentinel down-after-milliseconds mymaster 30000
# parallel-syncs,新的master别切换之后,同时有多少个slave被切换到去连接新master,重新做
同步,数字越低,花费的时间越多
# 比如:master宕机了,4个slave中有1个切换成了master,剩下3个slave就要挂到新的master上面
去
# 这个时候,如果parallel-syncs是1,那么3个slave,一个一个地挂接到新的master上面去,1个挂
接完,而且从新的master sync完数据之后,再挂接下一个。
# 如果parallel-syncs是3,那么一次性就会把所有slave挂接到新的master上去
sentinel parallel-syncs mymaster 1
#failover-timeout,执行故障转移的timeout超时时长,Default is 3 minutes.
sentinel failover-timeout mymaster 180000
- 如果有一个sentinel:
实际情况,其实单纯从代码的情况,其实1个Sentinel就能完成主观下线(sdown,SubjectivelyDown),客观下线(odown, Objectively Down) 的判断,自动发现 Sentinel 和从服务器,并且完成故障转移。
- 如果有一个sentinel:
-
如果有2个sentinel:
如果哨兵集群仅仅部署了个2个哨兵实例,quorum=1。s1和s2中只要有1个哨兵认为master宕机就可以还行切换,同时s1和s2中会选举出一个哨兵来执行故障转移。同时这个时候,需要majority,也就是大多数哨兵都是运行的,2个哨兵的majority就是2(2的majority=2,3的majority=2,5的majority=3,4的majority=3),如果一个节点挂了那么哨兵也就挂了,哨兵只剩下一个,那么就无法完成故障转移。
-
-
如果是经典的3节点哨兵集群
Configuration: quorum = 2。
majority=2(majority不能配置,由redis自行计算所得。)如果M1所在机器宕机了,那么三个哨兵还剩下2个,S2和S3可以一致认为master宕机,然后选举出一个来执行故障转移。同时3个哨兵的majority是2,所以还剩下的2个哨兵运行着,就可以允许执行故障转移。
这就是经典的sentinel3个节点的集群。节省资源的同时又满足了高可用。
-
在本地做了三个节点:
- copy /opt/module/redis-5.0.7/sentinel.conf 分别copy 到master/slave1/salve2下面
[root@bigdata111 redis-5.0.7]# cp sentinel.conf /usr/local/redis/redis-master/
[root@bigdata111 redis-5.0.7]# cp sentinel.conf /usr/local/redis/redis-slave1/
[root@bigdata111 redis-5.0.7]# cp sentinel.conf /usr/local/redis/redis-slave2
分别修改sentinel.conf 里面的端口号和ip
master中sentinel.conf配置如下
port 26379
sentinel monitor mymaster 192.168.11.111 6382 2
# Default is 3 minutes.这里改为30s
sentinel down-after-milliseconds mymaster 3000
slave1/salve2 也做相应的修改,然后启动三个监听模式:
master 启动如下
[root@bigdata111 redis-master]# ./redis-sentinel sentinel.conf
12292:X 08 Feb 2020 22:09:53.815 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
12292:X 08 Feb 2020 22:09:53.816 # Redis version=5.0.7, bits=64, commit=00000000, modified=0, pid=12292, just started
12292:X 08 Feb 2020 22:09:53.816 # Configuration loaded
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 5.0.7 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 12292
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
12292:X 08 Feb 2020 22:09:53.819 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
12292:X 08 Feb 2020 22:09:53.822 # Sentinel ID is 3e6f9547c5fce162914c73d452e8502f522a3fb6
12292:X 08 Feb 2020 22:09:53.822 # +monitor master mymaster 192.168.11.111 6380 quorum 2
12292:X 08 Feb 2020 22:09:53.823 * +slave slave 192.168.11.111:6381 192.168.11.111 6381 @ mymaster 192.168.11.111 6380
12292:X 08 Feb 2020 22:09:53.825 * +slave slave 192.168.11.111:6382 192.168.11.111 6382 @ mymaster 192.168.11.111 6380
可以看到现在6380 是主,6381、6382是从。但是强行关闭6380的时候
12315:X 08 Feb 2020 22:23:10.590 # +odown master mymaster 192.168.11.111 6380 #quorum 3/2
12315:X 08 Feb 2020 22:23:10.590 # Next failover delay: I will not start a failover before Sat Feb 8 22:29:09 2020
12315:X 08 Feb 2020 22:23:10.684 # +config-update-from sentinel a2771c8de97afa6a51a191a5036560a41330d5af 192.168.11.111 26381 @ mymaster 192.168.11.111 6380
12315:X 08 Feb 2020 22:23:10.684 # +switch-master mymaster 192.168.11.111 6380 192.168.11.111 6382
就会选取6382为master
再次启动6380此时它为从节点:
[root@bigdata111 redis-master]# ./redis-server redis.conf
12477:C 08 Feb 2020 22:27:24.295 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
12477:C 08 Feb 2020 22:27:24.295 # Redis version=5.0.7, bits=64, commit=00000000, modified=0, pid=12477, just started
12477:C 08 Feb 2020 22:27:24.295 # Configuration loaded
[root@bigdata111 redis-master]# ./redis-cli -h 192.168.11.111 -p 6380
192.168.11.111:6380> info replication
# Replication
role:slave
master_host:192.168.11.111
master_port:6382
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:225614
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:8c228f7ac3a3aa41d7be08839f31efa22269a1b3
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:225614
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:209764
repl_backlog_histlen:15851
6382还是主节点master:
192.168.11.111:6382> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.11.111,port=6381,state=online,offset=233535,lag=1
slave1:ip=192.168.11.111,port=6380,state=online,offset=233535,lag=1
master_replid:8c228f7ac3a3aa41d7be08839f31efa22269a1b3
master_replid2:a913a2fca40111e71ba65ea2d9e9b0e0a11b1313
master_repl_offset:233821
second_repl_offset:153464
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:957
repl_backlog_histlen:232865
- 这里需要注意的是当主从关系发生改变的时候,redis.conf里面的配置也会相应发生改变。