什么是Redis,有什么特点

Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API 的非关系型数据库。

  • 速度快:每秒10W的读写
  • 持久化:Redis所有的数据保持在内存中,对数据的更新将异步地保存到磁盘上
  • 多种数据结构:set(集合),zset(有序集合),Hash(哈希),List(列表),string(字符串)
  • 高可用&分布式
  • 支持多种编程语言

Redis都有哪些数据类型

  • String (字符串):String 类型是 redis 中最基本的数据类型,一个 key 对应一个 value 。并且 String 类型是二进制安全的,意思是 redis 的 String 可以包含任何数据。例如数字,字符串,jpg图片或者序列化的对象。最大存储512MB,常用于缓存,计数器等
  • List(链表):List 即链表(redis 使用双端链表实现的 List),它是有序的,value可以重复,可以通过下标取出对应的value值,左右两边都能进行插入和删除数据。主要用于队列
  • Hash(哈希):Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。它是一个Mapmap,指值本身又是一种键值对结构
  • Set(集合):集合类型用来保存多个字符串的元素,集合中没有重复的元素;集合中的元素是无序的;不能通过索引下标获取元素;支持集合间的操作,例如多个集合取交集、并集、差集。Redis中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
  • zset(有序集合):和Sets相比,Sorted Sets是将 Set 中的元素增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列。有序集合中的元素不可以重复,但是score分数可以重复。

Redis 底层数据类型

  • SDS(simple dynamic string)简单动态字符串

        struct sdshdr {
            // 记录buf数组中已使用字节的数量 // 等于SDS所保存字符串的长度 int len;
            // 记录buf数组中未使用字节的数量 int free;
            // 字节数组,用于保存字符串 char buf[];
        };
    
  • 链表 Redis 的链表实现的特性可以总结如下:

    1. 链表被广泛用于实现 Redis 的各种功能,比如列表键、发布与订阅、慢查询、监视 器等。
    2. 每个链表节点由一个 listNode 结构来表示,每个节点都有一个指向前置节点和后 置节点的指针,所以 Redis 的链表实现是双端链表
    3. 每个链表使用一个 list 结构来表示,这个结构带有表头节点指针、表尾节点指针, 以及链表长度等信息。
    4. 因为链表表头节点的前置节点和表尾节点的后置节点都指向 NULL,所以 Redis 的链 表实现是无环链表。
    5. 通过为链表设置不同的类型特定函数,Redis 的链表可以用于保存各种不同类型的值。
  • 字典 字典,又称为符号表(symbol table)、关联数字(associative array)或映射(map),是一种用来保存键值对的抽象结构。

    1. 字典被广泛用于实现Redis的各种功能,其中包括数据库和哈希键。
    2. Redis中的字典使用哈希表作为底层实现,每个字典带有两个哈希表,一个平时使用,另一个仅在进行rehash时使用。
    3. 当字典被用作数据库的底层实现,或者哈希键的底层实现时,Redis使用MurmurHash2算法来计算键的哈希值。
    4. 哈希表使用链地址法来解决键冲突,被分配到同一个索引上的多个键值对会连接成一个单向链表。
    5. 在对哈希表进行扩展或者收缩操作时,程序需要将现有哈希表包含的所有键值对rehash到新哈希表里面,并且这个rehash过程并不是一次性地完成的,而是渐进式地完成的。
  • 跳跃表(skiplist)

    1. 跳跃表是有序集合的底层实现之一
    2. 跳跃表中的节点按照分值大小进行排序,当分值相同时,节点按照成员对象的大小进行排序。
  • 整数集合(intset) 整数集合(intset)是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现。

    1. 整数集合的底层实现为数组,这个数组以有序、无重复的方式保存集合元素,在有需要时,程序会根据新添加元素的类型,改变这个数组的类型。
    2. 整数集合只支持升级操作,不支持降级操作。
    3. 升级操作为整数集合带来了操作上的灵活性,并且尽可能地节约了内存。
  • 压缩列表(ziplist)

    1. 压缩列表被用作列表键和哈希键的底层实现之一。当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现

Redis对象

  • 字符串对象(string)
    字符串对象的编码可以是int、raw或者embstr。
类型 场景
int 字符串对象保存的是整数值,并且这个整数值可以用long类型来表示
embstr 字符串对象保存的是一个字符串值,并且这个字符串值的长度小于等于32字节
raw 字符串对象保存的是一个字符串值,并且这个字符串值的长度大于等于32字节
  • 列表对象(list) 列表对象的编码可以是ziplist或者linkedlist。
类型 场景
ziplist 列表对象保存的所有字符串元素的长度都小于64字节列表对象保存的元素数量小于512个
linkedlist 列表对象保存的元素不满足ziplist均为linkedlist
  • 哈希对象(hash) 哈希对象的编码可以是ziplist或者hashtable。
类型 场景
ziplist 哈希对象保存的所有键值对的键和值的字符串长度都小于64字节且哈希对象保存的键值对数量小于512个
hashtable 不满足ziplist的均为hashtable类型
  • 集合对象(set) 集合对象的编码可以是intset或者hashtable
类型 场景
intset 集合对象保存的所有元素都是整数值,且数量小于512个
hashtable 不满足intset的集合对象均为hashtable类型
  • 有序集合 集合对象的编码可以是ziplist或者skiplist
类型 场景
ziplist 有序集合保存的元素数量小于128个,有序集合保存的所有元素成员的长度都小于64字节
skiplist 不满足ziplist的集合对象均为skiplist类型

Redis过期键删除策略

策略类型 策略 优缺点
定时删除            通过使用定时器,定时删除策略可以保证过期键会尽可能快地被删除,并释放过期键所占用的内存 对内存友好,对CPU不友好
惰性删除            程序只会在取出键时才对键进行过期检查 对CPU友好,对内存不友好
定期删除            定期删除策略每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时长和频率来减少删除操作对CPU时间的影响

RDB持久化(Redis DataBase)

RDB 是 Redis 默认的持久化方案。在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis 重启会通过加载dump.rdb文件恢复数据。

  1. save <指定时间间隔> <执行指定次数更新操作>,满足条件就将内存中的数据同步到硬盘中。官方出厂配置默认是 900秒内有1个更改,300秒内有10个更改以及60秒内有10000个更改,则将内存中的数据快照写入磁盘。
  2. Save 命令和 bgSave命令的区别: Save命令是阻塞的,即执行Save命令时,其他进程都在等待,故Save命令不建议在生产环境使用

AOF持久化(Append Only File)

Redis默认不开启AOF,需要在redis.conf 修改appendonly修改为 yes 更新频率:always 同步持久化,everysec 默认配置,每秒异步一次 no 不同步

主从复制

  1. 从服务器向主服务器发送SYNC命令。
  2. 收到SYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令。
  3. 当主服务器的BGSAVE命令执行完毕时,主服务器会将BGSAVE命令生成的RDB文件发送给从服务器,从服务器接收并载入这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态。
  4. 主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态。 Tips:2.8版本后加入了PSYNC

Sentinel(哨兵)

Sentinel(哨岗、哨兵)是Redis的高可用性(high availability)解决方案:由一个或多个Sentinel实例(instance)组成的Sentinel系统(system)可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。

  • Sentinel选举 当一个主服务器被判断为客观下线时,监视这个下线主服务器的各个Sentinel会进行协商,选举出一个领头Sentinel,并由领头Sentinel对下线主服务器执行故障转移操作。
  • 故障转移
    1. 在已下线主服务器属下的所有从服务器里面,挑选出一个从服务器,并将其转换为主服务器。
    2. 让已下线主服务器属下的所有从服务器改为复制新的主服务器。
    3. 将已下线主服务器设置为新的主服务器的从服务器,当这个旧的主服务器重新上线时,它就会成为新的主服务器的从服务器。

集群

Redis集群是Redis提供的分布式数据库方案,集群通过分片(sharding)来进行数据共享,并提供复制和故障转移功能。 命令:CLUSTER MEET

  • 复制 Redis集群中的节点分为主节点(master)和从节点(slave),其中主节点用于处理槽,而从节点则用于复制某个主节点,并在被复制的主节点下线时,代替下线主节点继续处理命令请求。
  • 故障转移 当一个从节点发现自己正在复制的主节点进入了已下线状态时,从节点将开始对下线主节点进行故障转移,以下是故障转移的执行步骤:
    1. 复制下线主节点的所有从节点里面,会有一个从节点被选中。
    2. 被选中的从节点会执行SLAVEOF no one命令,成为新的主节点。
    3. 新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己。
    4. 新的主节点向集群广播一条PONG消息,这条PONG消息可以让集群中的其他节点立即知道这个节点已经由从节点变成了主节点,并且这个主节点已经接管了原本由已下线节点负责处理的槽。
    5. 新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成。 Tips:新的节点也是通过选举产生的.

事务

Redis通过MULTI、EXEC、WATCH等命令来实现事务(transaction)功能。事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。

慢查询日志

Redis的慢查询日志功能用于记录执行时间超过给定时长的命令请求,用户可以通过这个功能产生的日志来监视和优化查询速度。 服务器配置有两个和慢查询日志相关的选项:

  1. slowlog-log-slower-than选项指定执行时间超过多少微秒(1秒等于1 000 000微秒)的命令请求会被记录到日志上。
  2. slowlog-max-len选项指定服务器最多保存多少条慢查询日志。

数据淘汰策略

  • LRU(Least Recent Used)最近最少使用。优先淘汰最近未被使用的数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
  • LFU (Least Frequently Used) 优先淘汰最近使用的少的数据,其核心思想是“如果一个数据在最近一段时间很少被访问到,那么将来被访问的可能性也很小”。
策略名称 含义
noeviction 默认策略,不淘汰数据;大部分写命令都将返回错误(DEL等少数除外)
allkeys 从所有数据中根据 LRU 算法挑选数据淘汰
volatile-lru 从设置了过期时间的数据中根据 LRU 算法挑选数据淘汰
allkeys-random 从所有数据中随机挑选数据淘汰
volatile-random 从设置了过期时间的数据中随机挑选数据淘汰
volatile-ttl 从设置了过期时间的数据中,挑选越早过期的数据进行删除
allkeys-lfu 从所有数据中根据 LFU 算法挑选数据淘汰(4.0及以上版本可用)
volatile-lfu 从设置了过期时间的数据中根据 LFU 算法挑选数据淘汰(4.0及以上版本可用)