【Redis】三种特殊数据类型

三种特殊数据类型

我们学习 Redis除了所谓的五大基本数据类型外,还有一些特殊的数据类型需要我们进行深或浅的了解;


但按照我个人的习惯,除非是我真的不懂 不然应该都会写得很详细,这也就是我写的东西动不动就万字的原因

我个人认为一门技术写得很浅不丢人,丢人的是写得很晦涩鲜有人能够真正看懂

所以我写东西会尽可能的降低阅读门槛;当然这只是我个人的看法 你完全可以不赞同


关于五大基本数据类型我前两天也有写,写得很长 各位有需要可以自行阅读 点击跳转

这些特殊的数据类型有Bitmap、Hyperloglogs、Geospatial,我们一个一个来看;首先是比较简单离大家生活也比较近的 Geospatial

Geo

Geospatial又简称 Geo,该数据类型主要要于存储地理位置信息,于 Redis3.2版本后加入

如何存储呢?很简单,将指定的地理位置信息(经度、纬度、名称)添加到指定的Zset中的;

这样做的目的是为了方便使用GEORADIUS或者GEORADIUSBYMEMBER命令对数据进行半径查询等操作。

到这里需要提一嘴,Geo相关的命令很少 险些一只手都能数的过来,命令这么少实现增删改查都够呛

所以 Zset能够使用的命令,Geo也是大概率适用的

Geo 主要用于存储地理位置信息,并对存储的信息进行操作

用于实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能,对于需要实现这些功能的开发者来说是一大福音。

我们先来看看存地理位置信息,到底是怎么一个存法 这就要介绍第一个命令了 geoadd key longitude latitude member [longitude latitude member ...]

和 Zset一样,允许我们存储多个成员,但在存储之前有两个必填参数为经纬度;其中经度在前纬度在后

这里强调经纬度的顺序是因为, Redis不允许你存储的位置信息为错误信息,它存在着一个有效经纬度概念

  • 有效的经度从 -180度到 180度。
  • 有效的纬度从 -85.05112878度到 85.05112878度。

当坐标位置超出上述指定范围时,该命令将会返回一个错误。

如果你维度在前很容易就会抛出这样一个错误,即使不抛出错误 存进去的信息也是错误的;到时候排错可就头疼了。

我们可以尝试存几个中国的城市,以下为示例:

geoadd china:city 115.858200 28.682892 nanchang 108.366547 22.817001 nanning 118.796875 32.060253 nanjing 120.311913 31.491169 wuxi 111.678101 29.031940 changde 117.943436 28.454863 shangrao 120.382670 36.067108 qingdao 122.069847 46.07083 wulanhaote 127.511840 46.871850 qingan 104.066803 30.572815 chengdu 105.870010 32.430000 guangyuan 121.537498 29.874910 ningbo 121.540199 25.016500 zhoushan

# 添加了 南昌、南京、南宁、无锡、常德、上饶、青岛、乌兰浩特、庆安、成都、广元、宁波、舟山

想要查看我们存进去的地理位置信息有哪些,可以使用 之前Zset中提到的命令 zrange key start end

127.0.0.1:6379> zrange china:city 0 -1
 1) "nanning"
 2) "chengdu"
 3) "changde"
 4) "guangyuan"
 5) "zhoushan"
 6) "nanchang"
 7) "shangrao"
 8) "wuxi"
 9) "ningbo"
10) "nanjing"
11) "qingdao"
12) "wulanhaote"
13) "qingan"

但不要带 withscores参数

如果要查看我们存进去的城市经纬度我们可以使用 geopos key member[members]命令以下为演示

127.0.0.1:6379> geopos china:city shangrao # 查看单个城市的经纬度
1) 1) "117.94343858957290649"
   2) "28.45486211317673764"
127.0.0.1:6379> geopos china:city shangrao nanchang guangyuan wulanhaote # 查看多个城市的经纬度
1) 1) "117.94343858957290649"
   2) "28.45486211317673764"
2) 1) "115.85819810628890991"
   2) "28.68289323283435266"
3) 1) "105.87001115083694458"
   2) "32.429999404617881"
4) 1) "122.06984549760818481"
   2) "46.07082944850478157"

除此之外如果要查看两个城市之间直线距离我们可以使用 geodist key member1 member2 [长度单位]

这里的长度单位支持 m、km、mi(英里)、ft(英尺),默认为米

127.0.0.1:6379> geodist china:city shangrao wulanhaote
"1992385.3469" # 上饶到乌兰浩特的直线距离为 1992385米
127.0.0.1:6379> geodist china:city shangrao wulanhaote km # 将单位修改为千米
"1992.3853" 

通过经纬度来计算两地的直线距离,还是蛮实用的一个命令

接下来要讲的命令相信大家都比较感兴趣,这个命令能够让我们查看指定经纬度多少多少半径内存在的位置信息集合

也就是类似附近的人这种功能,但我们这里并没有存储个人的位置信息,存储的是城市的位置信息;

那我们就通过通过该命令来查看指定半径内存在的城市位置信息了,你可以简单的理解为 附近的城市

该功能通过 georadius来实现,除此之外我们还可以使用 georadiusbymember命令来实现几乎一样的功能

二者的区别为,前者需要你给出一个经纬度 后者需要你给出一个成员

我们两个都来试试,首先是georadius key longitude latitude radius [长度单位]

127.0.0.1:6379> georadius china:city 117.94 28.45 500 km # 查看该经纬度(该位置在上饶市附近)半径 500km内的城市
1) "nanchang"
2) "shangrao"
3) "wuxi"
4) "ningbo"
5) "nanjing"
# 半径 500km内有如上这些城市

其次是 georadiusbymember key member radius [长度单位]

127.0.0.1:6379> georadiusbymember china:city shangrao 500 km
1) "nanchang"
2) "shangrao"
3) "wuxi"
4) "ningbo"
5) "nanjing"

这两个命令都可以在使用时携带多个参数,能携带的参数(常用)如下:

  • withcoord:携带该参数可以让返回的位置携带经纬度。
  • withdist:携带该参数后返回的位置会给出离指定 成员或经纬度的距离。
    • 距离的单位和用户给定的单位保持一致。
  • COUNT count:指定返回位置的数量。
  • asc|desc:返回结果按照离指定成员或经纬度的距离做升序或者降序。

大家感兴趣可以自行测试,我就不一一测试了


最后一个命令,也是平时用得最少的命令 geohash key member[members]

这个命令将返回 11个字符的 Geohash字符串

127.0.0.1:6379> geohash china:city shangrao nanchang
1) "wt5cgu5c960"
2) "wt47hmt3gw0"

以下引自「进击Redis」十七、保证你没用过Redis GEO

GeoHash本质上是空间索引的一种方式,其基本原理是将地球理解为一个二维平面,将平面递归分解成更小的子块,每个子块在一定经纬度范围内拥有相同的编码。以 GeoHash 方式建立空间索引,可以提高对空间 poi 数据进行经纬度检索的效率。具体可以看Geohash 原理 使用 geohash 将二维经纬度转换为一维字符串,geohash有如下特点:

  1. GEO 的数据类型为zset,Redis 将所有地理位置信息的geohash存放在zset中。
  2. 字符串越长,表示的位置更精确,例如geohash长度为 9 时,精度在 2 米左右。如下图
  3. 两个字符串越相似,它们之间的距离越近,Redis 利用字符串前缀匹配算法实现相关的命令。
  4. geohash编码和经纬度是可以相互转换的。

到这里 geo相关的命令就都讲完了,其余的操作你都可以通过 Zset的命令来对 Geo进行操作;这边就不再赘述了

Hyperloglog

Redis 在 2.8.9 版本添加了 HyperLogLog 结构。

Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。

在 Redis 里面,每个 HyperLogLog 键只需要花费 12KB 内存,就可以计算接近 264 个不同元素的基数。

这和计算基数时,元素越多耗费内存就越多的 Set集合形成鲜明对比。

但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

所以该数据结构类型经常会被使用在 网站的 UV上,如果你的项目还在使用 Set做 UV,可以考虑考虑使用 hyperloglog

hyperloglog存在一定的小问题,它会有 0.81%的误差,但如果是用作 UV,这么一点小误差并不会太致命

hyperloglog的命令比较少也比较简单,但如果要搞明白 hyperloglog的话需要一定的算法基础;

可惜的是算法这一块一直是我的短板,我就不献丑了 只说说 hyperloglog的命令就带过, 各位见谅(以后算法这块提上来一定填这个坑,我保证!)。

hyperloglog的命令都使用 pf开头,分别是 pfaddpfcountpfmerge

从名字也能看得出,分别是 添加、计数与合并,这三个命令都非常简单;以下为示例

# 假设我写了一篇文章,该文章发布后的 第一个时间段有如下用户点击了该文章
127.0.0.1:6379> pfadd views1 uid1 uid2 uid3 uid4 uid5 uid6 uid7 uid8
(integer) 1
# 第二个时间段又有如下用户点击了该文章
127.0.0.1:6379> pfadd views2 uid3 uid5 uid2 uid9 uid11 uid7 uid20 uid22 uid40 uid31 uid12 uid3 uid5
(integer) 1

此时我们想计算这两个时间段内一共有多少用户点击了这篇文章,但有单个用户多次点击的情况;

这种自然只能视作一次点击我们需要进行去重,也就是计算 views1 与views2合并后的基数

将这两个 hyperloglog集合合并我们可以使用 pfmerge命令

# 将这两个集合合并,合并后的集合命名为 "all"
127.0.0.1:6379> pfmerge all views1 views2
OK

再对 合并后的集合进行一个计数操作即可完成 点击量的统计

127.0.0.1:6379> pfcount all
(integer) 15

最后得出的基数为 15,这就是 hyperloglog最简单的使用,应该也是最多的应用场景了;以上

Bitmap

在日常开发中,偶尔我们需要存储的数据会出现仅有两种状态的情况;

这种情况如果我们要进行数据的存储,最好的方式就是使用 bit来进行存储

先科普一下什么是 bit,避免存在疑惑,bit(比特)是二进制单位( binary unit)或二进制数字(binary digit)的缩写

它是电脑记忆体中最小的单位,在二进位电脑系统中,每一bit 可以代表01(也就是二进制) 的数位讯号(非黑即白,非 0 即 1)。

假如七个人中有三位女性四位男性,要对该消息进行存储的话最简单的方式就是存三个 0四个 1或反之;


BitMap 原本的含义是用一个比特位来映射某个元素的状态。

由于一个比特位只能表示 0和 1两种状态,所以 BitMap能映射的状态有限,但是使用比特位的优势是能大量的节省内存空间

在 Redis中,可以把 Bitmaps想象成一个以比特位为单位的数组,数组的每个单元只能存储 0和 1,而数组的下标在 Bitmaps中叫做偏移量。

再往深了讲我就讲不好了,我们还是讲讲命令和演示吧作为文章的收尾

首先是 setbit key offset value

该命令用于设置或者覆盖 key 的 value(字符串)在 offset处的 bit值。

参数 offset需要大于等于0,并且小于232(限制 bitmap大小为512MB)。

按上面给出的例子来,我们需要往 Map中存储七条数据, 以下为演示:

127.0.0.1:6379> setbit people 1 0
127.0.0.1:6379> setbit people 2 0
127.0.0.1:6379> setbit people 4 0
127.0.0.1:6379> setbit people 5 0
127.0.0.1:6379> setbit people 0 1
127.0.0.1:6379> setbit people 3 1
127.0.0.1:6379> setbit people 6 1
# 添加七条数据,0代表男性 1代表女性

和 set一样,bitset也会有覆盖的情况;我们可以再一次的 bitset将数据进行修改(覆盖)

127.0.0.1:6379> setbit people 1 1
(integer) 0
127.0.0.1:6379> getbit people 1 # 查看偏移量为 1的数据存储的字符串为 0还是 1
(integer) 1 # 返回 1表示已经覆盖了;由于数据发生了变化此时 1代表男性 0代表女性

认识了简单的存取后,我们再来看一看如何统计 bitMap的计数,即统计指定 Map中存储字符串 1的个数

统计命令为 bitcount key [start end]其中 start 和 end是可选的,如果不携带 start end参数;默认返回整个 Map中存储的字符串 1的个数

127.0.0.1:6379> bitcount people
(integer) 4 # 该 Map中 1的个数为 4

此时因为 offset并没有超过 8,所以此时我们 key为 "people"的 bitMap实际上大小仍是一个字节

127.0.0.1:6379> strlen people
(integer) 1

而一旦 offset超过 8之后,该Map的大小就会变成两个字节

127.0.0.1:6379> setbit people 8 1
(integer) 0
127.0.0.1:6379> strlen people
(integer) 2

为什么要提这个呢?这和 bitcount命令有什么关系?

还是有关系的,因为 bitcount命令中 start end参数就是用来指定字节的,准确的说是用来指定一个字节范围

如果我们携带了这两个参数,那么该命令就会变成查看指定字节范围内存储字符串 1的个数,而字节范围又与 offset挂钩(最大 offset决定了该Map占用的字节)

例如:

127.0.0.1:6379> bitcount people 0 0 # 查看字节 0-0之间的存储字符串 1的个数
(integer) 4 # 这是我们一开始存进去的用于表示性别的 4个1
# 因为指定的范围为 0-0所以不会将 刚刚加进来的 offset为 8的字符串 1算进来(只会统计 offset小于8的)
127.0.0.1:6379> bitcount people 1 1 # 而我们再查看字节 1-1之间存储的 1的个数就会发现仅有我们刚加进来的那一个了
(integer) 1 # 这样的范围 offset小于 8、大于 15的字符串 1也不会被统计进来
127.0.0.1:6379> bitcount people 0 1 # 我们再将条件进行修改,此时 Map中 offset 0-15的字符串 1都会被统计进来
(integer) 5

放松一下眼睛

我永远喜欢蕾塞.jpg

原图P站地址

画师主页


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值