Redis 4.x系列(五):Redis 数据类型之Hash、HyperLogLog、Geo
Hash:表示字符串字段和字符串值之间的映射关系,因此 Hash 对于存储对象是一种完美的数据类型。
HyperLogLog:在需要唯一计数
的数据处理场景中使用,用于统计元数的个数,而不需获取数据的内容,性能高消耗内存低。
Geo:用于存储和查询与地理位置相关的位标(GPS经纬度),提供的 API 非常方便地计算位标距离和获取距离范围内的成员。
Hash
Hash
主要用于存储对象数据,但也可能存储多种类型元素。为了与 Redis 的键进行区分,使用字段(field)来表示 hash 值对象所关联的键,并且值和字段都必须是字符串类型。
具有少量字段的 Hash(少量指大约一百个)以一种占用空间非常小的方式存储,因此可以在一个小的 Redis 实例中存储数百万个对象。但对放入 Hash 中的字段数是没有实际限制(除了可用内存)。小 Hash (指具有小值的少数元数)以特殊方式编码存储,存储可以更高效。每个 Hash 最多可存 2 的 32 次方 减 1(超过40亿)个字段-值对。
备注: 以一个 Java 后台的理解, Redis 的 Hash 非常类似于 Java 的 HashMap;有键(字段),值是对象,但也可以是其它类型。
与 List 类型类似,使用 Hash 类型不需要在添加前先初始化一个空的 Hash, hset和hmset会自动实现这一点;当 Hash 为空时,Redis 负责将其删除。
内部编码
Redis 在内部使用两种编码来存储 Hash 对象
- ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries选项配置的值(默认 512),且所有元素的大小(值)都小于配置中 hash-max-ziplist-value选项配置的值(默认为 64 字节),内部采用此编码。ziplist使用更加紧凑的结构实现多个元素的连续存储,在节省内存方面比hashtable更优。
- hashtable:当哈希类型无法满足 ziplist 的条件时,Redis 会使用 hashtable作为哈希的内部实现。
常用命令
- hset:设置单个字段的值,可用于修改现有字段的值,字段不存在时则变成添加新的字段。
- hget:从一个 Hash 中获取某个字段对应的值。
- hsetnx:仅在字段不存在的情况下才设置值。可防止 hset 和 hmset 的默认覆盖行为。
- hmset:创建字段并设置属性。
- hmget:从一个 Hash 中获取多个字段对应的值。
- hdel:从 Hash 中删除字段。
- hexists:判断一个 Hash 中是否存在某个字段。
- hlen key:获取键下有多少个字段(field 的个数)。
- hkeys key:获取键下的所有字段,返回所有的 field 。
- hvals key:获取键下所有值(Value)。
- hgetall:获取一个 Hash 中的所有字段和值。在大 Hash 中使用此命令会阻塞服务器,不建议对大 Hash 使用此命令,可以使用
HSCAN
命令来增量地获取所有字段和值。 - hscan:HSCAN 是 Redis 中 SCAN 命令的一种(SCAN、HSCAN、SSCAN、ZSCAN),该命令会增量迭代遍历元素而不会造成服务器阻塞。HSCAN 命令是一种基于指针的迭代器,在使用此命令时需要指定一个游标(从 0 开始),当命令执行结束后会返回一个元素列表以及一个新的游标,这个游标用于下一次迭代,当返回的游标为
0
时,表示整个遍历完成。 - hstrlen key field:获取值(value)的字节长度,一个中文占两个字节。
- hincrby hincrbyfloat:对键-字段的值执行自增。
hmset
创建字段并设置属性
语法:hmset key field value [field value ...]
示例:
1 | 127.0.0.1:6379> hmset people name "Kitty" age "22" address "ShenZhen" birthday "2004-05-10" |
hmget
获取多个字段对应的值, 不存在的字段返回(nil)
语法:hmget key field [field ...]
示例:
1 | 127.0.0.1:6379> hmget people age address |
hget
获取单个字段对应的值, 不存在的字段返回(nil)
语法:hget key field
示例:
1 | 127.0.0.1:6379> hget people name |
hexists
查看字段是不存在,存在返回 1,不存在返回 0。
语法:hexists key field
示例:
1 | 127.0.0.1:6379> hexists people name |
hgetall
获取一个 Hash 中所有字段和值
语法:hgetall people
示例:
1 | 127.0.0.1:6379> hgetall people |
hscan
增量地迭代遍历元素,不会阻塞服务器。
语法:hscan key cursor [MATCH pattern] [COUNT count]
count 默认值是 10,但此参数是做参考,Redis 并不保证返回元素数是就是 count 个。
示例:
1 | 127.0.0.1:6379> hscan people 0 count 8 |
hset
给已存在的的字段设置新的值; 或字段不存在时添加新的字段
语法:hset key field value
示例:
1 | 127.0.0.1:6379> hset people email kitty@163.com |
hsetnx
仅在字段不存在时设置值。因为 hset 和 hmset 会覆盖现有字段,而 hsetnx 可防止此覆盖行为。
语法:hsetnx key field value
示例:
1 | 127.0.0.1:6379> hget people name |
hdel
从 Hash 中删除字段
语法:hdel key field
示例:
1 | 127.0.0.1:6379> hdel people email |
HyperLogLog
HyperLogLog 类据类型主要用于需要对唯一计数
进行统计的场景,若不需要获取数据集的内容,只是想要得到不同值的个数,可以使用HyperLogLog(HLL)
数据类型,相比其它集合类型,特别是在大数据量(上千万)的时候, HLL
类型性能更优并且消耗更少的内存。
Redis中的HLL
虽然在技术上是一种不同的数据结构,但是被编码为 Redis 字符串。HLL
与 Set
数据类型非常相似,使用 PFADD
添加新元素到计数中,并且元素是唯一性的,不会添加已存在的元素(重复),使用PFCOUNT
来获取唯一元素的数量。
HLL
相关的所有命令都是以PF
开头,用来向 HLL
数据结构的发明者 Philippe Flajolet 致敬。Redis 中的 HLL 的优势是能够使用固定数量的内存(每个 HyperLogLog 类 型的键只需占用 12KB 内存,却可以计算最多 2 的 64 次方 个不同元素的基数)和常数时间复杂度(每个键 O(1) 进行唯一计数)。不过 HLL 返回的计数可不准确(标准差小于1%),因此决定是否使用 HLL 需要进行权衡。
HLL
实际上是被当做字符串存储的,因此作为一个键值对,可以很容易地被持久化至外部或从外部持久化中恢复。
内部存储
Redis 内部存储 HLL 对象的两种方式:
- **稀疏(Sparse)**:对于那些长度小于配置中
hll-sparse-max-bytes
选项设置的值(默认为 3000)的 HLL 对象,采用此编码。此方式的存储效率更高,但可能消耗更多的 CPU 资源。 - 稠密(Dense):当稀疏不能适用时使用此编码。
基本命令
- PFADD:添加元素到计数中,成功返回
1
,失败返回0
,重复元素不添加。 - PFCOUNT:对计数中的元数进行计数统计,返回元素个数。
- PFMERGE:合并多个计数中的元数到一个新的计数中。
PFADD
添加元素到计数中
语法:pfadd key element [element ...]
示例:
1 | 127.0.0.1:6379> pfadd fruit "apple" "orange" "branana" |
PFCOUNT
对计数中的元数进行计数统计,返回元素个数
语法:pfcount key [key ...]
示例:
1 | 127.0.0.1:6379> pfcount fruit |
PFMERGE
合并多个计数中的元数到一个新的计数中。
语法:pfmerge destkey sourcekey [sourcekey ...]
示例:
1 | 127.0.0.1:6379> pfadd fruit "apple" "orange" "branana" |
Geo
随着以智能手机为代表的移动智能终端的普及, 基于地理位置的服务(LBS
)变得越来越受欢迎, 提供这类服务的系统也越来越多的使用到了地理位置的坐标数据(GPS经纬度),需要根据坐标数据计算距离,获取范围内的成员等业务。Redis 的 Geo
数据类型为基于地理位置相关场景提供了很好的支持。
Geo
相关的 API 用于支持存储和查询这些地理位置相关场景中的位标(GPS经纬度)。
常用命令
- geoadd:将元素添加到 Geo 集合中。
- geopos:从 Geo 集合中获取指定成员的坐标。
- georadius:获取距离范围内的成员。
- geodist:计算两个成员之间的距离。
内部存储
当通过 geoadd
设置坐标时,这些坐标会被转换成一个 52 位的 GEOHASH
。GEOHASH是一个被广泛接受的地理坐标编码系统(Geo-encoding system)。所在存储在 Geo 中的坐标和 GEOPOS 命令返回的坐标之间可能存在经微的差别。
GEOHASH 实现是基于一种 52 位整数的表示(实现啊低于 1 米的精度)。当需要一个标准的 GEOHASH 字符串时,可以使用 GEOHASH
命令来获取一个长度为 11 的字符串。
Geo 集合实际上被存储为一个有序集合(Sorted Set), 因此有序集合支持的所有命令都可以用于 Geo
数据类型。例如,可以使用zrem
从 Geo中移除成员,也可以使用 zrange
来获取 Geo 集合的所有成员。
georadius命令,在性能方面,georadius 的时间复杂度为 O(N+log(M))
, 其中 N为由中心点和半径所决定的圆形区域的外接矩形中成员的个数。因此,若想获得最优性能,在查询时将关径参数设置的尽可能的小,以覆盖尽可能少的点。
geoadd
将元素添加到 Geo 集合中
语法:geoadd key longitude latitude member [longitude latitude member ...]
示例:
1 | 127.0.0.1:6379> geoadd offical 113.9409770034 22.6781577868 "Star City" |
geopos
从 Geo 集合中获取指定成员的坐标
语法:**geopos key member [member ...]
**
示例:
1 | 127.0.0.1:6379> geopos offical "Garden" |
georadius
传入经纬度,获取距离此位置范围内的成员。
语法:**georadius key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC]
**
示例:
1 | 127.0.0.1:6379> georadius offical 113.94097656011581421 22.67930731281483503 5 km |
georadiusbymember
此命令与 georadius 相似,都可以用来找出位于指定范围内的成员,但 georadiusbymember
命令的中心点是由 Geo
集合中的成员决定的;而不是像 georadius 那使通过传入经纬度来决定的。
语法:**georadiusbymember key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
**
示例:
1 | 127.0.0.1:6379> georadiusbymember offical "Garden" 1 km |
在georadius
和 georadiusbymember
命令中, 可以使用 WITHCOORD
选项来返回成员的经纬度,WITHDIST
选项得到距离,ASC|DESC
选项控制返回结果的升序或降序;还可以通过STORE | STOREDIST
将返回的结果存到的 Redis 中的另一个 Geo 集合中。
geodist
计算两个成员之间的距离
语法:**geodist key member1 member2 [unit]
**
示例:
1 | 127.0.0.1:6379> geodist offical "Garden" "Star City" |
GEOHASH
获取成员位置坐标的 GEOHASH 字符串
语法:**geohash key member [member ...]
**
示例:
1 | 127.0.0.1:6379> geohash offical Garden |
Redis 4.x系列(五):Redis 数据类型之Hash、HyperLogLog、Geo
http://blog.gxitsky.com/2018/10/02/Redis-5-datatype-3-hash-hyperloglog-geo/