Redis高可用,高性能,架构演进史

来自:Java识堂,作者:李立敏

介绍

上个礼拜,我搭建了一个mongo分片集群,发现分布式系统保证高可用和高性能的套路都差不多。高性能就是做分片(可以类比为分库分表,将数据分到不同服务器上),在Kafka中叫分区,在mongodb中叫shard,在HDFS上叫DataNode。而保证高可用的方式就是做交叉备份。然后我很好奇Redis是怎么部署的。

上测试环境查看集群的状态

info replication

输出如下,好吧,没有做高可用,一个master节点开跑。

# Replication
role:master
connected_slaves:0

一个Redis实例其实有很多问题,最起码Redis崩了就没法提供服务了,而且单机能够承载的QPS在上万到几万不等

replication(复制)

如果业务要承载的QPS在几十万,单机是不可能做到的,此时就可以用到复制。做一个主从架构,一主多从,master节点负责写,slave节点负责读,假如说一个节点可以承载的5w读QPS,那么两个节点就可以承载10w的读QPS,水平扩容非常方便。


master节点挂太多slave节点会有性能问题,此时就可以在slave节点上挂slave节点

redis replication的核心机制有如下几点

1.redis采用异步方式复制数据到slave node,不会block master node的正常工作
2.一个master node可以配置多个slave node,slave node也可以连接其他的slave node
3.slave node主要用来进行横向扩容,做读写分离,扩容的slave node可以提高读的吞吐量

主从复制的实现原理

1.slave连接master,发送SYNC命令; 
2.master接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令; 
3.master BGSAVE执行完后,向slave发送快照文件,并在发送期间继续记录被执行的写命令; 
4.slave收到快照文件后丢弃所有旧数据,载入收到的快照; 
5.master快照发送完毕后开始向salve发送缓冲区中的写命令; 
6.slave完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;

sentinel(哨兵)

主从架构有一个缺点就是如果master节点挂了,那么写服务是不可用的,因为slave节点默认是只读的,这时就重启master节点或者重新配置主从,有没有更好的方案呢?类似zookeeper的组件,能自动完成主从切换。在Redis中还真有,就是sentinel节点,当master节点发生故障能自动完成主从切换。


当master节点挂掉时,sentinel将一个slave节点变成maste节点,当原先的master节点可用时,以slave的角色加入集群。


一个高可用的系统是很忌讳有单点问题的。看到没,sentinel就是一个单点,如果sentinel挂了,主从切换也就没人做了。所以应该将sentinel也做成一个集群

哨兵的作用有如下几点

1.集群监控,负责监控redis master和slave进程是否正常工作
2.消息通知,如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
3.故障转移,如果master node挂掉了,会自动转移到slave node上
4.配置中心,客户端初始化时,通过哨兵获得master地址,如果故障转移发生了,通知客户端新的master地址

redis cluster(集群)

主从+哨兵,只能保证Redis的高可用,并不能保证Redis的高性能,因为一个master节点并不能放海量数据,而且单个Redis的实例过大时,会导致rdb文件过大,当执行主从同步时时间过长。

如果想做到高性能该怎么办?分片啊,我一开始就提到了,都是一个套路。redis搞几个节点,每个节点存储一部分数据。可想而之,此时查询和插入都得按照一定的分片策略,总不能查询一个数据把所有的redis节点遍历一遍吧。而这种操作不应该放在客户端,中间件兴起了,常见的有codis,twemproxy


图片来自《Redis 深度历险:核心原理与应用实践》

客户端不连接具体的Redis,而是连接Codis,2个Codis节点保证高可用,和Mycat一个套路。

当然Redis作者也意识到这个问题了,redis cluster应用而生。

没时间写了,下一篇再补充sentinel 和 redis cluster的其他知识吧

参考博客

老钱《Redis 深度历险:核心原理与应用实践》
中华石杉《Java工程师面试突击》

推荐↓↓↓
数据库开发
上一篇:手把手搭建生产可用的Nacos集群 下一篇:你是一直认为MySQL count(1) 比 count(*) 效率高么?