redis小秘密

一、redis简单介绍

redis是基于内存的高性能key-value数据库,整个数据库都加载在内存中,速度非常快,单个value的最大限制是1G。redis的list可以用来做双向链表,实现一个高性能轻量级的消息服务,set做高性能tag系统。缺点是都在内存,收到物理内存限制,不能做海量数据读写,只能小数据量。

二、支持数据类型

String

底层用的简单动态字符串SDS:C字符串不记录自身长度,要获取长度,必须遍历整个字符串,时间O(N)。SDS在len属性中记录了本身的长度,复杂度是O(1)。SDS在修改数据时,会先检查空间够不够,不够先扩容到执行修改所需的大小,然后在操作。不会出现缓冲区溢出问题。
1、空间预分配:空间预分配用于优化 SDS 的字符串增长操作:当 SDS 的 API 对一个 SDS 进行修改, 并且需要对 SDS 进行空间扩展的时候,程序不仅会为 SDS 分配修改所必须要的空间,还会 为 SDS 分配额外的未使用空间。
空间预分配用于优化 SDS 的字符串增长操作:当 SDS 的 API 对一个 SDS 进行修改, 并且需要对 SDS 进行空间扩展的时候,程序不仅会为 SDS 分配修改所必须要的空间,还会 为 SDS 分配额外的未使用空间。
其中,额外分配的未使用空间数量由以下公式决定:
如果对 SDS 进行修改之后,SDS 的长度(也即是len属性的值)将小于1MB,
那么程序分配和 len 属性同样大小的未使用空间,这时 SDS len 属性的值将和 free 属性的值相同。举个例子,如果进行修改之后,SDS 的 len 将变成 13 字 节,那么程序也会分配 13 字节的未使用空间,SDS 的 buf 数组的实际长度将变成 13+13+1=27 字节(额外的一字节用于保存空字符)。
 如果对 SDS 进行修改之后,SDS 的长度将大于等于 1MB,那么程序会分配 1MB 的未 使用空间。举个例子,如果进行修改之后,SDS 的 len 将变成 30MB,那么程序会分 配 1MB 的未使用空间,SDS 的 buf 数组的实际长度将为30MB + 1MB +1byte。通过空间预分配策略,Redis 可以减少连续执行字 符串增长操作所需的内存重分配次数.
2、惰性空间释放:惰性空间释放用于优化 SDS 的字符串缩短操作:当 SDS 的 API 需要缩短 SDS 保存的 字符串时,程序并不立即使用内存重分配来回收缩短后多出来的字节,而是使用 free 属性 将这些字节的数量记录起来,并等待将来使用。

List

Hash

set

sortedSet

三、redis持久化

RDB

rdb文件的载入是在服务器启动时自动执行的,载入期间,服务器会一直处于阻塞状态,直到阻塞工作完成为止。所有载入操作没有专门的命令。开启了AOF持久化功能,服务器会优先用AOF还原数据库状态,AOF关闭时,才会载入rdb文件还原数据状态。rdb是一个经过压缩的二进制文件,有多个部分组成。
redis有两个命令来生成RDB文件,save和bgsave。

save:

会阻塞redis服务器进程,知道rdb文件创建完成为止。在阻塞期间,服务器不能处理任何命令请求。此时对于客户端发出的所有请求全部拒绝。

bgsave:

派生一个子进城来创建rdb文件,父进程继续处理请求命令。在bgsave期间,有如下几种情况:
1)客户端发送的save命令会被拒绝,服务器禁止save和bgsave同时执行为了避免父进程和子进程同时执行连个rdbSave调用,防止产生竞争条件。
2)客户端发送的bgsave命令也会被拒绝,因为同时执行两个bgsave命令会产生竞争条件。
3)bgrewriteaof和bgsave不能同时执行,如果bgsave正在执行,那么客户端发送的bgrewriteaof命令会被延迟到bgsave执行完毕之后执行;如果bgrewriteaof正在执行,客户端发送的bgsave会被拒绝。原因是两个都是子进程,不存在任何竞争,完全处于效率方面考虑,同时发出两个子进程,如果两个都同时执行大量的磁盘写入操作,性能不好。

AOF

通过保存redis执行的写命令来记录数据库状态。aof持久化可以通过命令追加、文件写入、文件同步三部分来完成。
1、文件追加:执行完一个写命令,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区(AOF缓冲区)的末尾。
2、文件写入和同步
通过flushappendonlyfile函数决定是否需要将aof_buff缓冲区的内容写入和保存到AOF文件。有三种配置:always、everysec、no
AOF重写:redis可以创建一个新的aof文件来替换旧的aof文件,新旧两个aof文件所保存的数据状态相同,但新aof不会包含冗余命令。读取服务器当是状态的数据,读取键现在的值,然后用一条命令去记录键值对(如果太多,多余一定数据量(默认64),会用多条命令去实现而不是一条命令),代替之前记录这个键值对的多条命令。这就是aof重写原理。
aof重写使用子进程来执行,父进程可以接受客户端请求命令;子进程带有父进程副本,使用子进程而不是线程,避免使用锁的情况下,保证数据安全性。
重写期间数据变化:重写期间,客户端对数据进行了修改。例如:刚开始重写时候,只有k1这一个键,重写完后,数据库中已经有k2,k3了,此时重写的aof和数据状态不一致,为解决这个问题,redis使用了重写缓冲区,这个缓冲区在服务器创建子进程之后开始使用,redis执行完一个写命令后会同时将这个写命令发送给aof缓冲区和aof重写缓冲区。子进程重写aof期间,服务器进程需要做以下三件事:
1)、执行客户度发来的命令;
2)、将执行后的写命令追加到aof缓冲区(aof缓冲区的内容会定期写入和同步到aof文件,对现有aof文件的处理工作正常进行)
3)、将执行后的写命令追加到aof重写缓冲区。
当子进程重写完aof之后,会向主进程发送一个信号,父进程接收到该信号之后,会调用一个信号处理函数,执行以下工作:
1)、将aof重写缓冲区的所有内容写入到新aof文件,到达数据一致
2)、对新aof文件改名,原子地覆盖现有的aof文件,完成新旧两个aof问津的替换。
这个信号处理函数完成之后(这个进程会阻塞主进程),父进程可以像往常一样接受请求命令了。

四、主从复制

老版本2.8前
初次复制:从服务器以前没有复制过主服务器,或者从服务器当前复制的主服务器和上一次的不一样用下面这种方法:
启动时候配置saveof或者直接客户端向一个服务器发送saveof命令,要求从服务器复制主服务器,从服务器首先需要执行数据同步操作,从服务器主要向主服务器发送sycn同步命令,该命令执行步骤:
1)、从服务器向主服务器发送sync命令:
2)、收到sync命令的主服务器执行bgsave,后台生成一个rdb,并使用一个缓冲区记录从现在开始的所有写命令。
3)、当主服务器执行完bgsave后,将生成的rbd文件发送给从服务器,从服务器接收并载入,将状态更新成主服务器一致
4)、主服务器将缓冲区中的所有写命令发给从服务器,从服务器执行这些写命令,让数据恢复成主服务器一致状态。
命令传播:同步完成后,有新的写操作,主服务器会将新的写操作命令发送给从服务器执行。
断线后重新复制:处于命令传播阶段的主从服务器因为断线后,从服务器通过自动重连接重新连上主服务器,并继续服务主服务器。
新版本2.8后,使用psync命令代替sync,具有完成重同步和部分重同步两种模式:
1)、完整重同步同上面初次重同步
2)、部分重同步:用于处理断线后重复制:从服务器重连后,主服务器将断开期间的写命令发给从服务器执行.
部分重同步的实现主要包括以下几个部分:
1)、主服务和从服务器都有复制偏移量:执行复制的双方都会分别维护一个偏移量,主服务器每次向从服务器发送N字节的数据时,将自己的复制偏移量加N,从服务每次接收到主服务器发送的N字节的数据时,将自己的偏移量也加N,两个偏移量一致,就证明状态一致。
2)、主服务器的复制积压缓冲区:主服务器维护的一个规定长度先进先出队列,默认1MB。主服务器进行命令传播时,会将写命令发送给所有从服务器,还会将写命令入队到复制积压缓冲区,因此复制积压缓冲区里面会保存一部分最近传播的写命令,并且缓冲区会为队列中的每个字节记录一个偏移量。当从服务器重新连接到主服务器上时,主服务器通过传过来的偏移量判断偏移量之后的数据还在缓冲区,直接把这部分数据传给从服务器,做一次部分重同步,如果发现积压缓冲区中已经没有该偏移量之后的数据,就做一次完整重同步。
3)、服务器运行id。
每个redis服务器都有一个运行id,当主次服务器进行初次复制时,主服务器给从服务器发送一个运行id,从服务器保存起来,断线后重复制时候,从服务器向主服务器发送之前的主服务id,主服务器判断是当前id,进行部分重同步,如果发现不是当前id,就进行全部重同步。

五、哨兵(sentinel)

redis高可用性解决方案,一个或多个sentinel实例组成的sentinel系统,可以监视任意多个主服务器以及主服务器对应的所有从服务器,并在监视的主服务器下线时候,能自动将该主服务器下的其中一个从服务器设置为新的主服务器继续执行程序。
sentinel会创建两个连向主服务器的网络异步连接:
1、命令连接:用于向主服务器发送命令,并接收命令回复;
2、订阅连接:用于订阅主服务器的频道。
一个sentinel可以通过分析接收到的频道来获知其他sentinel的存在,通过发送频道来让其他sentinel知道自己的存在,用户在使用的时候并不需要提供各个sentinel的地址信息,监视同一个主服务器的多个sentinel可以自动发现对方。sentinel之间通过相互的命令连接来形成以网络。使用命令相连的各个sentinel可以通过发送命令请求来进行信息交换。sentine之间不会建立订阅连接。

主观下线、客观下线

每秒向其他服务发送ping,如果一个实例在某个配置时间里连续向该sentinel发送无效的回复,被标记为主观下线,此时还要判断其他sentinel是否也判断该实例主观下线,如果数量超过配置数据,则把该实例判断为客观下线。

选举领头sentinel

当一个主服务器被判定为客观下线,监视这个服务器的各sentinel会进行协商,选出一个领导sentinel来对该下线实例进行故障迁移,半数以上胜出成为领头sentinel。

故障转移

1、已下线主服务器的下属从服务器中选出一个作为主服务器
2、已下线的其他从服务器改为复制新的主服务器
3、已下线主服务器设置为新的主服务器的从服务器,当旧的这个主服务器重新上线,会成为新主服务器的从服务器。

六、集群

redis的分布式数据库方案,集群通过分片进行数据共享,并提供复制和故障转移功能。
节点a通过meet命令和节点b建立连接,之后节点a通知集群中其他节点也和节点b建立连接。如此彼此都有连接。
集群的整个数据库被分为16384个槽,数据库中每个键都属于这16384个槽的其中一个,集群中的每个节点可以处理0个或最多16384个槽。当数据库中16384个槽都有节点在处理,集群处于上线状态,相反,数据库中其中一个槽没有得到处理,集群处于下线状态。
通过cluster addslot命令可以将一个或者多个槽指派给节点负责。
当客户端向节点发送与数据键有关的命令时,接受命令的节点会计算出命令要处理的数据库键属于那个槽,并检查这个槽是否指派给了自己:

  • 如果键所在的槽正好就指派给了当前节点,节点直接执行这个命令;
  • 如果键所在的槽没有指派给当前节点,节点会向客户端返回一个moved错误,指引客户端转向至正确的节点,并再次发送之前想要执行的命令。一个集群客户端通常会与集群中多个节点创建套接字连接,而所谓的节点转向实际上就是换一个套接字来发送命令,如果客户端尚未与要转向的节点创建套接字连接,那么会先根据moved错误提供的ip和端口号来连接节点,然后再进行转向。

重新分片

redis集群重新分片操作可以将任意数量已经指派给某个节点的槽改为指派给其他节点,并且相关槽所属的键值对也会从源节点移到目标节点。重新分片操作可以在线进行,分片过程中集群不需要下线,源节点和目标节点都可以继续处理命令请求。
迁移过程中问题点:
1、迁移过程中可能一部分键值对还在源节点,一部分键值对已经迁移到目标节点了。当客户端发送向源节点发送一个与数据库键有关的命令时,这个数据库键恰好属于正在被迁移的槽时:
1)源节点会现在自己的数据库里查找指定的键,如果存在,直接执行命令。如果不存在,这个键很可能被迁移到目标节点了,源节点向客户端返回一个ASK错误,指引客户端转到正在倒入槽的目标节点,并再次发送之前想要执行的命令。(客户端收到ask时候,要先向目标节点发送一个asking命令,因为不发送的话,正在迁移槽的目标节点会拒绝执行)。
集群中节点分主节点和从节点,当主节点下线时候,其他节点会选择主节点的其中一个从节点作为新的主节点,对应的从节点修改为新节点的从节点,类似sentinel的情况。选举主节点方法类似哨兵的形式,也是超过一半以上就被选举为新的主节点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值