Redis内核原理及读写一致企业级架构深刻剖析1-综合组件环境实战

本套技术专栏是做者(秦凯新)平时工做的总结和升华,并深度整理大量网上资源和专业书籍。经过从真实商业环境抽取案例进行总结和分享,并给出商业应用的调优建议和集群环境容量规划等内容,请持续关注本套博客。QQ邮箱地址:1120746959@qq.com,若有任何学术交流,可随时联系。node

1 Redis 工做模型

redis其实是个单线程工做模型,其拥有较多的数据结构,并支持丰富的数据操做,redis目前是原生支持cluster模式。若是须要缓存可以支持更复杂的结构和操做,基于以上缘由,选择线上使用Redis会是不错的选择。mysql

1.1 Redis 高效的缘由:

  • Redis高效的缘由:react

    1)纯内存操做redis

    2)核心是基于非阻塞的IO多路复用机制sql

    3)单线程反而避免了多线程的频繁上下文切换问题数据库

  • mysql单机支撑到2000qps就会出现性能问题了,因此要是你有个系统,高峰期一秒钟过来的请求有1万,那一个mysql单机绝对会死掉。由于 redis单机支撑的并发量轻松一秒几万十几万,支撑高并发so easy。单机承载并发量是mysql单机的几十倍。缓存

  • 使用Redis可能会遇到的问题:网络

    • 缓存与数据库双写不一致
    • 缓存雪崩
    • 缓存穿透
    • 缓存并发竞争

1.2 文件事件处理流程

  • (1) redis基于reactor模式开发了网络事件处理器,这个处理器叫作文件事件处理器,file event handler。这个文件事件处理器,是单线程的,redis才叫作单线程的模型,采用IO多路复用机制同时监听多个socket,根据socket上的事件来选择对应的事件处理器来处理这个事件。数据结构

  • (2) 文件事件处理器的结构包含4个部分:多个socket,IO多路复用程序,文件事件分派器,事件处理器(命令请求处理器、命令回复处理器、链接应答处理器,等等)。多线程

  • (3) 多个socket可能并发的产生不一样的操做,每一个操做对应不一样的文件事件,可是IO多路复用程序会监听多个socket,可是会将socket放入一个队列中排队,每次从队列中取出一个socket给事件分派器,事件分派器把socket给对应的事件处理器。

  • (4) 一个socket的事件处理完以后,IO多路复用程序才会将队列中的下一个socket给事件分派器。文件事件分派器会根据每一个socket当前产生的事件,来选择对应的事件处理器来处理。

1.3 文件事件

  • 若是一个客户端跟redis发起链接,此时会产生一个AE_READABLE事件,而后由链接应答处理器来处理跟客户端创建链接,建立客户端对应的socket,同时将这个socket的AE_READABLE事件跟命令请求处理器关联起来。

  • 若是是客户端要链接redis,那么会为socket关联链接应答处理器。

  • 若是是客户端要写数据到redis,那么会为socket关联命令请求处理器。

  • 若是是客户端要从redis读数据,那么会为socket关联命令回复处理器。

1.4 客户端与redis通讯流程

  • 建立客户端对应的socket

    在redis启动初始化的时候,redis会将链接应答处理器跟AE_READABLE事件关联起来,接着若是一个客户端跟redis发起链接,此时会产生一个AE_READABLE事件,而后由链接应答处理器来处理跟客户端创建链接,建立客户端对应的socket,同时将这个socket的AE_READABLE事件跟命令请求处理器关联起来。

  • 请求处理,socket产生一个AE_READABLE事件

    当客户端向redis发起请求的时候(不论是读请求仍是写请求,都同样),首先就会在socket产生一个AE_READABLE事件,而后由对应的命令请求处理器来处理。这个命令请求处理器就会从socket中读取请求相关数据,而后进行执行和处理。

  • 客户端这读取响应数据

    接着redis这边准备好了给客户端的响应数据以后,就会将socket的AE_WRITABLE事件跟命令回复处理器关联起来,当客户端这边准备好读取响应数据时,就会在socket上产生一个AE_WRITABLE事件,会由对应的命令回复处理器来处理,就是将准备好的响应数据写入socket,供客户端来读取。

  • 命令回复处理器写完以后,就会删除这个socket的AE_WRITABLE事件和命令回复处理器的关联关系。

2 redis 过时策略及内存淘汰机制

  • 用内存当缓存。内存是无限的吗?内存是很宝贵并且是有限的,磁盘是廉价并且是大量的。可能一台机器就几十个G的内存,可是能够有几个T的硬盘空间。redis主要是基于内存来进行高性能、高并发的读写操做的。

    set key value 过时时间(1小时)
      set进去的key,1小时以后就没了,就失效了,可是数据明明都过时了,怎么还占用着内存呢?
    复制代码
  • 按期删除+惰性删除

    所谓按期删除,指的是redis默认是每隔100ms就随机抽取一些设置了过时时间的key,检查其是否过时,若是过时就删除。假设redis里放了10万个key,都设置了过时时间,你每隔几百毫秒,就检查10万个key,那redis基本上就死了,cpu负载会很高的,消耗在你的检查过时key上了。注意,这里可不是每隔100ms就遍历全部的设置过时时间的key,那样就是一场性能上的灾难。实际上redis是每隔100ms随机抽取一些key来检查和删除的。

    所谓惰性删除了,指的是在你获取某个key的时候,redis会检查一下 ,这个key若是设置了过时时间那么是否过时了?若是过时了此时就会删除,不会给你返回任何东西。

    若是按期删除漏掉了不少过时key,而后你也没及时去查,也就没走惰性删除,此时会怎么样?若是大量过时key堆积在内存里,致使redis内存块耗尽了,也会出现严重的线上事故。

  • 内存淘汰

    若是redis的内存占用过多的时候,此时会进行内存淘汰,有以下一些策略:

    redis 10个key,如今已经满了,redis须要删除掉5个key
      1个key,最近1分钟被查询了100次
      1个key,最近10分钟被查询了50次
      1个key,最近1个小时倍查询了1次
    复制代码

    1)noeviction:当内存不足以容纳新写入数据时,新写入操做会报错。

    2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最经常使用的)

    3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key,这个通常没人用吧,为啥要随机,确定是把最近最少使用的key给干掉啊。

    4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过时时间的键空间中,移除最近最少使用的key(这个通常不太合适)

    5)volatile-random:当内存不足以容纳新写入数据时,在设置了过时时间的键空间中,随机移除某个key

    6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过时时间的键空间中,有更早过时时间的key优先移除

3 redis 高并发和高可用企业级实践

  • redis高并发:主从架构,一主多从,通常来讲,不少项目其实就足够了,单主用来写入数据,单机几万QPS,多从用来查询数据,多个从实例能够提供每秒10万的QPS。

    redis高并发的同时,还须要容纳大量的数据:一主多从,每一个实例都容纳了完整的数据,好比redis主就10G的内存量,其实你就最对只能容纳10g的数据量。若是你的缓存要容纳的数据量很大,达到了几十g,甚至几百g,或者是几t,那你就须要redis集群,并且用redis集群以后,能够提供可能每秒几十万的读写并发。

  • redis高可用:若是你作主从架构部署,其实就是加上哨兵就能够了,就能够实现,任何一个实例宕机,自动会进行主备切换。

4 redis replication基本原理

  • redis采用异步方式复制数据到slave节点,不过redis 2.8开始, slave node会周期性地确认本身每次复制的数据量

  • 一个master node是能够配置多个slave node的

  • slave node也能够链接其余的slave node

  • slave node作复制的时候,是不会阻塞master node正常工做的

  • slave node在作复制的时候,也不会block对本身的查询操做,它会用旧的数据集来提供服务; 可是复制完成的时候,须要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了

  • slave node主要用来进行横向扩容,作读写分离,扩容的slave node能够提升读的吞吐量。

5 Redis数据持久性重要性

5.1 数据丢失风险

  • 若是采用了主从架构,那么建议必须开启master node的持久化,不建议用slave node做为master node的数据热备,由于那样的话,若是你关掉master的持久化,可能在master宕机重启的时候数据是空的,而后可能一通过复制,salve node数据也丢了。

    master -> RDB和AOF都关闭了 -> 所有在内存中

    master宕机,重启,是没有本地数据能够恢复的,而后就会直接认为本身IDE数据是空的

    master就会将空的数据集同步到slave上去,全部slave的数据所有清空。

    100%的数据丢失,所以master节点,必需要使用持久化机制。

  • 采用高可用机制也是有风险的,slave node能够自动接管master node,可是也可能sentinal尚未检测到master failure,master node就自动重启了,仍是可能致使上面的全部slave node数据清空故障。

5.2 主从架构和断点续传原理

  • 当启动一个slave node的时候,它会发送一个PSYNC命令给master node。若是这是slave node从新链接master node,那么master node仅仅会复制给slave部分缺乏的数据; 不然若是是slave node第一次链接master node,那么会触发一次full resynchronization

    开始full resynchronization的时候,master会启动一个后台线程,开始生成一份RDB快照文件,同时还会将从客户端收到的全部写命令缓存在内存中。RDB文件生成完毕以后,master会将这个RDB 发送给slave,slave会先写入本地磁盘,而后再从本地磁盘加载到内存中。而后master会将内存中缓存的写命令发送给slave,slave也会同步这些数据。

    slave node若是跟master node有网络故障,断开了链接,会自动重连。master若是发现有多个slave node都来从新链接,仅仅会启动一个rdb save操做,用一份数据服务全部slave node。

  • 从redis 2.8开始,就支持主从复制的断点续传,若是主从复制过程当中,网络链接断掉了,那么能够接着上次复制的地方,继续复制下去,而不是从头开始复制一份

    master node会在内存中常见一个backlog,master和slave都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。若是master和slave网络链接断掉了,slave会让master从上次的replica offset开始继续复制

    可是若是没有找到对应的offset,那么就会执行一次resynchronization

  • 无磁盘化复制,master在内存中直接建立rdb,而后发送给slave,不会在本身本地落地磁盘了,

    repl-diskless-sync
    repl-diskless-sync-delay,等待必定时长再开始复制,由于要等更多slave从新链接过来
    复制代码
  • 过时key处理

    slave不会过时key,只会等待master过时key。若是master过时了一个key,或者经过LRU淘汰了一个key,那么会模拟一条del命令发送给slave。

5 Redis主从复制原理深刻剖析

5.1 完整流程

  • slave node启动,仅仅保存master node的信息,包括master node的host和ip,可是复制流程没开始时, master host和ip是从哪儿来的,是在redis.conf里面的slaveof配置的。
  • slave node内部有个定时任务,每秒检查是否有新的master node要链接和复制,若是发现,就跟master node创建socket网络链接
  • slave node发送ping命令给master node
  • 口令认证,若是master设置了requirepass,那么salve node必须发送masterauth的口令过去进行认证
  • master node第一次执行全量复制,将全部数据发给slave node
  • master node后续持续将写命令,异步复制给slave node

5.2 数据同步

  • master和slave都会维护一个offset

    master会在自身不断累加offset,slave也会在自身不断累加offset slave每秒都会上报本身的offset给master,同时master也会保存每一个slave的offset

    这个倒不是说特定就用在全量复制的,主要是master和slave都要知道各自的数据的offset,才能知道互相之间的数据不一致的状况。

  • backlog

    master node有一个backlog,默认是1MB大小

    master node给slave node复制数据时,也会将数据在backlog中同步写一份

    backlog主要是用来作全量复制中断后增量复制的

  • master run id

    info server,能够看到master run id,若是根据host+ip定位master node,是不靠谱的,若是master node重启或者数据出现了变化,那么slave node应该根据不一样的run id区分,run id不一样就作全量复制 若是须要不更改run id重启redis,可使用redis-cli debug reload命令

  • psync

    从节点使用psync从master node进行复制,psync runid offset

    master node会根据自身的状况返回响应信息,也多是FULLRESYNC runid offset触发全量复制,多是CONTINUE触发增量复制。

5.3 全量复制

  • master执行bgsave,在本地生成一份rdb快照文件
  • master node将rdb快照文件发送给salve node,若是rdb复制时间超过60秒(repl-timeout),那么slave node就会认为复制失败,能够适当调节大这个参数
  • 对于千兆网卡的机器,通常每秒传输100MB,6G文件,极可能超过60s
  • master node在生成rdb时,会将全部新的写命令缓存在内存中,在salve node保存了rdb以后,再将新的写命令复制给salve node
  • client-output-buffer-limit slave 256MB 64MB 60,若是在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,那么中止复制,复制失败
  • slave node接收到rdb以后,清空本身的旧数据,而后从新加载rdb到本身的内存中,同时基于旧的数据版本对外提供服务
  • 若是slave node开启了AOF,那么会当即执行BGREWRITEAOF,重写AOF
  • rdb生成、rdb经过网络拷贝、slave旧数据的清理、slave of rewrite,很耗费时间,若是复制的数据量在4G~6G之间,那么极可能全量复制时间消耗到1分半到2分钟。

5.4 增量复制

  • 若是全量复制过程当中,master-slave网络链接断掉,那么salve从新链接master时,会触发增量复制
  • master直接从本身的backlog中获取部分丢失的数据,发送给slave node,默认backlog就是1MB
  • msater就是根据slave发送的psync中的offset来从backlog中获取数据的。

5.5 heartbeat

  • 主从节点互相都会发送heartbeat信息,master默认每隔10秒发送一次heartbeat,salve node每隔1秒发送一个heartbeat。

5.6 异步复制

master每次接收到写命令以后,先在内部写入数据,而后异步发送给slave node

6 总结

结合大数据在咱们工业大数据平台的实践,总结成一篇实践指南,方便之后查阅反思,后续我会根据本篇博客进行代码技术实践实现。

凯新云技术社区

相关文章
相关标签/搜索