Redis 基础入门 - 安装、基础数据结构、跳表、验证码实现、PV,UV

Redis 基础入门

一、安装

  • 创建挂载文件
mkdir -p /mydata/redis/conf
touch /mydata/redis/conf/redis.conf
  • 启动 redis 容器
docker run -p 6379:6379 --name redis -v /mydata/redis/data:/data \
-v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf \
-d redis redis-server /etc/redis/redis.conf
  • 测试安装

进入 redis-cli

docker exec -it redis redis-cli

我们输入简单的 get set 指令,查看 redis 是否能成功执行

此时,虽然 redis 是可以运行的,但是数据都没有持久化,一旦重启,数据就消失了,所以需要添加配置

  • 进入下面的网址,添加 redis 的自描述文件

获取配置文件

将信息复制到我们的 redis.conf 文件夹中

找到 appendonly,将后面的 no 改为 yes (即开启 AOP 持久化策略)

我们再进行测试

  • 安装可视化管理程序

首先必须将 conf 文件中的 bind 数据进行修改

要将 bind 127.0.0.1 改成 bind 0.0.0.0

(标签:阿里云、腾讯云远程连接 redis失败,RDM)

安装 RDM ,填写 redis 所在的宿主机的 ip username password 即可



二、基础数据类型

基础数据类型的操作,可以参考下面这个链接:https://www.runoob.com/redis/redis-strings.html

这里就不再赘述了


1、String

String 使用 get set 进行设置,可以设置定时删除、步长增加,因为 redis 单线程的缘故,所以 incr decr 的操作是原子性的

其底层数据结构很简单,和 java 中的 ArrayList 类似:

string的数据结构

如图中所示,内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M的空间。需要注意的是字符串最大长度为512M


2、list

list 底层数据结构是双向链表:

双向链表

正因如此,list 有左插右插的操作


3、set

redis 中的 set 和 java 中的 HashSet 类似,可以自动排重,并提供判断元素是否在 set 中的接口


4、hash

redis 中的 hash 和 java 中的 map 类似

使用 hash 可能是出于下面这样的考量,比如我们想要存储用户的数据,有下面几个方案:

  • 方案1:

对象序列化后存储,要使用的时候再提取并反序列化

序列化存储

  • 方案2:

用冗余的 id+用户信息数据进行存储

方案2

这种方案还不如第一种方案

  • 方案3:

借助 hash 进行存储

通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题

使用 hash 进行存储

使用 方案3 的话,当我们希望获取用户的某个字段的信息,就不需要像 方案1 那样,将整个对象的信息提取出来再序列化了,提高了信息获取的效率

不过在实际开发中,这种实现方式除非在系统非优化到这步才使用,不然多数情况下为了图方便,我们还是使用 方案1


5、zset

相较于 set ,zset 为每个数据添加了 score 字段,所有元素会默认根据 score 的大小,从小到大排序

因为这种特性,遇到热榜排行、商城信息排行等业务,使用 zset 就显得分外方便

这里我们模拟一个热销榜:

热销榜

在像用户展示之前,我们获取的数据顺序如下:

可以看到,排序是按照分数递增的

获取的数据

如果哪件商品的热度下降了,我们可以通过修改其 score 值实现排名的降低:

排名下降

zset 的底层实现是跳表,跳表简单来说,就是为了解决链表这种数据结构定位慢的问题(O(N)),可以将链表定位的时间复杂度降为 O(logn),其使用的额外空间无限接近与原链表的大小

关于跳表的细节,可以点击下面这个链接去了解:

什么是跳表



三、发布/订阅模式

订阅者在订阅指定频道后,发布者在对应频道发布信息,所有订阅该频道的 redis 进程都会受到发布者发布的消息

订阅

发布

  • 打开一个客户端,订阅 channel1:

订阅

  • 打开另一个客户端,作为发布者向 channel1 中发布信息 hello:

发布者

  • 可以看到,订阅者收到了信息:

收到信息



四、Redis 新数据类型


1、Bitmap

在我们平时的开发过程中,会有一些boolean型数据需要存取,比如用户一年的签到记录,签了是1,没签是0,要记录365天。如果使用普通的key/value,每个用户要记录365个,当用户数上亿的时候,需要的存储空间是惊人的。

为了解决这个问题,redis提供了位图数据结构,这样每天的签到记录只占据一个位,365天就是365个位,46个字节(一个稍长一点的字符串)就可以完全容纳下,这就大大节约了存储空间。位图的最小单位是比特(bit),每个bit的取值只能是0或1。

1)使用

这里我们设置一个场景,使用 Bitmap 记录一款应用当天的用户登录情况

  • setbit:

假设 2022年3月22日,1、6、11、15、19号用户登录了应用

image-20220322202921155

这里我们要注意,有些情况下,为用户设置的 id 头几位可能有重复数,比如 1号用户为 100001,2号用户为100002,如果直接将用户 id 作为参数存入 bitmap 中,那么 bitmap 的偏移量会很大,导致初始化时间过长,进而使得 redis 产生阻塞

对于这种情况,我们应该在将数据存入 bitmap 之前,去除所有数字头部重复的部分

  • getbit:

为了了解某个用户在某天有无登录,可以使用 gitbit 获取bitmap 数据

1号用户在 2022年3月22日进行了登录操作:

getbit

  • bitcount:

为了统计某天的用户月活,我们可以使用 bitcount 统计 bitmap 中 1 的个数

2022年3月22日日活人数为 5:

bitcount

  • bitop:

bitop 可以对两个 bitmap 进行与、或、非、异或操作,并将操作结果存入一个新的 bitmap 中

统计 20220327 和 20220328 都在线的人数:

对于这个请求,我们可以对这两天的 bitmap 进行操作

结果存入新的 bitmap users:and:20220327_28

and操作

统计两日都在线人数,只需要对新的 bitmap 进行 bitcount 操作即可

统计两日都在线的人数


2)实际应用场景

除了上面介绍的统计日活月活外,涉及到大量用户、长时间统计的业务,都可以考虑使用 bitmap

比如签到业务,使用 uid 做 key,单个 bitmap 统计用户从注册开始的签到状态。这样,在3亿用户的前提下,一年的数据使用量是

300000000*365/8/1024/1024/1024 = 12.74 GB,还是很划算的


3)bitmap 与 set 比较

乍看之下,好像使用 bitmap 必定会比 set 节省空间

我们以统计日活做例子

  • 假设网站注册用户数为 1亿,日活 5千万,单个用户 id 占据 64bit

那么单日空间消耗为:

bitmap 占用空间:1 0000 0000 /8/1024/1024/1024 = 0.01 GB

set 占用空间:5000 0000 * 64 /8 /1024/1024/1024 = 0.32GB

一年下来空间消耗为:

Bitmap: 3.65GB

Set : 116.8 GB

可以发现,bitmap 节省的空间还是十分客观的

但是,假设网站虽然有很高的注册量,但是有大量僵尸粉,情况就大不相同了

  • 假设网站还是 1亿注册量,但是日活只有 10 万

单日空间消耗:

bitmap 占用空间:1 0000 0000 /8/1024/1024/1024 = 0.01 GB

set 占用空间:10 0000 * 64 /8 /1024/1024/1024 = 0.0007GB

一年下来的空间消耗:

Bitmap: 3.65GB

Set : 0.26 GB

所以说,是否使用 Bitmap,还是要依照业务量来的



2、hyperLogLog

1)介绍

实际业务中,我们有统计 PV(PageView)即页面观看数的需求,方便后续对业务做出调整,这个功能我们可以很方便的使用 redis 的 incr,incrby 统计

但是,像 UV(unique visiter) 即独立访客,在统计访问量的基础上,还需要进行去重,这时候单纯的使用 incr ,incrby 就不好使了,对于这样的业务需求,可以归于一种基数统计需求。所谓基数统计,即假设一个数据集 {1,1,2,2,3},则其基数集是其去重后的集合,即{1,2,3},则其基数统计的结果为 3

对于这个问题,解决方案有很多:

(1)使用 mysql 存储单个 ip 的访问记录

(2)使用 set bitmap 这些数据结构进行存储

但是对于亿级的用户,方案**(1)**自不必说,浪费大量不必要的空间的同时,数据库还有很大的概率寄,为了这么小一个功能耗费这么多资源显然不值当

方案 (2) 中的 set 因为占用量大也不合适,而 bitmap 虽然对空间已经做到尽可能压缩了,但是相对于功能的重要程度,其占用的空间还是大了一些,bitmap 长度为 2^32 ,对于类似 google 搜索页面要统计每日访问的独立 IP的数量,单就 几十亿级别的搜索次数,少说每日资源开销就是几百 mb,累计下来资源消耗还是蛮哈人的

其实对于十几亿级的统计需求,几十亿和几十亿零几百万的区别是不大的,这个时候,我们就需要一种精度可以不是很高,但是空间消耗一定要小的算法,HyperLogLog 边应运而生

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


2)使用
  • pfadd:

向 hll 中"添加"元素,并使用指定算法去重统计元素个数

hll添加数据

  • pfcount

返回基数

返回基数

pfcount 除了统计单个 hll 外,也可以同时统计多个 hll

假设我们想要统计两日的 UV 数据:

统计多日 uv

day1 访问基数集合为

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

FARO_Z

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

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

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

打赏作者

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

抵扣说明:

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

余额充值