set
set类型
- 新的存储需求:存储大量的数据,在查询方面提供更高的效率
- (list是能够存储大量数据,而且还有存储顺序,内部结构很鸡肋,是链表结构,存储效率效率不高,读取速度慢)
- 需要的存储结构:能够保存大量的数据,高效的内部存储机制,便于查询
- (hash,hash表存储结构,存储效率非常高)
- set类型:与hash存储结构完全相同,仅存储键,不存储值(nil),并且值是不允许重复的(名称本就不允许重复,重名就覆盖了)
set类型数据的基本操作
- 添加数据
- sadd key member1 [member2]
- 添加数据:sadd users zs
- 添加数据:sadd users lisi
- 添加数据:sadd users wangwu
- sadd key member1 [member2]
- 获取全部数据
- smembers key
- 查看数据:smembers users
- smembers key
- 删除数据
- srem key member1 [member2]
- 删除zs:srem users zs
- 查看一下:smembers users
- srem key member1 [member2]
- 获取集合数据总量(问集合中是否有数据)
- scard key
- 查询集合中是否有数据:scard users
- scard key
- 判断集合中是否包含指定数据(问集合中有没有那个数据)
- sismember key member】
- 集合中是否有wangwu这个value:
- sismember users wangwu
- sismember users zs
- sismember key member】
结果发现:存储的数据与添加数据的顺序并不一致
set 类型数据的扩展操作
业务场景
每位用户首次使用今日头条时会设置3项爱好的内容,但是后期为了增加用户的活跃度、兴趣点,必须让用户对其他信息类别逐渐产生兴趣,增加客户留存度,如何实现?
业务分析
- 系统分析出各个分类的最新或最热点信息条目并主组织成set集合
- 随机挑选其中部分信息
- 配合用户关注信息分类中的热点信息组织成展示的全信息集合
有一个问题怎么随机操作数据
解决方案
-
随机获取集合中指定数量的数据(从一个集合中随机挑选几个数据给你,原集合数据不变)
- srandmember key [count]
- 应用场景:现在给你推送这5种,下一还推这5种
- 先初始化四个数据
- sadd news n1
- sadd news n2
- sadd news n3
- sadd news n4
- 从集合中随机推送(取)一个
- srandmember news 1
- srandmember key [count]
-
随机获取集合中的某个数据并将该数据移除集合(从一个集合中随机挑选集合数据,原集合减少挑出数据)
- spop key
- 应用场景:推过了,下次就不推了
- 随机剔除一个
- spop news 1
- 查看一下
- smembers news
- spop key
Tips 8:
redis 应用于随机推荐类信息检索,例如热点歌单推荐,热点新闻推荐,热卖旅游线路,应用APP推荐,大V推荐等
业务场景
- 脉脉为了促进用户间的交流,保障业务成单率的提升,需要让每位用户拥有大量的好友,事实上职场新人不具有更多的职场好友,如何快速为用户累计更多的好友?
- 新浪微博为了增加用户热度,提高用户留存性,需要微博用户在关注更多的人,以此获得更多的信息或热门话题,如何提高用户关注他人的总量?
- QQ新用户入网年龄越来越低,这些用户的朋友圈交际圈非常小,往往集中在一所学校甚至一个班级中,如何帮助用户快速积累好友用户来带的更多的活跃度?
- 微信公众号是微信信息流通的渠道之一,增加用户关注的公众号成为提高用户活跃度的一种方式,如何帮助用户积累更多关注的公众号?
- 美团外卖为了提升成单量,必须帮助用户挖掘美食需求,如何推荐给用户最适合自己的美食?
解决方案
- 求两个集合的交、并、差集
- sinter key1 [key2]
- 添加数据:
- sadd u1 a1
- sadd u1 s1
- sadd u1 b1
- sadd u2 s1
- sadd u2 w1
- 两集合求交集
- sinter u1 u2
- 添加数据:
- sunion key1 [key2]
- 两集合求并集
- sunion u1 u2
- 两集合求并集
- sdiff key1 [key2]
- 两集合求差集
- sdiff u1 u2
- sdiff u2 u1
- 两集合求差集
- sinter key1 [key2]
- 求两个集合的交、并、差集并存储到指定的集合中
- sinterstore destnation key1 [key2]
- 存储交集在u3中
- sinterstore u3 u1 u2
- 查询u3
- smembers u3
- 存储交集在u3中
- sunionstore destination key1 [key2]
- sdiffstore destination key1 [key2]
- sinterstore destnation key1 [key2]
- 将指定数据从原始集合中移动到目标集合中
- smove source destination member
- 从u2里移动w1到u2
- smove u2 u1 w1
- 查看一下u1
- smembers u1
- smembers u2
- 从u2里移动w1到u2
- smove source destination member
Tips 9:
redis 应用于同类信息的关联搜索,二度关联搜索,深度关联搜索
显示共同关注(一度)
显示共同好友(一度)
由用户A出发,获取到好友用户B的好友信息列表(一度)
由用户A出发,获取到好友用户B的购物清单列表(二度)
由用户A出发,获取到好友用户B的游戏充值列表(二度)
set 类型数据操作的注意事项
- set类型不允许数据重复,如果添加的数据在set中已经存在,那么将只保留一份(添加的只有第一次是成功的,其他是失败的)
- set虽然与hash的存储结构相同,但是无法启动hash中存储值的空间(无法把它当hash用)
set 类型应用场景
业务场景
集团公司共具有12000名员工,内部OA系统中具有700多个角色,3000多个业务操作,23000多种数据,每一位员工有一个或多个角色,如何快速进行业务操作的权限校验呢?
解决方案
- 依赖set集合数据不重复的特征,依赖set集合hash存储结构特征完成数据过滤与快速查询
- 根据用户ID获取用户所有角色
- 根据用户所有角色获取用户所有操作权限放入set集合
- 根据用户所有角色获取用户所有数据全选放入set集合
实现
- 添加用户1
- sadd rid:001 getall
- sadd rid:001 getByID
- 添加用户2
- sadd rid:002 getCount
- sadd rid:002 getall
- sadd rid:002 insert
- 合并并存储
- sunionstore uid:007 rid:001 rid:002
- 查看所有数据 (第一种校验方式:看判断你过来那个在这里面吗,相对耦合度低,推荐使用)
- smembers uid:007
- 第二种校验方式:或者看是否存在 不鼓励使用
- sismember uid:007 insert
【疑问】校验工作:Redis提供基础数据还是提供校验结果?
老师建议:提供基础数据,不要融合
Tips 10:
redis应用于同类型不重复数据的合并操作
业务场景
- 公司对旗下新的网站做推广,统计网站的PV(访问量),UV(独立访客),IP(独立IP)
- PV:网站被访问次数,可通过刷新页面提高访问量
- UV:网站被不同用户访问的次数,可通过cookie统计访问量,相同用户切换IP地址,UV不变
- IP:网站被不同IP地址访问的总次数,可用过IP地址统计访问量,相同IP不同用户访问,IP不变
解决方案
- 利用set集合的数据去重特征,记录各种访问数据
- 建立string类型数据,利用incr统计日访问量(PV)
- 建立set模型,记录不同cookie数量(UV)
- 建立set模型,记录不同IP数量(IP)
实现
- 添加数据
- sadd ips 1.2.3.4
- sadd ips 2.3.4.5
- sadd ips 2.3.4.5 -->(integer) 0 再次访问就进不来了
- 一共有几个独立ip访问?
- scard ips
Tips 11:
redis 应用于同类型数据的快速去(利用它的特征,做数据过滤用的)
业务场景
黑名单
- 资讯类信息类网站追求高访问量,但是由于其信息的价值,往往容易被不法分子利用,通过爬虫技术,快速获取信息,个别特种行业网站信息通过爬虫获取分析后,可以转换成商业机密进行出售。例如第三方火车票、机票、酒店刷票代购软件,电商刷评论,刷好评。
- 同时爬虫带来的伪流量也会给经营者带来错觉,产生错误的决策,有效避免网站被爬虫反复爬取成为每个网站都要考虑的基本问题。在基于技术层面区分出爬虫用户后,需要将此类用户进行有效屏蔽,这就是黑名单的典型应用。
- PS:不是说爬虫一定做摧毁性工作,有些小型网站需要爬虫为其带来一些流量
白名单
- 对于安全性更高的应用访问,仅仅靠黑名单是不能解决安全问题的,此时需要设定可访问的用户群体,依赖白名单做更为苛刻的访问验证。
解决方案
- 基于经营战略设定问题用户发现、鉴别规则
- 周期性更新满足规则的用户黑名单,加入set集合
- 用户行为信息达到后与黑名单进行比对,确认行为去向
- 黑名单过滤IP地址:应用于开发游客访问权限的信息源
- 黑名单过滤设备信息:应用于设定访问设备的信息源
- 黑名单过滤用户:应用于基于访问权限的信息源
Tips 12:
redis 应用于基于黑名单与白名单设定的服务控制
sorted_set
sorted_set类型
- 新的存储需求:数据排序有利于数据的有效展示,需要提供一种可以根据自身特征进行排序的方式
- 需要的存储结构:新的存储模型,可以保证可排序的数据
- sorted_set类型:在set的存储结构基础上添加可排序字段
sorted_set 类型数据的基本操作
-
添加数据
- zadd key score1 member1 [score2 member2]
- 添加数据
- zadd scores 94 zhangsan
- zadd scores 100 lisi
- zadd scores 60 wangwu
- zadd scores 46 zhaoliu
- 添加数据
- zadd key score1 member1 [score2 member2]
-
获取全部数据
- zrange key start stop [WITHSCORES] 正向看(由小到大)
- zrange scores 0 -1 有顺序的概念 只会出现名字
- zrange scores 0 -1 withscores 会出现对应的值 奇数位是数据,偶数位是排序字段值
- zrevrange key start stop [WITHSCORES] 反向看
- zrevrange scores 0 -1
- zrevrange scores 0 -1 withscores
- zrange key start stop [WITHSCORES] 正向看(由小到大)
-
删除数据
- zrem key member [member …]
- 删除lisi
- zrem scores lisi
- 反向的看
- zrevrange scores 0 -1 withscores
- 删除lisi
- zrem key member [member …]
-
按条件获取数据
- zrangebyscore key min max [WITHSCORES] [LIMIT] 正向查
- 获取10-100之间的排序
- zrangebyscore scores 10 100 withscores
- 建立了索引的概念
- zrangebyscore scores 10 100 limit 0 2 withscores
- 获取10-100之间的排序
- zrevrangebyscore key max min [WITHSCORES] 反向查
- zrangebyscore key min max [WITHSCORES] [LIMIT] 正向查
-
条件删除数据
- zremrangebyrank key start stop 按照索引删除
- 删除索引为1的数据
- zremrangebyrank scores 0 0
- 查看一下
- zrange scores 0 -1 withscores
- 删除索引为1的数据
- zremrangebyscore key min max
- 删除50-70范围内的数据
- zremrangebyscore scores 50 70
- 查看一下
- zrange scores 0 -1 withscores
- 删除50-70范围内的数据
- zremrangebyrank key start stop 按照索引删除
注意:
- min与max用于限定搜索查询条件
- start与stop用于限定查询范围,作用于索引,表示开始和结束索引
- offset与count用于限定查询范围,作用于查询结果,表示开始位置和数据总量
还有几个比较简单的操作
- 获取集合数据总量
- zcard key
- 看你集合中还有多少数据
- zcard scores
- 看你集合中还有多少数据
- zcount key min max
- 看你指定范围里面有多少个数据
- zcount scores 90 100
- 看你指定范围里面有多少个数据
- zcard key
- 集合交、并操作
- zinterstore destination numkeys key [key …]
- 让它三个和在一起
- destination新的集合
- numkeys 和集合的个数
- key 就是集合
- zinterstore u1 3 s1 s2 s3
- 查看一下:
- zrange u1 0 -1 withscores
- 只有三方都有的时候,才会交和求和
- 求最大值
- zinterstore u2 3 s1 s2 s3 aggregate max
- 查看一下
- zrange u2 0 -1 withscores
- zunionstore destination numkeys key [key …]
- 构造环境
- zadd s1 50 aa 60 bb 70 cc
- zadd s2 60 aa 40 bb 90 dd
- zadd s3 70 aa 20 bb 100 dd
- zinterstore destination numkeys key [key …]
sorted_set 类型数据的扩展操作
业务场景
- 票选广东十大杰出青年,各类综艺选秀海选投票
- 各类资源网站TOP10(电影,歌曲,文档,电商,游戏等)
- 聊天室活跃度统计
- 游戏好友亲密度
- 这就牵扯到一点:排序
业务分析
- 为所有参与排名的资源建立排序依据
解决方案
- 获取数据对应的索引(排名)
- zrank key member 默认由小到大
- 添加数据
- zadd movies 147 aa 97 bb 201 cc
- bb电影排第几?
- zrank movies bb --> 0 指索引
- 添加数据
- zrevrank key member 由大到小排序
- cc电影排第几?
- zrevrank movies cc
- cc电影排第几?
- zrank key member 默认由小到大
- score值获取与修改
- zscore key member 就是拿value的值
- 查看aa电影有多少票
- zscore movies aa
- 查看cc电影有多少票
- zscore movies cc
- 查看aa电影有多少票
- zincrby key increment member
- 又有一个人给它投票了,那么这个值要不要变化?要
- aa这个电影再投一票
- zincrby movies 1 aa --> 148
- 查看一下
- zscore movies aa
- aa这个电影再投一票
- 又有一个人给它投票了,那么这个值要不要变化?要
- zscore key member 就是拿value的值
Tips 13:
redis 应用于计数器组合排序功能对应的排名,可以实现排行榜类的操作
sorted_set类型数据操作的注意事项
-
score保存的数据存储空间是64位,如果是整数范围是-9007199254740992~9007199254740992
-
score保存的数据也可以是一个双精度double值,基于双精度浮点数的特征,可能会丢失精度,使用时候要谨慎
-
sorted_set 底层存储还是基于set结构的,因此数据不能重复,如果重复添加相同的数据,score值将被反复覆盖,保留最后一侧的修改结果。set是只能是第一次的数据有效,其他重复的数据无效。
-
新建一个
- zadd test1 11 aa
-
查看
- zrange test1 0 -1 withscores
-
在添加重复的key
- zadd test1 22 aa --> integer 0 原来的11被覆盖了 现在是22
-
再添加一次
- zadd test1 33 aa 失败是失败,但value是真改了(失败是指,这个值没有进去,但不是这个值没有改成)
-
查看
- zrange test1 0 -1 withscores 33
sorted_set 类型应用场景
业务场景
- 基础服务+增值服务类网站会设定给为会员的试用,让用户充分体验会员优势。例如观影试用VIP、游戏VIP体验,云盘下载体验VIP,数据查看体验VIP。当体验到期后,如何有效管理此类信息。即便对于正式VIP用户也存在对应的管理方式。
- 网站会定期开启投票、讨论,限时进行,逾期作废。如何有效管理此类过期信息?
- 这是基于时效性的任务管理。
解决方案
-
对于基于时间线限定的任务管理,将处理时间记录为score值,利用排序功能区分处理的先后顺序
-
记录下一个要处理的时间,当到期后处理对应任务,移除Redis中的记录,并记录下一个要处理的时间
-
当新任务加入时,判定并更新当前下一个要处理的任务时间
-
为提升sorted_set的性能,通常将任务根据特征存储成若干个sorted_set。例如1小时内,1天内,周内,月内,季内,年度等,操作时逐级提升,将即将操作的若干个服务纳入到1小时内处理的队列中
-
获取当前系统时间
- time
-
- “1659845368” 秒单位
-
- “26748”
-
添加数据
- zadd ts 1509802345 uid:001
- zadd ts 1509802390 uid:007
- zadd ts 1509804284 uid:888
-
其本身天然的就有一个排序 【从小到大】
-
查看一下
- zrange ts 0 -1 withscores
- 第一个就是马上要到期的,移除一个,下一个也是即将到期的
Tips 14:
redis 应用于定时任务执行顺序管理或任务过期管理
业务场景
任务/消息权重设定应用
- 当任务或者消息待处理,形成了任务队列或消息队列时,对于高优先级的任务要保障对其优先处理,如何实现任务权重管理?
解决方案
- ①对于带有权重的任务,优先处理权重更高的任务,采用score记录权重即可
②如果权重条件过多时,需要对排序score值进行排序处理,保障score值能够兼容2条件或者多条件,例如外贸订单优先于国内订单,总裁订单优先于员工订单,经理订单优先于员工订单
- 因score长度受限,需要对数据进行截断处理,尤其是时间设置为小时或分钟级即可(折算后)
- 先设定订单类别,后设定订单发起角色类别,整体score长度必须是统一的,不足位补0.第一排序规则首位不得是0
- 例如外贸101,国内102,经理004,员工008. 保证大的靠后,小的靠前
- 员工下的贸易但score值为101008(优先)
- 经理下的国内单score值为102004
- ①添加几个任务
- zadd tasks 4 order🆔005 4号权重
- zadd tasks 1 order🆔425 1号权重
- zadd tasks 9 order🆔345 9号权重
- 这个几个任务在其中,究竟谁应该先处理呢?
- 排一下序 从大到小
- zrevrange tasks 0 -1 withscores
- 怎么移掉?
- zrevrange tasks 0 0 这样可以拿出第一个
- zrem tasks order🆔345 移除这个最高权重
- 再去查任务列表
- zrevrange tasks 0 -1 withscores 就发现之前的最高权重的任务被移除了
②
- 经理下的国内单
- zadd tt 102004 order🆔1
- 员工下的外贸单
- zadd tt 101008 order🆔2
- 来看一下顺序 从大到小
- zrevrange tt 0 -1 withscores
- 从小到大
- zrange tt 0 -1
- 有一个坑
- zadd ts 14 order🆔3
- zadd ts 1232 order🆔4
- 本来是按位比的关系,现在没法比了(所以要同样的位数)
数据类型实践案例
业务场景
人工智能领域的语义识别与自动对话将是未来服务业机器人应答呼叫体系中的重要技术,百度自研用户评价语义识别服务,免费开放给企业试用,同时训练百度自己的模型。现对试用用户的使用行为进行限速,限制每个用户每分钟最多发起10次调用
次数控制–>计数器
解决方案
- 设计计数器,记录调用次数,用于控制业务执行次数。以用户ID作为key,使用次数作为value
- 在调用前获取次数,判断是否超过限定次数
- 不超过次数的情况下,每次调用计数+1
- 业务调用失败,计数-1
- 为计数器设置生命周期为指定周期,例如1秒/分钟,自动清空周期内使用次数
构造
- 获取用户
- get 415 //nil 发现为空(没有)
- 改为1
- setex 415 60 1 60秒
- 再次get
- get 415 // 1 发现并不为空
- 用incr增值
- incr 415 //2 … incrby 415 8
- 一直下去,直到第10次了
- incr 415 //10
- 此时,已经超次,告诉用户,不让你用了;直到1分钟之后,再次拿它时,它就不在了
- get 415 //0
- 我们就可以反复的走这个过程
还有个问题:每次都需要判断一下它是否大于10,能否让它最后一次才去做判断呢?
解决方案改良
- 取消最大值的判定,利用incr操作超过最大值抛出异常的形式替代每次判断是否大于最大值
- 判断是否为nil
- 如果是,设置为Max-次数
- 如果不是,计数+1
- 业务调用失败,计数-1
- 遇到异常即+操作超过上限,视为使用达到上限
构造
- get一下
- get 415 //nil 发现没有
- 改为最大值
- setex 415 60 9223372036854775797
- 再get一下
- get 415 // 9223372036854775797 发现有值
- +1
- 9223372036854775798
- … 10之后,就会报错误,出现溢出,报异常,超次了
- 利用了一下数据上限的数据规则
- 一分钟之后,次数归零
Tips 16:
redis 应用于限时按次结算的服务控制
业务场景
使用微信的过程中,当微信接收消息后,会默认将最近接收到的消息置顶,当多个好友及关注的的订阅号同时发送消息时,该排序会不停的进行交替。同时还可以将重要的会话设置为置顶。一旦用户离线后,再次打开微信时,消息该按照什么样的顺序显示呢?
业务分析
解决方案
- 依赖list的数据具有顺序的特征对消息进行管理,将list结构作为栈使用
- 对置顶与普通会话分别创建独立的list分别管理
- 当某个list中接收到用户的消息后,将消息发送方的id从list的一侧加入list(此处设定左侧)
- 多个相同id发送的消息反复入栈会出现问题,在入栈之前无论是否具有当前id对应的消息,先删除对应id
- 推送消息时先推送置顶会话list,再推送普通会话list,推送完成的list清楚所有数据
- 消息的数量,也就是微信用户对话数量采用计数器的思想另行记录,伴随list操作同步更新
模拟
- 在100里删除200
- lrem 100 1 200 //肯定是失败的
- 把200放进100中
- lpush 100 200
- 300来了,删除300
- lrem 100 1 300
- 放入300
- lpush 100 300
- 400来了,删除400
- lrem 100 1 400
- 100中放入400
- lpush 100 400
- 200又来了,删除200
- lrem 100 1 200
- 放入200
- lpush 100 200
- 300 又来了,删除300
- lrem 100 1 300
- 放入300
- lpush 100 300
- 打印一下顺序是怎样的呢?
- lrange 100 0 -1
- 通过这个就可以控制顺序,模拟微信接收消息最终的顺序
Tips 17:
redis 应用于基于时间顺序的数据操作,而不关注具体时间。关注顺序
——此文档为学习笔记!