redis

115 阅读14分钟

常用五种数据

  1. 字符串String
  2. 列表List
  3. 哈希Hash
  4. 集合Set
  5. 有序集合Zset

底层结构

i6448038.github.io/2019/12/01/… *

模式

单机版、主从复制、哨兵、以及集群模式 juejin.cn/post/686363…

主从模式

原理

主从的原理还算是比较简单的,一主多从,主数据库(master)可以读也可以写(read/write),从数据库仅读(only read) 。 主从模式一般实现读写分离主数据库仅写(only write) ,减轻主数据库的压力 当开启主从模式的时候,他的具体工作机制如下:

全量复制

  1. Redis 从服务器会向主服务器发送一个 SYNC 命令
  2. 主服务器开始在后台保存快照执行 BGSAVE 命令
  3. 同时还会将保存快照期间接收到的所有写命令缓存起来。
  4. 快照保存完成后,主服务器将快照文件和所有缓存的写命令发送给从服务器,从服务器接收到这些数据后进行加载,这样就完成了全量复制。 部分复制
    在全量复制之后,主服务器会将后续接收到的所有写命令都发送给从服务器,从服务器接收并执行这些命令,以保持和主服务器的数据一致。这个过程叫做部分复制。

哨兵模式

在主从的基础上,实现哨兵模式就是实现了对主从的监控与自动故障转移

为啥抗并发

1.redis基于内存 读写速度快

2.redis是单线程,省去了切换线程的时间

3.redis是多路IO复用可以处理并发非阻塞IO内部采用epoll epoll中读写关闭连接都转化为事件 、

单进程单线程优势

代码更清晰,处理逻辑更简单

不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗

不存在多进程或者多线程导致的切换而消耗CPU

redis 过期key删除策略 1 定时删除 2 惰性删除 3 定期删除

二、RDB机制

RDB其实就是把数据以快照的形式保存在磁盘上。什么是快照呢,你可以理解成把当前时刻的数据拍成一张照片保存下来。

RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。也是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。

在我们安装了redis之后,所有的配置都是在redis.conf文件中,里面保存了RDB和AOF两种持久化机制的各种配置。

既然RDB机制是通过把某个时刻的所有数据生成一个快照来保存,那么就应该有一种触发机制,是实现这个过程。对于RDB来说,提供了三种机制:save、bgsave、自动化。我们分别来看一下

1、save触发方式

该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。具体流程如下:

转存失败,建议直接上传图片文件

执行完成时候如果存在老的RDB文件,就把新的替代掉旧的。我们的客户端可能都是几万或者是几十万,这种方式显然不可取。

2、bgsave触发方式

执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。具体流程如下:

转存失败,建议直接上传图片文件

具体操作是Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。基本上 Redis 内部所有的RDB操作都是采用 bgsave 命令。

3、自动触发

自动触发是由我们的配置文件来完成的。在redis.conf配置文件中,里面有如下配置,我们可以去设置:

①save:这里是用来配置触发 Redis的 RDB 持久化条件,也就是什么时候将内存中的数据保存到硬盘。比如“save m n”。表示m秒内数据集存在n次修改时,自动触发bgsave。

默认如下配置:

#表示900 秒内如果至少有 1 个 key 的值变化,则保存save 900 1#表示300 秒内如果至少有 10 个 key 的值变化,则保存save 300 10#表示60 秒内如果至少有 10000 个 key 的值变化,则保存save 60 10000

不需要持久化,那么你可以注释掉所有的 save 行来停用保存功能。

②stop-writes-on-bgsave-error :默认值为yes。当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了

③rdbcompression ;默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。

④rdbchecksum :默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。

⑤dbfilename :设置快照的文件名,默认是 dump.rdb

⑥dir:设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。

我们可以修改这些配置来实现我们想要的效果。因为第三种方式是配置的,所以我们对前两种进行一个对比:

转存失败,建议直接上传图片文件

4、RDB 的优势和劣势

①、优势

(1)RDB文件紧凑,全量备份,非常适合用于进行备份和灾难恢复。

(2)生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。

(3)RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

②、劣势

RDB快照是一次全量备份,存储的是内存数据的二进制序列化形式,存储上非常紧凑。当进行快照持久化时,会开启一个子进程专门负责快照持久化,子进程会拥有父进程的内存数据,父进程修改内存子进程不会反应出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据。

三、AOF机制

全量备份总是耗时的,有时候我们提供一种更加高效的方式AOF,工作机制很简单,redis会将每一个收到的写命令都通过write函数追加到文件中。通俗的理解就是日志记录。

1、持久化原理

他的原理看下面这张图:

转存失败,建议直接上传图片文件

每当有一个写命令过来时,就直接保存在我们的AOF文件中。

2、文件重写原理

AOF的方式也同时带来了另一个问题。持久化文件会变的越来越大。为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。将内存中的数据以命令的方式保存到临时文件中,同时会fork出一条新进程来将文件重写。

转存失败,建议直接上传图片文件

重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。

3、AOF也有三种触发机制

(1)每修改同步always:同步持久化 每次发生数据变更会被立即记录到磁盘 性能较差但数据完整性比较好

(2)每秒同步everysec:异步操作,每秒记录 如果一秒内宕机,有数据丢失

(3)不同no:从不同步

转存失败,建议直接上传图片文件

AOF文件有错位,redis是启动不起来的 用 redis-check-aof 去修复

redis 4.0 提供了 AOF + RDB方案 (aof-use-rdb-preamble yes),就是再两次快照之间用AOF,快照完成 删除AOF。     

redis集群

1 首先,我们把全量的缓存空间当做一个环形存储结构。环形空间总共分成2^32个缓存区,在Redis中则是把缓存key分配到16384个slot

2.每一个缓存key都可以通过Hash算法转化为一个32位的二进制数,也就对应着环形空间的某一个缓存区。我们把所有的缓存key映射到环 形空间的不同位置。

redis zset  底层是跳表和压缩表

 跳表的高度是 log2n ,n 是跳表中元素的个数就是zset的结合个数

跳表的时间复杂度是 O(logn)

跳表底层:www.cnblogs.com/wuyizuokan/…

(1) 跳表是可以实现二分查找的有序链表;

(2)每个元素插入时随机生成它的level;

(3)最低层包含所有的元素;

(4)如果一个元素出现在level(x),那么它肯定出现在x以下的level中;

(5)每个索引节点包含两个指针,一个向下,一个向右;

(6)跳表查询、插入、删除的时间复杂度为O(log n),与衡二叉树接

redis 字符串 

我们看上面对于 SDS 数据类型的定义:

  1、len 保存了SDS保存字符串的长度

  2、buf[] 数组用来保存字符串的每个元素

  3、free j记录了 buf 数组中未使用的字节数量

大多数情况下,Redis使用简单字符串SDS作为字符串的表示,相对于C语言字符串,SDS具有常数复杂度获取字符串长度,杜绝了缓存区的溢出,减少了修改字符串长度时所需的内存重分配次数,以及二进制安全能存储各种类型的文件,并且还兼容部分C函数。

  通过为链表设置不同类型的特定函数,Redis链表可以保存各种不同类型的值,除了用作列表键,还在发布与订阅、慢查询、监视器等方面发挥作用(后面会介绍)。

  Redis的字典底层使用哈希表实现,每个字典通常有两个哈希表,一个平时使用,另一个用于rehash时使用,使用链地址法解决哈希冲突。

  跳跃表通常是有序集合的底层实现之一,表中的节点按照分值大小进行排序。

  整数集合是集合键的底层实现之一,底层由数组构成,升级特性能尽可能的节省内存。

  压缩列表是Redis为节省内存而开发的顺序型数据结构,通常作为列表键和哈希键的底层实现之一。

  以上介绍的简单字符串、链表、字典、跳跃表、整数集合、压缩列表等数据结构就是Redis底层的一些数据结构,用来实现上一篇博客介绍的Redis五大数据类型,那么每种数据类型是由哪些数据结构实现的呢?下一篇博客进行介绍。

新版本

Redis 6.0 引入了 I/O 多线程模型,这是为了更有效地利用多核 CPU 的能力,以提高处理客户端请求的吞吐量。

在早期版本的 Redis 中,所有的网络 I/O 操作都是在单线程中执行的。这意味着,即使在多核 CPU 的系统上,Redis 也只能利用一个 CPU 核心来处理所有的客户端请求。在负载较高的情况下,这可能会成为性能的瓶颈。

为了解决这个问题,Redis 6.0 引入了一个可配置的 I/O 多线程模型。在这个模型中,主线程依然负责处理所有的读写操作以保证操作的原子性和一致性,而网络 I/O 操作(如接收客户端请求和发送响应)可以被分配到多个线程中并行处理。这样,Redis 就可以更有效地利用多核 CPU 的能力,从而提高处理客户端请求的吞吐量。

需要注意的是,尽管 Redis 6.0 支持 I/O 多线程,但这个功能默认是关闭的。因为在某些情况下,启用 I/O 多线程可能并不会带来性能提升,甚至可能导致性能下降。这主要取决于具体的工作负载和硬件环境。因此,是否启用 I/O 多线程需要根据实际情况来决定。

redis数据结构与底层实现

数据结构底层实现优势
字符串SDS((Simple Dynamic String)避免了常见的缓冲区溢出问题,提供了O(1)复杂度的字符串长度查询,且在修改字符串时能够保证部分操作的惰性(amortized)时间复杂度。
列表(List)双向链表或压缩列表(ziplist)实现的。在元素数量和元素大小都较小时,Redis会选择压缩列表作为底层实现以节省空间,否则会使用双向链表实现。
哈希表(Hash)压缩列表或者哈希表实现。小哈希时,会使用压缩列表,大哈希则会使用哈希表实现
集合(Set)Redis中的集合类型是由哈希表或者整数集合(intset)实现的。
有序集合(Zset)Redis中的有序集合类型可以由跳跃列表(skiplist)和压缩列表实现。对于元素数量和元素大小都较小的情况,会使用压缩列表;否则会使用跳跃列表。跳跃列表提供了较好的查找性能,同时插入和删除也能够保持相对稳定的时间复杂度。