Redis知识点总结

    技术2024-11-16  8

    Redis知识点总结

    0. 简单入门知识点

    1.redis是key-value的数据结构 每条数据都是一个键值对 键的类型是字符串,并且不能重复 值的数据类型分为五种:string、hash、list、set、zset

    2.数据库没有名字,默认有16个库,通过0-15来标识连接redis数据库。默认选择第一个数据库,即0

    1. 六大底层数据结构

    可以使用OBJECT ENCODING key来查看某个数据类型的底层实现

    1.1. SDS(simple dynamic string)简单动态字符串sdshdr

    // 不以空字符’\0’作为字符串的结束标志 struct sdshdr{ //等于 SDS 保存字符串的长度 int len; //记录 buf 数组中未使用字节的数量 int free; //字节数组,用于保存字符串 char buf[]; }

    1.2. 链表list

    双端:链表具有前置节点和后置节点的引用无环:表头节点的prev指针和表尾节点的next指针都指向NULL,对链表的访问都是以NULL结束;带链表长度计数器:通过len属性获取链表长度的时间复杂度为O(1);多态:链表节点使用void*指针来保存节点值,可以保存各种不同类型的值。

    1.3. 哈希表dictht

    typedef struct dictEntry{ // 键 void *key; // 值 union{ void *val; uint64_t u64; int64_t s64; }v; // 链地址法 struct dictEntry *next; }dictEntry

    哈希算法如下:

    // 1、使用字典设置的哈希函数,计算键 key 的哈希值 hash = dict->type->hashFunction(key); // 2、使用哈希表的sizemask属性和第一步得到的哈希值,计算索引值 index = hash & dict->ht[x].sizemask;

    1.4. 跳跃表zskiplist

    跳跃表具有以下性质:

    由很多层结构组成;每一层都是一个有序的链表,排列顺序为由高层到底层,都至少包含两个链表节点,分别是前面的head节点和后面的nil节点;最底层的链表包含了所有的元素;如果一个元素出现在某一层的链表中,那么在该层之下的链表也全都会出现(上一层的元素是当前层的元素的子集);链表中的每个节点都包含两个指针,一个指向同一层的下一个链表节点,另一个指向下一层的同一个链表节点;

    1.5. 整数集合intset

    typedef struct intset{ //编码方式 uint32_t encoding; //集合包含的元素数量 uint32_t length; //保存元素的数组 int8_t contents[]; }intset;

    1.6. 压缩列表ziplist

    压缩列表的每个节点构成如下:

    previous_entry_length用于反向遍历 encoding保存的是节点的content的内容类型以及长度,encoding类型一共有两种,字节数组和整数,encoding区域长度为1字节、2字节或者5字节长

    2. 五大数据类型的底层实现

    string、hash、list、set、zset

    可以使用type key来查看对应value的类型

    Redis中的每个对象都是由redisObject结构来表示

    typedef struct redisObject{ //类型(五大数据类型) unsigned type; //编码(底层实现的数据类型) unsigned encoding; //指向底层数据结构的指针 void *ptr; //引用计数 int refcount; //记录最后一次被程序访问的时间 unsigned lru; }robj

    其中type和encoding的对应关系如下:

    2.1. string的底层实现

    字符串对象的编码可以是int,raw或者embstr:

    int编码:保存的是可以用long类型表示的整数值;raw编码:保存长度大于44字节的字符串(redis3.2版本之前是39字节,之后是44字节);embstr编码:保存长度小于44字节的字符串(redis3.2版本之前是39字节,之后是44字节)。

    2.2. list的底层实现

    当同时满足下面两个条件时,使用ziplist(压缩列表)编码:

    列表保存元素个数小于512个;每个元素长度小于64字节; 不能满足这两个条件的时候使用linkedlist(双端链表)编码。 上面两个条件可以在redis.conf配置文件中的list-max-ziplist-value选项和list-max-ziplist-entries选项进行配置。

    2.3. hash的底层实现

    哈希对象的编码可以是ziplist或者dictht,具体规则与list类似

    2.4. set的底层实现

    集合对象的编码可以是intset或者dictht

    当集合同时满足以下两个条件时,使用intset编码:

    集合对象中所有元素都是整数集合对象所有元素数量不超过512 不能满足这两个条件的就使用dictht编码。第二个条件可以通过配置文件的set-max-intset-entries进行配置。

    2.5. zset的底层实现

    有序集合的编码可以是ziplist或者skiplist

    一个zset结构同时包含一个字典和一个跳跃表,字典的键保存元素的值,字典的值则保存元素的分值;跳跃表节点的object属性保存元素的成员,跳跃表节点的score`属性保存元素的分值。这两种数据结构会通过指针来共享相同元素的成员和分值,所以不会产生重复成员和分值,造成内存的浪费。

    当有序集合对象同时满足以下两个条件时,对象使用ziplist编码:

    保存的元素数量小于128;保存的所有元素长度都小于64字节; 不能满足上面两个条件的使用skiplist编码。以上两个条件也可以通过配置文件的zset-max-ziplist-entries选项和zset-max-ziplist-value进行修改

    3. 持久化

    3.1. RDB

    优点:

    直接二进制dump,文件紧凑,体积小;恢复快;

    缺点:

    只能全量持久化,一次耗时较长;二进制,基本没有可读性和可修改性。

    3.2. AOF

    优点:

    记录所有更新日志,可读性和可修改性强;支持持续无间断备份;

    缺点:

    文件体积大,恢复慢;比较影响整体性能。

    4. 缓存淘汰

    当 Redis 内存超出物理内存限制时,内存的数据会开始和磁盘产生频繁的交换 (swap)。交换会让 Redis 的性能急剧下降,对于访问量比较频繁的 Redis 来说,这样龟速的存取效率基本上等于不可用。 在生产环境中我们是不允许 Redis 出现交换行为的,为了限制最大使用内存,Redis 提供了配置参数 maxmemory 来限制内存超出期望大小。 当实际内存超出 maxmemory 时,Redis 提供了几种可选策略 (maxmemory-policy) 来让用户自己决定该如何腾出新的空间以继续提供读写服务。

    (volatile策略只会针对带过期时间的 key 进行淘汰,allkeys策略会对所有的 key 进行淘汰) 1.noeviction:不会继续服务写请求 (DEL 请求可以继续服务),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略。 2.volatile-lru:尝试淘汰设置了过期时间的 key,最少使用的 key 优先被淘汰。没有设置过期时间的 key 不会被淘汰,这样可以保证需要持久化的数据不会突然丢失。 3.volatile-ttl:跟上面一样,除了淘汰的策略不是 LRU,而是 key 的剩余寿命 ttl 的值,ttl 越小越优先被淘汰。 4.volatile-random:跟上面一样,不过淘汰的 key 是过期 key 集合中随机的 key。 5.allkeys-lru 区别于 volatile-lru,这个策略要淘汰的 key 对象是全体的 key 集合,而不只是过期的 key 集合。这意味着没有设置过期时间的 key 也会被淘汰。 6.allkeys-random 跟上面一样,不过淘汰的策略是随机的 key。

    5. 缓存穿透、缓存击穿、缓存雪崩

    1.缓存穿透: 用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。一般通过布隆过滤器、缓存空对象来解决

    2.缓存击穿:一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库。一般通过设置热点数据永远不过期、加互斥锁来解决

    3.缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。一般通过过期时间设置随机、数据预热来解决

    6.架构模式

    单机版:①内存容量有限;②处理能力有限;③无法高可用。主从复制:①降低master读压力在转交从库;②无法保证高可用;③没有解决master写的压力哨兵:Redis sentinel 是一个分布式系统中监控 redis 主从服务器,并在主服务器下线时自动进行故障转移。①保证高可用;②主从模式,切换需要时间丢数据;③没有解决 master 写的压力集群(proxy 型):增加了新的 proxy,需要维护其高可用。代表为Twemproxy、Codis集群(直连型):增加了新的 proxy,需要维护其高可用。代表为Redis Cluster(redis3之后的官方方案)。分布算法使用数据分片(sharding)而非一致性哈希(consistency hashing)来实现: 一个 Redis 集群包含 16384 个哈希槽。采用客户端重定向的方式
    Processed: 0.037, SQL: 9