Redis很少有直接通过客户端去操作的,更多的是被程序的业务代码调用。这一节我们就以python3为例,演示一下对Redis的常规操作,最后简单实现下前面提到的视频网站限制用户观看视频数量的案例。
我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。
环境准备
下面是我用来演示的环境:
- 可被外界访问的Redis实例
- Pycharm进行python代码编辑
- Python3进行python代码解析
- python环境和Redis实例之间网络通,Redis端口可正常访问
Redis实例可以直接按照前面的《Redis从入门到精通(1):centos7安装和启动redis》来启动;pycharm和python3环境可以按照另一篇博客《windows10安装anaconda 3和pycharm及常规使用详解》来安装和配置。
常规操作
连接Redis实例
首先需要安装两个第三方包:redis和redis-py,单单安装redis包无法连接。我安装的版本分别是:
- redis - 3.2.100
- redis-py - 3.4.1
之后就可以连接Redis实例了
import redis
r=redis.Redis(host='10.18.97.115',port=6379,db=0,password='xiaofu')
数据操作
虽说python对原来的Redis客户端命令都经过了封装,但是封装以后的命令和参数顺序和原先基本没有差别。下面对前面提到的五种类型的数据分别做简单的演示,为了清晰表示输入输出关系,下面的操作在ipython
环境中完成。
- string类型操作
赋值和取值操作:
In [11]: r.set('age',1)
Out[11]: True
In [12]: r.get('age')
Out[12]: b'1'
注意python3中所有返回的数据都是bytes类型,想要变为string类型用作后续处理,还需要经过decode操作,省略参数默认用utf-8解码。
In [13]: result=r.get('age')
In [14]: type(result)
Out[14]: bytes
In [15]: new_result=result.decode()
In [16]: type('new_result')
Out[16]: str
计数器操作:
In [18]: r.incrby('age',3)
Out[18]: 4
In [19]: r.incr('age')
Out[19]: 5
In [20]: r.decr('age',2)
Out[20]: 3
过期操作:
In [22]: r.setex('hobby',10,'basketball')
Out[22]: True
In [23]: r.ttl('hobby')
Out[23]: 3
In [24]: r.get('hobby')
In [25]: r.exists('hobby')
Out[25]: 0
- hash操作
赋值和取值操作:
如果是批量操作,第二个参数是python的字典类型
In [28]: r.hmset('info',{'name':'xiaofu','age':100,'hobby':'basketball'})
Out[28]: True
In [31]: r.hset('info','location','SG')
Out[31]: 1
In [32]: r.hgetall('info')
Out[32]:
{b'name': b'xiaofu',
b'age': b'100',
b'hobby': b'basketball',
b'location': b'SG'}
删除和计数操作:
In [34]: r.hincrby('info','age',-50)
Out[34]: 50
In [35]: r.hdel('info','location')
Out[35]: 1
In [36]: r.hgetall('info')
Out[36]: {b'name': b'xiaofu', b'age': b'50', b'hobby': b'basketball'}
- list操作
赋值和取值操作:
In [40]: r.rpush('players','yaoming','james','davis','xiaofu')
Out[40]: 4
In [43]: r.lrange('players',0,-1)
Out[43]: [b'yaoming', b'james', b'davis', b'xiaofu']
In [44]: r.lpop('players')
Out[44]: b'yaoming'
In [45]: r.lrange('players',0,-1)
Out[45]: [b'james', b'davis', b'xiaofu']
In [47]: r.lindex('players',-1)
Out[47]: b'xiaofu'
- set操作
赋值和取值操作:
In [49]: r.sadd('set1','a','b','c','d')
Out[49]: 4
In [50]: r.smembers('set1')
Out[50]: {b'a', b'b', b'c', b'd'}
In [51]: r.spop('set1')
Out[51]: b'd'
In [52]: r.scard('set1')
Out[52]: 3
In [53]: r.smembers('set1')
Out[53]: {b'a', b'b', b'c'}
集合操作:
In [55]: r.sadd('set2','b','c','d','e')
Out[55]: 4
In [56]: r.sinter('set1','set2')
Out[56]: {b'b', b'c'}
In [57]: r.sdiff('set1','set2')
Out[57]: {b'a'}
In [58]: r.sunion('set1','set2')
Out[58]: {b'a', b'b', b'c', b'd', b'e'}
- sorted set操作
赋值和取值操作:
In [60]: r.zadd('score',{'xiaofu':100,'james':80,'davis':60})
Out[60]: 3
In [61]: r.zrange('score',0,-1,withscores=True)
Out[61]: [(b'davis', 60.0), (b'james', 80.0), (b'xiaofu', 100.0)]
In [62]: r.zrevrange('score',0,-1,withscores=True)
Out[62]: [(b'xiaofu', 100.0), (b'james', 80.0), (b'davis', 60.0)]
score操作:
In [63]: r.zrangebyscore('score',80,100,withscores=True)
Out[63]: [(b'james', 80.0), (b'xiaofu', 100.0)]
In [64]: r.zremrangebyrank('score',0,1)
Out[64]: 2
In [65]: r.zrange('score',0,-1,withscores=True)
Out[65]: [(b'xiaofu', 100.0)]
实际案例
模拟视频网站限制普通用户单日只能观看10个视频的场景。这里稍微简化一下,限制60秒内只能观看10个视频,操作次数提醒用户升级为VIP。且用户每隔1-5秒尝试观看一次视频,观看视频的行为用打印来简单代替。
先展示下最后效果:
You are watching video number 1. [refresh after 60 seconds]
You are watching video number 2. [refresh after 58 seconds]
You are watching video number 3. [refresh after 56 seconds]
You are watching video number 4. [refresh after 53 seconds]
You are watching video number 5. [refresh after 49 seconds]
You are watching video number 6. [refresh after 45 seconds]
You are watching video number 7. [refresh after 42 seconds]
You are watching video number 8. [refresh after 41 seconds]
You are watching video number 9. [refresh after 39 seconds]
You are watching video number 10. [refresh after 34 seconds]
You have watched 10 videos, please upgrade to VIP. [refresh after 30 seconds]
You have watched 10 videos, please upgrade to VIP. [refresh after 28 seconds]
You have watched 10 videos, please upgrade to VIP. [refresh after 25 seconds]
You have watched 10 videos, please upgrade to VIP. [refresh after 24 seconds]
You have watched 10 videos, please upgrade to VIP. [refresh after 19 seconds]
You have watched 10 videos, please upgrade to VIP. [refresh after 15 seconds]
You have watched 10 videos, please upgrade to VIP. [refresh after 11 seconds]
You have watched 10 videos, please upgrade to VIP. [refresh after 10 seconds]
You have watched 10 videos, please upgrade to VIP. [refresh after 8 seconds]
You have watched 10 videos, please upgrade to VIP. [refresh after 2 seconds]
You are watching video number 1. [refresh after 60 seconds]
You are watching video number 2. [refresh after 59 seconds]
思路:以userid做为key创建一个计数器,过期时间为60秒。每观看一次视频加1,一直到10为止,不再增加,直到过期。
这里最主要是就是更新redis的操作,需要判断key是否存在,以及是否到10。创建一个函数,带入两个参数,分别是redis实例和userid,分三种情况来处理,返回一个tuple,两个元素分别为观看了的次数和剩余过期时间。如下:
def update_redis(r,userid):
ttl = r.ttl(userid)
if r.exists(userid) and r.get(userid).decode() == '10': # remember to decode to string
return (11,ttl)
elif not r.exists(userid):
r.setex(userid,60,1)
return (1,60) # can not use ttl here, will return -2
else:
watched_videos = r.incr(userid) # this will return integer
return (watched_videos,ttl)
然后针对用户观看视频的动作,用简单的打印来代替。首先更新下redis,根据返回的结果来决定用户是否能继续观看。如下:
def watch_video(r,userid):
result=update_redis(r,userid)
if result[0] == 11:
print('You have watched 10 videos, please upgrade to VIP. [refresh after {} seconds]'.format(result[1]))
else:
print('You are watching video number {}. [refresh after {} seconds]'.format(result[0],result[1]))
完整的代码如下,利用暂停随机数来模拟用户的观看行为
#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""
demo of non-VIP user limited to watch 10 videos within 100 seconds.
T型人小付 @ CSDN
"""
import redis
import random
import time
def conn_redis(address,port,password,db):
r = redis.Redis(host=address, port=port, db=db, password=password)
return r
def update_redis(r,userid):
ttl = r.ttl(userid)
if r.exists(userid) and r.get(userid).decode() == '10': # remember to decode to string
return (11,ttl)
elif not r.exists(userid):
r.setex(userid,60,1)
return (1,60) # can not use ttl here, will return -2
else:
watched_videos = r.incr(userid) # this will return integer
return (watched_videos,ttl)
def watch_video(r,userid):
result=update_redis(r,userid)
if result[0] == 11:
print('You have watched 10 videos, please upgrade to VIP. [refresh after {} seconds]'.format(result[1]))
else:
print('You are watching video number {}. [refresh after {} seconds]'.format(result[0],result[1]))
if __name__ == '__main__':
r=conn_redis('10.18.97.115',6379,'xiaofu',0)
while True:
watch_video(r,'userid:01')
time.sleep(random.randint(1,5))