028-86922220

建站动态

根据您的个性需求进行定制 先人一步 抢占小程序红利时代

Redis高可用心路历程以及多种业务场景下的使用模式-创新互联

Redis高可用心路历程

成都创新互联专注于管城企业网站建设,响应式网站设计,商城网站建设。管城网站建设公司,为管城等地区提供建站服务。全流程定制网站设计,专业设计,全程项目跟踪,成都创新互联专业和态度为您提供的服务1、单节点下的redis

正常业务中数据库承受大量的读写请求,造成数据库压力过大从而导致服务器宕机等情况时有发生,即使通过数据库的横向拓展,通过搭建主从复制实现读写分离或者垂直拓展实现分库分表的策略,虽然都在一定程度上能够缓解单节点数据库服务器的压力,但是随着业务量的持续增大,通过持续投钱买服务器显然并不是合理的方案

为了缓和CPU与内存之间的速度差异,计算机的制造商为CPU增加了缓存,从而缓存的概念深入人心,而java程序员都不陌生的redis,在使用起来既满足我们大部分业务需求但同时也带了部分隐患

热点数据的缓存

redis作为热点数据的缓存实现,客户端请求先去redis查询,redis没有再去数据库查询,再根据结果完成缓存重建,这是最常规的redis缓存使用方式,但是同时也引出了诸多问题

缓存穿透:客户端请求的id在redis缓存中没有,在数据库中也没有,短时间有大量的请求都会到达数据库,数据库会承受巨大的压力甚至可能导致服务器宕机而redis缓存也失去了意义

解决方案:

缓存击穿:某个高并发访问并且缓存重建逻辑复杂的key突然过期,在缓存重建期间大量的请求达到数据库导致服务器存在处理缓慢的情况甚至可能导致服务器宕机

解决方案:

通过对比互斥锁与逻辑过期的解决方案,可以发现其实是在数据的一致性跟服务的可用性之间做抉择,如选择互斥锁,则是牺牲了响应时间,保证了数据的一致性;若选择的事逻辑过期的方案,则是保证了服务的可用性,牺牲的数据的一致性,存在数据的短期不一致的问题

计数器

在当个jvm进程中通常使用JUC(java.util.concurrent)包下的AtomicInteger类使用的cas,避免了使用synchronized带了的性能损耗,但是在分布式集群部署下,多进程的jvm则不能够满足要求。Redis6.0之前只对外提供了单线程服务,即redis的所有单条命令都是原子性的,并且通过redis的单线程,可以将并行的请求转换为串行执行,依托这个特性,可以通过redis incr命令进行一些计数的操作,并且在多进程jvm下可见

常见的业务使用:秒杀业务,比如说某件商品的秒杀,肯定要判断商品的库存,依托每次都去数据库中查询,如果使用悲观锁(synchonized,数据库中查询库存,如果大于0,则执行更新操作)效率一定很低,通过改进乐观锁的sql,在update时set stockNum=stockNum-1 where stockNum>0;这个策略效率肯定比悲观锁要好,但是如果某件商品的库存是1w,同时有100w用户在抢购,岂不是如此大的并发量同时落到数据库中了吗? 所以我们在换种策略,在秒杀活动开始前,提前把商品的库存放入redis缓存中,通过incr -1的命令完成库存的自减,要知道官方提供的数据,redis每秒读11w次,写8.1w次,在库存为0时,对其他请求可以立刻返回失败,其次结合lua脚本,可以同时可以判断用户是否已下单(满足一人一单的需求)

缓存过期时间

使用redis命令时set nx px,可以设置缓存过期时间,通过这种缓存自动过期的策略,可以实现的业务场景有:优惠卷自动过期、订单超时未支付过期、手机验证码失效的场景

但是如果缓存的数据没有设置过期时间,当达到内存阈值瓶颈时,要依托内存淘汰策略去删除key

分布式锁

单节点的redis,独立于多节点部署的jvm进程之外,面对synchronized和ReentrantLock只在同一进程内有效的锁而分布式集群下部署的jvm则失去了对共享资源争夺的互斥性,多节点都能同时获取锁成功,此时需要存在多进程jvm都可见的锁,也就是分布式锁,分布式锁的实现方案业内常见的redis和zookeeper;

redis分布式锁常用的方式,依托string结构,set key value,nx:not exist 不存在则创建,px:设置锁的过期时间避免死锁

存在的问题:

针对上面这些问题,可以用redisson客户端解决这些问题

单节点带来的问题 1. redis单点发生故障,数据丢失,影响整体服务应用

如果部署redis服务器发生故障,如果只使用RDB的持久化策略,可能会丢失最后一次RDB后的数据,并且重启服务器的这段期间,服务都是不可用的状态,况且不清楚服务器宕机的具体原因,单节点下的redis并没有故障自动恢复的能力,导致长时间的服务不可用,此时就需要备份数据,通过冗余数据来保证服务的可用性;

2、单节点redis自身资源有限,无法承载更多资源分配

redis的缓存是基于内存实现的,单节点内存是有限的,如果内存占满了会启用淘汰策略,而这个期间客户端的连接请求都会超时,造成服务的短暂不可用,删除一部分缓存,删除缓存的操作并不是我们通过业务主观的操作,可能导致部分查询缓存的业务失效,从而使大量请求达到数据库;随着业务的发展,数据量的增大,当存在单台服务器的存储上限和算力上限影响业务的正常使用,此时就需要通过一些策略去横向拓展存储和算力

3、并发访问,给服务器主机带来压力,性能瓶颈

客户端的每一个tcp连接都会消耗redis服务器的资源,虽然redis官方号称每秒读11w次,写8.1w次,但是这仅仅是统计了理想状态下的读写请求,并没有其他外力因素,比如说此时主进程需要fork出子进程完成RDB,或者执行rebgwriteaof,对aof文件进行重写等影响redis服务器性能的操作,此时需要通过部署主从节点,对读写请求分开处理,从而提高redis服务器集群的响应能力,提高整体算力,在数据量非常大的情况下,还需要通过搭建分片集群,提高redis服务集群的存储上限

2、主从复制

为了提高redis的并发量,通过搭建redis的主从集群,利用读写分离来提高并发量;通过redis来缓存数据,客户端对redis的操作肯定是读多写少的情况,读操作主从库都可以接收,写操作,首先到主库上执行,在通过主从复制同步给从库

主从复制的作用如何进行主从复制

每台redis服务节点默认的角色都是master,可以登录redis服务端,通过info replication命令查看当前服务节点的主从关系,也可以通过slaveof ip port 主master 设置从节点绑定的主节点

主从复制是通过RBD实现的,分为全量复制增量复制

全量复制:

  1. 从节点执行slaveof master_ip master_port 命令后,slave向master发送psync命令,同时携带runId、offset,每个节点都有自己的runId,第一次进行主从复制发送的runId是自己的runId,offset默认为-1,master接收到psync命令后,发现runId不是自己,则返回fullresync命令,并且携带自身的runId,以及offset(为下一次增量同步做准备)
  2. master执行bgsave命令,fork出子进程将内存中的快照数据保存在RBD文件中,再次期间master接收的写命令会写入到repl_backlog中,在全量复制完成后,在将repl_backlog的数据发送给slave
  3. slave接收到RBD文件后,会先清空本地的数据,然后回放master发送的RDB文件完成数据同步

增量复制:

从节点向master发送psync命令,请求的runId是第一次全量同步时接收master的runId,以及当前同步的offset,master接收到psync命令后,确认runId是自己,就从repl_backlog_buffer(环形缓冲区),该缓冲区中存放了master 的offset,以及诸多slave 的salve_repl_offset,用于保存master与slave之间的数据的差异,master就将这部分差异的数据repl_backlog发送给slave

什么时候进行全量同步主从复制存在的问题

1、搭建主从集群后,Slave节点宕机恢复后可以找master节点同步数据,那master节点宕机怎么办?

2、Master宕机期间,重启数据恢复期间,都不能接收客户端的写请求该怎么办?

3、脑裂以及redis的数据丢失

  1. 异步复制导致的数据丢失
    ​ 因为主从复制是通过bgsave进行的复制是异步的,所以可能有部分数据还没复制到slave,master就宕机了,此时这些部分数据就丢失了

  2. 脑裂导致的数据丢失
    脑裂,主从集群中,master如果网络异常,被哨兵集群判定为客观下线,但是实际上master还运行着,开启选举,将最接近master的slave节点通过slave no one 将slave切换成主节点,其他slave执行slaveof ip port完成主从切换,此时整个集群就会有两个master;
    此时虽然某个slave被切换成了master,但是可能client还没来得及切换到新的master,还继续写向旧master的数据可能也丢失了,因此旧master再次恢复的时候,会被作为一个slave挂到新的master上去,自己的数据会清空,重新从新的master复制数据

3、哨兵集群

使用redis主从集群架构后,实现读写分离,但是不能够保证主节点宕机后依旧能够响应客户端请求,当然我们可以通过人工的方式手动执行 slaveof no one去完成slave切换为master,然后通过slaveof ip port命令去告知其他从节点更换了新的master,但是我们更希望的是提供故障的自动解决,如果由人工完成,则需要增加人力成本,且容易产生人工错误,还会造成一段时间的程序不可用;当master节点异常时自动从多个slave中选举出最接近master节点的新master,redis为我们提供了哨兵集群,保证Redis的高可用,使得系统更加健壮

哨兵的作用集群故障恢复原理

会从slave集群中选择与master数据最接近的slave作为新的master节点,一旦发现master故障,sentinel需要在salve中选择一个作为新的master,选择依据是这样的:

当选出一个新的master后,该如何实现切换呢?

流程如下:

为什么是哨兵集群?哨兵集群带来的问题

主从模式或哨兵模式每个节点存储的数据都是全量的数据,数据量过大时,就需要对存储的数据进行分片后存储到多个redis实例上。此时就要用到Redis Sharding技术。

4、分片集群 为什么要引入分片集群 哨兵集群留下的问题

Redis的master宕机后,在主从切换的过程中,Redis开启了保护机制,禁止一切的写操作,直到选举出新的Redis主节点

海量数据的问题

为了提供主从同步的性能,我们通过不会将的redis的master内存设置得太高,如果内存设置得太高,在一定频率下进行RDB持久化或者多从节点进行全量同步时会有很多进程争夺的磁盘带宽,并且redis的master主节点内存过大还会导致fork出子进程时阻塞的时间过长,此时无法接受客户端的写请求。

但是如果降低master节点的内存上限,此时还有海量数据该如何存储

高并发写的问题

我们通过搭建主从集群、哨兵集群来保证服务的高可用,并且为了适应读多写少的情况,通过读写分离分担master服务器压力,来解决高并发读的问题,并且从节点故障恢复后可以通过主从复制中的全量同步或者增量同步来保证数据的一致性,主节点宕机后通过哨兵集群完成服务的自动故障转移,保证读的可靠性,但是高并发写的问题依旧没有解决

分片集群

其他中间件也是通过主从复制来解决高并发读的问题,通过多主多从来解决高并发写的问题,在redis中提供了分片集群也是以多主多从的形式来解决高并发写的问题

使用分片集群与之前的主从集群、哨兵集群的区别:

分片集群的好处使用建议

主从集群:数据量不大,业务中只需要满足读写分离,并且对服务的可用性不高,允许短暂的服务不可用带来的风险,并且需要手动完成主从切换,则单使用主从集群完全够用

哨兵集群:数据量不大,并且对服务的可用性要求比较高,可以使用主从集群搭配哨兵集群,完成故障的自动转移,主节点不可用时自动完成主从切换

分片集群:主要针对海量数据、高并发、高可用的场景

以上便是Redis在多种业务场景下的使用方案,如有误解,请在评论区指出,谢谢

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


网页题目:Redis高可用心路历程以及多种业务场景下的使用模式-创新互联
路径分享:http://www.tsicrk.com/article/dhecid.html

其他资讯

让你的专属顾问为你服务

3.0662s