redis使用场景以及架构选择

redis使用场景以及架构选择

前言

redis最为一款高性能非关系性数据库,能够工作在内存中,以key value为键值存储,支持多种数据类型以及数据持久化,在企业中可是炙手可热。那么今天我将以三个问题来和大家一起学习redis

  • 为什么需要用redis?使用场景有哪些?
  • redis不能做什么?
  • 确定需求后,如何找到适合自己的redis架构?

一.为什么要用redis? 使用场景有哪些?

1.缓存

对于网站优化而言有一个定律 “让访问和请求尽可能在架构的前方处理”,什么意思呢?就是让请求尽可能不要访问到我们的数据库,因为在高并发场景下一旦大规模请求落到数据库上,数据库的压力是很大的,此时网站访问的速度的瓶颈就会落在数据库上。那么我们应该怎么解决呢?那就是做缓存。

  • 浏览器的缓存
  • cdn的缓存
  • redis的缓存

而redis本身就是工作在内存中,速度是极快的。所以我们常常把需要缓存内容放到redis上,从而加快网站的访问速度,降低数据库的压力。这也是redis的主要使用场景,也是推荐的场景之一,但是redis还有其他使用场景如

2.排行榜

如果使用传统的关系型数据库很难操作,但是利用redis的Sortset数据结构能够非常方便

3.计算器/限速器

利用redis的原子性自增操作,可以统计类似用户点赞数,访问数等。如果采用的是Mysql,频繁的读写会给带来相当大的压力,限速器比较典型的使用场景就是限制某个用户访问api的频率,常用的有秒杀或者抢购时,防止用户疯狂点击带来不必要的压力

4.好友关系

利用集合的一些命令,比如交集,并集,差集,可以方便的处理一些共同好友,共同爱好之类的功能。

5.简单的消息队列

除了redis自身的发布/订阅模式,也可以利用List来实现一个队列的机制,比如到货通知,邮件发送之类的需求,不需要高可靠,但是会带来很大的db压力,此时完全可以用List来是实现异步解耦。

6.session共享

业务的有状态和无状态限制了服务是否可以做集群,如果有状态的web业务,我们必须保证用一个户的请求落在同一个服务器上。否则会出现注册页面后刷新失败的问题。nginx有时候会采用ip hash的负载均衡算法去解决这个问题,但是并不是最优的,会有其他的问题,因此出现了比较中立的方案就是session共享,将redis保存在web集群的相对入口redis中,这样的话无论用户落在哪个机器,都可以在redis中找到对应的session信息,避免了用户因为落在不同机器而频繁登陆的现象。

二.redis不能做什么?

redis虽然有这么多的使用场景,但是并不是万能的。合适的使用会事半功倍,但是如果滥用会导致资源浪费,系统不稳定的问题

1.当redis用于保存用户的重要基本信息时候,虽然能持久化但是并不能保证数据的绝对安全,并且持久化过于频繁会导致redis性能下降,增大redis的压力。总结一点就是如果是数据量大,数据访问频率低的业务都不适合使用redis,数据太大会增加成本(内存增加),并且持久化的文件也增加,访问频率低,把数据保存在内存中也是资源的浪费。

三.Redis架构选择

对于一个合格的运维来说,核心服务一定要做高可用、故障转移,、数据备份等操作,从而避免单点故障带来的毁灭性灾难!

那么所谓的故障大体上包括哪些呢?

1.某个节点的redis进程down了。如进程被手误kill掉

2.某台服务器down掉,如服务器硬件故障,或者电源被拔掉

3.某两个主备节点之间的心跳网络通信中断,网线断了。导致的脑裂

redis也不例外,redis本身可以提供集群、redis主从、redis 哨兵模式,大规模redis场景下还有codis以及Twemproxy等方案来为你的架构保驾护航!我会从基础架构逐渐向推荐的架构介绍。

注意:所有图都按照以下标注

方案1:单机版 redis 无主从,无哨兵

这种仅适用于个人学习,研究。完全用不了生产环境!

方案2:

redis主从架构,单哨兵

工作原理:

1.此种模式master对外提供服务,而我们的slave只负责持久化和同步。并且在master节点上起了一个哨兵进程用来监控master和slave的可用性,当master挂掉后,及时把slave提升到master的角色继续提供服务。此时客户端只需要连接sentinel,sentinel后面的redis master slave角色互换对于用户客户端是透明的。

2.对于redis服务调用方来说,现在需要连接的是sentinel服务,常见的调用过程是client先连接sentinel。然后sentinel去询问当前redis服务哪个是master,哪些是slave。然后再连接相应的redis-server 操作。目前第三方库都实现了这一调用过程。不再需要手动实现。例如node js中的ioredis. php的predis,golang的go-redis/redis,java的jedis等。

缺点:虽然做了主从,也有哨兵监控master和slave 但是哨兵没有做高可用,如果我们的服务器1整台机器挂掉,哨兵无法提供故障转移,这样的架构根本不能用。并且哨兵投票机制就是半数及以上存活才可以对外提供服务本所以至少三个才可以提供完整的故障转移功能,下面我会介绍,

方案3:

这种情况下,假设排除setinel至少三个才能工作的限制,假设可以故障转移,如果服务器1只是和服务器2心跳失败,服务器1的哨兵和master还在保持连接,他认为master没挂,这回出现两个master提供服务,每次来请求,client会连服务器1的哨兵,也会连2的。

半数机制:

因为redis的机制是当50%以上的哨兵进程可以连通并投票选取新的master才能发生主从切换。而发生故障后本架构只剩下一个哨兵,根本没法半数投票,所以redis对外还是无法工作。

redis为什么要有半数以上机制?

因为如果服务器1和服务器2之间仅仅是心跳问题造成心跳失败,就会出现脑裂的问题,master还在提供服务,但是哨兵认为down了,把slave也提升了master,双master会引起数据会不一致,或者客户端连接的sentinel服务异常退出,严重甚至数据会损坏。)

方案4:redis主从+三实例sentinel

最终搭建的架构:

引入了服务器3,并且在3上面又搭建起一个Redis Sentinel进程,现在由三个Sentinel进程来管理两个Redis Server实例。这种场景下,不管是单一进程故障、还是单个机器故障、还是某两个机器网络通信故障,都可以继续对外提供Redis服务。

实际上,如果你的机器比较空闲,当然也可以把服务器3上面也开启一个Redis Server,形成1 master + 2 slave的架构,每个数据都有两个备份,可用性会提升一些。当然也并不是slave越多越好,毕竟主从同步也是需要时间成本的。

在方案4中,一旦服务器1和其他服务器的通信完全中断,那么服务器2和3会将slave切换为master。对于客户端来说,在这么一瞬间会有2个master提供服务,并且一旦网络恢复了,那么所有在中断期间落在服务器1上的新数据都会丢失。

如果想要部分解决这个问题,可以配置Redis Server进程,让其在检测到自己网络有问题的时候,立即停止服务,避免在网络故障期间还有新数据进来(可以参考Redis的min-slaves-to-write和min-slaves-max-lag这两个配置项)。

易用性:像使用单机版Redis一样使用Redis Sentinel

作为服务的提供方,我们总是会讲到用户体验问题。在上述方案当中始终有一个让Client端用的不是那么舒服的地方。对于单机版Redis,Client端直接连接Redis Server,我们只需要给一个ip和port,Client就可以使用我们的服务了。而改造成Sentinel模式之后,Client不得不采用一些支持Sentinel模式的外部依赖包,并且还要修改自己的Redis连接配置,这对于“矫情”的用户来讲显然是不能接收的。有没有办法还是像在使用单机版的Redis那样,只给Client一个固定的ip和port就可以提供服务呢?

 

答案当然是肯定的。这可能就要引入虚拟IP(Virtual IP,VIP),如上图所示。我们可以把虚拟IP指向Redis Server master所在的服务器,在发生Redis主从切换的时候,会触发一个回调脚本,回调脚本中将VIP切换至slave所在的服务器。这样对于Client端来说,他仿佛在使用的依然是一个单机版的高可用Redis服务。

总结

搭建任何一个服务,做到“能用”其实是非常简单的,就像我们运行一个单机版的Redis。不过一旦要做到“高可用”,事情就会变得复杂起来。业务中使用了额外的两台服务器,3个Sentinel进程+1个Slave进程,只是为了保证在那小概率的事故中依然做到服务可用。在实际业务中我们还启用了supervisor做进程监控,一旦进程意外退出,会自动尝试重新启动。

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注

Loading...