千家信息网

Redis中scan命令的作用是什么

发表于:2024-12-04 作者:千家信息网编辑
千家信息网最后更新 2024年12月04日,这期内容当中小编将会给大家带来有关Redis中scan命令的作用是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。SCAN 命令SCAN命令的有SCAN,SSCA
千家信息网最后更新 2024年12月04日Redis中scan命令的作用是什么

这期内容当中小编将会给大家带来有关Redis中scan命令的作用是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

SCAN 命令

SCAN命令的有SCAN,SSCAN,HSCAN,ZSCAN。

SCAN的话就是遍历所有的keys

其他的SCAN命令的话是SCAN选中的集合。

SCAN命令是增量的循环,每次调用只会返回一小部分的元素。所以不会有KEYS命令的坑。

SCAN命令返回的是一个游标,从0开始遍历,到0结束遍历。

今天我们主要从底层的结构和源码的角度来讨论scan是如何工作的。

Redis的结构

Redis使用了Hash表作为底层实现,原因不外乎高效且实现简单。说到Hash表,很多Java程序员第一反应就是HashMap。没错,Redis底层key的存储结构就是类似于HashMap那样数组+链表的结构。其中第一维的数组大小为2n(n>=0)。每次扩容数组长度扩大一倍。

scan命令就是对这个一维数组进行遍历。每次返回的游标值也都是这个数组的索引。limit参数表示遍历多少个数组的元素,将这些元素下挂接的符合条件的结果都返回。因为每个元素下挂接的链表大小不同,所以每次返回的结果数量也就不同。

SCAN的遍历顺序

关于scan命令的遍历顺序,我们可以用一个小栗子来具体看一下。

127.0.0.1:6379> keys *1) "db_number"2) "key1"3) "myKey"127.0.0.1:6379> scan 0 MATCH * COUNT 11) "2"2) 1) "db_number"127.0.0.1:6379> scan 2 MATCH * COUNT 11) "1"2) 1) "myKey"127.0.0.1:6379> scan 1 MATCH * COUNT 11) "3"2) 1) "key1"127.0.0.1:6379> scan 3 MATCH * COUNT 11) "0"2) (empty list or set)

我们的Redis中有3个key,我们每次只遍历一个一维数组中的元素。如上所示,SCAN命令的遍历顺序是

0->2->1->3

这个顺序看起来有些奇怪。我们把它转换成二进制就好理解一些了。

00->10->01->11

我们发现每次这个序列是高位加1的。普通二进制的加法,是从右往左相加、进位。而这个序列是从左往右相加、进位的。这一点我们在redis的源码中也得到印证。

在dict.c文件的dictScan函数中对游标进行了如下处理

v = rev(v);v++;v = rev(v);

意思是,将游标倒置,加一后,再倒置,也就是我们所说的"高位加1"的操作。

这里大家可能会有疑问了,为什么要使用这样的顺序进行遍历,而不是用正常的0、1、2……这样的顺序呢,这是因为需要考虑遍历时发生字典扩容与缩容的情况(不得不佩服开发者考虑问题的全面性)。

我们来看一下在SCAN遍历过程中,发生扩容时,遍历会如何进行。加入我们原始的数组有4个元素,也就是索引有两位,这时需要把它扩充成3位,并进行rehash。

原来挂接在xx下的所有元素被分配到0xx和1xx下。在上图中,当我们即将遍历10时,dict进行了rehash,这时,scan命令会从010开始遍历,而000和100(原00下挂接的元素)不会再被重复遍历。

再来看看缩容的情况。假设dict从3位缩容到2位,当即将遍历110时,dict发生了缩容,这时scan会遍历10。这时010下挂接的元素会被重复遍历,但010之前的元素都不会被重复遍历了。所以,缩容时还是可能会有些重复元素出现的。

Redis的rehash

rehash是一个比较复杂的过程,为了不阻塞Redis的进程,它采用了一种渐进式的rehash的机制。

/* 字典 */typedef struct dict { // 类型特定函数 dictType *type; // 私有数据 void *privdata; // 哈希表 dictht ht[2]; // rehash 索引 // 当 rehash 不在进行时,值为 -1 int rehashidx; /* rehashing not in progress if rehashidx == -1 */ // 目前正在运行的安全迭代器的数量 int iterators; /* number of iterators currently running */} dict;

在Redis的字典结构中,有两个hash表,一个新表,一个旧表。在rehash的过程中,redis将旧表中的元素逐步迁移到新表中,接下来我们看一下dict的rehash操作的源码。

/* Performs N steps of incremental rehashing. Returns 1 if there are still * keys to move from the old to the new hash table, otherwise 0 is returned. * * Note that a rehashing step consists in moving a bucket (that may have more * than one key as we use chaining) from the old to the new hash table, however * since part of the hash table may be composed of empty spaces, it is not * guaranteed that this function will rehash even a single bucket, since it * will visit at max N*10 empty buckets in total, otherwise the amount of * work it does would be unbound and the function may block for a long time. */int dictRehash(dict *d, int n) { int empty_visits = n*10; /* Max number of empty buckets to visit. */ if (!dictIsRehashing(d)) return 0; while(n-- && d->ht[0].used != 0) { dictEntry *de, *nextde; /* Note that rehashidx can't overflow as we are sure there are more  * elements because ht[0].used != 0 */ assert(d->ht[0].size > (unsigned long)d->rehashidx); while(d->ht[0].table[d->rehashidx] == NULL) {  d->rehashidx++;  if (--empty_visits == 0) return 1; } de = d->ht[0].table[d->rehashidx]; /* Move all the keys in this bucket from the old to the new hash HT */ while(de) {  uint64_t h;  nextde = de->next;  /* Get the index in the new hash table */  h = dictHashKey(d, de->key) & d->ht[1].sizemask;  de->next = d->ht[1].table[h];  d->ht[1].table[h] = de;  d->ht[0].used--;  d->ht[1].used++;  de = nextde; } d->ht[0].table[d->rehashidx] = NULL; d->rehashidx++; } /* Check if we already rehashed the whole table... */ if (d->ht[0].used == 0) { zfree(d->ht[0].table); d->ht[0] = d->ht[1]; _dictReset(&d->ht[1]); d->rehashidx = -1; return 0; } /* More to rehash... */ return 1;}

通过注释我们就能了解到,rehash的过程是以bucket为基本单位进行迁移的。所谓的bucket其实就是我们前面所提到的一维数组的元素。每次迁移一个列表。下面来解释一下这段代码。

  • 首先判断一下是否在进行rehash,如果是,则继续进行;否则直接返回。

  • 接着就是分n步开始进行渐进式rehash。同时还判断是否还有剩余元素,以保证安全性。

  • 在进行rehash之前,首先判断要迁移的bucket是否越界。

  • 然后跳过空的bucket,这里有一个empty_visits变量,表示最大可访问的空bucket的数量,这一变量主要是为了保证不过多的阻塞Redis。

  • 接下来就是元素的迁移,将当前bucket的全部元素进行rehash,并且更新两张表中元素的数量。

  • 每次迁移完一个bucket,需要将旧表中的bucket指向NULL。

  • 最后判断一下是否全部迁移完成,如果是,则收回空间,重置rehash索引,否则告诉调用方,仍有数据未迁移。

由于Redis使用的是渐进式rehash机制,因此,scan命令在需要同时扫描新表和旧表,将结果返回客户端。

上述就是小编为大家分享的Redis中scan命令的作用是什么了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注行业资讯频道。

元素 命令 就是 数组 顺序 结构 数量 索引 过程 一维 字典 底层 渐进式 游标 源码 结果 作用 不同 安全 接下来 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 机关加强网络安全宣传教育 网络安全法指的网络安全是指 无线网络安全答案 网络技术学考 软件开发人员作息规律 普洱高密度存储服务器费用 mssql数据库编码 创造与魔法服务器维修在什么时候 200平米机房服务器多大 apex手游港服连接服务器失败 河南第三方软件开发大概多少钱 物业管理信息系统课程设计数据库 阿里云数据库新建用户 华为网络技术学院建设方案 陕西戴尔服务器维修维保多少钱 云服务器可以分析转录组数据吗 国家网络安全宣传周广播稿 虚拟机上建的服务器密码忘了 教育系统网络安全风险分析 计算机网络技术工资高吗 威胁网络安全的四种方式 循环结构与数据库结合的应用 福建英才软件开发有限公司 计算机三级网络技术配置 阿里云数据库新建用户 西安中行软件开发 pg数据库添加只读权限账户 河北实用软件开发代理品牌 学生表数据库教程视频 msl数据库文件
0