这期内容当中小编将会给大家带来有关复述中扫描命令的作用是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
<强>扫描命令强>
扫描命令的有扫描,SSCAN, HSCAN, ZSCAN。
扫描的话就是遍历所有的键
其他的扫描命令的话是扫描选中的集合。
扫描命令是增量的循环,每次调用只会返回一小部分的元素,所以不会有钥匙命令的坑。
扫描命令返回的是一个游标,从0开始遍历,到0结束遍历。
今天我们主要从底层的结构和源码的角度来讨论扫描是如何工作的。
<强>复述的结构强>
复述,使用了哈希表作为底层实现,原因不外乎高效且实现简单。说到哈希表,很多Java程序员第一反应就是HashMap。没错,复述,底层关键的存储结构就是类似于HashMap那样数组+链表的结构,其中第一维的数组大小为2 n (n>=0)。每次扩容数组长度扩大一倍。
扫描命令就是对这个一维数组进行遍历。每次返回的游标值也都是这个数组的索引.limit参数表示遍历多少个数组的元素,将这些元素下挂接的符合条件的结果都返回。因为每个元素下挂接的链表大小不同,所以每次返回的结果数量也就不同。
<强>扫描的遍历顺序强>
关于扫描命令的遍历顺序,我们可以用一个小栗子来具体看一下。
127.0.0.1:6379> keys * 1),“db_number" 2),“key1" 3),“myKey" 127.0.0.1:6379>, scan 0, MATCH *, COUNT 1 1),“2”; 2),1),“db_number" 127.0.0.1:6379>, scan 2, MATCH *, COUNT 1 1),“1”; 2),1),“myKey" 127.0.0.1:6379>, scan 1, MATCH *, COUNT 1 1),“3”; 2),1),“key1" 127.0.0.1:6379>, scan 3, MATCH *, COUNT 1 1),“0”; 2),(empty list 或是集)
我们的复述中有3个键,我们每次只遍历一个一维数组中的元素。如上所示,扫描命令的遍历顺序是
0→2→1→3
引用>
这个顺序看起来有些奇怪。我们把它转换成二进制就好理解一些了。
00→10→01→11
引用>
我们发现每次这个序列是高位加1的。普通二进制的加法,是从右往左相加,进位。而这个序列是从左往右相加,进位的。这一点我们在复述的源码中也得到印证。
在dict类型。c文件的dictScan函数中对游标进行了如下处理
v =,牧师(v); v + +; v =,牧师(v);意思是,将游标倒置,加一后,再倒置,也就是我们所说的“高位加1”的操作。
这里大家可能会有疑问了,为什么要使用这样的顺序进行遍历,而不是用正常的0、1、2……这样的顺序呢,这是因为需要考虑遍历时发生字典扩容与缩容的情况(不得不佩服开发者考虑问题的全面性)。
我们来看一下在扫描遍历过程中,发生扩容时,遍历会如何进行。加入我们原始的数组有4个元素,也就是索引有两位,这时需要把它扩充成3位,并进行重复。
原来挂接在xx下的所有元素被分配到0 1 xx和xx下。在上图中,当我们即将遍历10时,dict进行了重复,这时,扫描命令会从010年开始遍历,而000年和100年(原00下挂接的元素)不会再被重复遍历。
再来看看缩容的情况。假设dict从3位缩容到2位,当即将遍历110时,dict发生了缩容,这时扫描会遍历10。这时010下挂接的元素会被重复遍历,但010年之前的元素都不会被重复遍历了,所以,缩容时还是可能会有些重复元素出现的。
<强>复述的重复强>
重复是一个比较复杂的过程,为了不阻塞复述的进程,它采用了一种渐进式的重复的机制。
/*,字典,*/typedef struct  dict { ,//类型特定函数 ,dictType *类型; ,//私有数据 ,void * privdata; ,//哈希表 ,dictht ht [2]; ,//rehash 索引 ,//当,rehash 不在进行时,值为,1 ,int rehashidx;/*, rehashing not 拷贝progress if rehashidx ==1, */,//目前正在运行的安全迭代器的数量 ,int 迭代器;/*,number of iterators currently running */},dict;复述中扫描命令的作用是什么