Redis学习笔记

 

一、优势和缺点

优点:
 单线程 性能高 抗并发
缺点:
1.缓存和数据库db双写一致性问题
2.缓存击穿
3.缓存雪崩
4.缓存并发竞争问题

二、为什么会这么快

1、 纯内存操作:内存相对于磁盘有天然的性能优势, 类似于 HashMap HashMap 的优势就是查找和操作的时间复杂度都是O(1);
2、 单线程:避免过多的上下文切换开销; 避免同步机制的开销(死锁,锁粒度等问题);简单可维护(作者代码间接性理念)
3、 I/O 多路复用 :基于epoll/select/kqueue等I/O多路复用技术,实现高吞吐的网络I/O
4、 数据结构简单,对数据操作也简单, Redis 中的数据结构是专门进行设计的, 使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样, Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的 时间去移动和请求
见下图:
备注:以上epoll evport kqueue对应不同的操作系统复用函数
4、Redis 底层封装了unix系统的select/epoll/poll等os命令,作为 文件事件分派器,当redis客户端进行读取和写入操作时,网络io会先经过os,os内部会作为 “中介传话者”,告诉redis服务器的有哪些socket需要处理,然后redis底层封装i/o多路复用程序然后分发到不通的 处理器
Redis的执行流程:
1.客户端接收用户的命令请求,比如set key value,对命令请求按照指定的协议格式进行封装
2.客户端和服务端建立socket连接
3.服务端采用的是 io多路复用 ,底层有select或者poll epoll几种,服务端可以同时接受多个客户端发送过来的套接字,会将所有的套接字一个接一个的写入到 文件事件派发器 (相当于一个队列)
4.文件事件派发器 出队列 交给 文件事件处理器 去执行(包括连接 命令执行 命令回复等事件处理器)
5.服务端接收到请求的参数后,进行协议的解析后拿到参数后,封装存储到该redis client对应的query_buf中包括argv数组 此时argv[0]=set argv[1]=key argv[2]=value,argc属性:参数的个数即3 cmd属性:代表这个操作对应的命令即set对应的redis的命令表(相当于定义好的常量)对应的指针
 6.判断cmd属性是否为空,为空即不支持该命令;同时判断cmd指令对应的参数和argc的值是否符合,不符合则说明语法错误
7.当语法校验都成功后,会进行权限,内存是否够用等验证
8.执行命令,同时会 根据是否开启aof操作 ,慢日志操作,主从同步等操作进行相关信息存储
9.最后将命令执行的结果写入到redisClient对应的 回复缓冲区 最后由 命令回复处理器 来将结果返回给客户端比如返回 \n OK!
10.客户端接收到响应数据后进行协议格式解析后返回给用户,即OK

三、数据类型的使用

在redis中key实际是byte[](string),value数据结构有以下几种    
(一)String(value最大为512M)
常规的set/get操作,value可以是String也可以是数字。一般做 一些复杂的计数功能的缓存。
(二)hash
这里value存放的是结构化的对象,方便的就是操作其中的某个字段。博主在做 单点登录 的时候,就是用这种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session的效果。
(三)list
使用List的数据结构,按照 插入顺序排序 ,可以 做简单的 消息队列的功能 。也可以利用lrange命令, 做基于redis的分页功能 ,性能极佳,用户体验好。
例如:redisTemplate.opsForList().range(key,startindex,endstartindex)
(四)set
因为set堆放的是一堆 不重复值 的集合。所以可以做 全局去重的功能,做一些交并差操作 。为什么不用JVM自带的Set进行去重?因为我们的系统一般都是集群部署,使用JVM自带的Set,比较麻烦,难道为了一个做一个全局去重,再起一个公共服务,太麻烦了。
另外,就是利用交集、并集、差集等操作,可以 计算共同喜好,全部的喜好,自己独有的喜好等功能
(五)sorted set( 数据结构:元素个数少于128个并且元素长度小于64字节时使用压缩列表,其他使用 跳跃表
sorted set多了一个权重参数 score, 集合中的元素能够按score进行排列。可以做 排行榜应用,取TOP N操作 。另外,参照另一篇 《分布式之延时任务方案解析》 ,该文指出了sorted set可以用来做 延时任务 。最后一个应用就是可以做 范围查找
备注:
1、以下为高级数据结构(加分项)
HyperLogLog、Geo、Pub/Sub, BloomFilter 1, bloomfilter2 ,RedisSearch,Redis-ML
2、跳跃表:链表上增加

四、过期策略

定期删除+惰性删除
定期:默认100ms随机抽取设置过期时间的key,检查是否过期,则删除
  • 周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度 
  • 特点1:CPU性能占用设置有峰值,检测频度可自定义设置 
  • 特点2:内存压力不是很大,长期占用内存的冷数据会被持续清理 
  • 总结:周期性抽查存储空间 (随机抽查,重点抽查)
  • hz 调大将会提高Redis主动淘汰的频率,如果你的Redis存储中包含很多冷数据占用内存过大的话,可以考虑将这个值调大,但Redis作者建议这个值不要超过100。将这个值调大到100,观察到CPU会增加2%左右,但对冷数据的内存释放速度确实有明显的提高(通过观察keyspace个数和used_memory大小)。 timelimit和server.hz是一个倒数的关系,也就是说hz配置越大,timelimit就越小。换句话说是每秒钟期望的主动淘汰频率越高,则每次淘汰最长占用时间就越短。这里每秒钟的最长淘汰占用时间是固定的250ms(1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/100),而淘汰频率和每次淘汰的最长时间是通过hz参数控制的
 
惰性:如果随机一直不到并且设置了过期时间,则在查询时发现已经过期返回空并删除该key
  • 数据到达过期时间,不做处理。等下次访问该数据时,如果未过期,返回数据 ;发现已过期,删除,返回不存在。 
  • 优点:节约CPU性能,发现必须删除的时候才删除 
  • 缺点:内存压力很大,出现长期占用内存的数据 
  • 总结:用存储空间换取处理器性能(拿空间换时间)
如果定期没有删,并且一直也没有查询,导致很多key:

五、内存淘汰机制

  1. noeviction策略,不进行数据淘汰的策略
  2. allkeys-random 策略,从所有键值对中随机选择并删除数据
  3. allkeys-lru 策略,使用 LRU 算法在所有数据中进行筛选
  4. allkeys-lfu 策略,使用 LFU 算法在所有数据中进行筛选
  5. volatile-ttl 在筛选时,会针对设置了过期时间的键值对,根据过期时间的先后进行删除,越早过期的越先被删除
  6. volatile-random 在设置了过期时间的键值对中,进行随机删除
  7. volatile-lru 会使用 LRU 算法筛选设置了过期时间的键值对  【Renault 默认淘汰策略】
  8. volatile-lfu 会使用 LFU 算法选择设置了过期时间的键值对

六、事务

本质是通过MULTI、EXEC、WATCH等一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
总结说:redis事务就是 一次性顺序性排他性的执行一个队列中的一系列命令。
特性:
1、事务不支持回滚,其中一旦失败,后面继续执行;
2、事务支持一致性、隔离性( 单进程)和持久性( 仅限开启aof 持久化模式, appendfsync参数为 always模型
3、redis命令支持原子性,但是事务不支持原子性
需要支持事务:
1、lua脚本,redis保证一个脚本下的命令一次性执行完毕;( 推荐
2、基于中间标记变量,通过外部标记变量来标识事务是否完成,读取数据时先获取到外部变量标记是否执行完毕;
 

七、高可用机制

  1. 持久化(rdb、aop和两者混合机制(4.0版本之后))
  2. 主从复制(主从和从从模式,主挂,手动将从作为主)
  3. 哨兵机制(自动切换主从服务器,监控) 重点在于高可用,主挂 从主动变主
  4. cluster集群(3.0版本以后,多个主从集群,多个主实现数据分片,16384)  重点着重于可扩展性,单个节点内存不够时, 使用Cluster进行分片存储
RDB(默认)
概念:RDB,redis database,是在 某个时间(默认五分钟)点将数据写入一个 临时文件(快照),持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。
优点: fork一个 单独子进程来进行持久化,主进程不会进行任何IO操作, 保证redis的高性能
缺点: 全量增加,RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。
应用场景:适合 数据要求不严谨的集群,更多适合做冷备份
 
AOF( mysql中binlogapache.log、zookeeper中txn-log
Append-only file,将“ 操作 + 数据”以 格式化指令的方式 追加操作日志文件尾部(只是追加,没有磁盘寻址开销)
执行步骤主要两步:
第一步:redis执行写命令时,根据保存模式( AOF_FSYNC_EVERYSEC)调用系统函数write写入AOF文件中;
第二步:根据刷盘策略,调用系统函数fsync将内存数据刷到磁盘上;
保存模式:
  1. AOF_FSYNC_NO :不保存。 丢失系统上次一次aof之后的数据
  2. AOF_FSYNC_EVERYSEC :每一秒钟保存一次。后台子线程执行不会阻塞,丢失一秒到两秒的数据
  3. AOF_FSYNC_ALWAYS :每执行一个命令保存一次。保存操作未主线程执行,append期间不能接受其他请求,丢失一个命令的时间
刷盘策略(syn配置 ):
  1. 每条指令刷新一次,不会丢失数据
  2. 定时刷新 每秒一次( 丢失一秒数据
  3. 不刷新,根据os机制来操作
 
AOF Rewrite机制:当aof文件大小超过配置后,主进程会fork一个子进程重写当前缓存中的数据指令,相当于aof瘦身版本。例如set a=1,set a=2,rewrite后只会记录最后一次写指令;在此期间的写入信息会存储主进程的一个aof重写缓冲区中,rewrite完成之后主进程在将缓冲区信息写入到新的aof文件中;
aof- rewrite- percentage=2 rewrite的比例 
aof- rewrite-min size=64M rewrite的最小内存大小
备注:当aof文件大小为64M时,触发第一次rewrite,rewrite之后文件大小10M,当aof文件大小继续增大至64M时再次rewrite;当rewrite后大小为40M,继续追加到80M时又一次rewrite;即同时满足aof文件大于64M和重写比例大于原先的两倍
 
优点:
  • 容易阅读和解析,因为文件内容是字符串;
  • 数据完整性更高,可靠性高,如果设置追加file的时间是1s,如果redis发生故障,最多会丢失1s的数据;且如果日志写入不完整支持redis-check-aof来进行日志修复;
  • AOF文件没被rewrite之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的flushall)。
缺点:
AOF文件比RDB文件大,且 恢复速度慢,由于记录了所有数据操作历史记录
 
两者区别:
1、rdb适合做冷备,aop适合做热备
2、rdb恢复更快,因为只是fork一个进程去操作
3、rdb没有aof数据全
混合模式(4.0之后)
redis4.0因为减少aof文件的大小,采取rdb和aof结合的混合模式,即当aof rewrite时,redis先以rdb格式在aof文件中写入一个快照,然后在此期间发生的写操作会append到aof文件后面,因为rdb文件是以二进制压缩形式存储所以aof文件会更小些
 
  • redis主从同步

目的: 为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有N-1个复制品。
形式:只能有一个主服务器,多个从服务器,分别有读写和读的操作
使用场景:集群,读写分离,日志备份,高可用, 更多适合做热备份
原理步骤:
1、 从服务器 配置指向 主服务器ip和端口,启动时会 向主服务器 发送psync命令包,如果是这个slave第一次连接到master,master就会启动一个线程,生成全量 RDB快照,同时把新的写请求都缓存在 内存中, RDB文件生成后,master会将这个 RDB发送给slave的,slave会先写进本地的磁盘,然后加载进内存,最后master会把内存缓存的那些新命名都发给slave。其中传输过程中如果断网或者服务器挂了,会自动重连,并且链接后会把缺少的数据补上的;
2、 主服务器收到 写命令操作时会保存数据快照文件记录,当数据快照文件发生变化时,会同步向从服务器发送通知;
结构:
  • 一主一从 适合并发少的
  • 一主多从 适合读多写少的
  • 一主多从从 树形结构 适合高qps读多写少的(解决master处理所有从节点数据同步和服务到从)
附录: 12
 
  • 哨兵机制

架构:
哨兵至少需要 3 个实例,来保证自己的健壮性。
哨兵 + redis 主从的部署架构,是 不保证数据零丢失的,只能保证 redis 集群的 高可用性。
Redis的哨兵(sentinel) 系统用于管理多个 Redis 服务器,该系统执行以下三个任务:
  • 监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master和Slave是否运作正常。
  • 提醒(Notification):当被监控的某个 Redis出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automatic failover):当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作,它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其他Slave改为复制新的Master; 当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用Master代替失效Master。
备注:
  • 哨兵(sentinel) 是一个 分布式系统,多个哨兵进程之间使用 流言协议(gossipprotocols)来接收关于Master是否下线的信息,并使用 投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master.
  • 主观认为宕机:每个哨兵(sentinel) 会向其它哨兵(sentinel)、master、slave定时发送消息,以确认对方是否”活”着,如果发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂;
  • 客观宕机:当 多个sentinel,都报告某一 master没响应,系统才认为该master"彻底死亡",通过 vote算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置。
 
  • 官方Redis Cluster 方案(服务端路由查询)

分布式数据库:
分布式数据库指把整个数据库按照 分区规则 映射到多个节点上,即把数据划分到多个节点上,每个节点负责数据的一个子集。
 

redis如何分区:

1、集群共有 16384个槽(slot)采用 哈希分区  详情2虚拟槽分区,所有key值根据hash函数 (CRC16[key]&16383)映射到0-16383槽内,集群中每个主节点都会负责一部分槽以及该槽所映射的键值数据
2、集群中所有节点信息(ip,port,slot),都会 定期通过节点之间 Gossip 协议交换更新,所以集群中所有的节点都保存这个其他节点信息
3、当get key时,key不在原先的主节点服务器里,集群内部也会通过 重定向,指向正确的节点上
使用 虚拟槽分区原因在于: 解耦 数据和节点 的关系,节点自身维护槽映射关系,数据 分布式存储
集群缺点
a,键的 批量操作支持有限,比如mset, mget, 多个键映射在不同的槽不支持批量操作( pipeline管道操作
b, 同一节点数据支持事务, 多个节点之间key不支持事务
c,键是数据分区的最小粒度,不能将一个很大的键值对映射到不同的节点
d, 不支持多数据库,只有0,select 0
e,主从复制结构只支持单层结构, 不支持树型结构。  
备注:
  • Redis并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可能会丢失写操作。
  • 集群之间采用异步复制
  • 单个集群下最多的节点数是槽数16384
例如:在某台节点上执行set name =1234
1.节点先计算卡槽位置:slot = CRC16(name) & 16384 ,比如计算的卡槽值为12000
2、发现槽为12000的分在节点2上,这时节点1会执行重定向命令到节点2上
每一个蓝色的圈都代表着一个redis的服务器节点。它们任何两个节点之间都是相互连通的。客户端可以与任何一个节点相连接,然后就可以访问集群中的任何一个节点。对其进行存取和其他操作
 

集群通信:

 1.  节点之间采用 Gossip 协议通信, Gossip 协议就是指节点彼此之间不断通信交换信息
当主从角色变化或新增节点,彼此通过ping/pong 进行通信知道全部节点的最新状态并达到集群同步
常见协议命令有:
  • meet消息:用于通知新节点增加,消息发送者会通知接受者加入到当前集群中,meet消息结束后,接受节点会加入到集群中,并进行周期性的ping/pong信息交换
  • ping消息:集群内 每个节点每秒都会 向其他节点发送ping信息,信息包括 自身和其他节点的状态信息,用于检测其他节点是否在线和其他节点的信息
  • pong消息:当节点接受到meet和ping消息后,作为响应,发送自身和其他节点信息给发送者
  • fail消息:当某个节点发现集群内部某个节点下线时,会向集群内广播一个fail消息
其中消息主要包括消息头,消息体,消息头包含发送者自身信息包括: 节点id,槽映射,节点角色和是否下线信息
 

集群扩容:

准备好新节点,加入集群后,迁移槽和映射的数据

主节点下线:

确定下线节点是否存在槽(是否为主节点),如果有,则先将槽迁移到其他主节点上,保证整个集群槽映射的完整性
如果下线节点为从节点,或者没有槽时,可以通知集群内其他节点下线该节点
 

故障转移:

redis集群实现了高可用,当集群内少量节点出现故障时,通过故障转移可以保证集群正常对外提供服务。
当集群里某个节点出现了问题,redis集群内的 节点通过ping pong消息发现节点是否健康,是否有故障,其实主要环节也包括了 主观下线客观下线
主观下线:指某个节点认为另一个节点不可用,即下线状态,当然这个状态不是最终的故障判定,只能代表这个节点自身的意见,也有可能存在误判;

客观下线

指真正的下线,集群内多个节点都认为该节点不可用,达成共识,将它下线,如果下线的节点为主节点,还要对它进行故障转移
假如节点 a 标记节点 b 为主观下线,一段时间后节点 a 通过消息把节点 b 的状态发到其它节点,当节点 c 接受到消息并解析出消息体时,会发现节点 b 的也发现 fail 状态时,会触发客观下线流程;
当下线为主节点时,此时 redis 集群会统计持有槽的主节点投票数是否达到一半,当下线报告统计数大于一半时,被标记为客观下线状态。

故障恢复:

故障主节点下线后,如果下线节点的是主节点,则需要在它的从节点中选一个替换它,保证集群的高可用;转移过程如下:
1,资格检查:检查该 从节点是否有资格替换故障主节点,如果此从节点与主节点断开过通信,那么当前从节点不具体故障转移;
2,准备选举时间:当从节点符合故障转移资格后, 更新触发故障选举时间,只有到达该时间后才能执行后续流程;
3,发起选举:当到达故障选举时间时,进行选举;
4,选举投票:只有持 有槽的主节点才有票,会处理故障选举消息,投票过程其实是一个领导者选举(选举从节点为领导者)的过程,每个主节点只能投一张票给从节点,当 从节点收集到足够的选票(大于N/2+1)后,触发替换主节点操作,撤销原故障主节点的槽,委派给自己,并广播自己的委派消息,通知集群内所有节点。
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值