您现在的位置是:首页 > 文章 > Redis 基本数据类型与高级数据结构 网站文章

Redis 基本数据类型与高级数据结构

孙玉超 2021-03-21 13:01:33 0 评论 870 浏览 0 收藏 0


Redis 五种基本数据类型


众所周知,Redis 五种基本数据类型:string,hash,list,set,zset。其中 string 可以说是我们最常用的类型,往 Redis 存入一些缓存数据。例如本人在做公司对接钉钉审批流的时候,就把访问钉钉接口的 accessToken 给缓存在 Redis 中,不然每次调用接口还要去远程请求获取 accessToken 实在是太浪费网络消耗。


hash 也是很常用的数据类型,比如之前做的会员档案模块,我们的工作人员在手机上录入会员的档案,保存操作的功能。由于工作人员不止一个,所以我以工作人员的 userId 作为 Redis 的 key,然后用 hash 存储会员档案的草稿信息。其中当前时间戳作为 hash 的 key,用户档案信息作为 value 。


list 也是较常用的数据类型,比如我们第二版秒杀模型,准备用它来做库存校验。因为秒杀的特点,在同一个时间点,大量请求涌过来,但是一个商品的秒杀库存通常是很有限的。比如你商品就 10 个库存,1000个用户来抢,那其实说到底只有 10 次请求是有效的。因此我们可以将商品的 id 在 list 中依次放 10 个。如下图:

当进来一个请求我们就判断 list 里面是否还有元素,如果有就 rpop 一次,如果没有就直接返回抢完,这样一来可以过滤掉无效请求。当然 list 还有其他很多用法,视具体场景。


set 和 zset(sortedset) 我用的比较少,set 是无顺序不重复集合,而 zset 是有顺序的,主要可以通过一个权重参数 score 来进行排序。还可以通过 score 的范围来获取元素列表。


如果不熟悉 Redis 基本命令,可以去官网或菜鸟教程等网站查阅 Redis 菜鸟教程


Redis 高级数据结构


bitmap


bitmap 和 string 类似,只是 bitmap 以位来存储,存储的是连续的二进制数字 0 和 1,只需要一个 bit 位来表示某个元素对应的值。因为 1 byte = 8 bit,所以 bitmap 存储能节省很大的内存空间。其实 bitmap 并不是一种新的数据类型,它就是字符串,不过它也可以对字符串的位进行操作。

例如我们要用 string 类型的命令存储一个字符串 "ABC"。

使用 getbit 命令可以获取偏移量位置上的值

set str ABC #设置字符串
getbit str 0 #0
getbit str 1 #1
getbit str 2 #0

上面的表格很清晰的反应了 bitmap 存储数据的结构。知道 bitmap 的存储方式,下面来看一个具体场景。

例如我们要存储一个用户 2021 年一年的签到记录,只要使用一个标识用户 id 的 key "user:sign:userId:2021:month",然后每个位代表那个月的每一天的签到记录,签了是 1,没签是 0 。这样就可以以很小的内存空间来完成这个需求。

setbit user:sign:userId:2021:3 0 1 #设置该用户三月第一天签到了
setbit user:sign:userId:2021:3 1 1 #设置该用户三月第二天签到了
setbit user:sign:userId:2021:3 2 0 #设置该用户三月第三天没签到
setbit user:sign:userId:2021:3 3 0 #设置该用户三月第四天没签到
...依次类推

我们还可以使用 bitcount 命令来统计偏移位置上所有值为 1 的数量。

bitcount  user:sign:userId:2021:3    #统计该用户三月份签到的天数

试想如果使用 string 的 KV 来记录,如果用户量稍微大一点,你将需要多少的内存来完成。


HyperLogLog


Redis 2.8.9 版本新增了 HyperLogLog 数据结构,这个数据结构用来计算基数(在一个集合中去除掉重复的元素之后剩余的个数),它是有误差的,但是误差率很小,在可接受范围内。并且它在输入元素的数量非常多时,所需的空间是固定的,并且很小。每个 HyperLoglog 的 只需要花费 12KB 内存,就可以计算接近 2^64 个不同元素的基数。

        for (int i = 0; i < 100000; i++) {
            stringRedisTemplate.opsForHyperLogLog().add("test", String.valueOf(i));
        }
        Long test = stringRedisTemplate.opsForHyperLogLog().size("test");
        System.out.println(test); // 99556

用上述代码随便测试一下 HyperLogLog 的计算。


HyperLogLog 就像是一个 set 但是它比 set 要节省空间的多,实际业务场景中可以用来统计网站的注册 IP 数量、页面的 UV 等需要去重统计的需求,虽然结果并不精确,但是对于业务而言,注册的 IP 数量是 5W 还是 5W 零 100 其实并不重要,他们只需要一个大概的数据。


BloomFilter


BloomFilter 叫做布隆过滤器,用来判断一个元素是否存在于一个集合中,底层数据结构和 bitmap 相关,所以很节省内存空间。与 HyperLogLog 一样,它存在一些误差,但是误差很小。当它说一个元素在一个集合中存在时,可能并不存在。当它说一个元素在一个集合中不存在时,就一定不存在。


默认安装的 Redis 没有这个模块,需要额外安装。可以去官网的 Modules 页面找到 RedisBloom 的 github 地址,下面安装过程

#安装布隆过滤器
wget https://github.com/RedisLabsModules/rebloom/archive/v2.2.4.tar.gz
tar -zxvf v1.1.1.tar.gz
cd redisbloom-2.2.4/
make
ls

#在 redis.conf 中加入该配置
loadmodule 路径/redisbloom-2.2.4/rebloom.so

#重启 redis ,然后使用布隆过滤器的命令来测试是否添加成功,
bf.add users 1
bf.add users 2
bf.exists users 1
bf.exists users 3

1. 布隆过滤器可以用来解决缓存穿透问题,我们可以将要请求的资源id 全部放在布隆过滤器中,访问时先判断布隆过滤器中有没有,如果没有直接 return;

2. 布隆过滤器还可以用来应对爬虫,将爬到的数据比如 URL 放入布隆过滤器,判断如果不存在就添加进去。

3. 布隆过滤器还可以用来对用户进行喜好推送,比如一个视频 APP,要给用户推荐短视频,那要保证推荐的是用户没有看过的,我们把用户已经看过的视频放进布隆过滤器,推送之前判断一下推送的视频是否不存在于布隆过滤器,如果不存在,说明用户一定没有看过。


具体 BloomFilter 的命令以及原理可以参考:https://zhuanlan.zhihu.com/p/102067467


SpringBoot 的 Redis Starter 并没有做 BloomFilter 的 API,可以选择 Google 的一个依赖,提供了很多 API。

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>30.1.1-jre</version>
</dependency>

Redisson Starter 也对此提供了相关 API

 <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.15.2</version>
        </dependency>

GEO


Redis GEO 主要用于存储地理位置信息,并对存储的信息进行操作,该功能在 Redis 3.2 版本新增。

待完善


转载请注明出处:转载请注明出处

上一篇 : 最近情绪些许低落 下一篇 : 为什么使用消息队列?

留言评论

所有回复

暮色妖娆丶

96年草根站长,2019年7月接触互联网踏入Java开发岗位,喜欢前后端技术。对技术有强烈的渴望,2019年11月正式上线自己的个人博客