Redis新的3种数据类型

Redis新的3种数据类型

Bitmaps:位操作字符串

现代计算机使用二进制(位)作为信息的基本单位,1个字节等于8位,例如“abc”字符串是有3个字节组成,但实际在计算机内存储时将其使用二进制表示,“abc”分别对应的ASCII码是:97、98、99,对应的二进制分别是01100001、01100010、01100011,如下图:

在这里插入图片描述

合理地使用位操作能够有效地提高内存使用率和开发效率。

Redis提供了Bitmaps这个“数据类型”可以实现对位的操作:

  • Bitmaps本身不是一种数据类型, 实际上它就是字符串(key-value) , 但是它可以对字符串的位进行操作,字符串中每个字符对应1个字节,也就是8位,一个字符可以存储8个bit位信息。
  • Bitmaps单独提供了一套命令, 所以在Redis中使用Bitmaps和使用字符串的方法不太相同。 可以把Bitmaps想象成一个以位为单位的数组, 数组的每个单元只能存储0和1, 数组的下标在Bitmaps中叫做偏移量。

在这里插入图片描述

常用命令

setbit:设置某个偏移量的值(0或1)

setbit key offset value

设置offset偏移位的值为value,offset的值是从0开始的。

offset参数必须大于或等于0 ,小于232 (bit映射被限制在512 MB之内)。value的值只能为0或1。

返回值:指定偏移量原来储存的位。

示例:

127.0.0.1:6379> setbit bit 10000 1
(integer) 0
127.0.0.1:6379> getbit bit 10000 #具体见下面
(integer) 1

现实中举例:

假设用来存放用户是否访问过网站,将用户访问的数据存放在bitmaps中,将访问的用户记做1,没有访问的用户记做0,用户id作为offset。

假设现在有20个用户,其中userid=1、6、11、15、19的用户对网站进行了访问,那么当前bitmaps初始化结果如图:
在这里插入图片描述

getbit:获取某个偏移位的值

getbit key offset

获取key所对应的bitmaps中offset偏移位的值。

返回值:0或者1

示例:

127.0.0.1:6379> flushdb #清空db,方便测试
OK
127.0.0.1:6379> setbit users 1001 1 #设置偏移量1001的bit位的值为1
(integer) 0
127.0.0.1:6379> getbit users 1001 #获取偏移位1001的bit位的值
(integer) 1
127.0.0.1:6379> getbit users 1000 #获取偏移位1000的bit位的值,未设置,返回0
(integer) 0

bitcount:统计bit位都为1的数量

bitcount key [start] [end]

统计字符串被设置为1的bit数,一般情况下,给定的整个字符串都会被进行统计,通过指定额外的start或者end参数,可以让计数只在特定的位上进行,start和end参数,都可以使用负数值:比如-1表示最后一个位,而-2表示倒数第二个位,以此类推。

注意了:start、end是指bit组的字节的下标数,一个直接对应8个bit,所以[a, b]对应的offset范围是[8a, 8b+7]

示例:

127.0.0.1:6379> flushdb # 清空db,方便测试
OK
127.0.0.1:6379> setbit user 7 1 # 设置user这个bitmaps中偏移量为7的bit为值为1,也就是第8个bit位的值
(integer) 0
127.0.0.1:6379> setbit user 15 1 # 设置user这个bitmaps中偏移量为15的bit为值为1
(integer) 0
127.0.0.1:6379> setbit user 23 1 # 设置user这个bitmaps中偏移量为23的bit为值为1
(integer) 0
127.0.0.1:6379> bitcount user # 获取user这个bitmaps中1的数量
(integer) 3
127.0.0.1:6379> bitcount user 0 1 # 获取[0,1]这个字节内bit位上1的数量,也就是offset是[0,15]的位置上1的数量,所以是2个
(integer) 2
127.0.0.1:6379> bitcount user 0 0 # 获取[0,0]这个字节内bit位上1的数量,也就是offset是[0,7]的位置上1的数量,只有7这个位置,所以是1个
(integer) 1

bittop:对一个多个bitmaps执行位操作

bittop operation destkey key [key ...]

对一个或多个保存二进制位的字符串key进行位元操作,并将结果保存到destkey上。

operation可以是and 、or 、not 、xor这四种操作中的任意一种:

  • bittop and destkey key [key …] ,对一个或多个key求逻辑并,并将结果保存到destkey。
  • bittop or destkey key [key …] ,对一个或多个key求逻辑或,并将结果保存到destkey。
  • bittop xor destkey key [key …] ,对一个或多个key求逻辑异或,并将结果保存到destkey。
  • bittop not destkey key ,对给定key求逻辑非,并将结果保存到destkey。

除了not操作之外,其他操作都可以接受一个或多个key作为输入。

返回值:保存到destkey的字符串的长度,和输入key中最长的字符串长度相等。

示例:略(复杂的数字逻辑计算,作者认为搞的没意义,我们并不是学术轮着,我们只是技术应用者,比如作者本人。)

bitmaps与set比较

假设网站有1亿用户, 每天独立访问的用户有5千万, 如果每天用集合类型和Bitmaps分别存储活跃用户可以得到表:

数据类型每个用户id占用空间需要存储的用户量全部内存量
set集合64位5000000064 位 * 50000000 = 400MB
Bitmaps1位1000000001 位 * 100000000 = 12.5MB

很明显, 这种情况下使用Bitmaps能节省很多的内存空间, 尤其是随着时间推移节省的内存还是非常可观的。

但Bitmaps并不是无敌存在的, 假如该网站每天的独立访问用户很少, 例如只有10万(大量的僵尸用户) , 那么两者的对比如下表所示, 很显然, 这时候使用Bitmaps就不太合适了, 因为基本上大部分位都是0。

数据类型每个用户id占用空间需要存储的用户量全部内存量
set集合64位10000064 位 * 100000 = 800KB
Bitmaps1位1000000001 位 * 100000000 = 12.5MB

所以,什么技术都有“调优”这个事。

HyperLoglog

在工作当中,我们经常会遇到与统计相关的功能需求,比如统计网站PV(PageView 页面访问量),可以使用Redis的incr、incrby轻松实现。但像UV(UniqueVisitor 独立访客)、独立IP 数、搜索记录数等需要去重和计数的问题如何解决?这种求集合中不重复元素个数的问题称为基数问题。

解决基数问题有很多种方案:

  • 数据存储在数据库表中,使用distinct count计算不重复个数。
  • 使用Redis提供的hash、set、bitmaps等数据结构来处理。

以上的方案结果精确,但随着数据不断增加,导致占用空间越来越大,对于非常大的数据集是不切实际的。

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

在Redis里面,每个HyperLogLog键只需要花费12 KB内存,就可以计算接近264个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

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

什么是基数?比如数据集 {1, 3, 5, 7, 5, 7, 8},那么这个数据集的基数集为 {1, 3, 5 ,7, 8},基数 (不重复元素) 为5。 基数估计就是在误差可接受的范围内,快速计算基数。

常用命令

pfadd:添加多个元素

pfadd key element [element ...]

向HyperLoglog类型的key中添加一个或者多个元素。添加一个或者多个元素到key对应的集合中。

返回值:1代表添加成功,0代表添加失败。

示例:

127.0.0.1:6379> flushdb # 清空db方便测试
OK
127.0.0.1:6379> pfadd pro java php c c++ #pro中添加4个元素[java,php,c,c++],添加成功发,返回1
(integer) 1
127.0.0.1:6379> pfadd pro java # 再次添加java,由于已经存在,所以添加失败,返回0
(integer) 0
127.0.0.1:6379> pfadd pro java js # 再次添加2个元素,java已经存在了,但是js不存在,添加成功,返回1
(integer) 1

pfcount:获取多个HLL合并后元素的个数

pfcount key1 key2 ...

统计一个或者多个key去重后元素的数量。

示例:

127.0.0.1:6379> flushdb # 清空db方便测试
OK
127.0.0.1:6379> pfadd pro1 a b c d e #pro1中5个元素:[a,b,c,d,e]
(integer) 1
127.0.0.1:6379> pfcount pro1 #pro1中数量为5
(integer) 5
127.0.0.1:6379> pfadd pro2 b c d e f #pro2中5个元素:[b,c,d,e,f]
(integer) 1
127.0.0.1:6379> pfcount pro2 #pro2中数量为5
(integer) 5
127.0.0.1:6379> pfcount pro1 pro2 # 获取pro1和pro2去重之后数量合集:[a,b,c,d,e,f],数量为5
(integer) 5

pfmerge:将多个HLL合并后元素放入另外一个HLL

pfmerge destkey sourcekey [sourcekey ...]

将多个sourcekey合并后放到destkey中。

示例:

127.0.0.1:6379> flushdb # 清空db方便测试
OK
127.0.0.1:6379> pfadd pro1 a b c d e #pro1中5个元素:[a,b,c,d,e]
(integer) 1
127.0.0.1:6379> pfcount pro1 #uv1中数量为5
(integer) 5
127.0.0.1:6379> pfadd pro2 b c d e f #pro2中5个元素:[b,c,d,e,f]
(integer) 1
127.0.0.1:6379> pfcount pro2 #pro2中数量为5
(integer) 5
127.0.0.1:6379> pfmerge pro_desk pro1 pro2 #将pro1和pro2合并后放入pro_desk
OK
127.0.0.1:6379> pfcount pro_desk #pro_desk元素个数为6
(integer) 6

Geographic

Reids3.2 中增加了对GEO类型的支持,GEO(Geographic),地理信息的缩写。

该类型,就是元素的2维坐标,在地图上就是经纬度,redis基于该类型,提供了经纬度设置、查询、范围查询、距离查询,经纬度Hash等常见操作。

常用命令

geoadd:添加多个位置的经纬度

geoadd key longitude latitude member [longitude latitude member ...]

longitude latitude member:经度 纬度 名称。

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

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

已经添加的数据,是无法再次往里面添加的。

经纬度数据没这么一条一条添加的,都是通过程序整个城市一次性导入。

示例:

127.0.0.1:6379> flushdb #清空db,方便测试
OK
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai #添加上海的经纬度
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen 116.38 39.90 beijing #添加重庆、深圳、北京 3 个城市的经纬度
(integer) 3
127.0.0.1:6379> type china:city #发现geo实际上使用zset类型存储的
zset
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "shenzhen"
3) "shanghai"
4) "beijing"
127.0.0.1:6379> zrange china:city 0 -1 withscores
1) "chongqing"
2) "4026042091628984"
3) "shenzhen"
4) "4046432193584628"
5) "shanghai"
6) "4054803462927619"
7) "beijing"
8) "4069885332386336"

geopos:获取多个位置的坐标值

geopos key member [member ...]

示例:

127.0.0.1:6379> flushdb #清空db,方便测试
OK
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai #添加上海的经纬度
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen 116.38 39.90 beijing #添加重庆、深圳、北京 3 个城市的经纬度
(integer) 3
127.0.0.1:6379> geopos china:city wuhan beijing chongqing #获取武汉、北京、重庆 3个城市的坐标,由于没有添加武汉的数据,所以没有获取到,其他2个获取到了
1) (nil)
2) 1) "116.38000041246414185"
   2) "39.90000009167092543"
3) 1) "106.49999767541885376"
   2) "29.52999957900659211"

geodist:获取两个位置的直线距离

geodist key member1 member2 [m|km|ft|mi]

单位:默认米,ft英里,mi英尺

示例:

127.0.0.1:6379> flushdb #清空db,方便测试
OK
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai #添加上海的经纬度
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen 116.38 39.90 beijing #添加重庆、深圳、北京 3 个城市的经纬度
(integer) 3
127.0.0.1:6379> geodist china:city beijing chongqing km #获取北京到重庆的直线距离
"1462.9505"

georadius:以给定的经纬度为中心,找出某一半径内的元素

georadius key longitude latitude radius m|km|ft|mi

示例:

127.0.0.1:6379> flushdb #清空db,方便测试
OK
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai #添加上海的经纬度
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen 116.38 39.90 beijing #添加重庆、深圳、北京 3 个城市的经纬度
(integer) 3
127.0.0.1:6379> georadius china:city 110 30 1000 km #在china:city中检索:以经纬度(110,30)为中心,半径为1000km内的位置列表
1) "chongqing"
2) "shenzhen"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WorkLee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值