在数据处理中使用缓存

举报
码乐 发表于 2024/04/22 16:20:51 2024/04/22
【摘要】 1 缓存数据库的结构分析 1.1 使用的数据结构和数据类型对象动态字符串动态字符串结构体是SDS定义,由源码sds.h/sdshdr结构表示一个SDS值。其c实现了包括减少修改字符串带来的内存分配次数,空间预分配方案,惰性空间释放方案,兼容部分C字符串函数,避免缓冲区溢出等。链表链表提供了高效的节点重排能力,以及顺序性的节点访问方式,可以通过增删节点灵活调整链表长度。reids的数据结构 ...

1 缓存数据库的结构分析

1.1 使用的数据结构和数据类型对象

  • 动态字符串

动态字符串结构体是SDS定义,由源码sds.h/sdshdr结构表示一个SDS值。

其c实现了包括减少修改字符串带来的内存分配次数,空间预分配方案,惰性空间释放方案,兼容部分C字符串函数,避免缓冲区溢出等。

  • 链表
    链表提供了高效的节点重排能力,以及顺序性的节点访问方式,可以通过增删节点灵活调整链表长度。
    reids的数据结构 列表 底层实现之一是链表。

redis的链表实现有以下特点:

    无环双端链表,带有表头指针和表尾指针。 通过list结构的head指针和tail指针。
    多态支持,链表节点使用 void* 指针保存节点值,可以通过list结构的dup,free,match三个属性为节点值设置类型特定函数,以保持不同数据类型。
  • 字典

保存键值对的抽象数据结构,又被称之为 symbol table,关联数组 associative array,映射 map
字典每个键唯一,但是值可以为空或相同。

redis使用 murmurHash 算法作为底层实现,这个算法在2008年出现,即使输入的键有规律,算法仍能给出一个很好的随机分布特点。
有兴趣的可以搜索它。

每个哈希表节点都有一个next指针,多个哈希标节点可以使用next指针构成一个单向链表,被分配到同一索引的多个节点可以用这个单向链表链接。

扩展和收缩hash表可以通过执行 rehash重新散列完成。

  • 跳跃表
    跳跃表skiptable是一种有序数据结构,它通过在每个节点维持多个指向其他节点的指针,从而达到快速访问节点的目的。

跳跃表效率可以和平衡树对比,但是实现更为简单,所以有不少程序使用跳跃表代替平衡树。
比如一个有序集,会员信息

    ZRANGE member-price 0 2 WITH-SCORES
    ZCARD member-price

member-price 有序集合的全部数据都保存在一个跳跃表,每个表节点都保存一个成员的信息。

与链表和字典不同,redis缓存数据库有两个地方使用这个结构,一个是有序集合键,另一个是集群节点的内部数据结构。

它由zskiplist和zskiplistNode两个结构组成,其中zskiplist用于保存跳跃表信息,比如表头,表尾节点,长度。Node保存跳跃表节点。

每个跳跃表节点层高都是1到32个随机数。在同一个跳跃表,多个节点可以包括相同分值。每个节点成员对象必须唯一。

跳跃表节点按分值大小进行排序。 节点按成员对象大小排序。

  • 整数集合
    redis缓存集合键的底层实现是 整数集合 inset。当一个集合包括集合元素不多时,Redis就使用整数集合作为集合键底层实现。

如果集合只包括2个整数元素,这个集合键底层实现就是 整数集合。

    127.0.0.1:6379> SADD numb 1 2
    (integer) 2 
    127.0.0.1:6379> OBJECT ENCODING numb
    "intset"

整数集合可以保存整数值的抽象数据结构,int16_t, int32_t, int64_t,并保存集合元素不重复

每当我们需要把一个新元素添加到整数集,并且新元素类型比整数集合全部元素都长,整数集合需要升级,然后添加。

升级过程就是一般的三个升级步骤:

    1, 分配新空间; 
    2,底层数组元素全部转换为新元素相同类型,放置到新空间正确位置,维护相对顺序;
    3,新元素添加到新空间。

整数集合支持升级操作,但是不支持降级操作。

  • 压缩列表

ziplist 时列表键和哈希键的底层实现。 当一个列表键只包括少量项目,并且每个列表项要么小整数型,要么就是长度比较短的字符串,

那么Redis就使用压缩列表做列表键底层实现。

    HMSET peoples "name" "jack" "age" 59 "job" "programmer"
    OK
    127.0.0.1:6379> OBJECT ENCODING peoples
    "ziplist"

当一个哈希键只包括少量键值对,并且键值对时小整数或短字符串,redis就使用压缩列表做哈希键底层。

每个压缩列表节点可以保存一个自己数组或一个整数值,其中字节数可以是以下三个长度的一个

<= 63 自己的字节数组 2^6 -1
<= 16383 字节的字节数组 2^14 - 1
<= 4294967295 的字节数组 2^32 -1

整数值可以是以下6个长度之一

4字节  0~12之间的无符号整型
1字节  有符号整型
3字节 有符号整型
int16_t 类型整型
int32_t 
int64_t

每个压缩列表ziplist 由 previous_entry_length, encoding, content 三个部分组成。
它们分别记录压缩列表

previous_entry_length 前一个节点长度(属性长度1或5字节),

encoding 节点的content属性所保存数据的类型和长度,

content 保存节点值

压缩列表是一种节省内存的顺序型数据结构。 常被用作列表键和哈希键的底层实现。
它可以包括多个节点,每个节点保存一个字节数组或整数值。

添加新节点到压缩列表,或者从压缩列表删除节点,可能引发连锁更新,但是几率不高。

  • 数据类型对象系统

Reids使用以上这些数据结构时,使用的一个独特的对象系统,这个系统包含字符串对象,列表对象,哈希对象,结合对象。有序集合对象这些。

根据对象指令,redis判断是否符合执行条件,并且基于引用计数的方式回收内存 和 对象共享,以方便在适当条件下,通过让多个数据库键共享一个对象节省内存。

    127.0.0.1:6379> RPUSH zst 1 2 3 1999 "hello" "world"
    (integer) 6
    127.0.0.1:6379> OBJECT ENCODING zst
    "quicklist"

redis对象带有访问事件记录信息,可用于计算数据库键的空转时长,在服务器启用了maxmemory功能时,空转时长较大的哪些可能被优先删除。

可以查看它们的类型:

字符串类型对象 REDIS_STRING

    SET infos "hello world"
    TYPE infos
    string

哈希类型对象 REDIS_STRING

    HMSET peoples "name" "jack" "age" 59 "job" "programmer"
    TYPE peoples
    hash  

列表对象 REDIS_LIST

    RPUSH rst 1 2 3 1999 "hello" "world"
    TYPE rst
    list

集合对象 REDIS_SET

    SADD numb 1 2 apple banana	
    TYPE numb
    set

有序集合对象 REDIS_ZSET

    ZADD price 8.5 apple 10.1 banana 9.8 cherry 
    TYPE price
    zset

未定义

    TYPE nothing
    none

1.2 数据库

  • 数据库的实现

服务器保持数据库的方法,
redis缓存将全部数据库保持在 服务器状态 redis.h/redisServer 结构的db数组中。

db数组每个项都是一个 redis.h/redisDb 结构。

在redis初始化时,程序根据服务器状态 dbnum属性决定创建多少数据库。
默认为16个,并且用户没有指定使用哪一个时,默认使用数据库0.

  • 键空间:

db数组每个项都是一个 redis.h/redisDb 结构,这个字典被称之为键空间。

键空间的键也就是redis数据库的键。 值可以是字符串对象,列表对象,哈希表对象,集合对象,有序集对象任意一个Redis对象。

在redis数据库中添加一个键,也就是在健空间字典添加一个键。 从redis数据库删除一个键,也就是从键空间删除一个键。

  • 键生存周期,过期策略

      指令:
    
      EXPIRE 
      PEXPIRE
    

在某个时间点过期

    EXPIREAT
    PEXPIREAT

本质上都在底层执行 PEXPIREAT 指令。

数据库接收客户端的秒或毫秒精度的键设置生存时间,TTL。
在经过指定秒数或毫秒之后,服务器就自动删除生存时间0的键。

TTL,PTLL指令接受一个带有生存时间或者过期时间的键,返回这个键的剩余生存时间,
也就是返回距离这个键被服务器自动删除还有多长时间。

SET key value
EXPIRE key 1000
TTL key

在底层的expires字典保存了数据库中全部键的过期时间。 也被称之为过期字典

过期字典键是一个指针,这个指针指向键空间某个键对象也就是某个数据库键。
过期字典值是一个 long long 类型整数,这个整数保存了键所指向的数据库键过期时间,毫秒精度的unix时间戳。

取消过期时间

        127.0.0.1:6379> set name jack
        OK
        127.0.0.1:6379> pexpire name 10000000
        (integer) 1
        127.0.0.1:6379> get name
        "jack"
        127.0.0.1:6379> ttl name
        (integer) 9996
        127.0.0.1:6379> ttl name
        (integer) 9993
        127.0.0.1:6379> PERSIST name
        (integer) 1
        127.0.0.1:6379> ttl name
        (integer) -1
        127.0.0.1:6379> get name
        "jack"

确定过期键状态

通过过期字典,程序可以用以下步骤检查一个给定键是否过期:

1 检查给定键是否存在过期字典,如果是,则取得过期时间
2 检查Unix时间戳是否大于键过期时间,如果是,键已过期,否则,键未过期。

  • 过期键删除策略
    缓存清理过期键的方式有三种

      1,定时删除
      用户最友好的方式,但是对CPU时间不友好。
      设置过期时间的同时,创建一个定时器 timer,让定时器在键的过期时间去临时,立即对键执行删除操作。
      使用大量定时器,是不现实的。
      2,惰性删除
      放任键过期不管,但是每次从键空间获取键,都检查是否过期,没有过期就返回改键。否则就删除。
      对CPU友好,只有程序取出键时才对键进行检查和删除。
      如果永远不使用该键,那么内存将被永远占用。
      3,被动定期删除 
      每隔一段时间,程序对数据库进行一次检查,删除其中过期键,算法决定删除多少过期键,以及检查多少数据库。
    

一般场景配合定时删除和惰性删除,就可以匹配大多数场景。

  • 持久化 RDB 与 AOF

在执行SAVE指令或BGSAVE指令后,redis将创建一个新的RDB文件。
程序对数据库的键进行检查,已经过期的不会被保存。 因此,数据库中包含过期键不会造成持久存储的影响。

服务启动时,加载RDB文件,如果服务为主服务,那么载入RDB文件时,程序会文件保存的键进行检查,没有过期的键被加载。

如果服务器是备机,在载入RDB文件时,将载入全部键,并且从主服务同步键的状态。

当过期键被惰性删除或定期删除后,程序向AOF文件追加 append 一条 DEL指令,显式记录该键已经被删除。
如果客户端通过 GET 类似的指令访问过期键 name,

1 服务器从数据库删除 name
2 追加一条 DEL name 到 AOF文件
3 向执行GET指令的客户端返回空回复。

数据库包括过期键,不会对AOF及其重写造成影响。
如果服务器为复制服务器,那么键的删除操作由主服务器控制。

  • 事件处理

文件事件,
redis基于Reactor模式开发了字节的网络事件处理器,这个处理器被称之为文件事件处理器 file event handler

    1 文件事件处理器使用 I/O 多路复用同时监听多个套接字,并根据套件字目前执行的认为为套接字关联不同事件处理。
    
    2 当被监听的套接字准备好执行链接应答 accept,读取 read, 写入 write, close 关闭时,与操作相对的文件事件将产生。

此时文件事件处理器就会调用套接字之前关联的事件处理器处理这些事件。

单线程redis服务,通过多路复用的方式高性能监听文件事件,可以很好地与redis服务器中其他同样单线程模块对接。保持简单性。

套接字                                               事件处理器
    socket1                                        -->   命令请求处理器
    socket2  --> I/O多路复用程序  --> 文件事件调度器 -->   指令回复处理器
    socket3  -->                                   -->   连接应答处理器
    socket4                                         

文件事件对套接字操作的抽象,每当一个套接字准备好执行连接应答 accpet,写入,读取,关闭时,就此时文件事件。

尽管多个事件可能并发产生,但是IO多路复用总是将产生的套接字放入一个队列,再处理这个队列:有序,同步地,每次一个套接字
当处理完上一个后,再处理下一个。

一个完整的客户端与服务器通信处理过程如下:
1 服务器 AE_READ-ABLE 事件处于监听状态下,而该事件所对应的处理器为连接应答处理器。

2 这是一个redis客户端向服务器发起连接,监听套接字将产生AE_READABLE事件,触发连接应答处理器执行。

处理器将应答客户端的连接请求,并将客户端套接字 AE_READ-ABLE 事件与命令请求处理器关联。

3 客户端向服务器发一条指令执行,客户端将产生 AE_READABLE事件,引发命令请求处理器执行。

4 执行命令产生响应的回复,服务器将客户端套接字 AE_WRITABLE事件与命令回复处理器关联。 当客户端尝试读取指令回复时。

触发命令回复处理器执行,当命令恢复处理器将命令回复全部写入到套接字后,服务器就会解除客户端套接字的 AE_WRITABLE事件与回复处理器的关联。

时间事件

定时事件,让一个程序在指定时间后执行一次,比如,让程序X在当前事件在30毫秒后执行一次。

周期性事件,比如每隔30秒执行一次。

时间事件由三部分组成:

id: 服务器为事件事件创建的全局唯一ID 标识号。 ID号按从小到大顺序递增,新事件的ID号总是大一些。

when: 毫秒精度的UNIX时间戳,记录了时间事件到达arrive的事件

timeProc: 时间事件处理器,一个函数。 当事件事件到达,服务就调用相关处理器处理事件。

周期性事件更加通用。

时间事件在底层被存入一个无序链表,每当时间执行器运行,它就遍历整个链表,查找已到达时间的事件,并调用相关事件处理器。

比如serverCron函数的应用,持续运行的Redis服务需要定期对自身资源和状态进行检测和调整,从而确保服务器可以长期,稳定运行。

这些定期操作由 redis.c/serverCron负责执行

1,更新服务器各类统计信息,比如时间,内存占用,数据库占用
2,清理数据库过期键值对
3,关闭和清理连接失效客户端
4,尝试进行AOF或RDB持久化操作
5,负责主服务与从服务的同步操作
6,如果是集群,对集群进行定期同步和连接测试

redis服务器以周期性事件方式运行serverCron函数,
在服务器运行期间,每隔一段时间(由配置的hz控制),serverCron就执行一次,直到服务器关闭。

默认情况下,"hz "被设置为10。提高该值将在Redis 空闲时使用更多的 CPU Redis 处于空闲状态,但同时也会使 Redis 在以下情况下反应更迅速有许多键同时到期,超时可能被处理得更精确。

范围在1到500之间,但是超过100的值通常不是个好主意。

大多数用户应该使用默认的10,并将其提高到只有在需要非常低的延迟的环境下才可以提高到100。

  • 服务器的调度执行方式

数据库添加,删除,查看,更新操作的实现方法。

由于存在文件事件和时间事件,决定何时分类处理它们将很重要。

事件的调度和执行由 ae.c/aeProcessEvents负责。它主要由以下步骤:

1, 获取到达时间离当前时间最接近的时间事件
2, 计算最接近的时间事件距离达到还有多少毫秒
3,如果事件已达到时间,那么 remaind_ms 的值可能为负,设置为0
4,根据 remaind_ms 的值,创建timeval结构
5, 阻塞并等待文件事件产生,最大阻塞事件由传入的timeval结构决定
6,如果remaind_ms 值为0,那么 aeApiPoll 调用之后马上返回,不阻塞
7,处理全部已产生的文件事件
8,处理全部已到时间点的时间事件

redis服务是一个事件驱动程序,分为文件事件和时间事件。
文件事件基于Reactor模式实现的网络通信程序。

文件事件是对接套接字操作的抽象: 每次套接字变为可应答 acceptable,可写 writable,可读 readable 相关事件就产生。

文件事件分为读/写两类(AE_READABLE, AE_WRITEABLE).

时间事件分为定时事件和周期性事件,服务器一般只执行serverCron函数这一个时间事件,并且是周期性的,通常会晚毫秒级执行时间。

文件事件和时间事件为合作关系,服务器轮流处理,不会进行抢占。

服务器启动到可以处理客户端数据需要进行以下步骤:

1 初始化服务器状态
2 载入服务器配置
3 初始化服务器数据结构
4 还原数据库状态
5 执行事件循环。

1.3 集群和插槽分片

  • 主从复制

用户可以通过执行SLAVEOF指令或者设置slaveof,让一个服务器去复制replicate 另一个服务器master对主服务器进行复制的服务器被称之为从服务器 slave

进行复制的主从服务器双方的数据库将保持相同的数据,概念上这种现象称之为: 数据库状态一致。
因此,在主服务设置某个键,可以到从服务获取这个键的信息。比如主服务设置

                SET name jack

从服务获取

                GET name
                jack

删除的时候,如果从主服务删除,从服务也将被删除,这也就是状态的一致的表现。

PSYNC 从服务器请求复制主服务器数据。

如果从服务器没有复制过主服务器数据,或者执行过 SLAVEOF no one指令,

那么从服务器在开始向主服务开始一次新的服务指令为 PSYNC ? -1

主动请求主服务器进行完整重同步。

如果从服务器已经复制了某个主服务器,那么从服务器在开始一次新的复制时,将向主服务器发
PSYNC runid offset 指令

runid 为上一次复制的主服务器运行id,而offset则是从服务器当前复制偏移量。

接受这个指令的主服务器通过这两个参数判断应该对从服务器执行哪个操作。

  • 高可用:哨兵Sentinel

在集群功能出现之前使用哨兵模式,可以提供一定的可靠性。

一个或多个Sentinel实例组成一个系统,监视任意多个服务器,这些服务器之下可能有多个从服务器。

在主服务器下线后,从服务器将被哨岗服务升级为新的主服务。

    在 5以下的旧版本,支持redis-sentinel
    在 5之上的版本,redis-server

1.3.1 分布式集群

  • 高可用:分布式集群

性能:支持1000 个节点的集群

安全:redis系统尝试 保留来自大多数主节点连接的客户端写入. 确认的写入可能会丢失.

可用性:大多数主节点可访问时 可存活下来. 而当主节点下线时,复制这个主节点的从节点将代替主节点处理数据。

特点:

    可自定义的插槽分片指令
    可用执行集群命令
    可重新分片
    故障转移功能支持
    消息转移功能支持

集群通过分片 sharding 进行数据共享,并提供复制和故障转移功能。

1 配置和启动 至少3*1 节点

2,Cluster数据分片

默认redis cluster不使用 一致散列算法,而是一种不同形式的分片。每个键在概念上都是散列槽的一部分。

redis 集群有16384个散列槽。 要计算给定键的哈希槽是哪一个,我们只需取密钥的 CRC16 模数值 16384

redis每个节点都负责哈希hash 槽的一个子集,您可能有一个包含3个节点的集群

            节点A包含从0到5500的哈希槽
            节点B包含从5501 到 11000的哈希槽
            节点C包含从11001 到16383的哈希槽

这允许在集群中轻松添加和删除节点。如果我想添加一个新节点D,我需要将一些哈希槽从节点A,B,C移动到D。

如果我想从集群中删除节点A,可以移动A提供的哈希槽到B或C,当节点A为空时,我可以将其从集群中完全删除。

由于移动 hash槽不需要停止操作,所以添加删除,更改节点持有的哈希槽百分比不需要任何停机时间。

同时redis支持多键操作(事务,lua脚本)所有键都属于同一个哈希槽,用户可以使用 散列标签 的概念强制多个键成为同一个散列槽的一部分。

散列标签纪录在redis 集群规范中,但如果键中{} 中有字符串,则仅对字符串内容进行散列。

如 this{foo}key,another{foo}key保证在同一个散列槽中,并且可以在具有多个键为参数的命令中一起使用

3,主从模式

    用于灾备恢复

集群主节点A,B,C与从节点A1,B1,C1组成,任何主节点故障,从节点成为主节点,系统将继续允许。

4, 集群一致性保证

使用异步复制,没有很强一致性保证,
客户端写入主A
主A想您的客户端回复 OK
主设备A将写入传播到它的从服务A1,A2...
A回复客户端时,不会等到全部从设备的确认回复。
如果强制 数据刷新到磁盘后才回复 客户端,那就相当于同步复制。需要在性能与一致性之间权衡。

redis的绝对同步写入数据到磁盘。 指令 wait 使得丢失写入的可能性大大降低。 但是在复杂的环境仍然 有可能将无法接收写入的从站选为主站。
官方解决办法为红锁。

写入的最大窗口:

如果分区的多数侧已经有足够的时间来选举一个从属 作为主,那么少数侧的每个主节点都将停止接受写入。

最大窗口的时间量 是一个非常重要的配置指令 称为节点超时。

节点超时后,主节点被视为发生故障,此时可以 用其副本之一替换。 同时将无法感知其他主节点,也不再接受写入。

集群的主要可靠性保证方式,特征如下:

1 集群节点通过握手将其他节点添加到自己所处的集群中。

2 集群中16385个槽可以分别指派给集群各个节点,每个节点都记录哪些槽位分配给自己,哪些给了其他节点。

3 节点在接到一个指令请求后,先检测这个指令请求要处理的键所在的槽位是否自己负责,不是的话将向客户端返回 MOVED错误和正确节点

4 集群重新分片工作是redis-trib负责的,重新分片的关键术语某个槽位的所有键值对从一个节点转移到另一个节点。

5 如果节点A正迁移槽位i到节点B,那么当节点A没有在自己的数据库找到命令指定的数据库键时,节点A向客户端返回ASK错误和节点B

6 MOVED错误表示槽位的负责权已经从一个节点转移到另一个,ASK错误指示两个节点迁移槽位的临时措施。

7 集群的从节点用于复制主节点,在主节点下线后,代替主节点继续处理数据。

8 集群节点通过发送和接受消息进行通信,常见的包括 MEET,PING,PONG,PUBLISH,FAIL 五类。

1.4 消息转发和事务处理

  • 高效的发布和订阅

      以PUBLISH,SUBSCRIBE,PSUBSCRIBE指令组成
    

执行SUBSCRIBE指令,客户端可以订阅一个或多个频道,从而成为这些频道的订阅者 subscriber,
每当有其他客户端向被订阅频道发消息,频道的全部订阅者都会收到该消息。

一个客户端发布,允许多个客户端 多次消费
即发即弃: 非持久化消息机制,发布者和订阅者必须同时在线。
不保证数据完整性。
不支持消息确认机制 Ack/Nack/Reject 需要自己在应用层面实现,但如果这样,可以直接使用MQ,ZeroMq,RobbitMQ,Kafka(需要另启用独立服务)。
  • 数据库的变更通知:通过发布订阅获知数据库的变更通知。

例: 获取 0号默认数据库的全部删除通知

    SUBSCRIBE "_ _keyevent@0_ _:del"

默认情况下,所有通知都是禁用的,因为大多数用户不需要 这个功能,而且这个功能有一些开销。
请注意,如果你没有指定K或E中的至少一个,就不会有事件被传递。

可以通知Pub/Sub客户在钥匙空间中发生的事件。 这个功能的文档在http://redis.io/topics/notifications

例如,如果钥匙空间事件通知被启用,并且一个客户端 对存储在数据库0中的密钥 "foo "进行DEL操作,两个 消息将通过Pub/Sub发布。

            # PUBLISH __keyspace@0__:foo del
            # PUBLISH __keyevent@0__:del foo

可以选择Redis将在一组的类。每个类都由一个字符来标识。

    # K 关键空间事件,以 __keyspace@<db>__ 前缀发布。
    # E 关键事件,用__keyevent@<db>__前缀发布。
    # g 通用命令(非特定类型),如DEL, EXPIRE, RENAME, ...
    # $ 字符串命令
    # l 列表命令
    # s 设置命令
    # h 哈希命令
    # z 排序的集合命令
    # x 过期事件(每次密钥过期时产生的事件)。
    # e 驱逐事件(当一把钥匙被驱逐到最大内存时产生的事件)。
    # A g$lshzxe的别名,因此 "AKE "字符串表示所有事件。

"notify-keyspace-events "的参数是一个字符串,它由由零或多个字符组成。
空字符串意味着通知被禁用。

例子:要启用列表和通用事件,从事件名称的角度来看事件名称,使用。

  notify-keyspace-events Elg

例子2:获取订阅频道的过期密钥流

  name __keyevent@0__:expired use:

  notify-keyspace-events Ex

数据库事件通知执行过程如下

1 server.notify_keyspace_events 属性就是服务器配置 
notigy-keyspace-events 选项所设置的值
2 如果给定的通知时服务器允许的,那么下一步将检查服务器是否允许发生键空间通知。
如果可以则构建并发生
3 最后,函数检测服务器是否允许发生键事件通知,如果允许,程序构建并发出

PUBLISH的底层实现函数就是 pubsubPubishMessage函数。

  • 支持事务

通过MULTI,EXEC,WATCH指令实现事务 transaction。

事务提供一种将多个指令请求打包,然后一次性,按顺序执行的机制,

并且在事务执行期间,服务器不会中断事务而改去执行其他客户端命令。

它需要执行完成事务的全部指令后,才处理其他客户端命令请求。

比如一个典型的事务执行过程:

 MULTI, 
 SET name jack, 
 GET name, 
 SET age 55, 
 GET age, 
 EXEC.
  • 扩展脚本Lua

通过在服务器嵌入Lua脚本的支持,客户端可以使用Lua脚本在服务器原子地执行Redis指令。

    127.0.0.1:6379> EVAL   "return 'hello,wrold'" 0 "hello world"
    "hello,wrold"

EVALSHA 指令对执行过的脚本做校验和验证。
管理指令包括 SCRIPT FLUSH,

    127.0.0.1:6379> SCRIPT FLUSH
            OK
    127.0.0.1:6379> SCRIPT EXISTS
    (empty list or set)
    127.0.0.1:6379> SCRIPT LOAD 
    127.0.0.1:6379> SCRIPT KILL
  • 排序

排序指令可以对列表键,集合键,有序集合的值进行排序。
支持数字和字母 升降序排序。
默认情况下,排序是数字的,元素按解释为双精度浮点数的值进行比较。这是SORT最简单的形式:

    SORT mylist

假设mylist是一个数字列表,此命令将返回相同的列表,其中的元素从小到大排序。为了将数字从大到小排序,使用DESC修饰符:

    SORT mylist DESC

当mylist包含字符串值并且您想按字典顺序对它们进行排序时,请使用ALPHA修饰符:

    SORT mylist ALPHA

Redis 支持 UTF-8,前提是您正确设置了LC_COLLATE环境变量。

  • 二进制位数组

缓存提供了 SETBIT, GETBIT, BITCOUNT, BITOP 四个指令用于处理二进制位数组

SETBIT 用于为位数组制定偏移量的二进制位设置值,位数组的偏移量从0开始计数,二进制位值可以为0 或 1

		SETBIT bit 3 1
		(integer) 0

GETBIT 用于获取位数组制定偏移量的二进制值。

		GETBIT bit 3
		(integer) 1

BITCOUNT 用于统计位数组,值为1的二进制为数量。

		BITCOUNT bit 
		(integer) 2

BITOP 对多个位数组按位与,或,亦或运算

    	SETBIT bit 3 1
		(integer) 1
		127.0.0.1:6379> SETBIT bit 1 1
		(integer) 0
		127.0.0.1:6379> SETBIT z 2 1
		(integer) 0
		127.0.0.1:6379> SETBIT z 0 1
		(integer) 0

    	BITOP AND and-result bit z
		(integer) 1
		127.0.0.1:6379> BITOP OR or-result bit z
		(integer) 1
		127.0.0.1:6379> BITOP XOR xor-result bit z
		(integer) 1
  • 慢查询日志
    慢查询日志用于记录执行事件超过给定时长的命令请求,用户也可通过此功能产生日志去监视和优化查询速度。

              127.0.0.1:6379> CONFIG SET slowlog-log-slower-than 0
              OK 
    

慢查询日志最多记录5个,CONFIG SET slowlog-max-len 5

            127.0.0.1:6379> CONFIG SET slowlog-max-len 5
            OK
            127.0.0.1:6379> SET msg "hello"
            OK
            127.0.0.1:6379> SETBIT z 2 1
            (integer) 1
            127.0.0.1:6379> SET database "mysql"
            OK

使用SLOWLOG GET指令查看服务器保存的慢查询日志:

            127.0.0.1:6379> SLOWLOG GET
            1) 1) (integer) 9
               2) (integer) 1675563638
               3) (integer) 28
               4) 1) "SET"
                  2) "database"
                  3) "mysql"
               5) "127.0.0.1:55216"
               6) ""
            2) 1) (integer) 8
               2) (integer) 1675563624
               3) (integer) 5
               4) 1) "SETBIT"
                  2) "z"
                  3) "2"
                  4) "1"
               5) "127.0.0.1:55216"
               6) ""
            3) 1) (integer) 7
               2) (integer) 1675563621
               3) (integer) 40
               4) 1) "SET"
                  2) "msg"
                  3) "hello"
               5) "127.0.0.1:55216"
               6) ""
            4) 1) (integer) 6
               2) (integer) 1675563605
               3) (integer) 5
               4) 1) "CONFIG"
                  2) "SET"
                  3) "slowlog-max-len"
                  4) "5"
               5) "127.0.0.1:55216"
               6) ""
            5) 1) (integer) 5
               2) (integer) 1675563598
               3) (integer) 15
               4) 1) "CONFIG"
                  2) "SET"
                  3) "slowlog-max-than"
                  4) "5"
               5) "127.0.0.1:55216"
               6) ""
  • 监视器 monitor
    通过执行monitor命令,客户端可以将自己变成监视器。 实时接收当前服务或数据库的执行情况。

监视数据库0,默认数据库

        127.0.0.1:6379> select 0
        OK
        127.0.0.1:6379> monitor
        OK

监视数据库10

        127.0.0.1:6379> select 10
        OK
        127.0.0.1:6379[10]> monitor
        OK

2 小结

本文企图在一个篇幅内说完缓存服务的结构注定是徒劳的。

但是读者可以大致一览,其实现的思想,对底层具体实现有一个了解,在执行和使用redis指令时将更清晰它是怎样运行的,
有助于定位问题和提高功能实现效率。

参考:

    https://redis.io/commands/sort/
    www.lua.org/manual/5.x/manual.html
    skip lists A probabilistic Alternative to BST
    <C语言实现> 1~4章节
    <Redis设计与实现>
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。