《Redis设计与实现》

来源:互联网 发布:淘宝平铺图拍摄技巧 编辑:程序博客网 时间:2024/06/06 12:09

一、引言

二、简单动态字符串

1、简单动态字符串SDS:

struct sdshdr {

         intlen; //记录buf数组中已使用字节的数量

         intfree; //记录buf数组中未使用字节的数量

         charbuf[]; //字节数组,用于保存字符串

}

注:SDS还被用作缓冲区buffer

 

2、SDS较C字符串的优势:

         1)Redis将获取字符串长度所需要的复杂度从O(n)降到了O(1)

         2)杜绝缓冲区溢出

         3)减少修改字符串时带来的内存重分配次数

 

3、

1)空间预分配:当SDS的API对一个SDS进行修改,并且需要对SDS进行空间扩展的时候,程序不仅会为SDS分配修改所必须要的空间,还会为SDS分配额外的未使用空间。

注:len小于1MB时,free=len;len大于1MB时,free=1MB

2)惰性空间释放:当SDS的API需要缩短SDS保存的字符串时,程序并不立即使用内存重分配来回收缩短后多出来的字节,而是使用free属性将这些字节的数量记录起来,并等待将来使用。

 

4、二进制安全:Redis用字节数组保存一系列二进制数据而不是字符,并通过len属性判断字符串是否结束。

 

三、链表

1、链表节点使用双端链表:前置节点指针,后置节点指针、节点值

 

2、list结构:表头指针head,表尾指针tail,链表长度len,节点值复制函数dup,节点值释放函数free,节点值对比函数match

 

3、Redis链表特性:1)双端;2)无环;3)带表头指针和表尾指针;4)带链表长度计数器;5)多态

 

四、字典

1、字典:又称为符号表、关联数组或映射,是一种用于保存键值对的抽象数据结构。

其中:

1)dict:字典

         2)type:一个指向dictType(每个dictType结构保存了一簇用于操作特定类型键值对的函数)的指针,Redis会为用途不同的字典设置不同的类型特定函数

         3)privdata:保存了需要传给哪些类型特定函数的可选参数

         4)ht:包含两个项的数组,数组中的每个项都是一个哈希表

         5)rehashidx:记录了rehash目前的进度,若目前没有在rehash,值为-1

         6)dictht:哈希表

         7)table:是一个数组,数组中每个元素是一个指向dictEntry的指针

         8)size:记录hash表的大小即table数组大小

         9)sizemask:size-1(虽然与计算索引有关,但是既然值始终为size-1为什么要单独拿出个字段,效率???)

         10:used:记录哈希表目前已有节点(键值对)的数量

         11)dictEntry:哈希表节点(包括key、value、next指针)

2、当要将一个新的键值对添加到字典里面时,程序需要先根据键值对的键计算出哈希值和索引值,然后在根据索引值,将包含新键值对的哈希表节点放到哈希表数组的指定索引上面。

3、键冲突:当有两个或以上数量的键被分配到了哈希表数组的同一个索引上面时

         注:Redis的哈希表使用链地址法来解决键冲突(next指针)

4、rehash:重新散列,来扩展和收缩哈希表

步骤:

1)为字典的ht[1]哈希表分配空间;

2)将保存在ht[0]中的所有键值对rehash到ht[1]上面;

3)释放ht[0],将ht[1]设置为ht[0],并在ht[1]新、创建一个空白哈希表

注:通常使用渐进式rehash,即只在原表增删改查时rehash对应的键值对到新表

5、扩展与收缩条件

1)哈希表扩展条件

a、服务器没有正在执行BGSAVE或BGREWRITEAOF命令,且哈希表负载因子大于等于1

b、哈希表的负载因子大于等于5

2)哈希表收缩条件:负载因子小于0.1

注:负载因子=哈希表以保存节点数量 / 哈希表大小

 

五、跳跃表

1、跳跃表:通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。节点查找平均复杂度O(logN),最坏O(N)

2、Redis使用跳跃表的地方:1)实现有序集合键;2)在集群节点中用作内部数据结构

3、跳跃表节点zskiplistNode

         1)层:level数组可以包含多个元素,每个元素都包含一个指向其他节点的指针

         2)前进指针:每个层都有一个指向表尾方向的前进指针

         3)跨度:用于记录两个节点之间的距离

                   注:排位:在查找某个节点的过程中,将沿途访问过的所有层的跨度累计起来,得到的结果就是目标节点在跳跃表中的排位。

         4)后退指针:用于从表尾向表头方向访问节点

         5)分值:一个double类型的浮点数,从小到大排序

         6)成员对象:是一个指针,指向一个字符串对象

4、跳跃表:

         1)header和tail指针分别指向跳跃表的表头和表尾节点

         2)length属性来记录节点的数量

         3)level:跳跃表中层高最大的那个节点的层数量

六、整数集合

1、整数集合(intset)是Redis用于保存整数值的集合抽象数据结构,并保证集合中不会出现重复元素,从小到大排序。其数据结构包括:1)编码方式;2)集合包含的元素数量;3)保存元素的数组

2、升级:将一个类型比整数集合现有所有元素的类型都要长的元素时,整数集合需要先进行升级,然后才能将新元素添加到整数集合里面。

注:整数集合不支持降级操作。

七、压缩列表

1、压缩列表:是由一系列特殊编码的连续内存块组成的顺序型数据结构,是列表键和哈希键的底层实现之一,存放整数值和字符串。

2、压缩列表数据结构:

1)zlbytes:记录整个压缩列表占用的内存字节数

2)zltail:记录压缩列表表尾节点距离压缩列表的起始地址有多少个字节

3)zllen:记录了压缩列表包含的节点数量

4)entryX:列表节点

5)zlend:特殊值0xFF,用于标记压缩列表的末端

3、压缩列表节点数据结构:

1)previous_entry_length:记录了压缩列表中前一个节点的长度

注:若前一节点长度小于254字节,该属性长度为1字节;若前一节点长度大于254字节,该属性为5字节

2)encoding:保存数据的类型及长度

3)content:保存节点的值

4、连锁更新:指在特殊情况下产生的连续多次空间扩展或收缩的操作

注:连锁更新在实际中造成性能问题的几率是很低的

八、对象

1、Redis的键总是字符串,值有五种不同类型的对象:字符串对象、列表对象、哈希对象、集合对象、有序集合对象。

2、Redis对象由redisObject结构表示,包括:类型、编码、指向底层实现数据结构的指针

3、命令:

1)TYPE命令:返回的结果为数据库键对应的值对象的类型

2)OBJECT ENCODING命令:可以查看一个数据库键的值对象的编码

4、字符串对象:

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

2)如果字符串对象保存的是一个字符串值,并且这个字符串值的长度大于39字节,则使用简单动态字符串SDS,编码设置为raw

3)如果字符串对象保存的是一个字符串值,并且这个字符串值的长度小于等于39字节,使用embstr编码

注:1)raw编码会调用两次内存分配函数来分别创建redisObject结构和sdshdr结构,而embstr编码则通过调用一次内存分配函数来分配一块连续的空间,空间中依次包含redisObject和sdshdr两个结构

2)embstr编码的字符串对象实际上是只读的

5、列表对象:编码可以是ziplist(压缩列表)或linkedlist(双端链表)

注:使用ziplist编码必须同时满足:1)列表对象保存的所有字符串元素的长度都小于64字节;2)列表对象保存的元素数量小于512个

6、哈希对象:编码可以是ziplist或hashtable

注:使用ziplist编码必须同时满足:1)哈希对象保存的所有键值对的键和值的字符串长度都小于64字节;2)哈希对象保存的键值对数量小于512个

7、集合对象:编码可以是intset或hashtable

intset编码的集合对象使用整数集合作为底层实现,即集合对象包含的所有元素都被保存在整数集合里面

注:使用intset编码必须同时满足:1)集合对象保存的所有元素都是整数值;2)集合对象保存的元素数量不超过512个

8、有序集合:编码可以是ziplist或skiplist

skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表

注:1)字典和跳跃表会共享元素的成员和分值,所以并不会造成任何数据重复,也不会因此而浪费任何内存

2)使用ziplist必须同时满足:a、有序集合保存的元素数量小于128个;b、有序集合保存的所有元素成员的长度都小于64字节

9、Redis中用于操作键的命令:一种命令可以对任何类型的键执行,另一种命令只能对特定类型的键执行

10、多态命令的实现:根据值对象的编码选择正确的命令来实现代码来执行命令

注:Redis类型检查是通过redisObject结构的type属性来实现的

11、内存回收:引用计数器法

12、空转时长:通过将当前时间减去键的值对象的lru时间计算得出

注:redisObject结构的最后一个属性为lru属性,该属性记录了对象最后一次被命令程序访问的时间

九、数据库

1、Redis服务器将所有数据库都保存在服务器状态redis.h/redisServer结构的db数组中,db数组的每个项都是一个redis.h/redisDb结构,每个redisDb结构代表一个数据库。

注:1)默认16个数据库

2)默认目标数据库为0号数据库,但客户端可以通过执行SELECT命令来切换目标数据库

3)客户端状态redisClient结构的db属性记录了客户端当前的目标数据库

2、redisDb结构的dict字典保存了数据库中的所有键值对,这个字典称为键空间

注:键空间和用户所见的数据库是直接对应的

3、FLUSHDB命令,通过删除键空间中的所有键值对来实现

4、通过EXPIRE或PEXPIRE命令,客户端可以以秒或者毫秒精度为数据库中的某个键设置生存时间

注:过期时间是一个UNIX时间戳,当键的过期时间来临时,服务器就会自动从数据库中删除这个键

5、redisDb结构的expires字典保存了数据库中所有键的过期时间,称为过期字典。其键是一个指针,指向键空间中的某个键对象;其值是一个long类型的整数,保存了键所指向的数据库键的过期时间(一个毫秒精度的UNIX时间戳)

6、过期键删除策略:

1)定时删除:在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。其缺点:定时器太多

2)惰性删除:从键空间获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。其缺点:垃圾数据太多

3)定期删除:每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。同java GC

注:Redis服务器实际使用的是惰性删除和定期删除两种策略

7、RDB文件对过期键的处理:

1)服务器以主服务器模式运行:不保存过期键

2)服务器以从服务器模式运行:保存过期键

注:已过期的键不会被保存到新创建的RDB文件中

8、当服务器以AOF持久化模式运行时,某个键已经过期,不会因为这个过期键而产生任何影响;当过期键被删除后,程序会向AOF文件追加一条DEL命令,来显式的记录该键已被删除。

9、复制模式时:主服务器来控制从服务器统一地删除过期键,保证主从服务器数据的一致性

10、通知:

1)键空间通知:关注某个键执行了什么命令的通知

2)键事件通知:关注某个命令被什么键执行了

十、RDB持久化

1、RDB持久化:将redis在内存中的数据库状态保存到磁盘里面

2、保存

1)SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止

2)BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件

注:自动间隔性保存:程序会遍历并检查saveparams数组中的所有保存条件,只要有任意一个条件被满足,那么服务器就会执行BGSAVE命令

3、服务器状态的结构

1)saveparams:是一个数组,其每个元素都是一个saveparam结构,记录秒数和修改数

2)dirty计数器:记录距离上一次成功执行SAVE命令或者BGSAVE命令之后,服务器对数据库状态(服务器中的所有数据库)进行了多少次修改

3)lastsave属性:是一个UNIX时间戳,记录服务器上一次成功执行SAVE命令或者BGSAVE命令的时间

4、RDB文件结构:

1)REDIS:长度5字节,保存着“REDIS”五个字符,用于载入文件时,快速检查是否为RDB文件

2)db_version:4字节,整数,记录RDB文件的版本号

3)databases:包含着零个或任意多个数据库,以及各个数据库中的键值对数据

4)EOF:1字节,标志着RDB文件正文内容的结束

5)check_sum:8字节,无符号整数,校验和

5、数据库结构:

1)SELECTDB:1字节,表示接下来要读入的是一个数据库号码

2)db_number:数据库号码,1、2、5字节

3)key_value_pairs:保存了数据库中的所有键值对数据

注:不带过期时间的键值对在RDB文件中由TYPE、key、value三部分组成

十一、AOF持久化

1、RDB持久化通过保存数据库中的键值对来记录数据库状态不同,AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的

2、AOF持久化功能的实现:

1)命令追加:服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾

2)文件写入

3)文件同步

3、AOF重写:创建一个新的AOF文件来代替现有的AOF文件,新旧两个AOF文件所保存的数据库状态相同,但新AOF文件不会包含任何浪费空间的冗余命令,体积通常会比旧AOF文件的体积小得多

注:1)通过读取服务器当前的数据库状态来实现

2)一个集合包含了超过64个元素,那么重写程序会用多条SADD命令来记录这个集合,64为默认大小,列表、各集合、哈希对象等通用

4、为了解决数据不一致问题,设置一个AOF重写缓冲区,在服务器创建子进程之后开始使用,当Redis服务器执行完一个写命令之后,它会同时将这个写命令发送给AOF缓冲区和AOF重写缓冲区。当子进程完成AOF重写工作之后,它会向父进程发送一个信号,父进程在接到该信号之后,会调用一个信号处理函数执行:1)将AOF重写缓冲区中的所有内容写入到新AOF文件中;2)对新的AOF文件进行改名,原子地覆盖现有的AOF文件,完成新旧两个AOF文件的替换

十二、事件

1、Redis服务器是一个事件驱动程序,需要处理:

1)文件事件:服务器对套接字操作的抽象

2)时间事件:服务器对定时操作的抽象

2、文件事件处理器组成部分:1)套接字;2)I/O多路复用程序;3)文件事件分派器;4)事件处理器

3、文件事件处理器的分类

1)连接应答处理器:用于对连接服务器监听套接字的客户端进行应答

2)命令请求处理器:负责从套接字中读入客户端发送的命令请求内容

3)命令回复处理器:负责将服务器执行命令后得到的命令回复通过套接字返回给客户端

4)复制处理器:用于主服务器和从服务器进行复制操作

十三、客户端

1、服务器为客户端建立相应的redis.h/redisClient结构(客户端状态)

2、客户端属性:一类是比较通用的属性,另一类是和特定功能相关的属性

3、套接字描述符,fd:-1为伪客户端,大于-1的整数为普通客户端

4、使用CLIENT setname命令可以为客户端设置一个名字

注:默认情况下,客户端没有名字

5、客户端的标志属性flags记录了客户端的角色,以及客户端目前所处的状态

6、客户端状态的输入缓冲区用于保存客户端发送的命令请求

注:最大大小不能超过1GB,否则服务器将关闭这个客户端

7、输出缓冲区:

1)固定大小的缓冲区用于保存那些长度比较小的回复

2)可变大小的缓冲区用于保存那些长度比较大的回复

注:固定缓冲区默认大小为16KB

8、客户端状态的authenticated属性用于记录客户端是否通过了身份验证,0表示未通过,1表示通过

十四、服务器

1、命令请求的执行过程

1)发送命令请求

2)读取命令请求

3)命令执行器

         a、查找命令实现

         b、执行预备操作

         c、调用命令的实现函数

         d、执行后续工作

         注:命令名字大小写不敏感

4)将命令回复发送给客户端

5)客户端接收并打印命令回复

2、serverCron函数,默认100毫秒执行一次,负责管理服务器的资源,并保持服务器自身的良好运转。

1)更新服务器时间缓存

         注:时间缓存可减少系统调用次数,优点:减少系统调用,提升性能;缺点:精度低

2)更新LRU时钟

         a、服务器状态中的lruclock属性保存了服务器的LRU时钟

         b、每个Redis对象都会有一个lru属性,保存对象最后一次被命令访问的时间

         c、空转时间=lruclock-lru

         d、lruclock的当前值查看命令:INFOserver

3)更新服务器每秒执行命令次数,即服务器在最近一秒处理的命令请求数量, 命令查看:INFOstatus

4)更新服务器内存峰值记录:服务器状态中的stat_peak_memory属性记录了服务器内存峰值大小

5)处理SIGTERM信号

6)管理客户端资源

7)管理数据库资源

8)执行被延迟的BGREWRITEAOF

9)检查持久化操作的运行状态

10)将AOF缓冲区中的内容写入AOF文件

11)关闭异步客户端

12)增加cronloops计数器的值

3、初始化服务器步骤

1)初始化服务器状态结构

2)载入配置选项

3)初始化服务器数据结构

4)还原数据库状态

5)执行事件循环

十五、复制

1、Redis中,复制方式:

1)执行命令:SLAVEOF

2)设置选项:slaveof

2、Redis复制功能分为同步和命令传播

1)同步操作用于将从服务器的数据库状态更新至主服务器当前所处的数据库状态

         注:从向主发送SYNC命令

2)命令传播操作用于在主服务器的数据库状态被修改,导致主从服务器的数据库状态出现不一致时,让主从服务器的数据库重新回到一致状态

         注:主将自己执行的写命令发送给从服务器执行

3、旧版(Redis2.8以前):断线重连后,从头开始重新复制

4、新版复制功能:PSYNC命令代替SYNC命令,具有完整重同步和部分重同步两种模式

1)完整重同步处理初次复制情况

2)部分重同步处理断线后重复制情况(只发送断线期间的写命令)

5、部分重同步功能构成:

1)主服务器的复制偏移量和从服务器的复制偏移量

         注:复制偏移量:传播N个字节则自身偏移量加N

2)复制积压缓冲区:由主服务器维护一个固定长度先进先出队列,默认1MB

         注:增量在缓冲区,断线重连则只同步增量,若增量超出缓冲区则同步全量

3)服务器运行ID

6、PSYNC命令的实现

1)进行完整重同步:PSYNC ? -1

2)PSYNC <runid><offset>

3)主服务器返回

         a、+FULLRESYNC <runid><offset>,则执行完整重同步操作

         b、+CONTINUE,则执行部分重同步

         c、-ERR,则主服务器版本低于Redis2.8,不能识别PSYNC命令,从向主发送SYNC,执行完整同步

7、复制的实现步骤

1)设置主服务器的地址和端口

2)建立套接字连接

         注:从服务器是主服务器的客户端

3)发送PING命令

         注:返回“PONG”可以继续复制,否则重新创建套接字

4)身份验证

         注:从服务器设置了masterauth选项,才进行身份验证

5)发送端口信息

6)同步

         注:在执行同步操作之后,主服务器也会成为从服务器的客户端

7)命令传播

8、心跳检测:从服务器默认每秒一次的频率,向主服务器发送命令,作用:

1)检测主服务器的网络连接状态

2)辅助实现min-slaves选项

3)检测命令丢失

         注:Redis2.8版本以前的命令丢失不做任何处理

十六、Sentinel

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

2、启动命令:

redis-sentinel /path/sentinel.conf 或redis-server /path/sentinel.conf –sentinel

执行步骤:

1)初始化服务器

2)将普通Redis服务器使用的代码替换成Sentinel专用代码

3)初始化Sentinel状态

4)根据给定的配置文件,初始化Sentinel的监视主服务器列表

5)创建连向主服务器的网络连接

         注:命令连接用于向主服务器发送命令请求,而订阅连接则用于接收指定频道的消息

3、主服务器返回的INFO命令回复包括主服务器本身的信息和属下所有从服务器的信息

4、Sentinel为主服务器创建的实例结构中的sentinels字典保存了除Sentinel本身之外,所有同样监视这个主服务器的其他Sentinel的资料。

注:a、Sentinel之间不会创建订阅连接;b、多个Sentinel设置的主观下线时长可能不同

5、当Sentinel从其他Sentinel那里接收到足够数量的已下线判断之后,会将从服务器判定为客官下线,并对主服务器执行故障转移操作

6、当一个主服务器被判断为客观下线时,各个Sentinel进行协商,选出一个领头Sentinel,并由该领头Sentinel对下线主服务器执行故障转移操作

7、故障转移步骤

1)选出新的主服务器

2)修改从服务器的复制目标

3)将旧的主服务器变为从服务器

十七、集群

1、将指定ip和port的节点添加到当前节点所在的集群中,命令:

CLUSTER MEET <ip> <port>

注:一个节点就是一个运行在集群模式下的Redis服务器,根据cluster-enabled配置选项是否为yes来决定是否开启服务器的集群模式。

2、集群数据结构clusterNode结构保存了一个节点的当前状态。

1)其link属性是一个clusterLink结构,保存了连接节点所需的有关信息

2)其clusterState机构记录了在当前节点的视角下,集群目前所处的状态

3、Redis集群通过分片的方式来保存数据库中的键值对,整个数据库被分为16384个槽,每个键都归属于一个槽,每个节点可以处理0-16384个槽。

注:当数据库中的16384个槽都有节点在处理时,集群处于上线状态;任何一个槽没有得到处理,集群处于下线状态

4、将一个或多个槽指派给节点负责,命令:CLUSTERADDSLOTS <slot> [slot …]

5、clusterNode结构

1)slots属性是一个二进制位数组,长度为2048个字节,如果slots数组在索引i上的二进制位的值为1表示节点负责处理槽i,为0表示节点不负责处理槽i。

2)numslots属性记录节点负责处理的槽的数量

6、clusterState结构中的slots数组记录了集群中所有16384个槽的指派信息,每个数组项都是一个指向clusterNode结构的指针

7、单机模式的redis-cli客户端直接将MOVED错误打印处理,不会自动转向,因为其不认识该命令。

8、Redis集群的重新分片操作可以将任意数量已经指派给某个节点(源节点)的槽改指派给另一个节点(目标节点),并且相关槽所属的键值对也会从源节点被移动到目标节点。

9、ASK错误:槽正在转移处理节点

ASK错误与MOVED错误区别:

1)MOVED错误代表槽的负责权转移到了另一个节点(客户端下次访问新节点)

2)ASK错误是两个节点再迁移槽的过程中的一种临时措施(客户端下次依然访问该节点,直到遇到MOVED错误)

10、Redis集群中主节点用于处理槽,从节点则用于复制某个主节点,在被复制的主节点下线时,代替下线主节点继续处理命令请求。

11、设置从节点命令:CLUSTERREPLICATE <node_id>,接收命令的节点成为node_id所指定节点的从节点,并开始对主节点进行复制

12、五种发送的消息:

1)MEET消息:请求接收者加入到发送者当前所处的集群里面

2)PING消息:检测是否在线

3)PONG消息:响应

4)FAIL消息:标记某节点为已下线

5)PUBLISH消息:接收到这条消息的节点都会执行相同的PUBLISH命令

十八、发布与订阅

1、Redis发布与订阅命令:

1)SUBSCRIBE:客户端可以订阅一个或多个频道,从而成为这些频道的订阅者

2)PUBLISH:发送消息

3)PSUBSCRIBE:订阅一个或多个模式,从而成为这些模式的订阅者

4)UNSUBSCRIBE:退订频道

5)PUNSUBSCRIBE:退订模式

2、服务器会将客户端与被订阅的频道在pubsub_channels字典中进行关联

3、服务器将所有模式的订阅关系都保存在服务器状态的pubsubpatterns属性里面

4、PUBSUB CHANNELS [pattern]子命令用于返回服务器当前被订阅的频道

5、PUBSUB NUMSUB[channel-1 channel-2 … channel-n]子命令接受任意多个频道作为输入参数,并返回这些频道的订阅者数量

6、PUBSUB NUMPAT子命令用于返回服务器当前被订阅模式的数量

十九、事务

1、Redis通过MULTI、EXEC、WATCH等命令来实现事务功能

2、事务实现阶段:

1)事务开始

         MULTI命令的执行标志着事务的开始,将执行该命令的客户端从非事务状态切换至事务状态

2)命令入队

3)事务执行

         当一个处于事务状态的客户端向服务器发送EXEC命令时,这个EXEC命令将立即被服务器执行

3、WATCH命令是一个乐观锁,在EXEC命令执行之前,监视任意数量的数据库键,在EXEC命令执行时,检查被监视的键是否至少有一个已经被修改过了,是的话服务器将拒绝执行事务,并向客户端返回失败

4、事务的ACID

1)原子性:Redis不支持事务回滚机制,即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下去,直到将事务队列中的所有命令都执行完毕为止。

2)一致性:

a、入队错误:服务器会拒绝执行入队过程中出现错误的事务

b、执行错误

c、服务器停

3)隔离性

4)耐久性

二十、Lua脚本

1、Redis客户端可以使用Lua脚本,直接在服务器端原子地执行多个Redis命令

2、创建并修改Lua环境

1)创建Lua环境

2)载入库函数:基础库、表格库、字符串库、数学库、调试库、Lua CJSON库、Struct库、Lua cmsgpack库

3)创建redis全局表格

4)使用Redis自制的随机函数来替换Lua原有的随机函数

5)创建排序辅助函数:保证相同的数据集总是产生相同的输出

6)创建redis.pcall函数的错误报告辅助函数

7)保护Lua的全局环境

8)将Lua环境保存到服务器状态的lua属性里面

3、Lua环境协作组件

1)伪客户端:执行Redis命令

2)lua_scripts字典:键为某个Lua脚本的SHA1校验和,值则是SHA1校验和对应的Lua脚本

二十一、排序

1、SORT <key>:对数字值的键key进行排序

2、使用ALPHA选项,SORT命令可以对包含字符串值的键进行排序:SORT <key> ALPHA

3、默认情况下,SORT升序排序,DESC 降序排序

注:升降排序均由快速排序算法执行

4、通过使用BY选项,指定某些字符串键,或者某个哈希键所包含的某些域来作为元素的权重,对一个键进行排序

注:BY选项默认权重键为数字值,若是字符串需配合使用ALPHA选项

5、LIMIT <offset><count>:只返回其中一部分已排序的元素,其中offset是要跳过的数量,count是要返回的元素数量

6、GET选项,返回元素中某些建的值

注:SORT命令返回被排序键本身所包含的元素

7、STORE:保存排序结果

8、选项的执行顺序:排序à限制排序结果集的长度à获取外部键à保存排序结果集à向客户端返回排序结果集

注:除GET选项外,改变选项的摆放顺序并不会影响SORT命令执行这些选项的顺序

二十二、二进制位数组

1、Redis处理二进制位数组命令

1)SETBIT命令:用于为位数组指定偏移量上的二进制位设置值,位数组的偏移量从0开始计数,而二进制位的值则可以是0或者1

2)GETBIT命令:用于获取位数组指定偏移量上的二进制位的值

3)BITCOUNT命令:用于统计位数组里面,值为1的二进制位的数量

4)BITOP命令:既可以对多个位数组进行按位与(and)、按位或(or)、按位异或(xor)、取反(not)运算

注:BITOP命令的所有操作都使用C语言内置的位操作来实现

2、Redis使用字符串对象来表示位数组

注:buf数组使用逆序来保存位数组可以简化SETBIT命令的实现,使得SETBIT命令可以在不移动现有二进制位的情况下,对位数组进行空间扩展

3、二进制位统计算法:

1)遍历算法:查表次数多

2)查表算法:内存压力

3)SWAR算法

4)Redis是查表和SWAR结合使用

二十三、慢查询日志

1、Redis的慢查询日志功能用于记录执行时间超过给定时长的命令请求

1)slowlog-log-slower-than选项指定执行时间超过多少微妙的命令请求会被记录到日志上

2)slowlog-max-len选项指定服务器最多保存多少条慢查询日志

2、相关命令

1)SLOWLOG GET:查看慢查询日志

2)SLOWLOG LEN:查看日志数量

3)SLOWLOG RESET:清除所有慢查询日志

3、耗费时长是微妙格式的UNIX时间戳之差

二十四、监视器

1、MONITOR:发送该命令,客户端可以将自己变为一个监视器,实时地接收并打印出服务器当前处理的命令请求的相关信息

注:每当一个客户端向服务器发送一条命令请求时,服务器除了会处理这条命令请求之外,还会将关于这条命令请求的信息发送给所有监视器

2、服务器将所有监视器都记录在monitors链表中,客户端变成监视器时其REDIS_MONITOR标识会被打开

0 0
原创粉丝点击