以北京为中心,方圆 200km 以内的城市找出来 3 个,按照远近顺序排列,这个命令不会排除 北京。
当然,也可以根据经纬度来查询(将 member 换成对应的经纬度):
scan 实际上是 keys 的一个升级版。
可以用 keys 来查询 key,在查询的过程中,可以使用通配符。keys 虽然用着还算方便,但是没有分页功能。同时因为 Redis 是单线程,所以 key 的执行会比较消耗时间,特别是当数据量大的时候,影响整个程序的运行。
为了解决 keys 存在的问题,从 Redis2.8 中开始,引入了 scan。
scan 具备 keys 的功能,但是不会阻塞线程,而且可以控制每次返回的结果数。
2.基本用法首先准备 10000 条测试数据:
package org.taoguoguo.scan; import org.taoguoguo.redis.Redis; /** * @author taoguoguo * @description ScanTest * @website https://www.cnblogs.com/doondo * @create 2021-04-25 14:27 */ public class ScanTest { public static void main(String[] args) { Redis redis = new Redis(); redis.execute(jedis -> { for (int i = 0; i < 10000; i++) { jedis.set("k" + i, "v" + i); } }); } }scan 命令一共提供了三个参数,第一个 cursor,第二个参数是 key,第三个参数是 limit。
cursor 实际上是指一维数组的位置索引,limit 则是遍历的一维数组个数(所以每次返回的数据大小可能不确定)。
scan 0 match k8* count 1000 3.遍历原理及渐进式 rehash机制SCAN的遍历顺序
假设目前有三条数据:
127.0.0.1:6379> keys * 1) "key1" 2) "db_number" 3) "myKey" 127.0.0.1:6379> scan 0 match * count 1 1) "2" 2) 1) "key1" 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) "db_number" 127.0.0.1:6379> scan 3 match * count 1 1) "0" 2) (empty list or set) 127.0.0.1:6379>在遍历的过程中,大家发现游标的顺序是 0 2 1 3,从十进制来看好像没有规律,但是从转为二进制,则是有规律的:
00->10->01->11这种规律就是高位进1,传统的二进制加法,是从右往左加,这里是从左往右加。
实际上,在 Redis 中,它的具体计算流程给是这样:
将要计算的数字反转
给反转后的数字加 1
再反转
那么为什么不是按照 0、1、2、3、4...这样的顺序遍历呢?因为主要考虑到两个问题:
字典扩容
字典缩容
根据Scan遍历原理假如我们将要访问 110 时,发生了扩容,此时 scan 就会从 0110 开始遍历,之前已经被遍历过的元素就不会被重复遍历了
假如我们将要访问 110 时,发生缩容,此时 scan 就会从 10 开始遍历,这个时候,也会遍历到 010,但是 010 之前的不会再被遍历了。所以,在发生缩容的时候,可能返回重复的元素。
Redis一共支持5种数据结构,hash是其中的一种,在hash扩容的时候采用的是渐进式rehash的方式。要想深入理解渐进式rehash,首先要了解以下Redis中hash的数据结构。 哈希表节点 typedef struct...
哈希表节点 typedef struct dictEntry { void *key; // 键 union { void *val; uint64_t u64; int64_t s64; double d; } v; // 值 struct dictEntry *next; // 下一个节点 } dictEntry; 哈希表 /* This is our hash table structure. Every dictionary has two of this as we * implement incremental rehashing, for the old to the new table. */ typedef struct dictht { dictEntry **table; // 哈希表数组 unsigned long size; // 哈希表大小 unsigned long sizemask; // 掩码,计算索引值,size-1 unsigned long used; // 哈希表已有节点的数量 } dictht; 字典 typedef struct dict { dictType *type; // 类型特定函数 void *privdata; // 私有数据 dictht ht[2]; // 哈希表 // rehash索引 long rehashidx; /* rehashing not in progress if rehashidx == -1 */ unsigned long iterators; /* number of iterators currently running */ } dict; 特定函数 typedef struct dictType { // 计算哈希值的函数 uint64_t (*hashFunction)(const void *key); // 复制键的函数 void *(*keyDup)(void *privdata, const void *key); // 复制值的函数 void *(*valDup)(void *privdata, const void *obj); // 对比键的函数 int (*keyCompare)(void *privdata, const void *key1, const void *key2); // 销毁键的函数 void (*keyDestructor)(void *privdata, void *key); // 销毁值的函数 void (*valDestructor)(void *privdata, void *obj); } dictType;字典中包含一个数据结构dictht的ht数组,一般情况下字典只是用ht[0]用来存储数据,ht[1]在rehash时使用。
哈希算法原理