【6.0】Redis高级用法
【一】慢查询
【1】生命周期
- 我们配置一个时间,如果查询时间超过了我们设置的时间,我们就认为这是一个慢查询.
- 慢查询发生在第三阶段
- 客户端超时不一定慢查询,但慢查询是客户端超时的一个可能因素
-
慢查询是指执行时间超过预设阈值的数据库查询语句。在慢查询的生命周期中,以下是一般的流程:
-
客户端发送查询请求给数据库。
-
数据库接收请求并开始执行查询。
-
数据库记录查询开始时间。
-
执行查询,包括索引查找、排序、聚合等操作。
-
数据库完成查询后记录查询结束时间。
-
数据库计算查询耗时(可通过结束时间减去开始时间得到)。
-
如果查询耗时超过了设置的慢查询阈值,将查询语句及相关信息存储到慢查询日志中。
-
-
需要注意的是,客户端超时并不一定代表慢查询发生了,而慢查询可能是导致客户端超时的一个可能因素。
[](http://photo.liuqingzheng.top/2023 04 16 22 54 38 /image-20230416224936638.png)
【2】两个配置
- 为了有效地管理慢查询,可以通过配置来设置慢查询的最大长度和慢查询的阈值。
(1)slowlog-max-len
- 慢查询是一个先进先出的队列
- 固定长度
- 保存在内存中
- 该配置表示慢查询日志的最大长度,它是一个先进先出的队列,在内存中保存,是一个固定长度的队列。
- 当慢查询日志达到最大长度时,旧的日志会被新的日志所替代。
(2)slowlog-max-len
- 慢查询阈值(单位:微秒)
- slowlog-log-slower-than=0,记录所有命令
- slowlog-log-slower-than <0,不记录任何命令
- 该配置表示慢查询的时间阈值,以微秒为单位。
- 只有当查询的执行时间超过该阈值时,才会被记录到慢查询日志中。
- 需要注意的是,当
slowlog-log-slower-than=0
时,所有命令都会被记录;- 当
slowlog-log-slower-than<0
时,则不会记录任何命令。
【3】配置方法
(1)默认配置
config get slowlog-max-len=128 Config get slowly-log-slower-than=10000
- 可以使用
config get
命令获取当前的默认配置:
pythonconfig get slowlog-max-len=128
config get slowlog-log-slower-than=10000
- 这将返回当前的慢查询最大长度和慢查询时间阈值。
(2)修改配置文件重启
- 可以通过修改 Redis 的配置文件,设置
slowlog-max-len
和slowlog-log-slower-than
参数的值,并且重启 Redis 服务使配置生效。
(3)动态配置
- 可以通过
config set
命令在运行时动态地修改配置参数,并且通过config rewrite
命令将修改后的配置持久化到本地配置文件中。 - 例如:
# 设置记录所有命令
config set slowlog-log-slower-than 0
# 最多记录100条
config set slowlog-max-len 100
# 持久化到本地配置文件
config rewrite
- 需要注意的是,以上代码设置了慢查询时间阈值为0,即记录所有命令;
- 设置的慢查询最大长度为100条。
config set slowlog-max-len 1000
config set slowlog-log-slower-than 1000
【4】三个命令
- 为了管理和查看慢查询日志,Redis 提供了以下三个命令:
slowlog get [n]
-
该命令用于获取慢查询队列中最近的 n 条慢查询日志。每条慢查询日志包含以下四个属性:
-
日志的标识 ID。
-
发生的时间戳。
-
命令执行的耗时。
-
执行的命令和参数。
-
-
使用示例:
slowlog get 10
可以获取最近的10条慢查询日志。
slowlog len #获取慢查询队列长度
-
该命令用于获取当前慢查询队列的长度。
-
使用示例:slowlog len 可以获取当前慢查询队列的长度。
slowlog reset #清空慢查询队列
-
该命令用于清空慢查询队列,即删除所有的慢查询日志。
-
使用示例:slowlog reset 可以清空慢查询队列。
【5】经验
- 1 slowlog-max-len 不要设置过大,默认10ms,通常设置1ms
- 2 slowlog-log-slower-than不要设置过小,通常设置1000左右
- 3 理解命令生命周期
- 4 定期持久化慢查询
-
以下是一些关于慢查询的经验和建议:
-
slowlog-max-len
参数不要设置过大,默认值为10,通常情况下可以设置为1,即只保留最后一条慢查询日志。 -
slowlog-log-slower-than
参数不要设置过小,通常情况下可以设置为1000(1毫秒),根据实际业务的性能要求来定。 -
理解查询命令的生命周期,从客户端发送请求到数据库执行完成的过程,以及对性能影响的各个环节。
-
定期持久化慢查询日志,将重要的慢查询日志保存到磁盘上供后续分析和优化使用。
-
-
通过以上的配置、命令和经验,可以更好地管理和分析慢查询,提高系统的性能和稳定性。
【6】Redis响应慢原因排查
- 公司好多项目用这一个redis实例
- 最近公司发现,redis响应非常慢
- 通过排查它的 慢查询--》排查出一些慢命令
- 找到对应的执行项目---》位置
- 避免再执行这些命令了
-
网络延迟:确保Redis服务器和客户端之间的网络连接稳定。您可以通过在Redis服务器和客户端之间运行网络连通性测试来检查网络是否存在问题。如果发现网络延迟过高,您可能需要优化网络配置或者考虑部署Redis集群以提高性能和稳定性。
-
Redis配置不当:检查Redis的配置文件,确保配置参数合理设置。例如,要确保合理设置maxclients参数以限制连接数,并根据实际情况调整Redis的内存限制。
-
慢查询命令:您已经通过排查慢查询命令找到了一些造成Redis响应慢的原因。对于这些慢查询命令,有几种处理方式可以尝试:
-
优化命令:针对执行较慢的命令,您可以通过重写命令或使用更高效的数据结构来提高其性能。例如,对于复杂的查询需求,您可以考虑使用Redis的有序集合(Sorted Set)或者创建适当的索引来加速查询操作。
-
数据分片:如果某些操作频繁地访问相同的Redis键,您可以考虑将数据分片到多个Redis实例上,以减轻单个实例的压力并提高整体性能。例如,根据不同的项目或业务逻辑,将数据分散存储在不同的Redis实例中,以避免对同一个实例过度的请求。
-
缓存策略:如果某些命令的结果可以被缓存并且不经常变动,您可以考虑使用Redis作为缓存服务,以减少对后端数据库或其他资源的频繁访问。通过合理设置缓存的过期时间和相关策略,可以大大降低命令执行的时间,提高系统的整体响应速度。
-
【7】实例
-
假设您的公司有一个电子商务平台,采用Redis作为商品信息的缓存服务。
- 最近发现查询商品详情的接口出现了响应延迟较大的问题,明显影响了用户体验。
-
通过排查慢查询命令,您发现在获取商品详情时,执行了一个复杂的查询操作,需要统计商品所有评论的平均评分,并返回给前端。
- 这个查询操作在评论较多的情况下会变得非常耗时。
-
为了解决这个问题,您可以采取以下几步:
-
优化查询:
-
通过Redis的有序集合和哈希表来存储商品的评分和评论信息,以便更高效地进行查询和统计。
-
您可以使用有序集合来存储每个商品的评论分数,并使用哈希表存储评论的详细信息。
-
这样,当需要计算平均评分时,您可以直接从有序集合中获取评论分数,并通过哈希表快速获取评论数量,从而减少查询的时间复杂度。
-
-
异步计算:
-
将评分的计算过程异步化,不需要在实时查询商品详情时计算评分数据。
-
您可以通过消息队列或异步任务来处理评分的计算,将结果缓存至Redis或其他数据存储中,并保证评分数据在变动时及时更新。
-
-
接口缓存:
- 考虑对商品详情接口进行缓存,将查询到的商品详情信息缓存在Redis中,设置合理的过期时间和缓存策略,以减少对后端数据库的频繁查询。
- 当有新的评论产生时,及时更新缓存数据。
-
【二】pipline与事务
【1】什么是pipeline(管道)
- Redis的pipeline(管道)功能在命令行中没有,但redis是支持pipeline的,而且在各个语言版的client中都有相应的实现
- 将一批命令,批量打包,在redis服务端批量计算(执行),然后把结果批量返回
- 1次pipeline(n条命令)=1次网络时间+n次命令时间
- pipeline期间将“独占”链接,此期间将不能进行非“管道”类型的其他操作,直到pipeline关闭;如果你的pipeline的指令集很庞大,为了不干扰链接中的其他操作,你可以为pipeline操作新建Client链接,让pipeline和其他正常操作分离在2个client中。
- 不过pipeline事实上所能容忍的操作个数,和socket-output缓冲区大小/返回结果的数据尺寸都有很大的关系;同时也意味着每个redis-server同时所能支撑的pipeline链接的个数,也是有限的,这将受限于server的物理内存或网络接口的缓冲能力
Pipeline(管道)是Redis提供的一种机制,可以将多个命令批量打包提交给Redis服务端,在服务端批量执行这些命令,并将结果批量返回给客户端。使用Pipeline可以有效减少网络延迟对性能的影响,提高命令的执行效率。
在Redis中,Pipeline的实现并不依赖于命令行工具,而是通过各语言的Redis客户端进行操作。因此,在不同编程语言的Redis客户端中都可以找到相应的Pipeline实现。
使用Pipeline时,一次Pipeline操作相当于一次网络时间加上多个命令的执行时间。在Pipeline期间,连接会被独占,其他非Pipeline类型的操作无法同时进行,直到Pipeline关闭。如果Pipeline的指令集很庞大,为了避免干扰其他操作,可以使用新建的客户端连接来进行Pipeline操作,将Pipeline和其他操作分离在两个客户端中。
同时需要注意的是,Pipeline的操作个数受限于socket-output缓冲区大小和返回结果的数据尺寸,也会受到Redis服务器的物理内存和网络接口缓冲能力的限制。
【2】客户端实现
import redis
# 创建Redis连接池
pool = redis.ConnectionPool(host='10.211.55.4', port=6379)
r = redis.Redis(connection_pool=pool)
# 创建Pipeline对象
pipe = r.pipeline(transaction=True) # 开启事务
# pipe = r.pipeline(transaction=False) # 不开启事务
# 在Pipeline中添加命令
pipe.set('name', 'dream')
pipe.set('role', 'nb')
# 执行Pipeline中的命令
pipe.execute()
- 上述代码首先创建了一个Redis连接池,然后使用该连接池创建了Redis对象
r
。 - 接下来,通过
r.pipeline()
方法创建了一个Pipeline对象pipe
,通过设置transaction
参数为True
来开启事务。 - 在Pipeline对象中,可以使用类似Redis对象的命令方法(例如
set
)来添加要执行的命令。 - 最后,通过调用
execute()
方法来执行Pipeline中的所有命令。
【3】与原生操作对比
- 通过pipeline提交的多次命令,在服务端执行的时候,可能会被拆成多次执行,而mget等操作,是一次性执行的,所以,pipeline执行的命令并非原子性的
- 与原生操作相比,通过Pipeline提交的多个命令在服务端执行时可能会被拆分成多个批次进行执行。
- 而像
mget
这样的操作是一次性执行的,所以使用Pipeline执行的命令并不具备原子性。
【4】使用建议
- 1 注意每次pipeline携带的数据量
- 2 pipeline每次只能作用在一个Redis的节点上
- 3 M(mset,mget….)操作和pipeline的区别
- 注意每次Pipeline携带的数据量,过大的Pipeline可能会导致性能问题。
- Pipeline每次只能作用于一个Redis节点,无法跨节点操作。
- 将大量的
mset
、mget
等类似操作与Pipeline进行对比。这些操作无法与Pipeline一起使用,但可以通过Pipeline的批量操作来提高执行效率。
【5】原生事务操作
(1)使用multi
和exec
开启和执行事务
- 开启事务,放到管道中一次性执行
multi # 开启事务
set name dream
set age 18
exec # 执行事务
- 首先使用
multi
命令开启一个事务,在multi
和exec
之间添加的所有命令都会被包含在这个事务中。 - 然后,可以像正常操作一样使用Redis命令(例如
set
)来添加要执行的命令。 - 最后,调用
exec
命令来执行事务中的所有命令。
(2)模拟事务
- 在开启事务之前,可以先使用
watch
命令对某个键进行监视。 - 这样,在事务执行之前,如果被监视的键发生了变化,事务的执行就会失败。
wathc age
multi
decr age
exec
- 先使用
watch
命令监视age
键,在开启事务之前调用watch
命令可以实现乐观锁的效果。 - 然后,使用
multi
命令开启事务,并添加要执行的命令。 - 最后,调用
exec
命令执行事务中的所有命令。 - 如果在
exec
执行之前,被监视的age
键发生了变化,那么事务执行将会失败。
(3)另一台机器
mutil
decr age
exec # 先执行,上面的执行就会失败(乐观锁,被wathc的事务不会执行成功)
- 在另一台机器上执行了一个事务。
- 在这个事务中,先执行
decr age
命令,然后再调用exec
命令来执行事务中的所有命令。 - 由于上面的事务已经对
age
键进行了监视,所以在另一台机器上执行的这个事务将会失败,保证了事务的一致性。
【三】发布订阅
- 发布订阅(Pub/Sub)模型是一种消息传递模式,其中消息的发送者称为发布者,消息的接收者称为订阅者。
- 在这种模型中,发布者发布消息到一个或多个频道,而订阅者则可以订阅一个或多个频道来接收消息。
【1】角色与设计模式
- 发布者/订阅者/频道
- 发布者发布了消息,所有的订阅者都可以收到,就是生产者消费者模型(后订阅了,无法获取历史消息)
- 设计模式中的:观察者模式
-
在发布订阅模型中,有以下三个角色:
-
发布者(Publisher):发布消息的实体。
-
订阅者(Subscriber):订阅消息的实体,可以订阅一个或多个频道来接收消息。
-
频道(Channel):消息被发布到的地方,订阅者可以通过订阅相应的频道来接收消息。
-
-
该模型也可以看作是观察者模式的一种实现,其中发布者充当被观察者,而订阅者充当观察者。
【2】模型
- 发布者发布消息到一个或多个频道。
- 所有订阅了相应频道的订阅者都会收到该消息。
- 订阅者没有办法获取历史消息,只能获取从其开始订阅后发布的消息。
【3】API
publish channel message #发布命令
publish souhu:tv "hello world" #在souhu:tv频道发布一条hello world 返回订阅者个数
subscribe [channel] #订阅命令,可以订阅一个或多个
subscribe souhu:tv #订阅sohu:tv频道
unsubscribe [channel] #取消订阅一个或多个频道
unsubscribe sohu:tv #取消订阅sohu:tv频道
psubscribe [pattern...] #订阅模式匹配
psubscribe c* #订阅以c开头的频道
unpsubscribe [pattern...] #按模式退订指定频道
pubsub channels #列出至少有一个订阅者的频道,列出活跃的频道
pubsub numsub [channel...] #列出给定频道的订阅者数量
pubsub numpat #列出被订阅模式的数量
【4】发布订阅和消息队列
- 发布订阅数全收到,消息队列有个抢的过程,只有一个抢到
- 发布订阅和消息队列的区别
- 发布订阅,订阅者都能收到,
- 消息队列有个抢的过程,只有一个抢到
-
发布订阅模型和消息队列模型在工作原理上存在一些区别:
-
发布订阅模型:发布者将消息发布到频道,所有订阅了该频道的订阅者都会接收到消息。
-
消息队列模型:只有一个消费者能够从队列中抢到消息并进行消费。
-
-
发布订阅模型适用于一对多的消息发布与订阅场景,而消息队列模型适用于一对一的消息传递场景。
-
需要根据具体的业务需求来选择使用哪种模型,如果需要广播消息给多个订阅者,那么发布订阅模型更为合适。
-
如果需要消息的顺序性和可靠性,并且只有一个消费者能够处理消息,那么消息队列模型更为适用。
【四】Bitmap位图
【1】位图是什么
- 下面是字符串big对应的二进制(b是98)
[](http://photo.liuqingzheng.top/2023 04 16 22 54 57 /image-20230416225004565.png)
- 位图(Bitmap)是一种数据结构,用于表示一组二进制位的集合。
- 位图通常作为字符串类型进行存储,每个位代表一个标记的状态(0或1)。
- 位图在某些场景下可以非常高效地进行操作和查询。
- 在传统的字节位图中,每个字节可以表示8个位,而在Redis中,位图实现为字符串类型的值,每个字符都含有8个位。
- 例如,在Redis中,如果一个字符串的值是"big",那么它对应的二进制表示如下:
b | i | g |
---|---|---|
0110 | 1001 | 0111 |
- 其中,每一位代表一个特定的标记,可以是0或1。
【2】相关命令
setbit key offset value
:将位图key中指定偏移量offset处的位设置为value。getbit key offset
:获取位图key中指定偏移量offset处的位的值。bitcount key [start end]
:统计位图key中值为1的位的数量,可选参数start和end用于指定范围。bitop op destkey key [key...]
:对多个位图进行与(and)、或(or)、非(not)或异或(xor)操作,并将结果保存在destkey中。bitpos key targetBit [start end]
:计算位图key中,第一个值等于targetBit的位的位置,可选参数start和end用于指定范围。
#放入key位hello 值为big的字符串
set hello big
#取位图的第0个位置,返回0
getbit hello 0
#取位图的第1个位置,返回1 如上图
getbit hello 1
##我们可以直接操纵位
#给位图指定索引设置值
setbit key offset value
#把hello的第7个位置设为1 这样,big就变成了cig
setbit hello 7 1
#test不存在,在key为test的value的第50位设为1,那其他位都以0补
setbit test 50 1
#获取位图指定范围(start到end,单位为字节,注意按字节一个字节8个bit为,如果不指定就是获取全部)位值为1的个数
bitcount key [start end]
#做多个Bitmap的and(交集)/or(并集)/not(非)/xor(异或),操作并将结果保存在destkey中
bitop op destkey key [key...]
#把lqz和lqz2按位与操作,放到after_lqz中
bitop and after_lqz lqz lqz2
#计算位图指定范围(start到end,单位为字节,如果不指定是获取全部)第一个偏移量对应的值等于targetBit的位置
bitpos key targetBit start end
#big 对应位图中第一个1的位置,在第二个位置上,由于从0开始返回1
bitpos lqz 1
#big 对应位图中第一个0的位置,在第一个位置上,由于从0开始返回0
bitpos lqz 0
#返回9:返回从第一个字节到第二个字节之间 第一个1的位置,看上图,为9
bitpos lqz 1 1 2
[](http://photo.liuqingzheng.top/2023 04 16 22 55 05 /image-20230416225016650.png)
【3】独立用户统计
-
1 使用set和Bitmap对比
-
假设有1亿个用户,其中大约有5千万人访问。
-
我们可以使用位图来存储活跃用户的信息。
-
下表比较了使用Set和Bitmap两种数据结构来存储用户量所需的空间:
-
数据类型 | 每个userid占用空间 | 需要存储用户量 | 全部内存量 |
---|---|---|---|
set | 32位(假设userid是整形,占32位) | 5千万 | 32位*5千万=200MB |
bitmap | 1位 | 1亿 | 1位*1亿=12.5MB |
-
可以看出,在这种情况下,使用位图来存储活跃用户的信息要比使用Set更加节省内存。
假设现在有10万个用户需要进行统计,使用位图仍然只占用12.5MB的内存空间,而使用Set则需要4MB的内存空间。
可以看出,无论是对于1亿用户还是10万用户,位图都能够更加高效地存储和查询用户信息。
【4】总结
- 1 位图类型是string类型,最大512M
- 2 使用setbit时偏移量如果过大,会有较大消耗
- 3 位图不是绝对好用,需要合理使用
- 位图是一种数据结构,用于表示一组二进制位的集合,Redis中的位图实现为字符串类型的值。
- Redis提供了一系列位图相关的命令,如setbit、getbit、bitcount、bitop和bitpos等,可以方便地对位图进行操作和查询。
- 在某些场景下,位图可以非常高效地进行用户统计、活跃用户标记等操作。
- 相比于使用Set存储用户信息,使用位图可以节省大量的内存空间。
- 需要根据具体需求和场景来选择数据结构,合理使用位图。位图并不是适用于所有情况,需要权衡其优缺点。
【5】面试题
- redis的key值最大多少 512M
- redis的string 类型vaule值最大多少 512M
- Redis的key值最大为512MB,这是由Redis设计所采用的有序字符串类型(zset)的内部表示方式决定的。
- 在Redis中,无论是string类型的value值还是其他类型的value值,其最大限制都是512MB。
- 对于Redis的string类型,它用于存储二进制安全的字符串数据,并且每个字符串都可以被视作一个字符数组或字节数组进行处理。
- 因此,对于任意一个string类型的value值,其最大限制为512MB。
【五】HyperLogLog
【1】介绍
- 基于HyperLogLog算法:极小的空间完成独立数量统计
- 本质还是字符串
【2】三个命令
pfadd key element
:向HyperLogLog中添加元素,可以同时添加多个元素。pfcount key
:计算HyperLogLog的基数(即元素的总数)。pfmerge destroy sourcekey1 sourcekey2
:合并多个HyperLogLog,将sourcekey1和sourcekey2合并为destroy。
#向hyperloglog添加元素,可以同时添加多个
pfadd key element
#计算hyperloglog的独立总数
pfcount key
#合并多个hyperloglog,把sourcekey1和sourcekey2合并为destroy
pfmerge destroy sourcekey1 sourcekey2
#向uuids中添加4个uuid
pfadd uuids "uuid1" "uuid2" "uuid3" "uuid4"
#返回4
pfcount uuids
#有一个之前存在了,其实只把uuid5添加了
pfadd uuids "uuid1" "uuid5"
#返回5
pfcount uuids
pfadd uuids1 "uuid1" "uuid2" "uuid3" "uuid4"
pfadd uuids2 "uuid3" "uuid4" "uuid5" "uuid6"
pfmerge uuidsall uuids1 uuids2 #合并
pfcount uuidsall #统计个数 返回6
【3】内存消耗&总结
- 百万级别独立用户统计,百万条数据只占15k
- 错误率 0.81%
- 无法取出单条数据,只能统计个数
【4】应用
- 爬虫去重
- 黑白名单
- 垃圾邮件过滤
- 独立用户统计
- 有个用户登录,就把用户id放到HyperLogLog中
- 最后只需要统计一下 个数 就能统计出今天的活跃人数
HyperLogLog在以上每个应用场景中都具有独特的作用。现在我将针对每个场景进行详细的介绍并提供相应的示例代码。
(1)爬虫去重
- 爬虫经常需要对获取到的数据进行去重操作,以避免爬取到重复的内容。
- HyperLogLog可以用于快速识别重复的URL或数据,并进行去重处理。
- 以下是一个简单的示例代码:
import redis
# 连接到Redis服务器
r = redis.StrictRedis(host='localhost', port=6379, db=0)
def process_data(data):
# 检查数据是否已存在
if r.pfadd('processed_data', data) == 0:
# 数据已存在,进行去重处理
return
# 数据不存在,进行后续处理操作
# ...
# 模拟几个重复的数据
data_list = ['data1', 'data2', 'data3', 'data2', 'data4']
for data in data_list:
process_data(data)
- 在上述示例中,我们定义了一个
process_data
函数,该函数用于处理爬取到的数据。 - 在处理数据之前,首先通过
pfadd
方法将数据添加到HyperLogLog中进行判断是否已存在。 - 如果返回值为0,表示数据已存在,我们就可以直接跳过该数据进行去重处理。
- 否则,说明数据不存在,可以进行后续的处理操作。
- 这样,通过HyperLogLog的快速判断,我们就可以在爬虫过程中高效地去除重复的数据。
(2)黑白名单
- 在许多场景中,需要进行黑白名单管理来控制某些操作的权限。
- HyperLogLog可以用于在一个大规模名单中高效地判断某个元素是否存在。
- 以下是一个示例代码:
import redis
# 连接到Redis服务器
r = redis.StrictRedis(host='localhost', port=6379, db=0)
def check_whitelist(user_id):
# 检查用户ID是否在白名单中
if r.pfexists('whitelist', user_id):
# 用户ID在白名单中
return True
# 用户ID不在白名单中
return False
def add_to_blacklist(user_id):
# 将用户ID添加到黑名单中
r.pfadd('blacklist', user_id)
# 检查用户ID是否在白名单中
user_id = '12345'
if check_whitelist(user_id):
print("用户ID在白名单中")
else:
print("用户ID不在白名单中")
# 添加用户ID到黑名单中
add_to_blacklist(user_id)
-
在上述示例中,我们定义了一个
check_whitelist
函数用于检查用户ID是否在白名单中。 -
通过调用
pfexists
方法判断用户ID是否存在于HyperLogLog中,如果存在,即表示用户在白名单中。 -
然后,我们定义了一个
add_to_blacklist
函数将用户ID添加到黑名单中,使用pfadd
方法实现。 -
通过HyperLogLog的查询和添加操作,我们可以高效地管理黑白名单,从而控制相关操作的权限。
(3)垃圾邮件过滤
- 垃圾邮件过滤是保护用户邮箱免受垃圾邮件侵扰的重要任务。
- HyperLogLog可以用于快速判断和识别已知的垃圾邮件信息。
- 以下是一个简单的示例代码:
import redis
# 连接到Redis服务器
r = redis.StrictRedis(host='localhost', port=6379, db=0)
def check_spam(email):
# 检查邮件是否为垃圾邮件
if r.pfexists('spam_emails', email):
# 邮件为垃圾邮件
return True
# 邮件不是垃圾邮件
return False
def add_spam_email(email):
# 将邮件添加到垃圾邮件集合中
r.pfadd('spam_emails', email)
# 检查邮件是否为垃圾邮件
email = 'example@spam.com'
if check_spam(email):
print("该邮件为垃圾邮件")
else:
print("该邮件不是垃圾邮件")
# 添加邮件到垃圾邮件集合中
add_spam_email(email)
-
在上述示例中,我们定义了一个
check_spam
函数用于检查邮件是否为垃圾邮件。 -
通过调用
pfexists
方法判断邮件是否存在于HyperLogLog中,如果存在,即表示该邮件为已知的垃圾邮件。 -
然后,我们定义了一个
add_spam_email
函数将邮件添加到垃圾邮件集合中,使用pfadd
方法实现。 -
通过HyperLogLog的查询和添加操作,我们可以快速判断和过滤掉已知的垃圾邮件。
(4)用户统计
- HyperLogLog在用户统计方面可以非常有效地进行活跃用户数量的统计。
- 每当有用户登录时,将其用户ID添加到HyperLogLog中,最后只需要统计一下个数,就能统计出今天的活跃人数。
- 通过HyperLogLog的高效统计功能,我们可以轻松地实现活跃用户的数量统计,无需存储每个用户的具体信息,只需要统计唯一的用户ID即可。
- 这对于追踪和统计网站或应用程序的活跃用户数量非常有帮助。
【六】GEO
【1】介绍
- GEO(地理信息定位)是一种在Redis中存储和处理地理位置数据的功能。
- 通过存储经纬度信息,可以计算两个地点之间的距离、查找指定范围内的城市等。
- GEO(地理信息定位):存储经纬度,计算两地距离,范围等
- 北京:116.28,39.55
- 天津:117.12,39.08
- 石家庄:114.29,38.02
- 唐山:118.01,39.38
- 保定:115.29,38.51
- 可以计算天津到北京的距离,天津周围50km的城市,外卖等
【2】5个城市纬度
城市 | 经度 | 纬度 | 简称 |
---|---|---|---|
北京 | 116.28 | 39.55 | beijing |
天津 | 117.12 | 39.08 | tianjin |
石家庄 | 114.29 | 38.02 | shijiazhuang |
唐山 | 118.01 | 39.38 | tangshan |
保定 | 115.29 | 38.51 | baoding |
【3】相关命令
#增加地理位置信息
geoadd key longitude latitude member
#把北京地理信息天津到cities:locations中
geoadd cities:locations 116.28 39.55 beijing
geoadd cities:locations 117.12 39.08 tianjin
geoadd cities:locations 114.29 38.02 shijiazhuang
geoadd cities:locations 118.01 39.38 tangshan
geoadd cities:locations 115.29 38.51 baoding
#获取地理位置信息
geopos key member
#获取北京地理信息
geopos cities:locations beijing
# 增加地理位置信息
#获取两个地理位置的距离 unit:m(米) km(千米) mi(英里) ft(尺)
geodist key member1 member2 [unit]
# 获取两个地理位置之间的距离
# 北京到天津的距离,89公里
geodist cities:locations beijing tianjin km
# 返回结果:116.88(单位为公里)
georadius key logitude latitude radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key][storedist key]
#获取指定位置范围内的地理位置信息集合
georadiusbymember key member radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key][storedist key]
'''
withcoord:返回结果中包含经纬度
withdist:返回结果中包含距离中心节点位置
withhash:返回解雇中包含geohash
COUNT count:指定返回结果的数量
asc|desc:返回结果按照距离中心店的距离做升序/降序排列
store key:将返回结果的地理位置信息保存到指定键
storedist key:将返回结果距离中心点的距离保存到指定键
'''
# 获取指定位置范围内的地理位置信息集合
georadiusbymember cities:locations beijing 150 km
# 返回结果:[beijing, tianjin, tangshan, baoding]
'''
1) "beijing"
2) "tianjin"
3) "tangshan"
4) "baoding"
'''
【4】总结
- 3.2以后版本才有
- geo本质时zset类型
- 可以使用zset的删除,删除指定member:zrem cities:locations beijing
- GEO是Redis中用于存储和处理地理位置数据的功能。
- 使用GEO命令可以增加和查询地理位置信息。
- GEO本质上借助有序集合(zset)来实现,因此可以使用有序集合的删除操作删除指定地理位置信息。
- 版本3.2及以上才支持GEO功能。
本文来自博客园,作者:Chimengmeng,转载请注明原文链接:https://www.cnblogs.com/dream-ze/p/17691100.html