网络知识 娱乐 深入理解redis数据类型

深入理解redis数据类型

缓存现在已经城为Java必会的技能了,最少也是之一了,说起缓存我们第一个想到的就是redis,大部分人使用的数据类型就是String类型,俗话说得好,万物皆可string,但是其实redis有9种数据类型,今天我们就详细的介绍一下这9种类型;

六种基础数据类型

数据类型

底层数据结构

常用命令

应用场景

备注

String

int, embstr, raw

set, mset, get, append, setbit ...

【字符串】万物皆可String

字符串,二进制安全

Hash

ht, ziplist

hget, hset, hegetall, hkeys, hexists ...

【存储对象】操作用户属性

散列,键值对集合,map

List

linkedlist, ziplist

lpush/lpop, rpush/rpop, lrange ...

【增删快】最新消息排行;【按序】消息队列

列表,双向链表

Set

intset, ht

sadd, spop, sinter, sunion, sdiff ...

【支持交/并/差集】共同好友;【不重复】统计访问网站IP

集合,不重复

ZSet

ziplist, skipklist

zadd, zrange, zrem, zcard, zrank ...

【有序】排行榜;【score】带权重的消息队列、延时队列

有序集合,按score排序

Stream

listpack, rax

xadd, xrange, xrevrange, xgroup, xread ...

【消息队列】IRC / 实时聊天系统, IoT数据采集

类似于日志的数据结构,本质是一个消息队列

基于基础数据类型的三种扩展数据类型

数据类型

依赖的数据结构

常用命令

应用场景

备注

Bitmap

String

setbit, getbit, bitop, bitcount, bitpos

活跃用户数统计; 统计某一天的用户登陆数量

存储与对象ID关联的节省空间并且高性能的布尔信息

Hyperloglog

String

pfadd, pfcount, pfmerge

基数统计; 统计每日访问IP数/页面UV 数/在线用户数等

bitmap的升级版;概率算法,不直接存储数据集合本身,概率统计方法预估基数值

Geo

ZSet

geoadd, geohash, geopos, geodist, georadius

基于位置的服务(LBS)

Redis3.2以后版本

注意:

  • 一般可以bitmap和hyperloglog配合使用,bitmap标识哪些用户活跃,hyperloglog计数
  • Geo数据建议使用单独的 Redis 实例部署(在项目开发中,有看到经纬度专用的Redis,与业务使用的Redis分离)

A. Bitmap

bitmap不是实际的数据类型是string类型上的一组面向bit操作的集合。

由于string是二进制安全的,并且它们的最大长度是512m,所以bitmap能最大设置2^32个不同的bit。

8 *1024*1024* 512 = 2^32 = 4294967296 = 40+ 亿

优点:

存储信息时可以节省大量的空间。例如在一个系统中,不同的用户被一个增长的用户ID表示。40亿(2^32=4*1024*1024*1024≈40亿)用户只需要512M内存就能记住某种信息,例如用户是否登录过。

操作:

  • 恒定时间的单个bit操作,例如把某个bit设置为0或者1。或者获取某bit的值。 SETBIT:设值 GETBIT:取值
  • 对一组bit的操作。例如给定范围内bit统计(例如人口统计)。 BITOP:执行两个不同string的位操作,包括AND,OR,XOR和NOT BITCOUNT:统计位的值为1的数量 BITPOS:寻址第一个为0或者1的bit的位置

使用场景:

  • 各种实时分析
  • 存储与对象ID关联的节省空间并且高性能的布尔信息 例如: 统计访问网站的用户的最长连续时间 统计某一天的用户登陆数量(以当天的日志加固定的前缀作为key,建立一个bitmap,每一位二进制的位做为一个用户ID的标识)

B. Hyperloglog

hyperLogLog是bitmap的升级版。本质上是一种概率算法,不直接存储数据集合本身,而是通过一定的概率统计方法预估基数值。这种方法可以大大节省内存,同时保证误差控制在一定范围内。

被编码成Redis字符串。因此可以通过调用GET命令序列化一个Hyperloglog(HLL),也可以通过调用SET命令将其反序列化到redis服务器。HLL的API类似使用SETS数据结构做相同的任务,SETS结构中,通过SADD命令把每一个观察的元素添加到一个SET集合,用SCARD命令检查SET集合中元素的数量,集合里的元素都是唯一的,已经存在的元素不会被重复添加。

而使用HLL时并不是真正添加项到HLL中(这一点和SETS结构差异很大),因为HLL的数据结构只包含一个不包含实际元素的状态。

操作:

  • PFADD:用于添加一个新元素到统计中。
  • PFCOUNT:用于获取到目前为止通过PFADD命令添加的唯一元素个数近似值。
  • PFMERGE:执行多个HLL之间的联合操作。

使用场景:

  • 统计基数数量(大量) 统计注册 IP 数 统计每日访问 IP 数 统计页面实时 UV 数 统计在线用户数 统计用户每天搜索不同词条的个数
  • 一般可以bitmap和hyperloglog配合使用,bitmap标识哪些用户活跃,hyperloglog计数

C. Geo

底层数据类型:zset

Redis的GEO特性在 Redis3.2版本中推出,这个功能可以将用户给定的地理位置(经度和纬度)信息储存起来,并对这些信息进行操作。

GEO相关命令只有6个:

  • GEOADD:GEOADD key longitude latitude member [longitude latitude member …],将指定的地理空间位置(纬度、经度、名称)添加到指定的key中
  • GEOHASH:GEOHASH key member [member …],返回一个或多个位置元素的标准Geohash值,它可以在http://geohash.org/使用
  • GEOPOS:GEOPOS key member [member …],从key里返回所有给定位置元素的位置(经度和纬度)
  • GEODIST:GEODIST key member1 member2 [unit],返回两个给定位置之间的距离。GEODIST命令在计算距离时会假设地球为完美的球形。在极限情况下,这一假设最大会造成0.5%的误差。
  • GEORADIUS:GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD][WITHDIST] [WITHHASH][COUNT count],以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。这个命令可以查询某城市的周边城市群。
  • GEORADIUSBYMEMBER:GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD][WITHDIST] [WITHHASH][COUNT count],这个命令和GEORADIUS命令一样,都可以找出位于指定范围内的元素,但是GEORADIUSBYMEMBER的中心点是由给定的位置元素决定的,而不是像 GEORADIUS那样,使用输入的经度和纬度来决定中心点。

使用场景:

  • 基于位置的服务(LBS)

GEO类型的底层数据结构是用Zset实现的。

例如,存储车辆/店铺的经纬度信息时,元素是车辆/店铺ID,元素的权重Sore是本应是经纬度信息,但Sore应该是float类型,因此,需要对一组经纬度进行编码(即GeoHash编码)。

简要步骤:

Step 1:将经度/纬度进行二分拆解,得到二叉树结构,并进行0/1编码,再通过N位bit进行存储(N越大,精度越高);

Step 2:将经度和纬度的N位bit进行交叉组合,得到GeoHash值。

GeoHash编码的基本原理是“二分区间,区间编码”,先对经度和纬度分别编码,再将经纬度各自的编码组合成一个最终编码。简单来说,GeoHash将一个空间分割成一个个小方块,我们可以通过查询给定经纬度所在方格周围的4个或者8个方格,以此进行“周边查找”。【GeoHash值相近,并不一定位置相近,故需计算邻居节点,以提高LBS精度】注意:

  • 一般可以bitmap和hyperloglog配合使用,bitmap标识哪些用户活跃,hyperloglog计数
  • Geo数据建议使用单独的 Redis 实例部署(在项目开发中,有看到经纬度专用的Redis,与业务使用的Redis分离)

A. Bitmap

bitmap不是实际的数据类型是string类型上的一组面向bit操作的集合。

由于string是二进制安全的,并且它们的最大长度是512m,所以bitmap能最大设置2^32个不同的bit。

8 *1024*1024* 512 = 2^32 = 4294967296 = 40+ 亿

优点:

存储信息时可以节省大量的空间。例如在一个系统中,不同的用户被一个增长的用户ID表示。40亿(2^32=4*1024*1024*1024≈40亿)用户只需要512M内存就能记住某种信息,例如用户是否登录过。

操作:

  • 恒定时间的单个bit操作,例如把某个bit设置为0或者1。或者获取某bit的值。 SETBIT:设值 GETBIT:取值
  • 对一组bit的操作。例如给定范围内bit统计(例如人口统计)。 BITOP:执行两个不同string的位操作,包括AND,OR,XOR和NOT BITCOUNT:统计位的值为1的数量 BITPOS:寻址第一个为0或者1的bit的位置

使用场景:

  • 各种实时分析
  • 存储与对象ID关联的节省空间并且高性能的布尔信息 例如: 统计访问网站的用户的最长连续时间 统计某一天的用户登陆数量(以当天的日志加固定的前缀作为key,建立一个bitmap,每一位二进制的位做为一个用户ID的标识)

B. Hyperloglog

hyperLogLog是bitmap的升级版。本质上是一种概率算法,不直接存储数据集合本身,而是通过一定的概率统计方法预估基数值。这种方法可以大大节省内存,同时保证误差控制在一定范围内。

被编码成Redis字符串。因此可以通过调用GET命令序列化一个Hyperloglog(HLL),也可以通过调用SET命令将其反序列化到redis服务器。HLL的API类似使用SETS数据结构做相同的任务,SETS结构中,通过SADD命令把每一个观察的元素添加到一个SET集合,用SCARD命令检查SET集合中元素的数量,集合里的元素都是唯一的,已经存在的元素不会被重复添加。

而使用HLL时并不是真正添加项到HLL中(这一点和SETS结构差异很大),因为HLL的数据结构只包含一个不包含实际元素的状态。

操作:

  • PFADD:用于添加一个新元素到统计中。
  • PFCOUNT:用于获取到目前为止通过PFADD命令添加的唯一元素个数近似值。
  • PFMERGE:执行多个HLL之间的联合操作。

使用场景:

  • 统计基数数量(大量) 统计注册 IP 数 统计每日访问 IP 数 统计页面实时 UV 数 统计在线用户数 统计用户每天搜索不同词条的个数
  • 一般可以bitmap和hyperloglog配合使用,bitmap标识哪些用户活跃,hyperloglog计数

C. Geo

底层数据类型:zset

Redis的GEO特性在 Redis3.2版本中推出,这个功能可以将用户给定的地理位置(经度和纬度)信息储存起来,并对这些信息进行操作。

GEO相关命令只有6个:

  • GEOADD:GEOADD key longitude latitude member [longitude latitude member …],将指定的地理空间位置(纬度、经度、名称)添加到指定的key中
  • GEOHASH:GEOHASH key member [member …],返回一个或多个位置元素的标准Geohash值,它可以在http://geohash.org/使用
  • GEOPOS:GEOPOS key member [member …],从key里返回所有给定位置元素的位置(经度和纬度)
  • GEODIST:GEODIST key member1 member2 [unit],返回两个给定位置之间的距离。GEODIST命令在计算距离时会假设地球为完美的球形。在极限情况下,这一假设最大会造成0.5%的误差。
  • GEORADIUS:GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD][WITHDIST] [WITHHASH][COUNT count],以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。这个命令可以查询某城市的周边城市群。
  • GEORADIUSBYMEMBER:GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD][WITHDIST] [WITHHASH][COUNT count],这个命令和GEORADIUS命令一样,都可以找出位于指定范围内的元素,但是GEORADIUSBYMEMBER的中心点是由给定的位置元素决定的,而不是像 GEORADIUS那样,使用输入的经度和纬度来决定中心点。

使用场景:

  • 基于位置的服务(LBS)

GEO类型的底层数据结构是用Zset实现的。

例如,存储车辆/店铺的经纬度信息时,元素是车辆/店铺ID,元素的权重Sore是本应是经纬度信息,但Sore应该是float类型,因此,需要对一组经纬度进行编码(即GeoHash编码)。

简要步骤:

Step 1:将经度/纬度进行二分拆解,得到二叉树结构,并进行0/1编码,再通过N位bit进行存储(N越大,精度越高);

Step 2:将经度和纬度的N位bit进行交叉组合,得到GeoHash值。

GeoHash编码的基本原理是“二分区间,区间编码”,先对经度和纬度分别编码,再将经纬度各自的编码组合成一个最终编码。简单来说,GeoHash将一个空间分割成一个个小方块,我们可以通过查询给定经纬度所在方格周围的4个或者8个方格,以此进行“周边查找”。【GeoHash值相近,并不一定位置相近,故需计算邻居节点,以提高LBS精度】

深入理解redis数据类型

注意:

在项目开发中,会看到将一个Redis单独划分出来,用于经纬度的计算。如果数据量过亿,就需要对 Geo 数据进行拆分,按国家/省/市拆分,甚至按区拆分,以降低单个 zset 集合的大小。

【原因:在一个地图应用中,数据可能会有百万千万条,如果使用Geo,位置信息将全部放在一个 zset 集合中。在 Redis 的集群环境中,集合 可能会从一个节点迁移到另一个节点,如果单个 key 的数据过大,会导致集群的迁移出现卡顿等问题,影响线上服务的正常运行。所以,建议 Geo 的数据使用单独的 Redis 实例部署。】

附:这九种数据类型对应底层的存储结构如图


深入理解redis数据类型

文章来源:https://cloud.tencent.com/developer/column/93816