Redis

Redis

Redis是一款基于内存的K-V非关系型数据库.

  • 在6.x之前Redis只使用Worker线程来进行数据的读取,计算,写入,这种只有单线程下的Redis会很大被IO瓶颈所限制还有本身会造成极大的硬件资源浪费。
  • 在6.x之后Redis里面实际上并不是单线程工作,而是分为worker和io thread,其中worker只负责串行提供的计算服务,而io thread只提供数据的并行read和write这样减少了Redis的处理时间的。但是实际上每个线程再对于数据的操作还是串行的单线程。

Redis是二进制安全的(在使用Redis客户端进行数据的操作时均已字节数组作为数据输入或者输出流,在传输数据时,bai保证二进制数据的信息安全,也就du是不被篡改、破译等,如果被zhi攻击,能够及时检测出来。)在Redis中保存的其实是字节并且在所需类型容积不够的情况下会进行自动的扩容。
Redis支持数据一致性,即在Redis中进行数据新增,修改,删除后Redis数据也会做出相应的改变,防止出现脏读的现象,QJM
Redis的服务是无状态的,即针对每个请求服务器都会进行无差别的处理,每个请求中都包含服务器要求的所有请求参数(有状态服务,http的session,会针对每个请求都建立一个session在请求中也会保存sessionID以便可以获取到请求上下文信息)。
Linux提供了一个简单的Tcp连接的命令以供服务器轻量级连接数据库。nc ip port
Redis4.x&6.x对比

IO模型(epoll)

Redis中默认IO模型,相比于poll,epoll效率更高。服务端在收到客户端的TCP连接或者有新数据传输给服务端时,服务端在内核中维护了一个红黑树表用来记录所有有状态的文件描述符(>1连接或者有数据传输的),每次再去遍历所有文件描述符(连接)的时候直接从红黑树中取对应有状态的文件描述符去读取其中的Buffer从而提高读写效率。Poll不会在内核中维护有状态的FD,所以每次遍历的时候会将所有的FD都遍历一遍。selector则效率更低会不断的切换内核态和用户态来占用CPU资源且其中FD连接有上限1024个。
Epoll工作模型

对比memcached

  • Memcached也是基于内存的的非关系型的数据库。memcached可以保存的数据库结构只有String一种,且server端不会提供任何关数据计算的逻辑,所以在接收数据进行保存的时候均是以字符串或者字符json串来进行保存。而在取数的时候总是对某个KEY的Value进行全量IO提取其中的所有数据在Client进行反序列化和计算。 数据向计算移动
  • Redis相比于Memcached虽然也是基于内存的非关系型数据库但是Redis有非常丰富的Value类型,String,List,Set,Zset(有序数组),Hash。而且每种类型都提供的Server本地的算法,减少get时对于IO的压力。计算向数据移动。
    memcached&Redis

Redis数据类型

String(字符串)
  • 字符串:append(追加字符串)、strlen(字符串的字节长度)
    Redis中字符串保存的是字节数组,所以实际上字符串类型不仅仅可以保存字符串还可以保存JSON串,图片,文件即可以通过Redis可以形成一个基于内存的文件系统(FS)。

  • 数值:incr(自加一),decr(自减一)

  • bitmap:其中保存二进制的数值,所以客户端在get时会翻译成ascii码。
    setbit(设置对应字符串指定偏移量上的位),getbit(获取指定Key的ascii码),bitop(对一个或多个二进制位的字符串进行元操作并把结果保存到新字符串上 bitop and/or/xor/not newkey key/[keys])
    bitmap实际上通过位操作来统计矩阵中保存的信息,通过位运算可以更快的得到计算结果并保存。

List(列表)

List中保存的是有序且可能重复的,其底层存储的是双向链表,并且存储有链表的头尾指针,这样可以保证从头和尾双向进行操作。

Hash(哈希表)

Redis的K-V结构中Value保存的是哈希表,也就是针对每个Value都有field来作为哈希的Key。每个 hash 可以存储 232 - 1 键值对。

Set(集合)

Set中保存的是无序的集合,不维护排序也不维护插入顺序。主要用于集合操作但也会产生随机事件。在数量小的时候是使用intTable构成集合,当数据量变大后就会由hashTable来进行存储。
SRANDMEMBER key count:随机取出一个结果集,如果正数则取出不重复的结果集,如果大于memers只会取出所有数据,如果负数则一定会取出固定数量但是可重复的结果集。

ZSet(有序集合)

实际上是Set集合,但是是维护可自定义顺序的集合,物理内存左小右大顺序,不会随着命令改变保存顺序。其底层实际上是由跳跃表实现的,在集合中的每个元素有三层维度(分值,数据,索引(index))该数据类型也支持集合操作交并差,再进行交并差时还会提供对于分值的处理(两分值取Max,Min或者求和,没有指定聚合函数的话默认是取sum)。其底层存储的数据结构为跳跃表实现。
跳跃表(类平衡树)主要有底层全数据元素构成的双向链表以及每个链表元素相同元素组成的子链表构成。但是每个链表元素相同元素的子链表的元素个数是完全随机的(随机早层),但是最大数量不超过64。其中子链表每个index下的都会与相邻最近存在相同参差得元素建立了连接。所以跳跃表查询的时候是从底层全数据构成的双向链表的最高层开始查询若是比该元素小则去一下该层的元素查询。(比目标元素小同层查询,比目标元素大降层查询 )

Redis持久化

Redis持久化有两种方式RDB和AOF:

RDB

该种方式是将当前进程产生的数据保存成二进制压缩文件保存到磁盘中,可以通过手动或者自动的方式出发:

  • 手动触发:
    • save:阻塞当前Redis服务器,知道RDB过程结束,会对服务器和内存造成较大的影响;
    • bgsave:Redis进程会使用fork操作创建出一个子进程负责RDB的过程,完成后自动结束,该种方式只有在fork阶段会造成阻塞;
  • 自动触发:
    • 1)使用save相关配置,如“save m n”。表示m秒内数据集存在n次修改 时,自动触发bgsave。
    • 2)如果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点,更多细节见6.3节介绍的复制原理。
    • 3)执行debug reload命令重新加载Redis时,也会自动触发save操作。
    • 4)默认情况下执行shutdown命令时,如果没有开启AOF持久化功能则 自动执行bgsave。
      RDB文件保存在dir配置指定的目录下,文件名通过dbfilename配 置指定。可以通过执行config set dir{newDir}和config set dbfilename{newFileName}运行期动态执行,当下次运行时RDB文件会保存到新目录。
优点

因为RDB是一个紧凑的二进制文件,代表某个时间段Redis上的快照,非常适合备份或者全量复制,但是因为时间过长所以容易造成Redis服务器阻塞但是从数据恢复速度来说RDF时间远远小于AOF。

缺点

无法做到秒级或者实时的持久化,在不影响Redis的使用情况下频繁的持久化会创建大量子进程占用资源,且Redis不同版本中存在多种格式的RDB文件,不同版本的RDB文件无法做到兼容。
在这里插入图片描述

AOF

AOF方式持久化是里独立日志的方式实时记录每次的命令,重启时再重新执行AOF文件中的命令以达到数据恢复的功能,该种方法主要数据实时持久化问题,目前主流持久化方法。
开启Redis的AOF持久化功能需要配置appendonly yes,默认是不开启的,AOF文件名通过appendfilename配置设定,还可以设定文件路径。AOF的工作流程大概分为四个命令写入(append),文件同步(sync),文件重写(rewrite)、重启加载(load)
AOF工作流程
其中AOF文件写入并不是直接写入到文件磁盘中,而是写入到缓存中避免将压力直接交给磁盘IO,而且Redis还提供多种缓存同步磁盘的策略可以在安全和性能方面自定义平衡。
AOF文件不会一直无限制的增长,而是在文件达到一定大小后会进行文件的重写,重写实际上是对AOF文件的压缩,而能产生压缩的方式有很多:

  • Redis中超时的数据不会再写入文件中;
  • 会排除无效指令 例如set att,set aaa等等;
  • 对于命令进行优化合并,例如ADD a 1,ADD a 2,ADD a 3。会合并成一个命令;但是为了方式单条命令过长溢出缓冲区对于Set,List,Hash,Zset等会以64个元素作为界限;

也可以用其他的方式来出发重写:

  • 手动触发:直接调用bgrewriteaof命令。
  • 自动触发:根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机
    在这里插入图片描述

Redis持久化

Redis过期淘汰制

Redis的过期淘汰制实际上分为两个部分:过期删除,内存淘汰

  • 过期删除:实际上是由定时删除+惰性删除构成,Redis默认是每个100ms就随机抽取一批设置了过期时间的Key进行检查删除(随机抽取减小的CPU单次检查的压力),而惰性删除则是如果该key已经超过过期时间,除非去查询该key否则的话该key会一直停留在内存之中直到定时删除。
  • 内存淘汰:Redis如果存在大量为删除的过期key会造成内存溢出所以针对内存会有一系列策略来保证Redis内存健康:
    • volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
    • volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
    • volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
    • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的).
    • allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
    • no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!

Redis集群

集群同步

数据同步有两种模式强一致性和弱一致性:

  • 强一致性:主节点在有更新数据的命令进来后会同步阻塞将该命令同步到各个从节点,知道主节点收到所有从节点都已经同步成功后才会结束阻塞请求返回,该同步方式会破坏Redis的高可用但是不会造成数据丢失;
  • 弱一致性:主节点对于从节点的数据同步采用异步方式,主节点执行完命令后异步将命令发送到各个从节点后不再阻塞二十直接返回请求,这种方式会造成从节点数据丢失;
    数据一致性

Redis集群有三种模式主从模式,哨兵模式,集群模式

  • 主从模式:Redis节点被主节点(负责读写)和从节点(只负责读),只能存在一个主节点。一个主节点可以绑定多个从节点但是一个从节点只能属于一个主节点,主节点挂了不影响从节点读写且不会重新自助选举,但是Redis将不再支持写数据,从节点挂了不影响读写并且重启后可以从主节点重新同步数据恢复使用。
    从节点在启动或者重启后会向主节点发送SYNC命令,主节点会将这段时间保存的RDB快照和缓存的命令都发给从节点达到数据一致。主从模式不具备高可用
    主从复制模式

  • 哨兵模式:在Redis主从节点之外会增加第三种节点Sentinel(哨兵节点),该节点可以布置成单个也可以布置成Sentinel集群。该节点主要是用来监控和管理目前Redis中所有节点,当有主节点挂掉后哨兵节点会在剩下的所有从节点中主动选举出来,首先修改当前已经变成主节点的从节点配置文件以及账号密码等信息,其次会修改其他从节点的信息变更其从节点中所依赖主节点的信息(slaveof属性会指向新的master)。而原来之前的挂掉的主节点重启后不再担任主节点。
    工作流程:哨兵节点启动后会持续监听Master,slave以及其他哨兵节点的心跳,以每秒一次的频率向其他节点发送ping命令。若是Master没有正常的返回(超时时间也可以设置)Ping心跳则该Master会被这个Sentinel节点标记为主观下线,如果有足够(可以手动设置)的Sentinel节点标记该Master为主观下线则该节点在Sentinel中会被标记为客观下线,一般情况下每个Sentinel每隔10S会向各个节点发送INFO命令来同步信息,当一个Master被标记为客观下线后该发送INFO给Slave消息的时间会从10S变成1s。
    当使用Sentinel模式时,客户端连接的不再是直接连接Master或者Slave节点,而是连接Sentinel节点,通过哨兵节点找到当前Master节点信息。

    • Sentinel选举:
      Sentinel如果要成为集群的话至少要大于等于三个,因为Sentinel选举的最低票数计算公式为(Sentinel节点数/2 + 1)如果节点只有两个的话剩下那个节点永远不会成为主节点。
      Sentinel选举
  • 集群模式:该模式下由多个Master以及下属的Slave构成Redis集群,该种模式下至少由六个节点构成,每个节点之间通过ping-pong来进行通信。该模式下的数据存储使用hash槽来进行数据分片,多个Master之间存储的完全不同的数据,Redisjian将Hash的Key划分为16484个hash槽,每个Master节点映射到不同的hash槽这样Redis集群中的数据可以均匀的存储在节点中。并且在扩容或者故障转移时只需将故障节点的槽位和数据进行转移或者将已分配的槽位进行重新拆分给新的节点就可以达到故障转移和扩容,在过程中不会影响其他的Master节点运行。 该模式主要使用crc16一种校验算法来实现hash。

数据分片

单节点容量优先所以就需要数据分片:
在这里插入图片描述

问题:

  • 缓存穿透问题:
    • 原因:因为缓存中没有高频的请求的数据,所以请求数据的压力全部转移到了数据库导致缓存失效不再分担数据压力,在节点扩容新增节点的时候也会出现这样的问题。因为再使用一致性哈希算法进行节点分片时虽然将数据进行全量重新计算,但是还是会将该节点前后相邻的两个节点上的部分数据进行重新调整所以再调整过程中数据这些数据是无法被hash到的就会造成缓存穿透。
    • 方案:可以使用布隆过滤器来进行筛选是请求数据是否被缓存到,布隆过滤器使用为预算且容量可以达到亿级别。或者从代码层级对无缓存的数据进行手动重新缓存。
  • 缓存雪崩:
    • 原因:因为某种原因造成缓存宕机导致请求压力全部到数据库而导致数据崩溃,或者在大面积更新缓存数据
    • 方案:事前增加备份节点或多节点共存(哨兵模式或Cluster),事中使用本地缓存或者使用熔断器进行降级服务,事后:持久化缓存恢复数据,
  • 缓存DB数据不一致问题:
    • 原因:高并发情况下缓存更新成功但数据库更新失败
    • 方案:再进行数据更新时,使用内存队列,在数据更新之前先将该缓存数据设置过期时间, 如果此时有用到缓存的查询则去检查该缓存的Key过期时间,如果该缓存已经过期则将查询的命令加入到内存队列中,等数据库数据更新完成后再依次将内存队列中同步缓存的请求执行。

Redis集群详解

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值