什么是Redis?简述其主要特性及其应用场景
Redis 是一个基于C语言开发的开源数据库,与传统数据库不同的是,Redis 的数据是存在内存中的,读写速度非常快,被广泛应用于缓存方向。
Redis 存储的是 KV 键值对数据。为了满足不同的业务场景,Redis 内置了多种数据类型实现,如String、Hash、SortedSet、Bitmap.同时,Redis 还支持事务、持久化、Lua脚本、多种开箱即用的集群方案。
1:特性
-
读写很快:因为是基于内存且读写一次的数据量都不会很大的,所以redis的读写速度很快;
-
事件处理单线程(原子操作):redis的原子性在于其对于事件处理是单线程的,但是6.0之后能够通过配置使得其I/O处理是多线程处理,因此redis不是完全单线程,常说单线程是说单线程保障了redis的原子性,比如删除大量的key、无用连接的释放,持久化的时候fork子线程等就是多线程处理的。
-
数据类型丰富:redis的提供的5种基本数据类型(string,set,zset,list,map)和6.0出的3种新类型(Bitmaps,HyperLogLog,Geospatial)几乎足够百分之90的场景使用
-
支持多种编程语言:这个意思是不管你是写java,c++,python还是php都可以使用redis。
-
可持久化:确实是基于内存的,但是可以通过配置实现数据持久化,Redis支持RDB, AOF等持久化方式,其实持久化并不是什么难得见的特性,但是基于内存的情况下,可持久化就是特性!
-
使用简单:命令一看就懂,一用就会
-
功能丰富:支持Lua脚本,发布订阅,事务,pipeline(流水线)等功能
- 主从复制
- 支持高可用和分布式
2:使用场景
- 做缓存:对于热点数据且不易更改的数据使用redis做缓存
- 定时:对于定时删除的数据,可以使用redis设置其过期时间
- 分布式锁:redis的incr命令可以保证两次的返回值一定是不同的
- 点赞,评论数、排行榜:Redis 利用集合的一些命令,比如求交集、并集、差集等。在微博应用中,每个用户关注的人存在一个集合中,就很容易实现求两个人的共同好友功能。
-
限流
-
消息队列
参考文章
redis的数据结构有哪些,有哪些命令
本小章节中的具体参数的使用可以看文档朋友们,我就是列举一下基本的数据类型以及常用的命令,也没有很详细,够面试使用就好。而且redis的这种基础命令真的很简单很好使用!cao!
1:string:常用的基础命令,其value值可以是字符串、整形、小数类型
- get: 返回key所存储的value值,不存在返回nil,仅仅只针对value是string类型的,不是的话会报错
- set: 若key已包含一个值,则无论其类型如何,都会覆盖该值。在SET操作成功时,将丢弃与key相关联的任何先前生存时间。也就是操作成功之前的过期时间会被重置
- getex: 获取key的值,并可选择设置其过期时间。GETEX类似于GET,但它是一个带有附加选项的写入命令。
- getset: 将键
key
的值设为value
, 并返回键key
在被设置之前的旧值。返回给定键key
的旧值。如果键key
没有旧值, 也即是说, 键key
在被设置之前并不存在, 那么命令返回nil
。当键key
存在但不是字符串类型时, 命令返回一个错误。- append:如果
key
已经存在并且是一个字符串, APPEND 命令将 value 追加到key
原来的值的末尾。如果key
不存在, APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样。- incr:如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误
- incrby:其效果也是在原有的value上进行增加 ,如果想要减少直接 增加一个-1即可
2: list:特点会保存其存储顺序,可以重复,先进先出,和栈类似,但是其尾部也可以输入和输出。
- Lpush:如果 key 不存在,那么在进行 push 操作前会创建一个空列表。如果 key 对应的值不是 list 类型,那么会返回一个错误。可以使用一个命令把多个元素 push 进入列表,只需在命令末尾加上多个指定的参数,从头部插入
- Rpush:如果 key 不存在,那么在进行 push 操作前会创建一个空列表。如果 key 对应的值不是 list 类型,那么会返回一个错误。可以使用一个命令把多个元素 push 进入列表,只需在命令末尾加上多个指定的参数,从尾部插入
- Lpushx:在当 key 存在并且存储着一个 list 类型值的时候,向值 list 的头部插入 value。 与 LPUSH 相反,当 key 不存在的时候不会进行任何操作。
- Rpushx:在当 key 存在并且存储着一个 list 类型值的时候,向值 list 的尾部插入 value。 与 LPUSH 相反,当 key 不存在的时候不会进行任何操作。
- Rpop:用于移除并返回列表
key
的第一个元素。- Lpop:用于移除并返回列表
key
的最后一个元素。- Blpop:命令移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。它是 LPOP 的阻塞版本
- Brpop:命令移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。它是 LPOP 的阻塞版本
- Lrem: 用于从列表 key 中删除前 count 个值等于
element
的元素,具体参数使用看文档
3: set:数据不能重复
- Sadd:命令将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略。
假如集合 key 不存在,则创建一个只包含被添加的元素作为成员的集合。当集合 key 不是集合类型时,返回一个错误。
Spop:命令用于从集合 key中删除并返回一个或多个随机元素。这个命令和 SRANDMEMBER 相似, SRANDMEMBER 只返回随机成员但是不删除这些返回的成员。
Srandmember: 命令仅使用
key
参数,那么随机返回集合key
中的一个或者多个随机元素。多个由count决定,具体看文档Sismember:用于判断元素
member
是否集合key
的成员。Smembers:命令返回存储在
key
中的集合的所有的成员。 不存在的集合被视为空集合Srem:用于在集合中删除指定的元素。如果指定的元素不是集合成员则被忽略。
下面几个命令取交集并集差集的使用场景可以是”查找你和另一个人的公友“,”查询你和他人的共同点赞/喜欢“,”某个场景的个性化推荐“等
Sunion:命令用于返回所有给定集合的并集,对于不存在 key 被当做空集合处理
Sunionstore:命令的功能类似于 SUNION,不同的是不反回结果集,而是存储在指定的一个列表中。如果指定的列表已经存在,则被覆盖。
Sinter:回所有给定集合的成员交集,类似命令Sinterstore
Sdiff:返回第一个集合与其他集合之间的差异,也可以认为说第一个集合中独有的元素。不存在的集合 key 将视为空集,类似命令Sdiffstore
4: zset:数据不能重复,会对数据进行score排序
- Zadd:命令用于将一个或多个 member 元素及其 score 值加入到有序集 key 当中。如果某个 member 已经是有序集的成员,那么更新这个 member 的 score 值,score 值可以是整数值或双精度浮点数,score 可为正也可以为负。
- Zcard:返回有序集的成员个数。
- Zcount:返回有序集
key
中,score
值在min
和max
之间(默认包括score
值等于min
或max
)的成员的数量。- Zincrby:有序集
key
的成员member
的score
值加上增量increment,score
值可以是字符串形式表示的整数值或双精度浮点数。- Zmscore:返回有序集中指定一个或多个成员的
members
的scores,
可用版本>= 6.2.0- Zpopmax:删除并返回最多
count
个有序集合key
中的最高得分的成员,类似的还有Zpopmin获取最小值- Zrange:命令返回有序集中,指定区间内的成员,其中成员的按分数值递增(从小到大)来排序,ZREVRANGE类似,区别是它是从大到小排序
- Zrangebyscore:返回有序集
key
中,所有score
值介于min
和max
之间(包括等于min
或max
)的成员。有序集成员按score
值递增(从小到大)次序排列,也可以指定limit字段限制返回的数量- Zrank:命令返回有序集
key
中成员member
的排名,其中有序集成员按score值从低到高排列。排名从0开始,也就是说,score值最低的成员排名为0
。使用ZREVRANK
命令可以获得成员按score值递增(从高到低)排列的排名。- Zrem:从有序集合
key
中删除指定的成员member
- Zscore:用于返回有序集
key
.中成员member
的分数和set一样,他也有交集并集等的相关操作,比较类似,不写了,想看的自己看文档哈
5: hash
- Hset:命令用于为存储在
key
中的哈希表的field
字段赋值value
。如果哈希表不存在,一个新的哈希表被创建并进行 HSET 操作。如果字段(field
)已经存在于哈希表中,旧值将被覆盖.- Hmset:命令用于同时将多个 field-value (字段-值)对设置到哈希表中,会覆盖
- Hget:命令用于返回哈希表中指定字段
field
的值- Hmget:用于返回哈希表中,一个或多个给定字段(field)的值
- Hdel:删除哈希表 key 中的一个或多个指定字段,不存在的字段将被忽略
- Hexists:命令用于查看哈希表的指定字段
field
是否存在- Hgetall:命令用于返回存储在
key
中的哈希表中所有的域和值- Hincrby:哈希表
key
中的域field
的值加上增量increment
- Hkeys
:
返回存储在key
中哈希表的所有key值- Hlen:命令用于获取哈希表中字段(fields)的数量。
- Hvals: 命令返回哈希表所有域(field)的值
另外还有其他的功能命令比如发布订阅、根据前缀获取所有key,监视一个key等,感兴趣的自己可以看看哈~
Redis为什么这么快? 请列举影响其性能的关键因素
根据Redis官网描述,在理想情况下Redis每秒可以提交一百万次请求,为什么快?
- 基于内存:redis的数据都是基于内存的,CPU不用从磁盘取出和存入从而减少了很多的时间(寻道、旋转延迟、磁盘旋转等),而内存的响应时长大约为100纳秒为磁盘的上千倍。
- 对网路连接使用I/O多路复用:有一套高效的事件处理模型,主要是单线程事件循环和IO多路复用。Redis 将所有产生事件的套接字都放到一个队列里面,以有序、同步、每次一个套接字的方式向文件事件分派器传送套接字,文件事件分派器根据套接字对应的事件选择响应的处理器进行处理,从而实现了高效的网络请求。
- redis的序列化协议和高性能 Redis 协议分析器:Redis 协议很简单性且协议的实现性能可以和二进制协议的实现性能相媲美。
- 数据结构优化: 内置了多种优化后的数据结构实现,性能非常高
影响其性能的关键因素:
- 内存大小/网络:增加机器的内存或者使用 Redis 集群。
- 慢指令:比如当Value 类型为 Set 时,SORT、SUNION/SMEMBERS 操作复杂度分别为 O(N+M*log(M)) 和 O(N)。复杂度增加了很多。处理方式:用其他高效命令代替。比如说,如果你需要返回一个 SET 中的所有成员时,不要使用SMEMBERS 命令,而是要使用 SSCAN 多次迭代返回,避免一次返回大量数据,造成线程阻塞。当你需要执行排序、交集、并集操作时,可以在客户端完成,而不要用 SORT、SUNION、SINTER 这些命令,以免拖慢 Redis
- 返回的数据量很大:优化代码,大数据量除了导致i/o传输慢还可能导致内存的swap操作,内存 swap 是操作系统里将内存数据在内存和磁盘间来回换入和换出的机制,涉及到磁盘的读写,肯定会影响redis的性能
- 过期数据的删除:频繁多次的使用带过期参数的命令,可能会使得同时需要删除大量的过期键。优化代码
参考文章Redis为什么这么快?
解释Redis的持久化机制,包括RDB和AOF的区别与适用场景。
什么是持久化?
将数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)的过程。
持久化的意义?
在于故障恢复,如果没有持久化,redis遇到灾难性故障的话,数据会全部丢失。如果通过持久化将数据拷贝一份到硬盘,然后定期比如同步和备份到一些云存储服务上去,那就可保证数据不丢失全部,可以恢复一部分
redis的持久化有两种:RDB和AOF
RDB持久化
RDB 持久化既可以手动执行,也可以根据服务器配置选项自动执行,该功能可以将某
个时间点上的数据库状态保存到一个RDB文件中,也就是复制某个时刻的redis所有数据到磁盘中保存,新建一个RDB文件。
手动执行:
1:save方法:会阻塞主进程,期间不执行任何的用户请求,直到save完成。如果save的数据量很大,那么在一段时间内用户的请求得不到应答,不推荐使用
2:bgsave方法:如果有子进程,主进程直接返回,否则,由主进程fork一个子进程来处理新建RDB文件,主进程fork完成之后会直接返回, 子进程来新建RDB文件,子进程完成之后会告诉主进程。再此期间任何的save、bgsave、bgrewriteaof命令会被拒绝或者延后,避免资源竞争或消耗性能。
自动执行:
redis自动执行bgsave命令,因为他是异步的,所以可以自动执行。在redis配置文件可以配置执行的条件,当条件满足的时候触发自动执行bgsave命令,默认的触发条件是如下, 满足条件之一即触发自动bgsave
save 900 1 服务器在900秒之内,对数据库进行了至少1次修改。
save 300 10 服务器在300秒之内,对数据库进行了至少10次修改。
save 60 1000 服务器在60秒之内,对数据库进行了至少10000次修改。
用户也可以自己配置条件,redis服务器每隔100毫秒就会检查一次是否满足条件。
AOF持久化
AOF持久化需要手动开启,与RDB通过保存数据库中的键值对来记录数据库状态不同的是,AOF持久化是通过记录redis执行过的写命令来记录数据库的状态的。也就是记录set、zadd等写命令。读命令不需要记录。
AOF持久化功能的实现分为命令追加、文件写入、文件同步三个步骤
- 追加:将一个写命令以固定的协议格式追加到服务器状态缓冲区aof_buf的末尾,AOF数据加载的时候是从头加载的,这个时候缓冲区aof_buf的数据是在内存的,还没有写入AOF文件中,也就是还没有到磁盘中区去。如果每次修改都访问一次磁盘的话严重影响性能。
- 文件写入:服务器每次结束一个事件之前(例如set get操作),都会去判断是否把aof_buf缓冲区的内容写入到aof文件中,那么条件是取决于服务器配置的appendfsync内容。默认为everysec
always数据丢失的最少,但是最耗费性能和时间,everysec最多丢失一秒钟的数据,no最多丢失自上一次同步AOF文件之后的所有内容,因为NO模式的同步是由操作系统决定的,everysec与no的效率相当
redis数据恢复由哪个文件为准呢?
如果开启了AOF持久化,优先使用AOF恢复数据,否则使用RDB恢复数据。
RDB和AOF的区别与适用场景
RDB持久化的优势:
- 快速:RDB通过快照机制实现数据的持久化,是一种非常高效的持久化方式。当发生持久化操作时,Redis会fork出一个子进程来负责将数据写入硬盘,这个过程不会阻塞主进程,保证了Redis的高性能。
- 空间效率高:RDB文件是Redis数据的二进制表示,相比于AOF文件,其占用的磁盘空间更小。这有助于节省存储空间成本。对于大规模数据的恢复速度比AOF快。
RDB持久化的应用场景:
- 数据备份与恢复:RDB持久化可以将Redis的数据保存为快照文件,方便进行数据的备份和恢复。当Redis发生意外情况或重启时,可以通过加载RDB文件来恢复数据。
- 数据迁移与迁移验证:RDB文件可以方便地将数据从一个Redis实例迁移到另一个Redis实例,迁移过程中可以使用RDB文件验证数据的完整性和一致性。
AOF持久化的优势:
- 可靠性高:AOF持久化记录了Redis的操作日志,可以实现更精确的数据恢复。当Redis重启时,通过重新执行AOF日志中的指令,可以将数据恢复到重启前的状态。
- 实时性好:AOF持久化记录了每条写操作指令,可以提供更实时的数据持久化。在默认情况下,Redis每秒将AOF缓冲区中的写入操作同步到AOF文件中,保证数据的实时性。
AOF持久化的应用场景:
- 数据可靠性要求高:AOF持久化可以提供更精确和实时的数据恢复,适用于对数据可靠性要求较高的场景,如金融、电商等行业。
- 数据持久化频率要求高:AOF持久化写入操作日志时,可以调整同步频率,适用于对数据实时性要求较高的场景,如实时统计、在线游戏等。
Redis的主从复制是如何工作的?描述同步和命令传播过程
主从复制就是把数据从redis的主节点复制到另一个redis的从节点中,那肯定是有两台及以上的redis的对吧,那么一台redis已经能够实现数据的读写了,且官方数据也说了redis很快,为什么话要做redis的集群呢?
(1)单个redis存在不稳定性。当redis服务宕机了,就没有可用的服务了。这个时候就没有办法对数据进行读写了
(2)单个redis的读写能力是有限的。小服务没关系,当数据量达到一定的程度,单个redis可能不够用或性能下降。
总结:redis集群是为了强化redis的读写能力
多台redis服务器,往往负责写数据为主节点(master节点),负责读取数据为从节点(slave节点)。负责写数据的redis服务总要以某种方式把新的数据复制(同步)到只读redis中,不然永远读不到新数据,这个集群做了也白做。且master节点总是单向向salve节点同步数据,这个过程称为主从复制
按照复制数据量区分分为:完整重同步和部分重同步,一个命令PSYNC实现。
完整重同步:master节点生成收到从服务器同步命令开始时刻的RDB文件,传送给从服务器并将命令缓冲区的数据也传过去,从服务器通过主服务器的RDB文件和命令缓冲区实现主从一致
部分重同步:部分意味着之前是存在过主从之间的数据同步的,由于某种原因导致中间阶段的数据漏了导致数据不一致,这个时候实行的是部分重同步,部分重同步的原因是完整重同步是需要主节点执行BGSAVE、生成RDB文件、发送RDB文件等且期间都不能执行客户端命令,是个耗时的操作,而某些情况下仅仅只是少部分的数据没有同步上,所以把这部分缺失的数据补上就好了。
什么时候该执行完整重同步还是部分重同步呢?
浅浅的了解一下主从复制相关的服务器存储的数据
主服务器这边记录一个偏移量,offset偏移量记录着我同步给从服务器的数据同步到哪里了,以及一个复制积压缓冲区----固定长度的先进先出队列,主服务器同步数据(每一个修改或使得主从数据不一致的命令)到从服务器时候还会把同步的数据往复制积压缓冲区中写一份。
从服务器也会记录一个offset偏移量表示自己接受到的数据到了哪里,比如主服务器向从服务器同步了33个字节,那么主从服务器的offset数据都会增加33。比对两个offset的数值是否相同可以判断主从服务器的数据知否一致。从服务器还记录主服务器的runid,可以理解为每个redis启动的时候都有一个身份证。
分为两种情况:首次连接和短线重连
1:当从服务器首次与主服务器进行同步时候,这个时候执行的是完整重同步,这个没什么疑问
2:短线重连:从服务器短线期间主服务器还是在执行客户端命令,从服务器连接上了之后会给主服务器发消息,
- 当服务器之前执行过SLAVEOF no one命令 从服务器会给主服务器发 PSYNC ? -1 命令,主动请求主服务器进行完整的重同步
- 其他情况从服务器都是发送 PSYNC runid offset ,runid是第一次连接保存在从服务器的主服务器的id,offset是从服务器当时的接受的数据偏移量,主服务器收到命令之后会判断,
1:从服务器传过来runid是否和主服务器的runid相同,不相同则意味着上一次这个从服务器同步的数据不是我的,这个时候主服务器返回 +FULLRESYNC 新的runid offset, 告诉从服务器我主服务器的新rundi和我主服务器的offset偏移量,准备开始执行完整重同步
2:是同一个runid,这个时候计算从服务器的offset+1偏移量的数据是在复制积压缓冲区中,如果不在,则意味着从服务器断开连接太久了,数据丢失了太多了,复制积压缓冲区的数据即使全部给了从服务器数据也不一致,所以开始完整重同步。
3:是同一个runid且offset+1偏移量的数据是在复制积压缓冲区中,主服务器会告诉从服务开始部分重同步,并且offset+1开始到复制积压缓冲区最后的数据全部发给从服务器。完成主从的数据一致。
因此复制积压缓冲区的大小是否合理关系着完整重同步的发生是否频繁,用平均每秒的写数据量*平均短线重连的时间作为复制积压缓冲区的大小。
命令传播过程
当主从服务器完成同步之后,主从数据一致了,主服务器就会进入命令传播阶段,主服务器的每条会使得主从数据不一致的命令都会发一份给从服务器进行同步,以保持主从数据一致。
但并不是每次主服务器仅仅是把当前修改命令传给从服务器。从服务器默认每1秒会给主服务器发送一个命令用于心跳检测。REPLCONF ACK <replication_offset>,其中replication_offset是从服务器的当前复制偏移量,这有什么作用呢?
- 检测主从服务器的网络连接状态:当主服务器超过1秒没有收到从服务器的的ack命令,那么主服务器就知道主从之间的的连接出现了问题
- 辅助实现min-slaves选项:redis中可以配置min-slaves的数值,例如当主服务器超过多少秒没有收到从服务器的ack信息的时候,就会拒绝给从服务器同步数据,如何判断超过了多少秒就是需要从服务器的ack命令间隔来判断的
- 检测命令丢失:如果因为网络故障导致主从同步数据的时候在传输过程中数据丢失,导致的主从不一致的情况,那么势必主从的offset数值不一致,而从服务器的ACK命令中是有带其offset值得,主服务器就能通过ack的offset值判断下一次给从服务器传送多少数据。所以这种情况下并不是仅仅把修改命令给从服务器,上次因为网路波动丢失的数据也会再次给从服务器。
这个模块还涉及一些其他的东西,写不下了哈哈哈哈哈,一个小题目说太多别人看不下去了~
在Redis主从架构中,如何处理主节点故障?
玩外之意就是专门用于写数据的master服务器挂了怎么办?不能处理命令了不管他?
当你仅仅只是开启了读写分类的redis集群,没有开启什么哨兵(sentinel)模式的情况下,那挂了了就是挂了,其他的从服务器会一直等着主服务器上线从新连接。这个时候你要倒大霉哈哈哈哈
所以我们只需要开启一个哨兵模式的redis监控所有的redis服务器就好了,哨兵模式的redis听名字就知道是个哨兵的作用,说他是个redis是因为他的启动和redis很相似,但是实际上是个哨兵的角色。可以把他理解为专门用户这种处理主节点故障的管理者。 主服务器挂了这个哨兵就会从你的从服务器当中选一个当主服务器。而其他还活着的从服务会成为这个新的主服务器的从节点,而那个挂掉的旧redis如果再次重连,也会成为这个新redis的从服务器。
这个选举的过程是怎样的呢?
1:领头sentinel先删掉所有处于下线状态的从服务器,保证剩余的所有的redis都是可用的
2:再删掉最近5秒内都没有回复过sentinel的从服务器,保证剩余的都是最近成功进行通信过的
3:再删除所有与已下线主服务断开连接超过down-after-milliseconds *10毫秒的服务器,这是保证剩余的服务器的数据都是比较新的,因为我毕竟要选择一个新的主服务器嘛,数据肯定是越新越好啊。down-after-milliseconds指的是主节点下线多长时间算是真的下线
4:之后再根据从服务的优先级进行排序,取优先级最高的,优先级相同的再取offset偏移量最大的,最大意味着数据更全,如果优先级相同offset偏移量也相同,那就取ID最小的从服务器作为主节点,为什么取ID最小的呢?应该是随便吧,最大最小都无所谓,反正偏移量都相同了,我猜的哈~
之后这个新的主服务器就代替了原来的旧主服务器,现在没故障了,新主服务开始处理写命令,故障没有了,嘻嘻开心~
这个地方还涉及到如果多个sentinel哨兵监控的话,还需要在多个哨兵之间选择一个领头sentinel。
主节点挂掉主要是哨兵来管理的,那哨兵挂了岂不是一样没办法故障转移?所以一般都会设置多个哨兵的朋友们~
什么是Redis Sentinel(哨兵)? 它如何实现高可用性?
redis sentinel哨兵是redis的高可用性解决方案,他会监控所有的redis节点,当主节点出现故障的时候自行进行故障的转移。如果你仅仅只使用一个redis sentinel来监视所有的主从服务器也可以实现自动的故障转移,但是redis sentinel也是一个redis服务器,所以当这个redis sentinel出现故障的时候旧没办法进行故障转移了,因此一般redis sentinel都会设置多个,最少3个。挂了一个其他的redis sentinel还能接着用。这样先保证redis sentinel的高可用。redis sentinel的作用是检测其他主从节点,保证redis的高可用。具体是如何实现的呢?
这边以3个redis sentinel 监控1主3从的模式举例
首先我们需要启动4个redis服务器,并且把其他3个服务器设置为另一个的从服务器,完成1主3从的模式。
接着启动3个redis sentinel服务器,启动的方式和正常redis启动差不多,就是命令参数有些许变更,这个要了解的自己去看哈。启动的时候会指定redis sentinel的配置文件,配置文件中会指名你监视的master服务器是哪个。
这个时候3个redis sentinel都会知道对方,并且3个redis sentinel都知道哪个是主服务器,哪些是从服务器。他们是怎么知道的?
1:redis sentinel会每10秒钟向master服务器发送一个info命令,master会返回自己本身的信息以及自己的从服务器信息,比如主从的runid、IP、端口、本身的角色等信息,拿到回复的redis sentinel会记录这个master节点的信息并且会根据返回的从服务器信息去连接从服务器,同时也会每10秒给从服务器发送info信息,从服务器同样也会返回给redis sentinel相应的信息,可能格式和主服务器返回的不完全相同,总是也就是这些信息。这样整个redis sentinel就已经知道了所有服务器的主从关系以及状态
2:redis sentinel同时会订阅master服务器的一个频道“__sentinel__:hello”,并且以每两秒一次的频率向这个频道发送消息:publish __sentinel__:hello <参数1>,<参数2>,xxx,<参数8>。每一个订阅了该频道的都会收到这条消息。参数内容大概为 发送sentinel的IP,发送sentinel的端口号,发送sentinel的运行ID,发送sentinel的当前的纪元,主服务器的名字,主服务器的端口号,主服务器的IP,主服务器的当前纪元。收到这条消息的sentinel会根据内容判断是否丢弃和更新本地的其他数据。这样3个原本并不知道对方的sentinel因为监控了同一个master就知道对方的存在了。
怎样才算是主服务器下线?
1:主观下线
每个sentinel启动的时候都会从配置文件中读取服务器主观下线判断条件。sentinel每秒一次的频率向主主服务器、从服务器、其他在线的sentinel服务器发送ping命令,以获得他的信息和状态。当主服务器有down-after-milliseconds毫秒内没有进行回复的话,该sentinel主观上认为该服务已经下线了,这个时候会更新本地对于该服务的状态。并且会告诉其他sentinel服务器我认为这个服务器已经下线了。
2:客观下线
当一个sentinel将一个主服务器判断为主管下线之后为了确认这个主服务器是否真的下线了,他会去询问其他sentinel,看他们是否也认为这个主服务器已经下线,其他sentinel会返回这个主服务器再本sentinel的状态,收到这个回答之后,sentinel会把其他sentinel返回对于这个主服务器的状态记录下来,如果判定有一半以上的sentinel认为这个主服务已经下线了,哪个这个时候大家都会客观的认为主服务器客观下线了。准备开始故障转移
故障转移步骤
1:选取领头sentinel
并不是所有在线的sentinel进行从服务的投票,而是选取一个领头sentinel,由它一个人来进行投票。选取领头sentinel的规则是跟简单,当sentinel认为主服务器客观下线之后,会向其他的sentinel发出投票邀请,邀请他们投票自己为领头sentinel,每个sentinel只能投票一次。谁的投票邀请先到达,就投谁为领头sentinel,投过一次之后其他sentinel的投票邀请都会被拒绝。当某个sentinel由超过一半的票时候,他就会成为领头sentinel。
2:领头sentinel选择一个新的master---上一个写过了
1:领头sentinel先删掉所有处于下线状态的从服务器,保证剩余的所有的redis都是可用的
2:再删掉最近5秒内都没有回复过sentinel的从服务器,保证剩余的都是最近成功进行通信过的
3:再删除所有与已下线主服务断开连接超过down-after-milliseconds *10毫秒的服务器,这是保证剩余的服务器的数据都是比较新的,因为我毕竟要选择一个新的主服务器嘛,数据肯定是越新越好啊。down-after-milliseconds指的是主节点下线多长时间算是真的下线
4:之后再根据从服务的优先级进行排序,取优先级最高的,优先级相同的再取offset偏移量最大的,最大意味着数据更全,如果优先级相同offset偏移量也相同,那就取ID最小的从服务器作为主节点,为什么取ID最小的呢?应该是随便吧,最大最小都无所谓,反正偏移量都相同了,我猜的哈~
3:通知其他sentinel,并让其他从服务器变更主服务器目标
向主服务器发送SLAVEOF no one 命令,并且每一秒(平时是十秒的)向他发送info命令看他的角色是否变成了master,如果身份变更完成,sentinel会让其他的从服务器变更复制目标。也就是变更他们的master信息。并将旧的服务器变成新的从服务器复制这个新的master
故障转移完成!
感觉内容写的有点长 我换个文档写嘻嘻