Redis整理

Redis简介

Redis是一个速度非常快的非关系数据库(non-relational database),它可以存储键(key)与5种不同类型的值(value)之间的映射(mapping),可以将存储在内存的键值对数据持久化到硬盘,可以使用复制特性来扩展读性能,还可以使用客户端分片来扩展写性能。

Redis与其他数据库和缓存服务器的对比

Redis整理_第1张图片
Redis与其他数据库和缓存服务器的对比

Redis 数据结构

SDS

SDS(simple dynamic string)简单动态字符串
Redis整理_第2张图片
SDS数据结构

  SDS的API都是二进制安全的(binary-safe),所有SDS API都会以处理二进制的方式来处理SDS存放在buf数组里的数据,程序不会对其中的数据做任何限制,过滤,或者假设数据在写入时是什么样的,它被读取时就是什么样。

链表

Redis整理_第3张图片
链表结构图

字典

Redis整理_第4张图片
字典hash表数据结构
Redis整理_第5张图片
字典数据结构
  • 使用Murmurhash2算法来计算键的哈希值
  • 使用链地址法来解决键冲突
  • rehash:随着操作的不断执行,哈希表保存的键值对会逐渐地增多或者减少,为了让hash表的负载因子(load factor)维持在一个合理的范围内,当hash表中保存的键值对数量太多或者太少时,程序需要对hash表的大小进行相应的扩展或者收缩,扩展或者收缩hash表的工作就是通过rehash(重新散列,hash函数还是原来的函数)操作来完成的;负载因子 = hash表已经保存节点数量 / hash表大小
  • 渐进式rehash:rehash动作不是一次性,集中式地完成的,而是分多次,渐进式地完成的。

跳跃表skiplist

  跳跃表skiplist是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的,如下图所示:
Redis整理_第6张图片
跳跃表skiplist例子
  • 层(level): 节点中用L1,L2,L3等字样标记节点的各个层,L1代表第一层,L2代表第二层,一次类推。每个层都带有两个属性:前进指针和跨度,前进指针用于访问位于表尾方向的其他节点;而跨度则记录了前进指针所指向节点和当前节点的距离。
  • 分值:各个节点中的1.0,2.0,3.0是节点所保存的分值,在跳跃表中,节点按照各自所保存的分值从小到大排列。

整数集合

  整数集合(intset)是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现,结构定义如下图所示:
Redis整理_第7张图片
数据结构定义
  • 有序性和唯一性:整数集合的每个元素都是contents数组的一个数组项(item),每个项在数组中按值的大小从小到大有序地排列,并且数组中不包含任何重复键
  • encoding属性决定了contents数组真正的类型,数组中的几个元素算作一个元素的问题,如下图所示:
    Redis整理_第8张图片
    intset encoding例子说明
  • 升级:每当要将一个新元素添加到整数集合里面,并且新元素的类型比整数集合现有所有元素的类型都要长时,整数集合需要先进行升级(upgrade),然后才能将新元素添加到整数集合里面。

压缩列表ziplist

  压缩列表(ziplist)是列表键和哈希键的底层实现之一。当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现。压缩列表是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型(sequential)数据结构,一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值,如下图所示:
ziplist结构
  • 有连锁更新的问题:在特殊情况下产生的连续多次空间扩展操作称之为“连锁更新(cascade update)”。

对象

  Redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,包含如下的5中对象类型:
Redis整理_第9张图片
Redis对应键值的5中数据结构
  • 通过这五种不同类型的对象,Redis可以在执行命令之前,根据对象的类型来判断一个对象是否可以执行给定的命令
  • 内存回收机制:Redis的对象系统基于引用计数技术实现了内存回收机制;
  • 对象共享机制:Redis使用引用计数技术实现了对象共享机制。
       Redis使用对象来表示数据库中的键和值,每当我们在Redis数据库中新创建一个键值对时,至少会创建两个对象,一个对象用作键值对的键(键对象,键总是一个字符串对象),另一个对象用作键值对的值(值对象)。
数据结构 对象类型
Redis整理_第10张图片
数据结构
Redis整理_第11张图片
对象类型

字符串对象

  字符串对象的编码可以是int,raw或者embstr,这三种字符串编码在一定条件下会进行转换。

  • 如果一个字符串对象保存的是整数值,并且这个整数值可以用long类型来表示,那么字符串对象会将整数值保存在字符串对象结构的ptr属性中(将void转换为long),并将字符串对象的编码设置为int。

  • 如果字符串对象保存的是一个字符串的值,并且这个字符串值的长度大于32字节,那么字符串对象将使用一个简单动态字符串SDS来保存这个字符串的值,并将对象的编码设置为raw,如下图所示
    Redis整理_第12张图片
    raw编码字符串对象结构
  • 如果字符串对象保存的是一个字符串值,并且这个字符串值的长度小于等于32字节,那么字符串对象将使用embstr(embstr编码是专门用于保存端字符串的一种优化编码方式)编码的方式来保存这个字符串值;这种编码和raw编码一样,都是用redisObject结构和sdshdr结构来表示字符串对象,但raw编码会调用两次内存分配函数来分别创建redisObject结构和sdshdr结构,而embstr编码则通过调用一次内存分配函数来分配一块连续的空间,空间中一次包含redisObject和sdshdr两个结构,如下图所示:

    embstr编码字符串结构

  • 字符串对象对应的命令实现:
    Redis整理_第13张图片
    字符串对象对应的命令实现

列表对象

列表对象的编码可以是ziplist或者linkedlist;ziplist编码使用的压缩列表作为底层实现,linkedlist使用的是双端链表作为底层实现,这两种编码会在一定的条件自动进行转换,两种列表编码如下表对比所示:

ziplist 编码 linkedlist 编码
Redis整理_第14张图片
ziplist编码的列表对象
Redis整理_第15张图片
linkedlist编码的列表对象

列表命令实现入下图所示:
Redis整理_第16张图片
列表命令实现

哈希对象

哈希对象的编码可以ziplist或者hashtable;hashtable编码的哈希对象使用字典作为底层实现;这两种编码可以在一定的条件下相互转换;下表显示了两种编码的对比:

ziplist 编码 hashtable 编码
ziplist编码的哈希对象
Redis整理_第17张图片
hashtable编码的哈希对象

哈希对象的命令实现如下图所示:
Redis整理_第18张图片
哈希对象的命令实现

集合对象

  集合对象的编码可以是intset或者hashtable,intset编码的集合对象使用整数集合作为底层实现;这两种编码在一定条件下可以相互转换,对比如下图所示:

intset 编码 hashtable 编码
Redis整理_第19张图片
intset编码的集合对象
Redis整理_第20张图片
hashtable编码的集合对象

集合对象对应命令实现如下图所示:
集合对象的命令实现
Redis整理_第21张图片
集合对象的命令实现-续

有序集合

  有序集合的编码可以是ziplist或者skiplist(跳跃表),skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表,如下代码所示:

typedef struct zset {
        zskiplist *zsl;
        dict *dict;
}

这两种编码在一定条件下可以相互转换,对比如下表所示:

ziplist 编码 skiplist 编码
ziplist编码的有序集合对象
Redis整理_第22张图片
skiplist编码的有序集合对象

有序集合对应的命令实现如下图所示:
Redis整理_第23张图片
有序集合命令实现

redis常用命令

TYPE命令

  对于Redis数据库保存的键值对来说,键总是一个字符串对象,而值则可以是字符串对象,列表对象,哈希对象,集合对象或者有序集合对象中一种,因此在对一个数据库键执行TYPE命令时,命令返回的结果为数据库键对应的值对象的类型,而不是键对象的类型,其中格式:type 键名称 ,对应的输出如下图所示:

Redis整理_第24张图片
不同类型值对象的TYPE命令输出

OBJECT ENCODING命令

对象的编码就是对象使用了什么数据结构作为对象的底层实现,格式为:OBJECT ENCODING 键名称,对应的输出下图所示:

Redis整理_第25张图片
OBJECT ENCODING对不同编码的输出
Redis整理_第26张图片
OBJECT ENCODING对不同编码的输出-续

OBJECT IDLETIME命令

   OBJECT IDLETIME命令可以打印出给定键的空转时间,空转时间=当前时间 - 键的值对象最后一次被命令程序访问的时间

对象通用命令

  • DEL命令
  • EXPIRE命令
  • EXPIREAT 命令
  • RENAME命令
  • SORT命令
  • TYPE命令
  • OBJECT命令
  • TTL命令:TTL命令以秒为单位返回键的剩余生存时间
  • PTTL命令:PTTL命令则以毫秒为单位返回键的剩余生存时间
  • SAVE命令:SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求
  • BGSAVE命令:BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求
  • FLUSHALL命令:清除当前数据库中的所有键值对
  • CLIENT list命令:列出目前所有连接到服务器的普通客户端,命令输出中的fd域显示了服务器连接客户端所使用的套接字描述符
  • AUTH命令:如果authenticated的值为0,那么表示客户端未通过身份验证;如果authenticated的值为1,那么表示客户端已经通过了身份验证,AUTH命令可以是客户端的状态authenticated的值从0到1
  • PING命令
  • SLAVEOF命令:通过向从服务器发送SLAVEOF命令,可以让一个从服务器去复制一个主服务器,命令SLAVEOF
  • EXISTS命令
  • INFO replication: 客户端向主服务器发送INFO replication命令得到的副本信息回复
  • redis-sentinel命令:redis-sentinel /path/to/your/sentinel.conf 或者 redis-server /path/to/your/sentinel.conf --sentinel
  • SUBSCRIBE命令:订阅
  • PSUBSCRIBE命令:订阅一个或多个模式
  • PUBLISH命令
  • 事务命令:MULTI/EXEC
  • WATCH命令:WATCH命令是一个乐观锁,它可以在EXEC命令执行之前,监视任意数量的数据库键,并在EXEC命令执行时,检查被监视的键是否至少有一个已经被修改过了,如果是的话,服务器将拒绝执行事务,并向客户端返回代表事务执行失败的空回复。
  • EVAL命令:EVAL命令可以直接对输入的脚本进行求值
  • EVALSHA命令:根据脚本的SHA1校验和来对脚本进行求值
  • SCRIPT LOAD命令:加载脚本
  • SETBITS/GETBITS/BITCOUNT/BITOP命令:位数组
  • CONFIG SET命令
  • SLOWLOG GET命令:查看服务器所保存的慢查询日志
  • MONITOR命令

select命令

  每个Redis客户端都有自己的目标数据库,每当客户端执行数据库写命令或者数据库读命令的时候,目标数据库就会成为这些命令的操作对象;默认情况下,Redis客户端的目标数据库是0号数据库,但客户端可以通过执行select命令来切换目标数据库;数据库对应的结构如下:

redisServer redisDb 示例图
Redis整理_第27张图片
redisServer
Redis整理_第28张图片
redisDb
Redis整理_第29张图片
示例图

数据库持久化

RDB持久化

RDB持久化功能是将Redis在内存中的数据库状态保存到磁盘里面,避免数据意外丢失,如下图所示:
Redis整理_第30张图片
RDB持久化

AOF持久化

AOF(append only File)持久化功能,与RDB持久化通过保存数据库中的键值对来记录数据库状态不同,AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的,如下图所示:
AOF持久化

参考

  1. redis设计与实现(第二版)
  2. 源码解析:https://github.com/huangz1990/redis-3.0-annotated
  3. 测试用例:https://yq.aliyun.com/articles/62845
  4. 源码对应的测试用例讲解:https://yq.aliyun.com/articles/62845
  5. Mac redis: https://www.cnblogs.com/xrhou12326/p/7103106.html

你可能感兴趣的:(Redis整理)