redis - 2 -位图操作 | Hash散列数据类型 | 集合数据类型(set)| 有序集合sortedset

redis 1回顾

Redis的特点

1、基于key-value的非关系型数据库
2、基于内存存储,速度很快
3、基于内存存储,经常当作缓存型数据库使用,常用信息缓存在redis数据库中

五大数据类型

1、字符串类型(string)
2、列表类型(list3、哈希类型(hash4、集合类型(set5、有序集合类型(sorted set

字符串类型

# 设置key相关操作
1set key value
2set key value nx
3、mset k1 v1 k2 v2 k3 v3
4set key value ex seconds
5set key value
5、expire key 5
5、pexpire key 5
5、ttl key
5、persist key
# 获取key相关操作
6、get key
7、mget k1 k2 k3
8、strlen key 
# 数字相关操作
7、incrby key 步长
8、decrby key 步长
9、incr key
10、decr key
11、incrbyfloat key number

列表类型

# 插入元素相关操作
1、LPUSH key value1 value2 
2、RPUSH key value1 value2
3、RPOPLPUSH source destination
4、LINSERT key after|before value newvalue
# 查询相关操作
5、LRANGE key start stop
6、LLEN key
# 删除相关操作
7、LPOP key
8、RPOP key
9、BLPOP key timeout
10、BRPOP key timeout
11、LREM key count value
12、LTRIM key start stop
# 修改指定元素相关操作
13、LSET key index newvalue

思考:

Redis列表如何当做共享队列来使用???

# 同学你好,你还记得小米应用商店爬取URL地址的案例吗?
1、生产者消费者模型
2、生产者进程在列表中 LPUSH | RPUSH 数据,消费者进程在列表中 BRPOP | BLPOP 数据

Python与redis交互注意

1、r.set('name','Tom',ex=5,nx=True)
2、r.mset({'user1:name':'Tom','user1:age':'25'})
# 有元素时返回弹出元素,否则返回None
3、r.brpop('mylist',3)

redis 2

位图操作bitmap

定义

1、位图不是真正的数据类型,它是定义在字符串类型中
2、一个字符串类型的值最多能存储512M字节的内容,位上限:2^32
# 1MB = 1024KB
# 1KB = 1024Byte(字节)
# 1Byte = 8bit(位)

强势点

可以实时的进行统计,极其节省空间。官方在模拟1亿28百万用户的模拟环境下,在一台MacBookPro上,典型的统计如“日用户数”的时间消耗小于50ms, 占用16MB内存

设置某一位上的值(setbit)

# 设置某一位上的值(offset是偏移量,从0开始)
setbit key offset value
# 获取某一位上的值
GETBIT key offset
# 统计键所对应的值中有多少个 1 
BITCOUNT key

示例

# 默认扩展位以0填充
127.0.0.1:6379> set mykey ab
OK
127.0.0.1:6379> get mykey
"ab"
127.0.0.1:6379> SETBIT mykey 0 1
(integer) 0
127.0.0.1:6379> get mykey
"\xe1b"
127.0.0.1:6379> 

获取某一位上的值

GETBIT key offset

127.0.0.1:6379> GETBIT mykey 3
(integer) 0
127.0.0.1:6379> GETBIT mykey 0
(integer) 1
127.0.0.1:6379> 

bitcount

统计键所对应的值中有多少个 1

127.0.0.1:6379> SETBIT user001 1 1
(integer) 0
127.0.0.1:6379> SETBIT user001 30 1
(integer) 0
127.0.0.1:6379> bitcount user001
(integer) 2
127.0.0.1:6379> 

应用场景案例

# 网站用户的上线次数统计(寻找活跃用户)
	用户名为key,上线的天作为offset,上线设置为1
# 示例
	用户名为 user1:login 的用户,今年第1天上线,第30天上线
	SETBIT user1:login 0 1 
	SETBIT user1:login 29 1
	BITCOUNT user1:login

代码实现

import redis

r = redis.Redis(host='127.0.0.1',port=6379,db=0)

# user001: 一年中第5天和200天登录
r.setbit('user:001',4,1)
r.setbit('user:001',199,1)
# user002: 一年中第100天和第300天登录
r.setbit('user:002',99,1)
r.setbit('user:002',299,1)
# user:003: 登录了100次以上
for i in range(1,366,2):
  r.setbit('user:003',i,1)
# user:004: 登录了100次以上
for i in range(1,366,3):
  r.setbit('user:004',i,1)

user_list = r.keys('user:*')

# 存放活跃用户列表
active_users = []
# 存放不活跃用户列表
no_active_users = []

for user in user_list:
  login_count = r.bitcount(user)
  if login_count >= 100:
    active_users.append((user,login_count))
  else:
    no_active_users.append((user,login_count))

print('活跃用户:',active_users)
print('不活跃用户:',no_active_users)

Hash散列数据类型

  • 定义
1、由field和关联的value组成的键值对
2、field和value是字符串类型
3、一个hash中最多包含2^32-1个键值对
  • 优点
1、节约内存空间
2、每创建一个键,它都会为这个键储存一些附加的管理信息(比如这个键的类型,这个键最后一次被访问的时间等)
3、键越多,redis数据库在储存附件管理信息方面耗费内存越多,花在管理数据库键上的CPU也会越多
  • 缺点(不适合hash情况)
1、使用二进制位操作命令:SETBIT、GETBIT、BITCOUNT等,如果想使用这些操作,只能用字符串键
2、使用过期键功能:键过期功能只能对键进行过期操作,而不能对散列的字段进行过期操作
  • 基本命令操作
# 1、设置单个字段
HSET key field value
HSETNX key field value
# 2、设置多个字段
HMSET key field value field value
# 3、返回字段个数
HLEN key
# 4、判断字段是否存在(不存在返回0)
HEXISTS key field

# 5、返回字段值
HGET key field
# 6、返回多个字段值
HMGET key field filed

# 7、返回所有的键值对
HGETALL key
# 8、返回所有字段名
HKEYS key
# 9、返回所有值
HVALS key

# 10、删除指定字段
HDEL key field 
# 11、在字段对应值上进行整数增量运算
HINCRBY key filed increment
# 12、在字段对应值上进行浮点数增量运算
HINCRBYFLOAT key field increment

Hash与python交互

# 1、更新一条数据的属性,没有则新建
hset(name, key, value) 
# 2、读取这条数据的指定属性, 返回字符串类型
hget(name, key)
# 3、批量更新数据(没有则新建)属性,参数为字典
hmset(name, mapping)
# 4、批量读取数据(没有则新建)属性
hmget(name, keys)
# 5、获取这条数据的所有属性和对应的值,返回字典类型
hgetall(name)
# 6、获取这条数据的所有属性名,返回列表类型
hkeys(name)
# 7、删除这条数据的指定属性
hdel(name, *keys)

Python代码hash散列

'''设置1个字段,更改1个字段,设置多个字段,获取相关信息'''
import redis

r = redis.Redis(host='127.0.0.1',port=6379,db=0)
# 设置
r.hset('user1','name','bujingyun')
# 更新
r.hset('user1','name','kongci')
# 取数据
print(r.hget('user1','name'))
# 一次设置多个field和value
user_dict = {
  'password':'123456',
  'gender':'F',
  'height':'165'
}
r.hmset('user1',user_dict)
# 获取所有数据,字典
print(r.hgetall('user1'))

# 获取所有fields和所有values
print(r.hkeys('user1'))
print(r.hvals('user1'))

应用场景:微博好友关注

1、用户ID为key,Field为好友ID,Value为关注时间
       key       field    value
	 user:10000   user:606 20190520
	              user:605 20190521
2、用户维度统计
   统计数包括:关注数、粉丝数、喜欢商品数、发帖数
   用户为key,不同维度为field,value为统计数
   比如关注了5人
	 HSET user:10000 fans 5
	 HINCRBY user:10000 fans 1

应用场景: redis+mysql+hash组合使用

  • 原理

    用户想要查询个人信息
    1、到redis缓存中查询个人信息
    2、redis中查询不到,到mysql查询,并缓存到redis
    3、再次查询个人信息
    
  • 代码实现

      import redis
      import pymysql
      
      # 1. 先到redis中查询
      # 2. redis中没有,到mysql查询,缓存到redis(设置过期时间)
      # 3. 再查询1次
      r = redis.Redis(host='192.168.153.148', port=6379, db=0)
      username = input('请输入用户名:')
      
      result = r.hgetall(username)
      if result:
          print(result)
      else:
          # redis中没有缓存,需要到mysql中查询
          db = pymysql.connect(
              host='192.168.153.148',
              user='tiger',
              password='123456',
              database='userdb',
              charset='utf8'
          )
          cursor = db.cursor()
          sele = 'select age,gender from user where username=%s'
          cursor.execute(sele, [username])
          # userinfo: (('guoxiaonao',36,'M'),)
          userinfo = cursor.fetchall()
          if not userinfo:
              print('用户不存在')
          else:
              # 打印输出
              print('mysql', userinfo)
              # 缓存到redis
              user_dict = {
                  'age': userinfo[0][0],
                  'gender': userinfo[0][1]
              }
              r.hmset(username, user_dict)
              # 设置过期时间
              r.expire(username, 30)
    
    

mysql数据库中数据更新信息后同步到redis缓存

用户修改个人信息时,要将数据同步到redis缓存

import redis
import pymysql

# 1.先到mysql更新update更新数据
# 2.缓存到redis
    # 1>redis中之前没有缓存过 - 缓存(所有字段)
    # 2>redis中缓存过 - 直接更新字段

class Update:
    def __init__(self):
        self.db=pymysql.connect('localhost','root','123456','userdb',charset='utf8')
        self.cursor=self.db.cursor()
        self.r=redis.Redis(host='localhost',port=6379,db=0)

    # 更新mysql表记录
    def update_mysql(self,wuqi,username):
        upd='update user set wuqi=%s where name=%s'
        try:
            self.cursor.execute(upd,[wuqi,username])
            self.db.commit()
            return True
        except Exception as e:
            self.db.rollback()
            print(e)


    # 更新redis
    def update_redis(self, username, wuqi):
        user=self.r.hgetall(username)
        if not user:
            user_dict=self.select_mysql(username)
            self.r.hmset(username,user_dict)
            self.r.expire(username,30)
        else:
            self.r.hset(username,'wuqi',wuqi)

    # 查询mysql
    def select_mysql(self, username):
        sel = 'select name,gender,wuqi from user where name=%s'
        self.cursor.execute(sel, [username])
        user = self.cursor.fetchone()
        user_dict = {
            'name': user[0],
            'gender': user[1],
            'wuqi': user[2]
        }
        return user_dict


    # 入口函数
    def main(self):
        username=input('请输入用户名:')
        wuqi=input('请输入新的武器:')
        if self.update_mysql(username=username,wuqi=wuqi):
            self.update_redis(username=username,wuqi=wuqi)
        else:
            print('更新失败')




if __name__ == '__main__':
    up=Update()
    up.main()

集合数据类型(set)

  • 特点
1、无序、去重
2、元素是字符串类型
3、最多包含2^32-1个元素
  • 基本命令
# 1、增加一个或者多个元素,自动去重
SADD key member1 member2
# 2、查看集合中所有元素
SMEMBERS key
# 3、删除一个或者多个元素,元素不存在自动忽略
SREM key member1 member2
# 4、元素是否存在
SISMEMBER key member
# 5、随机返回集合中指定个数的元素,默认为1个
SRANDMEMBER key [count]
# 6、弹出成员
SPOP key [count]
# 7、返回集合中元素的个数,不会遍历整个集合,只是存储在键当中了
SCARD key

# 8、把元素从源集合移动到目标集合
SMOVE source destination member

# 9、差集(number1 1 2 3 number2 1 2 4 结果为3)
SDIFF key1 key2 
# 10、差集保存到另一个集合中
SDIFFSTORE destination key1 key2

# 11、交集
SINTER key1 key2
SINTERSTORE destination key1 key2

# 12、并集
SUNION key1 key2
SUNIONSTORE destination key1 key2

案例: 新浪微博的共同关注

# 需求: 当用户访问另一个用户的时候,会显示出两个用户共同关注过哪些相同的用户
# 设计: 将每个用户关注的用户放在集合中,求交集即可
# 实现:
	user001 = {'peiqi','qiaozhi','danni'}
	user002 = {'peiqi','qiaozhi','lingyang'}
  
user001和user002的共同关注为:
	SINTER user001 user002
	结果为: {'peiqi','qiaozhi'}

python操作set

# 1、给name对应的集合中添加元素
sadd(name,values)
r.sadd("set_name","tom")
r.sadd("set_name","tom","jim")

# 2、获取name对应的集合的所有成员: python集合
smembers(name)
r.smembers('set_name')

# 3、获取name对应的集合中的元素个数
scard(name)
r.scard("set_name")

# 4、检查value是否是name对应的集合内的元素:True|False
sismember(name, value)
r.sismember('set_name','tom')

# 5、随机删除并返回指定集合的一个元素
spop(name)
member = r.spop('set_name')

# 6、删除集合中的某个元素
srem(name, value) 
r.srem("set_name", "tom")

# 7、获取多个name对应集合的交集
sinter(keys, *args)

r.sadd("set_name","a","b")
r.sadd("set_name1","b","c")
r.sadd("set_name2","b","c","d")

print(r.sinter("set_name","set_name1","set_name2"))
#输出:{b'b'}

# 8、获取多个name对应的集合的并集: python集合
sunion(keys, *args)
r.sunion("set_name","set_name1","set_name2")

python代码实现微博关注

import redis

r = redis.Redis(host='127.0.0.1',port=6379,db=0)

# user1关注的人
r.sadd('user1:focus','peiqi','qiaozhi','danni')
# user2关注的人
r.sadd('user2:focus','peiqi','qiaozhi','lingyang')
# 共同关注: 求交集 {b'qiaozhi', b'peiqi'}
focus_set = r.sinter('user1:focus','user2:focus')

# 创建空集合,存放最终结果
result = set()

for focus in focus_set:
  result.add(focus.decode())

print(result)

有序集合sortedset

  • 特点
1、有序、去重
2、元素是字符串类型
3、每个元素都关联着一个浮点数分值(score),并按照分值从小到大的顺序排列集合中的元素(分值可以相同)
4、最多包含2^32-1元素
  • 示例

    一个保存了水果价格的有序集合

分值2.04.06.08.010.0
元素西瓜葡萄芒果香蕉苹果

一个保存了员工薪水的有序集合

分值600080001000012000
元素lucytomjimjack

一个保存了正在阅读某些技术书的人数

分值300400555666777
元素核心编程阿凡提本拉登阿姆斯特朗比尔盖茨
  • 有序集合常用命令
# 在有序集合中添加一个成员
zadd key score member
# 查看指定区间元素(升序)
zrange key start stop [withscores]
# 查看指定区间元素(降序)
ZREVRANGE key start stop [withscores]
# 查看指定元素的分值
ZSCORE key member

# 返回指定区间元素
# offset : 跳过多少个元素
# count : 返回几个
# 小括号 : 开区间  zrangebyscore fruits (2.0 8.0   --->不包括2.0,包括8.0
zrangebyscore key min max [withscores] [limit offset count]
# 每页显示10个成员,显示第5页的成员信息: 
# limit 40 10
# MySQL: 每页显示10条记录,显示第5页的记录
# limit 40,10
# limit 2,3   显示: 第3 4 5条记录

# 删除成员
zrem key member
# 增加或者减少分值
zincrby key increment member
# 返回元素排名
zrank key member
# 返回元素逆序排名
zrevrank key member
# 删除指定区间内的元素
zremrangebyscore key min max
# 返回集合中元素个数
zcard key
# 返回指定范围中元素的个数
zcount key min max
zcount salary 6000 8000 
zcount salary (6000 8000   # 6000<salary<=8000
zcount salary (6000 (8000  # 6000<salary<8000     
          
# 并集
zunionstore destination numkeys key [weights 权重值] [AGGREGATE SUM|MIN|MAX]
# zunionstore salary3 2 salary salary2 weights 1 0.5 AGGREGATE MAX
# 2代表集合数量,weights之后 权重1给salary,权重0.5给salary2集合,算完权重之后执行聚合AGGREGATE
# 若不设置权重和aggregate,则默认 weights 1 1 aggregate sum
                     
# 交集:和并集类似,只取相同的元素
ZINTERSTORE destination numkeys key1 key2 WEIGHTS weight AGGREGATE SUM(默认)|MIN|MAX

python操作sorted set

import redis

r = redis.Redis(host='127.0.0.1',port=6379,db=0)
# 注意第二个参数为字典
# 命令行:ZADD salary 6000 tom 8000 jim
r.zadd('salary',{'tom':6000,'jim':8000,'jack':12000})
# 结果为列表中存放元组[(),(),()]
print(r.zrange('salary',0,-1,withscores=True))
print(r.zrevrange('salary',0,-1,withscores=True))
# start:起始值,num:显示条数
print(r.zrangebyscore('salary',6000,12000,start=1,num=2,withscores=True))
# 删除
r.zrem('salary','tom')
print(r.zrange('salary',0,-1,withscores=True))
# 增加分值
r.zincrby('salary',5000,'jack')
print(r.zrange('salary',0,-1,withscores=True))
# 返回元素排名
print(r.zrank('salary','jack'))
print(r.zrevrank('salary','jack'))
# 删除指定区间内的元素
r.zremrangebyscore('salary',6000,8000)
print(r.zrange('salary',0,-1,withscores=True))
# 统计元素个数
print(r.zcard('salary'))
# 返回指定范围内元素个数
print(r.zcount('salary',6000,20000))
# 并集
r.zadd('salary2',{'jack':17000,'lucy':8000})
r.zunionstore('salary3',('salary','salary2'),aggregate='max')
print(r.zrange('salary3',0,-1,withscores=True))
# 交集
r.zinterstore('salary4',('salary','salary2'),aggregate='max')
print(r.zrange('salary4',0,-1,withscores=True))

练习

1、网易音乐排行榜 - Python

1、每首歌的歌名作为元素
2、每首歌的播放次数作为分值
3、使用ZREVRANGE来获取播放次数最多的歌曲

2、 京东商品畅销榜 - Python

# 第1天
ZADD mobile-001 5000 'huawei' 4000 'oppo' 3000 'iphone'
# 第2天
ZADD mobile-002 5200 'huawei' 4300 'oppo' 3230 'iphone'
# 第3天
ZADD mobile-003 5500 'huawei' 4660 'oppo' 3580 'iphone'
问题:如何获取三款收集的销量排名?
ZUNIONSTORE mobile-001:003 3 mobile-001 mobile-002 mobile-003 # 可否?
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值