笔记:Redis核心原理与实战分析(上)

Redis是如何执行的

命令执行流程

一条命令的执行过程有很多细节,但大体可分为:客户端先将用户输入的命令,转化为 Redis 相关的通讯协议(RESP),再用 socket 连接的方式将内容发送给服务器端,服务器端在接收到相关内容之后,先将内容转化为具体的执行命令,再判断用户授权信息和其他相关信息,当验证通过之后会执行最终命令,命令执行完之后,会进行相关的信息记录和数据统计,然后再把执行结果发送给客户端,这样一条命令的执行流程就结束了。如果是集群模式的话,主节点还会将命令同步至子节点。

img

socket

每个 socket 被创建后,会分配两个缓冲区,输入缓冲区和输出缓冲区。 写入函数并不会立即向网络中传输数据,而是先将数据写入缓冲区中,再由 TCP 协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是 TCP 协议负责的事情。 注意:数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,这取决于当时的网络情况、当前线程是否空闲等诸多因素,不由程序员控制。 读取函数也是如此,它也是从输入缓冲区中读取数据,而不是直接从网络中读取。

IO多路复用

Redis 使用的是 I/O 多路复用功能来监听多 socket 链接的,这样就可以使用一个线程链接来处理多个请求,减少线程切换带来的开销,同时也避免了 I/O 阻塞操作,从而大大提高了 Redis 的运行效率。

img

“多路”指的是多个网络连接,“复用”指的是复用同一个线程,采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求,这样就减少了创建和销毁线程所带来的时间消耗,从而到达高效处理大量并发请求的目的

Redis持久化

https://juejin.cn/post/6844903939339452430

RDB

RDB持久化是通过快照的方式,即在指定的时间间隔内将内存中的数据集快照写入磁盘。在创建快照之后,用户可以备份该快照,可以将快照复制到其他服务器以创建相同数据的服务器副本,或者在重启服务器后恢复数据。RDB是Redis默认的持久化方式

AOF

除了RDB持久化,Redis还提供了AOF(Append Only File)持久化功能,AOF持久化会把被执行的写命令写到AOF文件的末尾,记录数据的变化。默认情况下,Redis是没有开启AOF持久化的,开启后,每执行一条更改Redis数据的命令,都会把该命令追加到AOF文件中,这是会降低Redis的性能,但大部分情况下这个影响是能够接受的,另外使用较快的硬盘可以提高AOF的性能

数据结构(常用五种)

字符串

redis字符串基于SDS(redis内部实现的一种数据结构)实现,SDS对象包含三种不同的数据类型:int、embstr、raw

int

当我们设置的值为整数类型时,在redis中以int类型存储;

embstr

当我们设置的值为字符串类型时,在redis中以embstr类型存储;

raw

当我们设置的字符串长度大于44字节时,在redis中以raw类型存储;

(1字节=8位)按编码类型字与字节的关系如下:

  1. 英文

    字节数 : 1;编码:GB2312 字节数 : 1;编码:GBK 字节数 : 1;编码:GB18030 字节数 : 1;编码:ISO-8859-1 字节数 : 1;编码:UTF-8 字节数 : 4;编码:UTF-16 字节数 : 2;编码:UTF-16BE 字节数 : 2;编码:UTF-16LE

    1. 中文

    字节数 : 2;编码:GB2312 字节数 : 2;编码:GBK 字节数 : 2;编码:GB18030 字节数 : 1;编码:ISO-8859-1 字节数 : 3;编码:UTF-8 字节数 : 4;编码:UTF-16 字节数 : 2;编码:UTF-16BE 字节数 : 2;编码:UTF-16LE

为什么是44字节:在redis中,如果SDS的存储值大于64字节时,Redis的内存分配器会认为此对象为大字符串,并使用raw类型来处理,当数据小64字节时,会使用embstr类型存储。既然内存分配器的判断标准是64字节,那为什么embstr类型和raw类型的存储判断值是44字节;因为Redis在存储对象时,会创建此对象的关联信息,redisObject对象头和SDS自身属性信息,这些信息都会占用一定的存储空间,在redis的源码中:

redisObject信息占用16字节

typedef struct redisObject {
    unsigned type:4; // 4 bits (0.5字节)
    unsigned encoding:4; // 4 bits  (0.5字节)
    unsigned lru:LRU_BITS; // 3 个字节
    int refcount; // 4 个字节
    void *ptr; // 8 个字节
} robj;
// type 对象的数据类型,例如:string、list、hash 等,占用 4 bits 也就是半个字符的大小;
// encoding 对象数据编码,占用4bits
// lru 记录对象的LRU信息(Least Recently Used 的缩写,即最近最少使用)信息,内存回收时会用到此属性,占用 24 bits(3 字节);
// refcount:引用计数器,占用 32 bits(4 字节);
// *ptr:对象指针用于指向具体的内容,占用 64 bits(8 字节)。

SDS信息占用7字节,SDS 的存储类型一共有 5 种:SDSTYPE5、SDSTYPE8、SDSTYPE16、SDSTYPE32、SDSTYPE64,在这些类型中最小的存储类型为 SDSTYPE5,但 SDSTYPE5 类型会默认转成 SDSTYPE8(如图)

img
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; // 1 byte
    uint8_t alloc; // 1 byte
    unsigned char flags; // 1 byte
    char buf[];
};

可以看出除了内容数组(buf)之外,其他三个属性分别占用了 1 个字节,最终分隔字符等于 64 字节,减去 redisObject 的 16 个字节,再减去 SDS 自身的 3 个字节,再减去结束符 \0 结束符占用 1 个字节,最终的结果是 44 字节(64-16-3-1=44)

img

字典

字典又称散列类型或哈希表。使用redis的字典类型来存储信息(比如go map)不需要手动进行序列化和反序列化(如果使用字符串存则需要手动序列化成json字符串然后取出使用时进行反序列化)

哈希表

在redis中使用数组(桶)加链表(拉链法解决冲突)实现哈希表

typedef struct dictEntry { // dict.h
    void *key;
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next; // 下一个 entry
} dictEntry;
img

哈希冲突

字典类型的存储流程是先将键值进行 Hash 计算,得到存储键值对应的数组索引,再根据数组索引进行数据存储,但在小概率事件下可能会出完全不相同的键值进行 Hash 计算之后,得到相同的 Hash 值,这种情况我们称之为哈希冲突

查询流程

  • 通过计算(Hash,取余等)操作获得数组的索引值,根据索引值找到对应元素

  • 判断元素和查找值是否相等,相等则成功返回数据(说明没有出现冲突),否则需要查看next指针是否还有对应其他元素,如果没有则返回null(说明不存在),如果存在则重复此步骤

渐进式reHash

redis为了保证应用的高性能运行,提供了一个重要的机制——渐进式reHash。在字典进行扩容或者缩容时采用的都是渐进式reHash机制

列表

redis列表类型

在3.2之前基于字节数组的ziplist实现,3.2之后是基于双向链表+ziplist实现的quicklist(本质上是双端队列)

集合

当集合元素都是int类型且数量在一定范围内(默认是512个,可以通过配置修改)时,集合类型为整数集合;其他类型则是基于hashtab实现的,key为集合元素值,value为null

有序集合

有序集合在元素小于128个且所有元素都小于64字节时(默认是64字节,可以通过配置修改),基于ziplist存储;反之以dict + skiplist(跳表,对普通链表进行改进,插入数据时保持整体有序,在查找时通过二分进行查找)来存储

事务

事务指的是提供一种将多个命令打包,一次性按顺序地执行的机制,并且保证服务只有在执行完食物中的所有命令后,才会继续处理此客户端的其他命令

redis中的事务从开始到结束也是要经历三个阶段:开启事务,命令入列,执行事务/放弃事务

事务错误

执行时错误

img

即使事务队列中的某个命令在执行期间发生了错误,事务也会继续执行,直到事务队列中的所有命令执行完成

入列时错误,但不终止整个事务

img

重复执行multi会导致入列错误,但不会终止事务,最终查询的结果事务是执行成功的

入列时错误,终止整个事务

img

不支持运行时错误的事务回滚

  • redis作者认为 Redis 事务的执行时,错误通常都是编程错误造成的,这种错误通常只会出现在开发环境中,而很少会在实际的生产环境中出现,所以他认为没有必要为 Redis 开发事务回滚功能;

  • 不支持事务回滚是因为这种复杂的功能和 Redis 追求的简单高效的设计主旨不符合

监控

watch命令用于客户端在并发情况下,为事务提供一个乐观锁(CAS,check and set),也就是可以用watch命令来监控一个或多个变量,如果在事务的过程中,某个监控项被修改了,那么整个事务就会终止执行

watch 命令只能在客户端开启事务之前执行,在事务中执行 watch 命令会引发错误,但不会造成整个事务失败

过期

过期策略

常见的过期策略有:定时删除(每隔一段时间进行删除);惰性删除(软删除);定期删除(每个时间段的同一个时间点进行删除)

定时删除

在设置键值过期时间时,创建一个定时事件,当过期时间到达时,由事件处理器自动执行键的删除操作。

  • 优点:保证内存可以被尽快地释放。

  • 缺点:在 Redis 高负载的情况下或有大量过期键需要同时处理时,会造成 Redis 服务器卡顿,影响主业务执行。

惰性删除

不主动删除过期键,每次从数据库获取键值时判断是否过期,如果过期则删除键值,并返回 null。

  • 优点:因为每次访问时,才会判断过期键,所以此策略只会使用很少的系统资源。

  • 缺点:系统占用空间删除不及时,导致空间利用率降低,造成了一定的空间浪费。

定期删除

每隔一段时间检查一次数据库,随机删除一些过期键。

Redis 默认每秒进行 10 次过期扫描,此配置可通过 Redis 的配置文件 redis.conf 进行配置,配置键为 hz 它的默认值是 hz 10

需要注意的是:Redis 每次扫描并不是遍历过期字典中的所有键,而是采用随机抽取判断并删除过期键的形式执行的。

Redis 使用的是惰性删除加定期删除的过期策略

管道(pipeline)

管道技术是一种批处理技术,用于一次处理多个redis命令,从而提高整个交互的性能

管道技术主要解决了多个命令集中请求造成网络资源浪费的问题

需要注意的事项

  1. 发送的命令数量不会被限制,但输入缓存区(也就是命令的最大存储体积)最大为1GB,当发送的命令超过此限制时,命令不会被执行,并且会被redis服务器断开链接

  2. 如果管道的数据过多可能会导致客户端的等待时间过长,导致网络阻塞

  3. 部分客户端自己本身也有缓存区大小的设置,如果管道命令没有没执行或者是执行不完整,可以排查此情况或发送较少的管道内的命令重新尝试执行

游标迭代器 Scan

在redis中keys的时间复杂度是O(n),所以数据量越大查询时间就越长,如果数据足够大时执行keys命令会导致Redis假死不可用,导致出现故障

在redis 2.8以后可以通过Scan命令实现keys的匹配功能;Scan时通过游标进行查询的不会造成假死;Scan提供了count参数,可以规定遍历的数量;Scan会把下标返回给客户端,用户客户端继续遍历查询;Scan返回的结果可能会有重复数据;单次返回值不为0则说明遍历还没有结束;Scan可以保证开始检索之前,被删除的元素一定不会被查询出来;在迭代过程中如果有元素被修改,Scan不保证能查询出相关的元素。

优秀的基数统计算法 HyperLogLog

  1. 能够使用极少的内存来统计巨量的数据,它只需要12K的空间就能统计2**64的数据;

  2. 统计存在一定的误差,误差率整体较低,标准误差为0.81/100

  3. 误差通过设置辅助计算因此进行降低

操作:添加一个元素pfadd key element [element ...];统计不重复的元素pfcount key [key ...];合并一个或多个HLL至新结构pfmerge destkey sourcekey [sourcekey ...]

内存淘汰机制与算法

在redis中,过期策略和内存淘汰策略是两个完全不同的概念;首先redis过期策略指的是redis使用哪种策略来删除已经过期的键值对,而redis内存淘汰机制指的是当运行内存已经超过redis设置的最大内存之后,将采用什么策略删除键值对

最大允许内存

配置项为maxmemory,只有在redis的运行内存达到了某个阈值,才会触发内存淘汰机制。如果maxmemory配置值不大于0则不会出发内存淘汰机制

查询最大运行内存

使用config get maxmemory来查看设置的最大运行内存(在64位操作系统中maxmemory默认值为0,在32位操作系统中默认值为3GB)

内存淘汰策略

查看内存淘汰策略

使用命令config get maxmemory-policy来查看当前的内存淘汰策略

内存淘汰策略分类

早期版本的 Redis 有以下 6 种淘汰策略:

  1. noeviction:不淘汰任何数据,当内存不足时,新增操作会报错,Redis 默认内存淘汰策略;

  2. allkeys-lru:淘汰整个键值中最久未使用的键值;

  3. allkeys-random:随机淘汰任意键值;

  4. volatile-lru:淘汰所有设置了过期时间的键值中最久未使用的键值;

  5. volatile-random:随机淘汰设置了过期时间的任意键值;

  6. volatile-ttl:优先淘汰更早过期的键值。

在 Redis 4.0 版本中又新增了 2 种淘汰策略:

  1. volatile-lfu:淘汰所有设置了过期时间的键值中,最少使用的键值;

  2. allkeys-lfu:淘汰整个键值中最少使用的键值。

其中 allkeys-xxx 表示从所有的键值中淘汰数据,而 volatile-xxx 表示从设置了过期键的键值中淘汰数据。

修改redis内存淘汰策略

  • 方式一:通过“config set maxmemory-policy 策略”命令设置。它的优点是设置之后立即生效,不需要重启 Redis 服务,缺点是重启 Redis 之后,设置就会失效。

  • 方式二:通过修改 Redis 配置文件修改,设置“maxmemory-policy 策略”,它的优点是重启 Redis 服务后配置不会丢失,缺点是必须重启 Redis 服务,设置才能生效。

内存淘汰算法

LRU算法

LRU(Least Recently Used)最近最少使用的,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰

  1. 实现:基于链表实现,链表中的元素按照操作顺序从前往后排列,最新操作的键会被移动到表头,需要内存淘汰时,只需要删除链表尾部的元素。

  2. 近LRU算法:redis使用的是一种近似LRU算法,目的是为了更好的节约内存,它的实现方式是给现有的数据结构添加一个额外的字段,用于记录此键值的最后一次访问时间,redis内存淘汰时,会以随机采样的方式来淘汰数据(默认配置未5个),然后淘汰最久没有使用的那个。

LFU算法

LFU(Least Frequently Used)最不常用的,LFU算法是根据总访问次数来淘汰数据的,它认为“如果数据过去被访问多次,那么将来被访问的频率也更高”

消息队列

发布订阅模式

发布订阅模式的三个命令:

  • subscribe channel 普通订阅

  • publish channel message 消息推送

  • psubscribe pattern 主题订阅

普通发布与订阅

发布者生产消息

订阅者消费消息

订阅者需要关注该发布者

主题订阅

发布者生产了该主题类型消息

订阅者收到该主题类型消息

订阅者订阅该主题

在redis5.0之前发布订阅模式存在以下两个缺点

  1. 无法持久化保存消息,如果 Redis 服务器宕机或重启,那么所有的消息将会丢失;

  2. 发布订阅模式是“发后既忘”的工作模式,如果有订阅者离线重连之后不能消费之前的历史消息。(在该模式下,如果所有消费者都离线,redis生产者生产的消息并不会保留等待消费者节点来消费)

在5.0之后添加了 Stream 类型解决上述两个缺点

基于Redis List的消息队列

lpush向队列中推送消息(生产消息),brpop(阻塞读,当队列中没有数据时就会进入休眠状态,避免一直循环执行消耗系统资源)从队列中拉取消息(消费消息)

优缺点分析

List 优点:

  • 消息可以被持久化,借助 Redis 本身的持久化(AOF、RDB 或者是混合持久化),可以有效的保存数据;

  • 消费者可以积压消息,不会因为客户端的消息过多而被强行断开。

List 缺点:

  • 消息不能被重复消费,一个消息消费完就会被删除;

  • 没有主题订阅的功能

基于Redis zset的消息队列

zadd向队列中推送消息(生产消息),zrangebyscore从队列中拉取消息(消费消息)

优缺点分析

ZSet 优点:

  • 支持消息持久化;

  • 相比于 List 查询更方便,ZSet 可以利用 score 属性很方便的完成检索,而 List 则需要遍历整个元素才能检索到某个值。

ZSet 缺点:

  • ZSet 不能存储相同元素的值,也就是如果有消息是重复的,那么只能插入一条信息在有序集合中;

  • ZSet 是根据 score 值排序的,不能像 List 一样,按照插入顺序来排序;

  • ZSet 没有向 List 的 brpop 那样的阻塞弹出的功能。

终极解决方案 Stream

在redis5.0之前消息队列的实现方式各有缺点:

  1. 发布订阅模式PubSub,不能持久化也就无法可靠的保存消息,并且对于离线重连的客户端不能读取历史消息

  2. 列表实现的消息队列,消息不能重复消费,一个消息消费完就会被删除

  3. 有序集合消息队列,不能存储相同value的消息,并且不能阻塞读消息

并且以上方式实现的消息队列只能存储单value值,也就是如果你要存储一个对象则需要先序列化成json字符串,消费时还需要进行反序列化。

基于以上问题redis提供了Stream类型(支持消息持久化、消息轨迹、支持ack确认消息),相关操作:

  • xadd 添加消息;

  • xlen 查询消息长度;

  • xdel 根据消息 ID 删除消息;

  • del 删除整个 Stream;

  • xrange 读取区间消息

  • xread 读取某个消息之后的消息。

基于stream实现简易版的消息队列:生产消息(xadd),消费消息(xread)

基于stream消息分组实现的发布订阅模式消息队列:同一个分组内的多个 consumer 会读取到不同消息,不同的 consumer 不会读取到分组内的同一条消息

Redis Stream 适用于小型、廉价的应用程序

分布式锁详解

什么是锁(程序级别)

锁是一种常用的并发控制机制,用于保证一项资源在任何时候只能被一个线程使用,如果其他线程也要使用同样的资源,必须排队等待上一个线程使用完(释放锁)

img

什么是分布式锁

分布式锁是用于分布式环境下并发控制的一种机制,用于控制某个资源在同意时刻只被一个应用所使用

img

常见的分布式锁实现方式:

  1. 基于Memcached实现:使用add命令,添加成功表示创建分布式锁成功

  2. 基于Zookeeper实现:使用ZooKeeper顺序临时节点来实现分布式锁

  3. 基于Redis实现分布式锁:使用setnx(set if not exists),创建成功表示创建分布式锁成功

基于Redis实现分布式锁

使用setnx lock true命令创建锁

使用del lock命令删除锁(释放锁)

如果锁未被释放而使用setnx lock true尝试创建锁则会返回"0"表示创建失败

使用setnx存在的问题

如果一个程序创建锁之后异常退出,那么这个锁将永远得不到释放,就造成了死锁问题

我们可以使用setnx创建锁之后,使用expire lock 过期时长来设置超时时间,但是如果在执行完创建锁之后出现断电或redis异常退出则会导致设置过期时间命令未执行,依然会造成死锁

使用带参数的set

使用set lock true ex 30 nx创建过期时间为30秒的锁(保证了创建过期锁的原子性),这样可以解决基于setnx命令实现分布式锁可能会造成死锁的问题

分布式锁执行超时问题

如果我们设置锁的最大超时时间为30秒,但业务处理使用了35秒,这就导致原有业务还未执行完成,锁就被释放了,新的程序和旧的程序一起操作就会带来线程安全问题

img

因此:1. 把执行比较耗时的任务不要放到加锁的方法呢,锁内的方法尽量控制执行时长;2. 适当延长最大超时时间

锁被误删问题

假设锁的最大超时时间为30秒,应用1执行了35秒,而应用2在30秒时(由于应用1创建的锁已过期)创建锁,然后在35秒后,应用1执行完之后,就会把应用2创建的锁给删除(释放),从而出现锁被误删问题

img

解决方法:在使用set命令创建锁时,给value值设置一个归属人标识用于区分锁是否属于该线程,只有属于该线程时才能操作锁。

需要注意的是在代码中的判断逻辑和删除逻辑可能不具备原子性,我们需要使用Lua脚本来执行判断和删除操作

布隆过滤器

更多原理见数据结构的布隆过滤器

开启布隆过滤器

在redis中不能直接使用布隆过滤器,在4.0版本之后提供的modules(扩展模块)的方式引入

编译方式开启

git clone https://github.com/RedisLabsModules/redisbloom.git
cd redisbloom
make # 编译redisbloom,编译成功后会在根目录生成一个redisbloom.so文件

# 启动redis服务器
./src/redis-server redis.conf --loadmodule ./src/modules/RedisBloom-master/redisbloom.so
# 其中 --loadmodule 为加载扩展模块的意思,后面跟的是 redisbloom.so 文件的目录

docker方式开启

# docker命令方式
docker pull redislabs/rebloom  # 拉取镜像
docker run -p6379:6379 redislabs/rebloom  # 运行容器

# docker-compose方式
# docker-compose.yml文件的版本
version: "2"
# 管理的服务
services:
  redis:
    # 指定镜像
    image: redislabs/rebloom:latest
    ports:
    # 端口映射
    - 6380:6379
    volumes:
    # 目录映射
    - "${REDIS_DIR}/conf:/usr/local/etc/redisbloom"
    - "${REDIS_DIR}/data:/data"

操作布隆过滤器

布隆过滤器的命令不是很多,主要包含以下几个:

  1. bf.add:添加元素

  2. bf.exists:判断某个元素是否存在

  3. bf.madd:添加多个元素

  4. bf.mexists:判断多个元素是否存在

  5. bf.reserve:设置布隆过滤器的准确率

127.0.0.1:6379> bf.reserve user 0.01 200
(error) ERR item exists #已经存的 key 设置会报错
127.0.0.1:6379> bf.reserve userlist 0.9 10
OK

可以看出此命令必须在元素刚开始执行,否则会报错,它有三个参数:key、error_rate 和 initial_size。

  • error_rate:允许布隆过滤器的错误率,这个值越低过滤器占用空间也就越大,以为此值决定了位数组的大小,位数组是用来存储结果的,它的空间占用的越大(存储的信息越多),错误率就越低,它的默认值是 0.01。

  • initial_size:布隆过滤器存储的元素大小,实际存储的值大于此值,准确率就会降低,它的默认值是 100。

原理

Redis 布隆过滤器的实现,依靠的是它数据结构中的一个位数组,每次存储键值的时候,不是直接把数据存储在数据结构中,因为这样太占空间了,它是利用几个不同的无偏哈希函数,把此元素的 hash 值均匀的存储在位数组中,也就是说,每次添加时会通过几个无偏哈希函数算出它的位置,把这些位置设置成 1 就完成了添加操作。

当进行元素判断时,查询此元素的几个哈希位置上的值是否为 1,如果全部为 1,则表示此值存在,如果有一个值为 0,则表示不存在。因为此位置是通过 hash 计算得来的,所以即使这个位置是 1,并不能确定是那个元素把它标识为 1 的,因此布隆过滤器查询此值存在时,此值不一定存在,但查询此值不存在时,此值一定不存在

并且当位数组存储值比较稀疏的时候,查询的准确率越高,而当位数组存储的值越来越多时,误差也会增大。

位数组和 key 之间的关系,如下图所示:

image.png

布隆过滤器使用场景

它的经典使用场景包括以下几个:

  • 垃圾邮件过滤

  • 爬虫里的 URL 去重

  • 判断一个元素在亿级数据中是否存在

布隆过滤器在数据库领域的使用也比较广泛,例如:HBase、Cassandra、LevelDB、RocksDB 内部都有使用布隆过滤器。

延迟队列

延时队列是指把当前要做的事情往后推迟一段时间再做

使用场景有:

  1. 超过30分钟未支付的订单,将会被取消

  2. 外卖商家超过5分钟未接单的订单,将会被取消

  3. 在平台注册但30天内未登录的用户,发短信提醒

基于MQ实现延迟队列

RabbitMQ 本身并不支持延迟队列,但可以通过添加插件 rabbitmq-delayed-message-exchange 来实现延迟队列。

优点

  1. 支持分布式

  2. 支持持久化

缺点

框架比较重,需要搭建和配置 MQ。

基于Redis实现延迟队列

Redis 是通过有序集合(ZSet)的方式来实现延迟消息队列的,ZSet 有一个 Score 属性可以用来存储延迟执行的时间。

优点

  1. 灵活方便,Redis 是互联网公司的标配,无序额外搭建相关环境;

  2. 可进行消息持久化,大大提高了延迟队列的可靠性;

  3. 支持分布式

  4. 高可用性,利用 Redis 本身高可用方案,增加了系统健壮性。

缺点

需要使用无限循环的方式来执行任务检查,会消耗少量的系统资源。

定时任务

定时任务指的是指定一个时间来执行某个任务

开启键空间通知

redis服务端默认是不开启键空间通知的,需要我们手动开启。

配置 notify-keyspace-events Ex (该配置表示开启键事件通知里面的 key 过期事件)

更多配置项说明:

  • K:键空间通知,所有通知以 __keyspace@<db>__ 为前缀

  • E:键事件通知,所有通知以 __keyevent@<db>__ 为前缀

  • g:DEL、EXPIRE、RENAME 等类型无关的通用命令的通知

  • $:字符串命令的通知

  • l:列表命令的通知

  • s:集合命令的通知

  • h:哈希命令的通知

  • z:有序集合命令的通知

  • x:过期事件,每当有过期键被删除时发送

  • e:驱逐(evict)事件,每当有键因为 maxmemory 政策而被删除时发送

  • A:参数 g$lshzxe 的别名

订阅发布

使用订阅者订阅元素的过期事件,然后再执行固定的任务

RedisSearch高性能的全文搜索引擎

RedisSearch是一个高性能的全文搜索引擎,它可以作为一个Redis Module(扩展模块)运行在Redis服务器上

特性

  • 基于文档的多个字段全文索引

  • 高性能增量索引

  • 文档排序(由用户在索引时手动提供)

  • 在子查询之间使用 AND 或 NOT 操作符的复杂布尔查询

  • 可选的查询子句

  • 基于前缀的搜索

  • 支持字段权重设置

  • 自动完成建议(带有模糊前缀建议)

  • 精确的短语搜索

  • 在许多语言中基于词干分析的查询扩展

  • 支持用于查询扩展和评分的自定义函数

  • 将搜索限制到特定的文档字段

  • 数字过滤器和范围

  • 使用 Redis 自己的地理命令进行地理过滤

  • Unicode 支持(需要 UTF-8 字符集)

  • 检索完整的文档内容或只是 ID 的检索

  • 支持文档删除和更新与索引垃圾收集

  • 支持部分更新和条件文档更新

安装

推荐使用docker方式安装

查询

如果要查询中文内容的话,需要在添加数据时设置中文编码,并且在查询时也要设置中文编码,指定“language "chinese"”

慢查询

Redis慢查询作用和MySQL慢查询作用类似,都是为我们查询出不合理的执行命令,然后让开发人员和运维人员一起来规避这些耗时的命令,从而让服务器更加高效和健康的运行。对于单线程的Redis来说,不合理的使用更是致命的,因此掌握Redis慢查询技能对我们来说非常的关键。

相关配置

  • slowlog-log-slower-than:用于设置慢查询的评定时间,也就是说超过此配置项的命令,将会被当成慢操作记录在慢查询日志中,它执行单位是微秒(默认10000微秒,注:1 秒等于 1000000 微秒)

  • slowlog-max-len:用来配置慢查询日志的最大记录数(默认保存128条)。

查询慢日志

命令slowlog show

(当慢查询日志超过设定的最大存储条数之后,会把最早的执行命令依次舍弃)

最后更新于

这有帮助吗?