day10
内容概要
- redis之字符串
- redis之列表
- redis之hash
- redis其他操作
- redis管道
- django中使用redis
- celery介绍和安装
- celery快速使用
- celery包结构
redis之字符串
from redis import Redis
conn = Redis(host='127.0.0.1', port=6379)
# conn.set("age", 18, ex=)
"""
set(name, value, ex=None, px=None, nx=False, xx=False)
ex, 过期时间(秒)
px,过期时间(毫秒)
nx,如果设置为True,则只有name不存在时,当前set操作才执行,值存在就修改不了,执行没有效果
xx,如果设置为True,则只有name存在时,当前set操作才执行,值不存在,执行没有效果
"""
# conn.set("age", 18, ex=5)
# conn.set("age", 18, px=3000)
# conn.set("age", 15, nx=True)
# conn.set("age", 19, nx=True)
# conn.set("age", 19, xx=True)
# conn.set("age", 19, xx=True)



"""
setnx(name, value)
设置值,只有name不存在时,执行设置操作(添加)如果存在,不会修改
相当于 set(nx=True) 默认nx=True
"""
"""
setex(name, time, value)
设置值
参数
time,过期时间(数字秒, 或 timedelta对象)
"""
# conn.setex("age", 3, 18)
"""
psetex(name, time_ms, value)
设置值
参数
time_ms, 过期时间(数字毫秒 或 timedelta对象)
"""
# conn.psetex("<", 30000, 19)
# print(conn.get(":"))
"""
mset(*args, **kwargs)
mset(k1='v1', k2='v2')
"""
# conn.mset({'name': "呵呵", "age": "18"})
"""
get(name)
获取值
"""
# print(conn.get("name"))
# print(str(conn.get("name"), encoding="utf8"))

"""
mget(keys, *args)
批量获取
如 mget("k1", "k2)
或 mget(["k1","k2")
"""
# print(conn.mget(["name", "age"])) # [b'\xe5\x91\xb5\xe5\x91\xb5', b'18']
"""
getset(name, value)
设置新值并获取原来的值
"""
# res = conn.getset("age", 200)
# print(res)

"""
getrange(key, start, end)
获取子序列
参数:
name, Redis 的name
start, 起始位置(字节)
end, 结束位置(字节)
"""
# print(conn.get("name"))
# res = conn.getrange("name", 0, 0)
# print(res)

"""
setrange(name, offset, value)
修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
参数:
offset,字符串索引,字节(一个汉字三个字节)
value,要设置的值
"""
# conn.setrange("name",0, "李")
# print(str(conn.get("name"), encoding="utf8"))
"""
setbit(name, offset, value)
# 对name对应值的二进制表示的位进行操作
# 参数:
# name,redis的name
# offset,位的索引(将值变换成二进制后再进行索引)
# value,值只能是 1 或 0
# 注:如果在Redis中有一个对应: n1 = "foo",
那么字符串foo的二进制表示为:01100110 01101111 01101111
所以,如果执行 setbit('n1', 7, 1),则就会将第7位设置为1,
那么最终二进制则变成 01100111 01101111 01101111,即:"goo"
"""
"""
getbit(name, offset)
# 获取name对应的值的二进制表示中的某位的值 (0或1)
"""
"""
bitcount(key, start=None, end=None)
# 获取name对应的值的二进制表示中 1 的个数
# 参数:
# key,Redis的name
# start,位起始位置
# end,位结束位置
"""
"""
bitop(operation, dest, *keys)
# 获取多个值,并将值做位运算,将最后的结果保存至新的name对应的值
# 参数:
# operation,AND(并) 、 OR(或) 、 NOT(非) 、 XOR(异或)
# dest, 新的Redis的name
# *keys,要查找的Redis的name
# 如:
bitop("AND", 'new_name', 'n1', 'n2', 'n3')
# 获取Redis中n1,n2,n3对应的值,然后讲所有的值做位运算(求并集),然后将结果保存 new_name 对应的值中
"""
"""
strlen(name)
返回name对应值的字节长度(一个汉字3个字节)
"""
# print(conn.strlen("name")) # 6
"""
incr(self, name, amount=1)
自增 name对应的值, 当name不存在时, 则创建name=amount,否则,
则自增
参数:
name.Redis的name
amount.自增数(必须是整数)
"""
# conn.incr("age") # 不传amount默认自增 1
"""
incrbyfloat(self, name, amount=1.0)
# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。
# 参数:
# name,Redis的name
# amount,自增数(浮点型)
"""
"""
decr(self, name, amount=1)
# 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减。
# 参数:
# name,Redis的name
# amount,自减数(整数)
但是 float 自减1报错 ,
"""
# conn.incrbyfloat("age", amount=1.2)
# conn.decr("age") # value is not an integer or out of range
"""
append(key, value)
在redis name 对应的值后面追加内容
参数:
key, redis 的name
value, 要追加的字符串
"""
conn.append("name", "handsome")
print(conn.get("name")) # 李呵handsome
redis之List操作
list操作,redis中的list在内存中按照一个name对应一个list来存储
from redis import Redis
from pool import POOL
conn = Redis(connection_pool=POOL)
'''
1 lpush(name, values)
2 rpush(name, values) 表示从右向左操作
3 lpushx(name, value)
4 rpushx(name, value) 表示从右向左操作
5 llen(name)
6 linsert(name, where, refvalue, value))
7 r.lset(name, index, value)
8 r.lrem(name, value, num)
9 lpop(name)
10 rpop(name) 表示从右向左操作
11 lindex(name, index)
12 lrange(name, start, end)
13 ltrim(name, start, end)
14 rpoplpush(src, dst)
15 blpop(keys, timeout)
16 r.brpop(keys, timeout),从右向左获取数据
17 brpoplpush(src, dst, timeout=0)
'''
"""
lpush(name,values)
在name对应的list中添加元素,每个新的元素都添加到列表的最左边
如:
r.lpush("oo", 11,22,33)
rpush(name, values) 表示从右向左操作
"""
conn.lpush("hobby", "篮球", "足球")

"""
lpushx(name, value)
在name对应的list中添加元素,只有name已经存在时,值添加到列表的
相当于 set的 xx 只有存在才会修改
"""
conn.lpushx("hobby", "呵呵呵") #
conn.lock("userinfo", "撒旦卡")
"""
llen(name)
name对应的list元素个数
"""
print(conn.llen("hobby")) # 4
"""
linsert(name, where, refvalue, value)
在name对应的列表的某一个前或后插入一个新值
参数:
name, redis的name
where, BEFORE或AFTER(小写也可以)
refvalue, 标杆值,即:在它前后插入数据(如果存在多个标杆值,以找到的第一个为准)
value,要插入的数据
"""
# conn.linsert("hobby", "before", "呵呵呵", "李金科")
"""
lset(name, index, value)
对name对应的list中的某一个搜索引位置重新赋值
参数:
name, redis的name
index, list的索引位置
value, 要设置的值
"""
# conn.lset("hobby", 1, "芜湖起飞")
# 会把 hobby 的索引1 的值 修改为 芜湖起飞
"""
lrem(name, count, value, )
对name对应的list中删除指定的值
参数:
name, redis的name
value, 要删除的值
num, num=0, 删除列表中所有指定的值
num=2,从前到后, 删除2个
num=-2, 从后往前删除2个
"""
# conn.lrem("hobby", 1, "李金科")
# 从左往右把李金科删除掉
"""
lpop(name)
在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素
"""
# print(conn.lpop("hobby"))
"""
lindex(name, index)
在name对应的列表中根据索引获取列表元素
"""
# print(conn.lindex("hobby", 0))
"""
lrange(name, start, end)
在name, redis的name
start,索引的起始位置,
end, 索引的结束位置
"""
# print(conn.lrange("hobby", 0, 0)) # 这是一个闭区间就是区第一个值, 取出来是一个列表
"""
ltrim(name, start, end)
在name对应的列表中留下在start-end索引之间的值
name, redis的name
start, 索引的起始位置
end, 索引结束位置(大于列表长度,则代表不移除任何)
"""
# conn.ltrim("hobby", 0, 2)

"""
rpoplpush(src, dst)
从一个列表中取出最右边的元素,同时将其添加到另一个列表的最左边
参数:
src,要取数据的列表的name
dst,要添加数据的列表的name
"""
# conn.lpush("hobby1", "足球")
# conn.rpoplpush("hobby1", "hobby")


"""
blpop(keys, timeout)
将多个列表排列,按照从左到右去pop对应得元素
参数:
keys, redis的name的集合
timeout, 超时时间, 当元素搜友列表的元素获取完之后,阻塞等待
列表内有数据的时间(秒), 0表示永远阻塞
# 更多:
# r.brpop(keys, timeout),从右向左获取数据
爬虫实现简单分布式:多个url放到列表里,往里不停放URL,程序循环取值,但是只能一台机器运行取值,可以把url放到redis中,多台机器从redis中取值,爬取数据,实现简单分布式
"""
"""
# 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
# 参数:
# src,取出并要移除元素的列表对应的name
# dst,要插入元素的列表对应的name
# timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞
"""
redis之hash
'''
1 hset(name, key, value)
2 hmset(name, mapping)
3 hget(name,key)
4 hmget(name, keys, *args)
5 hgetall(name)
6 hlen(name)
7 hkeys(name)
8 hvals(name)
9 hexists(name, key)
10 hdel(name,*keys)
11 hincrby(name, key, amount=1)
12 hincrbyfloat(name, key, amount=1.0)
13 hscan(name, cursor=0, match=None, count=None)
14 hscan_iter(name, match=None, count=None)
'''
import redis
conn = redis.Redis()
"""
hset(name,key, value)
name对应的hash中设置一个键值对(不存在则创建,否则,修改)
参数
# name,redis的name
# key,name对应的hash中的key
# value,name对应的hash中的value
# 注:
# hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加)
"""
# conn.hset("userinfo", "name", "lqz")
"""
hmset(name, mapping)
在name,redis的name
mapping,字典,如:{"k1":'v1',"k2":'v2'}
"""
# conn.hmset("userinfo", {"age": '18', "芜湖": "起飞"}) # 虽然报错但还是添加进去了
"""
hget(name,key)
# 在name对应的hash中获取根据key获取value
"""
# res = conn.hget("userinfo", "age")
# print(res)
"""
hmget(name, keys, *args)
在name对应的hash中获取多个key的值
参数:
name, redis对应的name
keys,要获取key集合,如:['k1', 'k2', 'k3']
# *args,要获取的key,如:k1,k2,k3
"""
# res = conn.hmget("userinfo", ["name", 'age'])
#
# res1 = conn.hmget("userinfo", "name", "age")
# print(res)
# print(res1)
"""
hgetall(name)
获取name对应hash的索引
"""
# res = conn.hgetall("userinfo")
# print(res)
"""
hlen(name)
获取name对应hash中键值对的个数
"""
# res = conn.hlen("userinfo") # 1
# print(res)
"""
hkeys(name)
获取name对应的hash中所有的key的值
"""
# res = conn.hkeys("userinfo")
# print(res) # [b'name']
"""
hvals(name)
获取name对应的hash中所有的values值
"""
# res = conn.hvals("userinfo")
# print(res) # [b'lqz']
"""
hexists(name, key)
检查name对应的hash是否存在当前传入的key
"""
# res = conn.hexists("userinfo", "age") # False
# res1 = conn.hexists("userinfo", "name") # True
#
# print(res)
# print(res1)
"""
hdel(name, *key)
将name对应的hash中指定key的键值对删除
"""
# res = conn.hdel("userinfo", "name") # 1
# res1 = conn.hdel("userinfo", "123") # 0
# print(res1)
"""
hincrby(name, key, amout=1)
自增name对应的hash中指定key的值,不存在则创建key=amout
参数:
name, redis中的name
key, hash对应的key
amount, 自增数(整数)
"""
# res = conn.hincrby("userinfo", "age")
"""
hincrbyfloat(name, key, amount=1.0)
自增name对应的hash中的指定key的值,不存在则创建key=amout
参数:
name, redis 中的name
key, hash对应的key
amount, 自增数(浮点数)
自增name对应的hash中指定key
"""
# res = conn.hincrbyfloat("userinfo", "age")
# print(res)
"""
hscan(name, cursor=0, match=None, cout=None)
增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的
获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆
参数:
name redis的name
cursor,游标(基于游标分批获取数据)
match,匹配指定key,默认None,表示所有的key
count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
如:
# 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None)
# 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None)
# ...
# 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕
"""
# res = conn.hscan("userinfo", count=10)
# print(res)
"""
hscan_iter(name, match=None, count=None)
# 利用yield封装hscan创建生成器,实现分批去redis中获取数据
# 参数:
# match,匹配指定key,默认None 表示所有的key
# count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
# 如:
# for item in r.hscan_iter('xx'):
# print item
"""
redis其他操作
''' 通用操作,不指定类型,所有类型都支持
delete(*names)
# 根据删除redis中的任意数据类型
exists(name)
# 检测redis的name是否存在
keys(pattern='*')
# 根据模型获取redis的name
# 更多:
# KEYS * 匹配数据库中所有 key 。
# KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
# KEYS h*llo 匹配 hllo 和 heeeeello 等。
# KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo
expire(name ,time)
# 为某个redis的某个name设置超时时间
rename(src, dst)
# 对redis的name重命名为
move(name, db))
# 将redis的某个值移动到指定的db下
randomkey()
# 随机获取一个redis的name(不删除)
type(name)
# 获取name对应值的类型
scan(cursor=0, match=None, count=None)
scan_iter(match=None, count=None)
# 同字符串操作,用于增量迭代获取key
'''
import redis
conn = redis.Redis()
# conn.set("name", "lqz")
# conn.set("age", "19")
#
# conn.lpush("hobby", "篮球", "足球")
# conn.delete("age") # 删除
# res = conn.exists("age") # 0
# print(res)
# # *是匹配所有 ?是匹配单个
# res1 = conn.keys("*") # [b'hobby', b'name', b'userinfo']
# print(res1)
# res2 = conn.keys("?ame") # [b'name']
# print(res2)
# conn.expire("name", 2) # 给 name设置一个过期时间 2秒
#
# conn.rename("userinfo", "userinfo1") # 重命名
#
# conn.move('name', 1) # 移动到 1数据库
# res = conn.randomkey() # 随机一个key b'hobby'
# print(res)
# res = conn.type("hobby") # b'list'
# res1 = conn.type("userinfo1") # b'hash'
# print(res)
# print(res1)
redis管道
事务----》四大特性:
- 原子性
- 一致性
- 隔离性
- 持久性
redis支持事务吗?
单实例才支持所谓的事务,支持事务是基于管道的
-
执行命令 一条一条执行
import redis conn = redis.Redis() p = conn.pipeline(transaction=True) p.multi() # conn.set("p1", 500) # conn.set("p2", 500) """ 如果在他们转账的时候报错了 """ conn.decr("p1", 100) raise Exception() conn.incr("p2", 100) conn.close()
p1 减少了
但是 p2没有增加

用了管道
import redis conn = redis.Redis() p = conn.pipeline(transaction=True) # 产生一条管道 p.multi() p.decr("p1", 100) # 放到管道里面 p.incr("p2", 100) # 放到管道里面 # raise Exception p.execute() # 只有执行这一条代码才会执行 那两条代码 conn.close()
django中使用redis
方式一:自定义包方案(通用的, 不针对框架,所有框架都可以使用)
第一步 : 写一个pool.py
产生一个连接池
第二步,以后在别的地方,直接导入使用即可
在utils文件里面创建一个py文件 里面写
import redis
POOL = redis.ConnectionPool(max_connections=10)
在视图函数中写
from redis import Redis
from utils import POOL
from django.http import JsonResponse
def redis_test(reqeust):
conn = Redis(connection_pool=POOL)
conn.incr("p2", 100)
res = conn.get("p2")
return JsonResponse({"name": str(res)})
方式二
-
方案一:
django的缓存使用redis-settings.py中配置
需要先下载
pip install django-redissettings.py
CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "CONNECTION_POOL_KWARGS": {"max_connections": 100} # "PASSWORD": "123", } } }view层
from django.core.cache import cache def redis_test(reqeust): cache.set("p3", 100) res = cache.get("p3") return JsonResponse({"name": res})
-在使用redis的地方:cache.set('count', res+1) -pickle序列化后,存入的 -
方案二:使用第三方 模块
django-redis模块from django_redis import get_redis_connection def redis_test(reqeust): conn = get_redis_connection() res = conn.incr("age") return JsonResponse({"name": str(res)})
celery介绍和按照
Celery是什么
翻译过来是,芹菜 的意思,跟芹菜没有关系
框架:服务,python的框架,跟django无关
能用来做什么:
- 异步任务
- 定时任务
- 延时任务
理解celery的运行原理
"""
1. 可以不依赖任何服务器,通过自身命令,启动服务
2.celery服务为其他项目服务提供异步解决任务需求的
注: 会有两个服务同时运行,一个是项目服务,一个是celery服务,项目服务将需要异步处理的任务交给celery服务,celery就会在需要时异步完成项目的需求
人是一个独立运行的服务 | 医院也是一个独立运行的服务
正常情况下,人可以完成所有健康情况的动作,不需要医院的参与;但当人生病时,就会被医院接收,解决人生病问题
人生病的处理方案交给医院来解决,所有人不生病时,医院独立运行,人一旦生病,一眼就来解决人生病需要治病的需求
"""
celery架构(Broker, backend 都使用redis)
-
任务中间件
Broker(中间件),其他服务提交的异步任务,放在里面排队需要借助于第三方
redis rabbitmq -
任务执行单元
worker真正执行异步任务的进程celery提供
-
结束存储
backend结果存储, 函数的返回结果,函数的返回结果存到 backend中需要借助于第三方:redis,msyql
使用场景
- 异步执行:解决耗时任务
- 延时执行:解决延时任务
- 定时执行:解决周期(周期)任务
celery 不支持win,通过evnetlet支持在win上运行
celery快速使用
安装 --》安装完成,会有一个可执行文件 celery
pip install celery
win:pip install eventlet
快速使用
第一步:新建 main.py
from celery import Celery
# 提交的异步任务,放在里面
broker = 'redis://127.0.0.1:6379/1'
# 执行完的结果,放在这里
backend = 'redis://127.0.0.1:6379/2'
app = Celery('test', broker=broker, backend=backend)
@app.task
def add(a, b):
import time
time.sleep(3)
print('------',a + b)
return a + b
第二步提交任务
在外部创建一个s1 提交任务
# 提交任务
from main import add
# 同步调用
# res = add(5, 6)
# print(res)
# 异步调用
res = add.delay(5,6) #原来add的参数,直接放在delay中传入即可
print(res) # 704b5a2a-cf16-4870-a4d4-a84dca099b8e
第三步:启动worker
win:
-4.x之前版本
celery worker -A main -l info -P eventlet
-4.x之后
celery -A main worker -l info -P eventlet
mac:
celery -A main worker -l info

第四步:worker会执行消息中间件中的任务,把结果存起来
第五步:咱们要看执行结果,拿到执行的结果
# 查看结果
from main import app
from celery.result import AsyncResult
id = '704b5a2a-cf16-4870-a4d4-a84dca099b8e'
if __name__ == '__main__':
a = AsyncResult(id=id, app=app)
if a.successful(): # 执行完了
result = a.get() #
print(result)
elif a.failed():
print('任务失败')
elif a.status == 'PENDING':
print('任务等待中被执行')
elif a.status == 'RETRY':
print('任务异常后正在重试')
elif a.status == 'STARTED':
print('任务已经开始被执行')

celery包结构
project
├── celery_task # celery包
│ ├── __init__.py # 包文件
│ ├── celery.py # celery连接和配置相关文件,且名字必须交celery.py
│ └── tasks.py # 所有任务函数
├── add_task.py # 添加任务
└── get_result.py # 获取结果
第一步:新建包celery_task
在包下新建【必须叫celery】的py文件,celery.py写代码
第二步:在包内部,写task,任务异步任务
在task.py文件中
from .celery import app
@app.task
def add(a, b):
print("我执行了, 我是add")
return a + b
在task1.py
from .celery import app
@app.task
def send_sms():
print("发送短信")
return True
第三步:启动worker ,包所在目录下
在这个包的上级目录
celery -A celery_task worker -l info -P eventlet

第四步:其他程序 提交任务,被提交到中间件中,等待worker执行,因为worker启动了,就会被worker执行
在外部创建一个 main1.py

from celery_t1.task import add
from celery_t1.task1 import send_sms
res1 = add.delay(1, 2) # a93bc96a-de51-447e-ac66-a0334595bbe2
res2 = send_sms.delay() # fe28b0af-3d4c-44d5-9899-9938c9e9f89c
print(res1)
print(res2)
提交任务
第五步 worker会自动执行,结果存到backend中
第六步:我们查看结构
from celery_t1.celery import app
from celery.result import AsyncResult
# id = 'fe28b0af-3d4c-44d5-9899-9938c9e9f89c'
id = 'b0b5cfb0-aa5d-42a8-8aba-907894c2e520'
if __name__ == '__main__':
a = AsyncResult(id=id, app=app)
if a.successful():
result = a.get()
print(result)
elif a.failed():
print('任务失败')
elif a.status == 'PENDING':
print('任务等待中被执行')
elif a.status == 'RETRY':
print('任务异常后正在重试')
elif a.status == 'STARTED':
print('任务已经开始被执行')

浙公网安备 33010602011771号