【6.0】Redis高级用法

【一】慢查询

【1】生命周期

  • 我们配置一个时间,如果查询时间超过了我们设置的时间,我们就认为这是一个慢查询.
  • 慢查询发生在第三阶段
  • 客户端超时不一定慢查询,但慢查询是客户端超时的一个可能因素
  • 慢查询是指执行时间超过预设阈值的数据库查询语句。在慢查询的生命周期中,以下是一般的流程:

    • 客户端发送查询请求给数据库。

    • 数据库接收请求并开始执行查询。

    • 数据库记录查询开始时间。

    • 执行查询,包括索引查找、排序、聚合等操作。

    • 数据库完成查询后记录查询结束时间。

    • 数据库计算查询耗时(可通过结束时间减去开始时间得到)。

    • 如果查询耗时超过了设置的慢查询阈值,将查询语句及相关信息存储到慢查询日志中。

  • 需要注意的是,客户端超时并不一定代表慢查询发生了,而慢查询可能是导致客户端超时的一个可能因素。

[image-20230416224936638](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-lenslowlog-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节点,无法跨节点操作。
  • 将大量的msetmget等类似操作与Pipeline进行对比。这些操作无法与Pipeline一起使用,但可以通过Pipeline的批量操作来提高执行效率。

【5】原生事务操作

(1)使用multiexec开启和执行事务

  • 开启事务,放到管道中一次性执行
multi   # 开启事务
set name dream
set age 18
exec   # 执行事务
  • 首先使用multi命令开启一个事务,在multiexec之间添加的所有命令都会被包含在这个事务中。
  • 然后,可以像正常操作一样使用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】模型

  • 发布者发布消息到一个或多个频道。
  • 所有订阅了相应频道的订阅者都会收到该消息。
  • 订阅者没有办法获取历史消息,只能获取从其开始订阅后发布的消息。

image-20230416224951625

【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)

[image-20230416225004565](http://photo.liuqingzheng.top/2023 04 16 22 54 57 /image-20230416225004565.png)

  • 位图(Bitmap)是一种数据结构,用于表示一组二进制位的集合。
  • 位图通常作为字符串类型进行存储,每个位代表一个标记的状态(0或1)。
  • 位图在某些场景下可以非常高效地进行操作和查询。
  • 在传统的字节位图中,每个字节可以表示8个位,而在Redis中,位图实现为字符串类型的值,每个字符都含有8个位。
  • 例如,在Redis中,如果一个字符串的值是"big",那么它对应的二进制表示如下:
big
011010010111
  • 其中,每一位代表一个特定的标记,可以是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

[image-20230416225016650](http://photo.liuqingzheng.top/2023 04 16 22 55 05 /image-20230416225016650.png)

【3】独立用户统计

  • 1 使用set和Bitmap对比

  • 假设有1亿个用户,其中大约有5千万人访问。

    • 我们可以使用位图来存储活跃用户的信息。

    • 下表比较了使用Set和Bitmap两种数据结构来存储用户量所需的空间:

数据类型每个userid占用空间需要存储用户量全部内存量
set32位(假设userid是整形,占32位)5千万32位*5千万=200MB
bitmap1位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.2839.55beijing
天津117.1239.08tianjin
石家庄114.2938.02shijiazhuang
唐山118.0139.38tangshan
保定115.2938.51baoding

【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功能。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值