0.基本概念
-
数据结构
-
基本类型
- String
- List
- Hash
- Set
- Sorted Set
-
扩展数据类型
- Bitmap
- HyperLogLog
- GEO
-
自定义数据类型
-
1.面向 LBS 应用的 GEO 数据类型
-
位置信息服务(Location-Based Service,LBS),GEO 就非常适合应用在 LBS 服务的场景中\
-
如果使用hash记录一系列车辆 ID 和经纬度的对应关系
- 不支持范围查询(因为元素是无序的)
-
如果使用Sorted Set,key 就是 Sorted Set 中的元素,而 value 则是元素的权重分数
-
使用GeoHash
2.GeoHash 的编码方法
-
目标:将一组经纬度转换成float,作为score分数
-
核心原理:当我们要对一组经纬度进行 GeoHash 编码时,我们要先对经度和纬度分别编码,然后再把经纬度各自的编码组合成一个最终编码
-
具体编码原理
-
经度范围是[-180,180]\
-
GeoHash 编码会把一个经度值编码成一个 N 位的二进制值,我们来对经度范围[-180,180]做 N 次的二分区操作\
-
进行第一次二分区时,经度范围[-180,180]会被分成两个子区间:[-180,0) 和[0,180]
-
如果是落在左分区,我们就用 0 表示
-
如果落在右分区,就用 1 表示
-
每做完一次二分区,我们就可以得到 1 位编码值\
-
做完 N 次的二分区后,经度值就可以用一个 N bit 的数来表示了\
-
组合规则:最终编码值的偶数位上依次是经度的编码值,奇数位上依次是纬度的编码值(2N位)1110011101
-
-
使用 GeoHash 编码后,完成物理地址分区(可以很快识别那些元素在模块中)
-
\
3.如何操作 GEO 类型?
-
GEOADD 和 GEORADIUS\
-
GEOADD:用于把一组经纬度信息和相对应的一个 ID 记录到 GEO 类型集合中\
-
GEORADIUS 命令:会根据输入的经纬度位置,查找以这个经纬度为中心的一定范围内的其他元素(半径可以自行定义)\
-
GEORADIUS cars:locations 116.054579 39.030452 5 km ASC COUNT 10 5km内,最近的,10个
-
-
可以很轻松操作经纬度
4.如何自定义数据类型?
-
Redis 的基本对象结构 RedisObject(dictentry是必备的)
-
元数据
-
type 值类型,5大基本类型\
-
encoding 值的编码方式 底层数据结构:例如 SDS、压缩列表、哈希表、跳表等\
-
lru 记录了这个对象最后一次被访问的时间,用于淘汰过期的键值对\
-
refcount 记录了对象的引用计数
-
-
*ptr指向数据的指针\
-
-
定义了新的数据类型后,也只要在 RedisObject 中设置好新类型的 type 和 encoding,再用*ptr指向新类型的实现\
-
创建NewTypeObject 的新数据类型
-
定义新数据类型底层结构
-
在 RedisObject 的 type 属性中,增加这个新类型的定义\
- Redis 的 server.h 文件中\
-
开发新类型的创建和释放函数\
-
数据类型的创建和释放函数都定义在了XXXobject.c 文件中\
-
释放函数zfree
-
-
开发新类型的命令操作\
-
在 t_newtype.c 文件中增加命令操作的实现\
-
在 server.h 文件中,声明我们已经实现的命令,以便在 server.c 文件引用这个命令\
-
在 server.c 文件中的 redisCommandTable 里面,把新增命令和实现函数关联起来\
-
-
第一步:
//单向列表
struct NewTypeObject { struct NewTypeNode *head; size_t len; }NewTypeObject;
struct NewTypeNode { long value; struct NewTypeNode *next; };
第二步:
#define OBJ_STRING 0 /* String object. */
#define OBJ_LIST 1 /* List object. */
#define OBJ_SET 2 /* Set object. */
#define OBJ_ZSET 3 /* Sorted set object. */
…
#define OBJ_NEWTYPE 7
第三步:
robj *createNewTypeObject(void){
NewTypeObject *h = newtypeNew();
robj *o = createObject(OBJ_NEWTYPE,h);
return o;
}
//需要自定义的内置函数newtypeNew 为新数据类型初始化内存结构
//位置:newtypeNew 函数定义在名为 t_newtype.c 的文件中(类似接口实现)
//t_string.c 和 t_list.c 分别对应 String 和 List 类型
NewTypeObject *newtypeNew(void){
NewTypeObject *n = zmalloc(sizeof(*n));//使用zmalloc分配内存,以便写入数据
n->head = NULL;
n->len = 0;
return n;
}
//通用的内置函数createObject
robj *createObject(int type, void *ptr) {
robj *o = zmalloc(sizeof(*o));
o->type = type;
o->ptr = ptr;
...
return o;
}
第四步
//声明插入操作
void ntinsertCommand(client *c){ //基于客户端传递的参数,实现在NewTypeObject链表头插入元素}
//引用命令 void ntinsertCommand(client *c)
//绑定指令和函数 struct redisCommand redisCommandTable[] = {
...
{"ntinsert",ntinsertCommand,2,"m",...}
}
5.小结
-
GEO 类型使用 GeoHash 编码方法实现了经纬度到 Sorted Set 中元素权重分数的转换,这其中的两个关键机制就是对二维地图做区间划分,以及对区间进行编码。一组经纬度落在某个区间后,就用区间的编码值来表示,并把编码值作为 Sorted Set 元素的权重分数\
-
扩展类型
-
基于现有的数据类型,通过数据编码或是实现新的操作的方式,来实现扩展数据类型
-
geohash\
-
Bitmap\
-
-
开发自定义的数据类型,具体的操作是增加新数据类型的定义,实现创建和释放函数,实现新数据类型支持的命令操作
-