文章目录

  • Redis核心技术与实战
    • 实践篇
      • 13 | GEO是什么?还可以定义新的数据类型吗?
        • 面向 LBS 应用的 GEO 数据类型
        • GEO 的底层结构
        • GeoHash 的编码方法
        • 如何操作 GEO 类型?
        • 如何自定义数据类型?
        • Redis 的基本对象结构
        • 开发一个新的数据类型

Redis核心技术与实战

实践篇

13 | GEO是什么?还可以定义新的数据类型吗?

面向 LBS 应用的 GEO 数据类型

LBS(Location-Based Service,位置信息服务) 应用访问的数据是和人或物关联的一组经纬度信息,而且要能查询相邻的经纬度范围,GEO 非常适合应用在 LBS 服务的场景中。

GEO 的底层结构

GEO 类型的底层数据结构就是用 Sorted Set 来实现的。

以叫车应用为例,用 Sorted Set 来保存车辆的经纬度信息时,Sorted Set 的元素是车辆 ID,元素的权重分数是经纬度信息,如下图所示:

Sorted Set 元素的权重分数是一个浮点数(float 类型),而一组经纬度包含的是经度和纬度两个值,没法直接保存为一个浮点数。

GeoHash 的编码方法

为了能高效地对经纬度进行比较,Redis 采用了业界广泛使用的 GeoHash 编码方法,这个方法的基本原理就是 “二分区间,区间编码”

对一组经纬度进行 GeoHash 编码时,先对经度和纬度分别编码,然后再把经纬度各自的编码组合成一个最终编码。

经度和纬度的单独编码过程:

  1. 对于一个地理位置信息来说,它的经度范围是[-180,180]。GeoHash 编码会把一个经度值编码成一个 N 位的二进制值,来对经度范围[-180,180]做 N 次的二分区操作,其中 N 可以自定义。
  2. 在进行第一次二分区时,经度范围[-180,180]会被分成两个子区间:[-180,0) 和[0,180](左、右分区)。此时,查看要编码的经度值落在了左分区还是右分区。如果是落在左分区,用 0 表示;如果落在右分区,用 1 表示。
  3. 当做完 N 次的二分区后,经度值就可以用一个 N bit 的数来表示。

示例

假设要编码的经度值是 116.37,用 5 位编码值,编码过程如下表所示:

纬度的编码方式和经度一样,只是纬度的范围是[-90,90],编码过程如下表所示:

当一组经纬度值都编完码后,再把它们的各自编码值组合在一起,组合的规则是:最终编码值的偶数位上依次是经度的编码值,奇数位上依次是纬度的编码值,其中,偶数位从 0 开始,奇数位从 1 开始

以经纬度(116.37,39.86)为例,最终编码如下图所示:

用了 GeoHash 编码后,原来无法用一个权重分数表示的一组经纬度(116.37,39.86)就可以用 1110011101 这一个值来表示,可以保存为 Sorted Set 的权重分数。

使用 GeoHash 编码后,相当于把整个地理空间划分成了一个个方格,每个方格对应了 GeoHash 中的一个分区。

示例

把经度区间[-180,180]做一次二分区,把纬度区间[-90,90]做一次二分区,就会得到 4 个分区。

这 4 个分区对应了 4 个方格,每个方格覆盖了一定范围内的经纬度值,分区越多,每个方格能覆盖到的地理空间就越小,也就越精准。

使用 Sorted Set 范围查询得到的相近编码值,在实际的地理空间上,也是相邻的方格,这就可以实现 LBS 应用“搜索附近的人或物”的功能。

有的编码值虽然在大小上接近,但实际对应的方格却距离比较远。

为了避免查询不准确问题,可以同时查询给定经纬度所在的方格周围的 4 个或 8 个方格。

如何操作 GEO 类型?

  • GEOADD 命令:用于把一组经纬度信息和相对应的一个 ID 记录到 GEO 类型集合中;
    把 ID 号为 33 的车辆的当前经纬度位置存入 GEO 集合中:
GEOADD cars:locations 116.034579 39.030452 33
  • GEORADIUS 命令:会根据输入的经纬度位置,查找以这个经纬度为中心的一定范围内的其他元素。
    查找以经纬度(116.054579,39.030452 )为中心的 5 公里内的车辆信息
GEORADIUS cars:locations 116.054579 39.030452 5 km ASC COUNT 10

使用 ASC 选项,指定按照距离这个中心位置从近到远的方式来排序,COUNT 选项指定返回信息的数量。

如何自定义数据类型?

Redis 键值对中的每一个值都是用 RedisObject 保存的。

RedisObject 包括元数据和指针。其中,元数据的一个功能就是用来区分不同的数据类型,指针用来指向具体的数据类型的值。

Redis 的基本对象结构

RedisObject 的内部组成包括了 type、encoding、lru 和 refcount 4 个元数据,以及 1 个*ptr指针。

  • type:表示值的类型;
  • encoding:是值的编码方式,用来表示 Redis 中实现各个基本类型的底层数据结构,例如 SDS、压缩列表、哈希表、跳表等;
  • lru:记录了这个对象最后一次被访问的时间,用于淘汰过期的键值对;
  • refcount:记录了对象的引用计数;
  • *ptr:是指向数据的指针。

开发一个新的数据类型

首先,需要为新数据类型定义好它的底层结构、type 和 encoding 属性值,然后再实现新数据类型的创建、释放函数和基本命令。

以开发一个名字叫作 NewTypeObject 的新数据类型为例,解释下具体的 4 个操作步骤。

1. 第一步:定义新数据类型的底层结构

用 newtype.h 文件来保存这个新类型的定义,具体定义的代码如下所示:

struct NewTypeObject {struct NewTypeNode *head; size_t len;
}NewTypeObject;

NewTypeNode 结构是自定义的新类型的底层结构。设计两个成员变量:一个是 Long 类型的 value 值,用来保存实际数据;一个是*next指针,指向下一个 NewTypeNode 结构。

struct NewTypeNode {long value;struct NewTypeNode *next;
};

2. 第二步:在 RedisObject 的 type 属性中,增加这个新类型的定义

定义在 Redis 的 server.h 文件中。

#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

3. 第三步:开发新类型的创建和释放函数

Redis 把数据类型的创建和释放函数都定义在了 object.c 文件中。

robj *createNewTypeObject(void){NewTypeObject *h = newtypeNew(); robj *o = createObject(OBJ_NEWTYPE,h);return o;
}

newtypeNew 函数用来为新数据类型初始化内存结构。这个初始化过程主要是用 zmalloc 做底层结构分配空间,以便写入数据。

NewTypeObject *newtypeNew(void){NewTypeObject *n = zmalloc(sizeof(*n));n->head = NULL;n->len = 0;return n;
}

newtypeNew 函数涉及到新数据类型的具体创建,而 Redis 默认会为每个数据类型定义一个单独文件,实现这个类型的创建和命令操作,例如,t_string.c 和 t_list.c 分别对应 String 和 List 类型。按照 Redis 的惯例,把 newtypeNew 函数定义在名为 t_newtype.c 的文件中。

createObject 是 Redis 本身提供的 RedisObject 创建函数,它的参数是数据类型的 type 和指向数据类型实现的指针*ptr。

robj *createObject(int type, void *ptr) {robj *o = zmalloc(sizeof(*o));o->type = type;o->ptr = ptr;...return o;
}

对于释放函数来说,它是创建函数的反过程,是用 zfree 命令把新结构的内存空间释放掉。

4. 第四步:开发新类型的命令操作

  • 在 t_newtype.c 文件中增加命令操作的实现。
void ntinsertCommand(client *c){//基于客户端传递的参数,实现在NewTypeObject链表头插入元素
}
  • 在 server.h 文件中,声明已经实现的命令,以便在 server.c 文件引用这个命令。
void ntinsertCommand(client *c)
  • 在 server.c 文件中的 redisCommandTable 里面,把新增命令和实现函数关联起来。例如,新增的 ntinsert
    命令由 ntinsertCommand 函数实现,然后就可以用 ntinsert 命令给 NewTypeObject 数据类型插入元素。
struct redisCommand redisCommandTable[] = {
...
{"ntinsert",ntinsertCommand,2,"m",...}
}

13 | GEO是什么?相关推荐

  1. 01.elasticsearch metric aggregation 查询

    文章目录 1. 数据准备 2. metric aggregation分类 3.使用样例 1 . Avg Aggregation : 求query出来的结果的average 值 2 . Weighted ...

  2. PIE SDK 坐标系创建、定义、对比

    1.    坐标系创建 1.1    从WKT字符串导入空间参考 ISpatialReference接口是一个任何空间参考对象都实现的接口,它包含了所有空间参考对象都公有的方法和属性,如获得空间参考对 ...

  3. Elasticsearch 5.4 Mapping详解

    为什么80%的码农都做不了架构师?>>>    前言 一Field datatype字段数据类型 1string类型 2 text类型 3 keyword类型 4 数字类型 5 Ob ...

  4. Redis命令合集和设计场景

    目录 常规命令 String字符串类型 BIT位图 [使用bit命令实现签到功能] List有序链表 Hash哈希 Set无序集合 [使用无序集合统计用户的留存数] Sorted Set有序集合 [使 ...

  5. 用python解释exif数据的GPS信息

    用python解释exif数据的GPS信息 - 问答 - Python中文网 我正在写一个小程序来获取iphone jpg照片的GPS信息. 我使用的库是python中的PIL.现在我可以得到GPSI ...

  6. mahout安装测试

    Mahout 是 Apache Software Foundation(ASF) 旗下的一个开源项目,提供一些可扩展的机器学习领域经典算法的实现,旨在帮助开发人员更加方便快捷地创建智能应用程序.Apa ...

  7. 13|redis GEO是什么?

    目录 面向 LBS 应用的 GEO 数据类型 GEO 的底层结构 GeoHash 的编码方法 编码过程 误解

  8. Python系列之入门篇——python2.7.13安装

    Python2.7.13 安装 说明 以下所有操作都基于centos6.9 1. Issue zlib zlib-devel是安装setuptools依赖的模块,需要在安装python之前先安装这两个 ...

  9. 典型医学设计实验GEO数据分析 (step-by-step) - 数据获取到标准化

    GEO是当今最大.最全的公共基因数据资源库,包括基因的表达.突变.修饰等信息,涵盖几乎所有的疾病,且单个实验检测样品数目较多,是我们分析.学习的很好资源. 实验设计 原始文章对14个溃疡性结肠炎病人 ...

最新文章

  1. Anaconda使用
  2. 怎么修改gif图片中的文字[实用]
  3. js和php获取页面的url信息
  4. DayDayUp:《P2P行业最高端的玩法》源于网友网络收集
  5. C语言试题五十七之假定输入的字符串中只包含字母和*号。请编写函数function,它的功能是:删除字符串中所有*号。在编写函数时,不得使用c语言提供的字符串函数。
  6. PowerShell入门(三):如何快速地掌握PowerShell?
  7. 302状态码_HTTP协议详解(基础概念 方法 状态码 首部 连接 Cookie 新特性 安全)
  8. redis key/value 前面出现\xac\xed\x00\x05t\x00\x06 已解决
  9. php函数find的用法,fleaphp fleaphp crud操作之find函数的使用方法
  10. js 设置cookie
  11. 使用librdkafka
  12. 360浏览器Linux版内核,360浏览器推出Linux版下载,主要特性解说
  13. Min GW 安装教程(转载)
  14. 手机游戏连接计算机屏幕,手机怎么投屏到电脑?简单几个步骤就能实现,看电影玩游戏爽爆了...
  15. html盒子浮动分栏,DIV浮动定位实现CSS分栏布局
  16. VUCA时代:软件架构解决复杂性之道
  17. python爬虫下载小说
  18. 三天,我通过了Apsara Clouder基础技能认证:阿里巴巴Android开发规范
  19. 顾客满意度意义与作用
  20. 日期推算/日历(小程序)

热门文章

  1. 商业版微信小程序开发流程
  2. 5、TORCH.RANDOM
  3. sketch android 切图,Sketch如何快速切图?三分钟教你掌握切图方案
  4. 转-CRC问题的解决
  5. 重识Nginx - 09 使用Nginx内置变量$limit_rate实现带宽限速
  6. 解决oracle数据库conn scott /tiger 连接不上的问题;待验证
  7. 【硬件设备】CPU 高速缓存知识
  8. python到底要学多久才能找到工作?
  9. [导入]2008张国立倾情打造《想爱都难》全30集
  10. 漏洞挖掘工具-CE(Cheat Engine) 简介