redis----day02(哈希类型操作、列表类型操作、集合类型操作、有序集合操作、获取长慢命令、pipeline与事务、发布订阅(观察者模式)、Bitmap位图、HyperLogLog)

.
.
.
.
.
.

今日内容

1 哈希类型操作

------------------------------------------

###1---hget,hset,hdel  给字典添加键值对,获取键对应的值,删除键值对

hget name key     # 获取 name对应的字典中key对应的值    时间复杂度为 o(1)
hset name key value   # 设置name对应的字典中,k v 键值对  时间复杂度为 o(1)
hdel name key   # 删除hash key对应的field的值 时间复杂度为 o(1)
#测试
hget userinfo age
hset userinfo kkk vvv
hdel userinfo kkk

------------------------------------------

###2---hexists,hlen
    判断字典的长度以及字典里键存不存在  时间复杂度为 o(1)
hexists userinfo name   # 判断userinfo字典里面的 键name 存不存在 存在返回1  不存在返回0

hlen userinfo  # 返回字典里键值对的数量  时间复杂度为 o(1)

------------------------------------------

###3---hmget,hmset  批量获取字典值与批量设置字典

hmget userinfo name age  # 批量获取字典 多个键 对应的值  时间复杂度是o(n)
hmset userinfo name1 lihua age1 26  # 批量设置字典多个键值对 时间复杂度是o(n)

------------------------------------------

###4--hgetall,hvals,hkeys   获取字典全部键值对、全部的值、全部的键

hgetall userinfo   #  获取字典全部键值对  时间复杂度是o(n)
hvals key   #  获取字典全部的值  时间复杂度是o(n)
hkeys key   #  获取字典全部的键  时间复杂度是o(n)

# 小心使用hgetall   会造成阻塞


##1 计算网站每个用户主页的访问量
hincrby userinfo pageview count
hincrby userinfo pageview 1   # userinfo字典里 pageview键对应值加1
hget userinfo pageview
------------------------------------------

##其他操作 hsetnx,hincrby,hincrbyfloat

hsetnx userinfo key value  # 设置字典对应key的value(如果field已存在,则失败),时间复杂度o(1)

hincrbyfloat userinfo key 1.2  # 设置userinfo字典对应key的值自增1.2   时间复杂度o(1)

------------------------------------------

image
.
.
.
.
.
.
.
.
.
.

2 列表类型操作

--------------------------------------------------------
# 插入操作

# rpush 从list11列表右侧插入
rpush list11 value1 value2   # 时间复杂度为o(1~n)
# lpush 从列表左侧插入


# linsert 从list11列表某一个元素插入
linsert list11 before|after oldValue newValue
#从元素value的前或后插入newValue 时间复杂度o(n) ,需要遍历列表

linsert list11 before aaa aaa111
linsert list11 after aaa aaa222

--------------------------------------------------------

# 删除操作

lpop list11  # 从列表list11左侧弹出一个item 时间复杂度o(1)

rpop list11  # 从列表list11右侧弹出一个item 时间复杂度o(1)

lrem list11 n value
# 根据n值,从list11列表中删除所有值是value的数据  时间复杂度o(n)
1 n>0 列表从左到右,删除最多n个 值是value的数据
2 n<0 列表从右向左,删除最多n个 值是value的数据
3 n=0 列表删除所有 值是value的数据

lrem list11 0 a   # 删除列表中所有值是a的数据
lrem list11 -1 c  # 从右侧删除1个值是c的数据


ltrim list11 start end    # 按照索引范围修剪列表    o(n)
ltrim list11 1 4   # 只保留下表1到4之间的元素

-----------------------------------------------------------
-----------------------------------------------------------

# 查询操作  获取列表指定开始与结束索引范围的值

lrange list11 start end   # 获取列表指定索引范围所有item    o(n)
lrange list11 0 2
lrange list11 0 -1   # 获取列表第一个位置到倒数第一个位置的值

lindex list11 index   # 获取列表指定索引的item  o(n)
lindex list11 0
lindex list11 -1

llen list11  # 获取list11列表长度

-----------------------------------------------------------
-----------------------------------------------------------

# 修改操作 按索引位置改值

lset list11 index newValue  # 设置list11列表,指定索引位置的值为newValue   o(n)
lset list11 2 ppp   # 把第二个位置设为ppp


# 列表实战
实现timeLine功能,时间轴,微博关注的人,按时间轴排列
所有人发了一个微博,在数据库里就有一条记录,假设现在总用户是100个人,但是该用户只有10个好友
现在就想这10个人发的微博,按时间去排序,在该用户的朋友圈展示出来,怎么办
redis建立一个用户的朋友圈列表,只要该用户的好友发了微博,就把该微博的id 放到该用户的朋友圈列表里面,
这样当用户看朋友圈的时候,从列表里面,通过微博的id 拿到微博数据,按时间排列一下,
展示出来就是朋友圈看到的效果了

----------------------------------------------------------
----------------------------------------------------------

# 其他操作

blpop list11 timeout
# 移出并获取列表的第一个元素,
# 如果列表没有元素,会阻塞列表直到等待超时 或发现可弹出元素为止。
# timeout是阻塞超时时间,timeout=0为拥有不阻塞 o(1)

brpop list11 timeout  #同上



lpush+lpop  # 实现栈的功能  先进后出

lpush+rpop  # 实现队列功能  先进先出

lpush+ltrim  # 固定大小的列表 有新的数据添加到列表里 通过修剪踢掉对应数量的老数据


# 重要
lpush+brpop
# 可以实现简单的消息队列 左侧添加数据 从右侧取的时候如果没有,就阻塞在这了
# 但不够专业,如果一个进程放了一个重要的数据,另一个进程取出来后,数据在用的过程中
# 突然进程报错了,但数据还没用完,这个时候列表里该数据已经没了,想再拿也没有了
# 所以真正好的消息队列,应该有一个确认机制,进程先用,用完告诉队列没有问题了,
# 消息队列再删除该数据,比如rabbitmq或kafka

---------------------------------------------------------------

.
.
.
.
.
.
.
.
.

3 集合类型操作


# 首先集合里的数据是无序的、无重复的 !!!

sadd set88 v1 v2 v3
# 向集合set11添加元素(如果元素存在,添加失败,其他不存在的还能正常添加进去) o(1)


srem set88 value1    # 从集合中移除指定元素  o(1)

scard set88   # 计算set88集合的大小

sismember set88 v1     # 判断v1是否在集合中

srandmember set88 n   # 从集合中随机取出n个元素,不会破坏原集合

spop set88 n   # 从集合中随机弹出一个元素 会破坏原集合

smembers set88  # 获取集合中所有元素 ,无序,小心使用,会阻塞住


sdiff set1 set2    # 计算set1和set2的差集  set1集合的独有部分
sinter set1 set2   # 计算set1和set2的交集 共有部分
sunion set1 set2  # 计算并集 两集合所有的部分


sunionstore set3 set1 set2 set8   # 将set1 set2 set8集合的并集保存到set3集合中
sinterstore set4 set1 set2    # 将set1 set2集合的交集保存到set4集合中
sdiffstore set5 set1 set2    # 将set1 set2集合的差集保存到set5集合中

-----------------------------------------------

# 应用场景
推荐场景
两个集合如果交集大于多少,说明两集合比较相似,可以把对应集合的差集数据推给,另一个集合

去重场景

抽奖系统 :通过spop来弹出用户的id,活动取消,直接删除

点赞,点踩,喜欢等,用户如果点了赞,就把用户id放到该条记录的集合中

标签:给用户/文章等添加标签,sadd user:1:tags 标签1 标签2 标签3

给标签添加用户,关注该标签的人有哪些

共同好友:集合间的操作

-----------------------------------------------

.
.
.
.
.
.
.
.

4 有序集合(zset)操作


# 有顺序的,不重复的 集合
# 特点:不能重复 并有一个分值字段,来保证顺序

key                  score                value
user:ranking           1                   lqz
user:ranking           99                  lqz2
user:ranking           88                  lqz3


# 集合与有序集合区别
集合:无重复元素,无序,element
有序集合:无重复元素,有序,element+score


# 列表和有序集合区别
列表:可以重复,有序,element
有序集合:无重复元素,有序,element+score

----------------------------------------------

#  使用命令

zadd zset88 score1 v1 score2 v2
# score可以重复,可以多个同时添加,元素不能重复 o(logN)

zrem zset88 v1   # 删除元素,可以多个同时删除 o(1)

zscore zset88 v1   # 获取元素的分数 o(1)

zincrby zset88 2 v1    # 增加或减少元素v1对应的分数2  o(1)

zcard zset88   # 返回元素总个数 o(1)

zrank zset88 v1   # 返回v1元素的排名(从小到大排)

zrange zset88 0 -1   # 返回有序集合所有元素不带分数

zrange zset88 0 -1 withscores    # 返回所有元素,带分数
zrange zset88 -2 -1  # 拿有序集合里分数排最大的两个

zrangebyscore zset88 minScore maxScore   # 返回指定分数范围内的元素
zrangebyscore zset88 90 210 withscores   # 获取90分到210分的元素,带分数


zcount zset88 minScore maxScore     # 返回指定分数范围内元素的个数

zremrangebyrank zset88 start end     # 删除指定排名范围内的元素 前开后闭
zremrangebyrank zset88 2 4     # 删除有序集合中 排名是3与4 的元素


zremrangebyscore zset88 minScore maxScore    # 删除指定分数内的元素
zremrangebyscore zset88 2 100   # 删除分数2到100之间的元素

------------------------------------------------

# 其他操作

zrevrank zset88 v1     # 返回v1元素 按分数降序排序的排名

zrevrange zset88 0 -1     # 从高到低排序取一定范围的有序集合元素

zinterstore    # 对两个有序集合交集
zunionstore    # 对两个有序集合求并集

------------------------------------------------

# 实战

# 所有的排行榜都可以用有序集合:
    音乐排行榜,销售榜,关注榜,游戏排行榜

------------------------------------------------

.
.
.
.
.
.
.
.
.
.
.

5 慢查询


# 单线程架构,命令一个个执行,

# 如果有长慢命令,会造成整个redis的阻塞

# redis提供一种方式,可以记录长慢命令【放到慢查询队列中】,用于后续的排查修改工作

# 配置一个时间,如果查询时间超过了我们设置的时间,我们就认为这是一个慢查询.

--------------------------------------------------------

# 配置慢查询------两个主要配置
	slowlog-max-len :慢查询队列的长度 也就是说队列里最多放多少个慢查询命令
	slowly-log-slower-than :超过多少微妙,就算慢命令,就会记录到慢查询队列中


# 实战----在redis客户端敲命令----给配置文件设置上
config set slowlog-log-slower-than 1000    # 默认是10000微秒  一般设1000微秒就是1毫秒
config set slowlog-max-len 2000      # 通常设1000左右
config rewrite  # 写了永久生效,如果不写,只是暂时生效


# 查看慢查询队列
slowlog len     # 查看当前慢查询队列里 有多少慢查询命令
slowlog reset            # 清空慢查询队列

slowlog get     # 获取慢查询队列的所有慢命令

--------------------------------------------------------

客户端超时不一定慢查询,但慢查询是客户端超时的一个可能因素
4个阶段都有可能导致客户端超时
image
.
.
.
.
.
.
.
.
.

6 pipeline与事务

# redis通过管道可以支持事务

# Redis的pipeline(管道)功能在命令行中没有,但redis是支持pipeline的,
而且在各个编程语言版的client中都有相应的实现(Redis模块)

将一批命令,批量打包,在redis服务端批量计算(执行),然后把结果批量返回

1次pipeline(n条命令)执行时间 = 1次网络时间 + n次命令执行时间

------------------------------------------------

# python实现pipline
import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379)
r = redis.Redis(connection_pool=pool)

#创建pipeline
pipe = r.pipeline(transaction=True)

#开启事务
pipe.multi()
pipe.set('name', 'lqz')

# 其他代码,可能出异常

pipe.set('role', 'nb')

pipe.execute()  # 一次性把管道里所有命令,按顺序全部执行完,有一个命令执行失败,回滚到开始

---------------------------------------------------

# redis原生也能实现事务    直接在命令行里敲命令也能实现事务mutil

multi   # 1 开启管道
set name lqz
set age 18   # 命令都放管道里了

exec    # 放到管道中一次性执行

---------------------------------------------------
---------------------------------------------------

# 模拟实现乐观锁  watch+multi实现乐观锁

# 在开启事务之前,先watch
watch age   # 监听 age 的值
multi
decr age
exec    # 后执行 会失败  被watch的数据 乐观的认为不会不会被别人改,一旦自己要改该数据的时候
        # 发现该数据被别人改过了,那么该改数据的命令就会执行失败,最终导致事务回滚到开始状态

------------------

# 另一台机器
multi  # 和上面机器同时开启管道,两个机器开启的管道没有联系
decr age
exec  # 先执行,上面的执行就会失败(乐观锁,被wathc的事务不会执行成功)

.
.
.
.
.
.
.
.
.

7 发布订阅

------------------------------------------------------------

发布订阅是观察者模式,只要订阅了某个东西,这个东西发生变化,我们就能收到

# 观察者(Observer) 模式定义:
对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。


发布者发布了消息,所有的订阅者都可以收到通知
redis就能实现这样的功能 !!!

# 应用场景:比如关注了某个明星,该明星发了微博,所有关注他的人都能收到通知

------------------------------------------------------------
------------------------------------------------------------

# redis客户端 创建一个发布频道lqz  并发了条信息 hello

publish lqz hello    # 只要有订阅者,客户端发送消息,所有订阅者都能收到


subscribe lqz    # 另外的客户端,订阅lqz频道,等待接收消息


pubsub numsub lqz    # 查看某个频道有几个订阅者


pubsub channels    # 列出活跃的频道


# 发布订阅和消息队列的区别
发布订阅数全收到,消息队列有个抢的过程,只有一个抢到

发布订阅是观察者模式的实现,每个订阅者监听一个自己的队列,发布者发布消息后,放到每一个订阅者的队列里
这样所有的订阅者就都能收到了!!!
image

.
.
.
.
.
.
.

8 Bitmap位图

位图不是实际的数据类型,而是在字符串类型上定义的一组面向位的操作。

Bitmap位图:
# 是字符串类型,但是以二进制形式存储的,获取,设置某个二进制位的

--------------------------------------------------

set hello big
getbit hello xx  # 返回从左起0开始,第xx个比特位,对应的数字0或1

setbit hello 7 1   # 把第7个比特位对应数字设置为1

get hello      # big就变成了cit

---------------------------

.
.

bitcount name start end    # start end 不写默认就是字符串全部索引对应的字符
# 统计字符串,索引范围内对应字符的对应二进制数字里有几个1
bitcount hello
bitcount hello  0 1
# 由于上面把一个0改成1了,所以结果是13个1


# 独立用户统计
	假设:1亿用户,5千万活跃用户
	要统计每一天的活跃用户是多少,怎么办?
数据类型 每个userid占用空间 需要存储用户量 全部内存量
set集合 32位(假设userid是整形,占32位) 5千万 32位*5千万=200MB
bitmap 1位 1亿 1位*1亿=12.5MB

int32类型  4个字节表示一个数字---》 可以表示 正负 231次方-110进账数字范围
	存一个数字1       都要占4个字节
	存一个数字1001    都要占4个字节


-方式一:登录,把id放到集合中---》统计集合大小,就算出今天的用户活跃量了
-方式二:登录,操作位图,把id对应的数字设为1 ,以后统计1的个数

# 方式二具体实现:前提是用户量大的情况下
首先每天按日期在redis里面创一个对应的字符串
比如 set data1 c                        # 随便设置个简单的字符串 c
每天0点计数器归零,每来一个用户登录 setbit data1 n 1
# 给位图对象按计数器数字,给对应位置设置为1
# 这样一天下来,只要用 bitcount data1  统计一下该字符串里面有多少个1
# 就是今天所有的登录用户数了

-----------------------------------------

# 位图的其他操作方法  (好像没啥用)

bitpos hello 1 start end
# start end 指定从字符串的哪个索引位置开始查找 默认是从0到最后

# bitpos用于在位图指定比特位上查找第一个被设置为1或0的位置
bitpos hello 1   # 位图对象中第一个1的位置,结果是 1
bitpos hello 0   # 结果是 0

bitpos hello 1 1 2
# 返回从第一个字节到第二个字节之间 第一个1的位置,为 9


image
.
.
image
.
.
.
.
.
.
.
.
.

9 HyperLogLog


HyperLogLog是一种算法

redis中支持这种算法,
基于HyperLogLog算法:  极小的空间完成独立数量统计!!!
类似于布隆过滤器,但算法不一样

------------------------------------------
# HyperLogLog本质是字符串类型的数据,
# 但是可以往里面放值,而且只能往里放值,但是取不出来
# 只用来做个数统计与去重

pfadd key element  # 向hyperloglog添加元素,可以同时添加多个
pfcount key  # 计算hyperloglog的独立总数

pfadd uuids "uuid1" "uuid2" "uuid3" "uuid4"  # 向uuids中添加4个uuid
pfcount uuids  # 返回4


# 也可以做独立用户统计

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

posted @   tengyifan  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示