本套技术专栏是做者(秦凯新)平时工做的总结和升华,并深度整理大量网上资源和专业书籍。经过从真实商业环境抽取案例进行总结和分享,并给出商业应用的调优建议和集群环境容量规划等内容,请持续关注本套博客。QQ邮箱地址:1120746959@qq.com,若有任何学术交流,可随时联系。node
redis其实是个单线程工做模型,其拥有较多的数据结构,并支持丰富的数据操做,redis目前是原生支持cluster模式。若是须要缓存可以支持更复杂的结构和操做,基于以上缘由,选择线上使用Redis会是不错的选择。mysql
Redis高效的缘由:react
1)纯内存操做redis
2)核心是基于非阻塞的IO多路复用机制sql
3)单线程反而避免了多线程的频繁上下文切换问题数据库
mysql单机支撑到2000qps就会出现性能问题了,因此要是你有个系统,高峰期一秒钟过来的请求有1万,那一个mysql单机绝对会死掉。由于 redis单机支撑的并发量轻松一秒几万十几万,支撑高并发so easy。单机承载并发量是mysql单机的几十倍。缓存
使用Redis可能会遇到的问题:网络
(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当前产生的事件,来选择对应的事件处理器来处理。
若是一个客户端跟redis发起链接,此时会产生一个AE_READABLE事件,而后由链接应答处理器来处理跟客户端创建链接,建立客户端对应的socket,同时将这个socket的AE_READABLE事件跟命令请求处理器关联起来。
若是是客户端要链接redis,那么会为socket关联链接应答处理器。
若是是客户端要写数据到redis,那么会为socket关联命令请求处理器。
若是是客户端要从redis读数据,那么会为socket关联命令回复处理器。
建立客户端对应的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事件和命令回复处理器的关联关系。
用内存当缓存。内存是无限的吗?内存是很宝贵并且是有限的,磁盘是廉价并且是大量的。可能一台机器就几十个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优先移除
redis高并发:主从架构,一主多从,通常来讲,不少项目其实就足够了,单主用来写入数据,单机几万QPS,多从用来查询数据,多个从实例能够提供每秒10万的QPS。
redis高并发的同时,还须要容纳大量的数据:一主多从,每一个实例都容纳了完整的数据,好比redis主就10G的内存量,其实你就最对只能容纳10g的数据量。若是你的缓存要容纳的数据量很大,达到了几十g,甚至几百g,或者是几t,那你就须要redis集群,并且用redis集群以后,能够提供可能每秒几十万的读写并发。
redis高可用:若是你作主从架构部署,其实就是加上哨兵就能够了,就能够实现,任何一个实例宕机,自动会进行主备切换。
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能够提升读的吞吐量。
若是采用了主从架构,那么建议必须开启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数据清空故障。
当启动一个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。
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触发增量复制。
master每次接收到写命令以后,先在内部写入数据,而后异步发送给slave node
结合大数据在咱们工业大数据平台的实践,总结成一篇实践指南,方便之后查阅反思,后续我会根据本篇博客进行代码技术实践实现。
凯新云技术社区