【Python】2022 python Redis 使用整合

发布于:2022-11-28 ⋅ 阅读:(392) ⋅ 点赞:(0)

本文内容仅整合 python 操作 redis 的一些函数,包括函数参数,解释以及部分示例,可作为一个python redis 教程使用。

简介

引用菜鸟教程的内容

Redis 简介
Redis 是完全开源的,遵守 BSD 协议,是一个高性能的 key-value 数据库。

Redis 与其他 key - value 缓存产品有以下三个特点:
Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
Redis支持数据的备份,即master-slave模式的数据备份。

Redis 优势
性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

Redis与其他key-value存储有什么不同?
Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问

入门

安装redis包

pip install redis

连接redis

按照其他文章的说法redis提供了Redis和StrictRedis
但在现在新版的源码里面定义了StrictRedis = Redis
而且在源码和文档中也没有找到区分开StrictRedis 和 Redis的代码和说明。
所以两者应该已经没有区别了

import redis

host = 'greenkonglong.blog.csdn.net'  # redis服务地址
# host='127.0.0.1'
port = 6379  # redis服务默认端口
db = 0
# Redis 推荐用这种方式
cache = redis.Redis(host=host, port=port, db=db, decode_responses=True)
# from_url
'''
redis://[[username]:[password]]@localhost:6379/0
rediss://[[username]:[password]]@localhost:6379/0
unix://[[username]:[password]]@/path/to/socket.sock?db=0
'''
cache_from_url = redis.from_url(f'redis://{host}:{port}/{db}?decode_responses=true')

redis源码Redis类下的__init__初始化方法如下

def __init__(
            self,
            host="localhost",
            port=6379,
            db=0, # redis有0-15 共计16个数据库 默认是0 可以根据不同类型数据存放到不同的数据库下
            password=None,
            socket_timeout=None, # socket响应超时时间
            socket_connect_timeout=None, # socket连接超时时间
            socket_keepalive=None, 
            socket_keepalive_options=None,
            connection_pool=None, # 连接池
            unix_socket_path=None,
            encoding="utf-8",
            encoding_errors="strict",
            charset=None, # 新版源码注释了该项已弃用改用encoding
            errors=None, # 新版源码注释了该项已弃用改用encoding_errors
            decode_responses=False, # 此项为False时从redis取出的结果默认是字节类型
            retry_on_timeout=False, # 超时是否重试
            retry_on_error=[],
            ssl=False, # ssl证书连接
            ssl_keyfile=None,
            ssl_certfile=None,
            ssl_cert_reqs="required",
            ssl_ca_certs=None,
            ssl_ca_path=None,
            ssl_ca_data=None,
            ssl_check_hostname=False,
            ssl_password=None,
            ssl_validate_ocsp=False,
            ssl_validate_ocsp_stapled=False,
            ssl_ocsp_context=None,
            ssl_ocsp_expected_cert=None,
            max_connections=None, # 最大连接数
            single_connection_client=False,
            health_check_interval=0,
            client_name=None,
            username=None,
            retry=None,
            redis_connect_func=None,
    ):
    	 # 此处省略1万代码...

连接池

pool = redis.ConnectionPool(host=host,port=port,db=0,decode_responses=True)
cache = redis.Redis(connection_pool=pool)

新版源码中初始化默认就定义了连接池,可以不自己定义

        if not connection_pool:
            # 此处省略1万代码...
            connection_pool = ConnectionPool(**kwargs)
        self.connection_pool = connection_pool

存储第一个数据

import redis

cache = redis.Redis(host='127.0.0.1', port=6379, db=11)
# key:my_cache value:hello redis 存活时间:30s
cache.set('my_cache', 'hello redis', ex=30)
if cache.exists('my_cache'):
    print(cache.get('my_cache'))
else:
    print('没有这个键值对')

在这里插入图片描述在这里插入图片描述


String操作

set存操作(单个存、批量存)

函数 参数 说明 示例
set
参数 解释
ex 过期时间(秒)
px 过期时间(毫秒)
nx True时,只有name**不存在**时,set操作才执行,值存在则不会修改
xx True时,只有name**存在**时,set操作才执行,值存在才修改
在Redis中设置值,默认,不存在则创建,存在则修改 cache.set('my_cache', 'hello redis', ex=30)
setnx name
value
等同于set(name,value,nx=True) -
setex name
value
time 过期时间(数字秒 或 timedelta对象)
等同于set(name,value,ex=expiration_time) -
psetex name
value
time_ms 过期时间(数字毫秒 或 timedelta对象)
等同于set(name,value,px=expiration_time) -
mset mapping:dict 1、批量设置值;
2、只能批量设置键值对,不能设置过期时间
cache.mset({'k1': 'v1', 'k1': 'v1'})
msetnx mapping:dict 1、批量设置值;
2、只能批量设置键值对,不能设置过期时间
3、同setnx,即使只有一个键已经存在,也不会执行任何操作
cache.msetnx({'k1': 'v1', 'k1': 'v1'})

get取操作(单个取、批量取、获取原来的设置新的)

函数 参数 说明 示例
get name:str 1、根据key值获取value值
2、如果没有设置decode_responses=True 返回的结果则是bytes类型字符串
-
mget keys:str
*args
批量获取 1、cache.mget(['k3', 'k4'])
2、cache.mget('k3', 'k4')
getset name:str
value:str
设置新值,并获取原来的值 -

其他(获取key长度、自增、自减、追加)

函数 参数 说明 示例
strlen name:str 返回name对应值的字节长度(一个汉字3个字节) -
incr(incr = incrby) name:str
amount:int=1 自增数
自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。 1、cache.mget(['k3', 'k4'])
2、cache.mget('k3', 'k4')
incrbyfloat name:str
amount:float=1.0 自增数
自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。 -
decr name:str
amount:int=1 自减数
自减数 -
append key:str
value:str
在redis key对应的值后面追加内容 -

Hash(字典)操作

set存操作(单个存、批量存)

函数 参数 说明 示例
hset name:str
key:str
mapping:dict=None
name对应的hash map中设置一个键值对(不存在,则创建;否则,修改) 1、cache.hset(name='hash_key', key='k1', value='v1')
下面这种写法相当于是hmset(name,dict) 但是新版中hmset已经弃用建议使用下面这种写法了
2、cache.hset(name='hash_key', mapping={'k2': 'v2', 'k3': 'v3'})
mapping:dict
name:str
key:str=None
value:str=None
hsetnx name: str
key: str
value: str
当name对应的hash中不存在当前key时则创建(相当于添加)
缺点:只能单个创建
-
hmset(已弃用请看hset) - DeprecationWarning: Redis.hmset() is deprecated. Use Redis.hset() instead. -

get取操作(单个取、批量取、取所有键值对、取所有键、取所有值)

函数 参数 说明 示例
hget name:str
key:str
在name对应的hash中根据key获取value -
hmget name:str
keys:list
*args
在name对应的hash中获取多个key的值 cache.hmget(name='hash_key', keys=['k2', 'k3'])
-> ['v2', 'v3']
hgetall name:str
获取name对应hash的所有键值 cache.hgetall(name='hash_key')
-> {'k2': 'v2', 'k3': 'v3'}
hkeys name:str 获取name对应的hash中所有的key的值 cache.hkeys(name='hash_key')
-> ['k2', 'k3']
hvals name:str 获取name对应的hash中所有的value的值 cache.hvals(name='hash_key')
-> ['v2', 'v3']

其他(获取长度、检查是否存在、删除键、自增)

函数 参数 说明 示例
hlen name:str 获取name对应的hash中键值对的个数 -
hexists name:str
key:str
检查name对应的hash是否存在当前传入的key -
hdel name: str
*keys: List
将name对应的hash中指定key的键值对删除 cache.hdel('hash_key',*('k1','k2'))
hincrby name:str
key:str
amount:int=1 自增数
自增name对应的hash中的指定key的值,不存在则创建key=amount -
hincrbyfloat name:str
key:str
amount:float=1 自增数
自增name对应的hash中的指定key的值,不存在则创建key=amount -

List操作

set存操作(单个存/追加、中间追加)

函数 参数 说明 示例
lpush/rpush name: str
*values: List
在name对应的list中添加元素,每个新的元素都添加到列表的最左边(rpush为从右向左) cache.lpush('hash_key',*['k1','k2','k3'])
保存顺序为k3,k2,k1(rpush->k1,k2,k3)
lpushx/rpushx name: str
*values: List
在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边(rpushx为从右向左) -
linsert
参数 解释
name: str reids 的内对应数据的name
where: str BEFORE或AFTER(小写也可以) 对应前or 后
refvalue: str 标杆值,即:在它前后插入数据(如果存在多个标杆值,以找到的第一个为准)
value: str 要插入的数据
在name对应的列表的某一个值前或后插入一个新值 cache.linsert(name='hash_key',where='before',refvalue='k3',value='4')
表示在hash_key下'k3'的前面插入一个'k4'
lset name:str
value:str
index:int
对name对应的list中的某一个索引位置重新赋值 -

get取操作(获取并删除、索引取值、切片取值、获取并添加、获取并删除添加)

函数 参数 说明 示例
lpop/rpop name: str 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素(rpop为从右删除) -
lindex name: str
index:int
在name对应的列表中根据索引获取列表元素 -
lrange name: str
start:int
end:int
在name对应的列表切片获取数据 -
rpop/lpush src: str
dst:str
从一个列表获取最右边的元素,同时将其添加至另一个列表的最左边 -
brpop/lpush src: str
dst:str
timeout:int=0
当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞
从一个列表取出并移除最右边的元素,同时将其添加至另一个列表的最左边 -

其他(获取长度、删除)

函数 参数 说明 示例
llen name:str name对应的list元素的个数 -
lrem name:str
value:str
num:int 删除几次
在name对应的list中删除指定的值 -
ltrim name:str
start:int
end:int
索引结束位置(大于列表长度,则代表不移除任何)
在name对应的列表中移除没有在start-end索引之间的值 -

Set集合操作

set存操作(单集合添加)

函数 参数 说明 示例
sadd name:str
*values
name对应的集合中添加元素 cache.sadd('key',*['v1','v2','v3','v3'])
重复的值只会保留一个
有序集合...待续

get读操作(获取多集合元素并集/交集、pop类取值)

函数 参数 说明 示例
sdiff keys: list
*args
在第一个name对应的集合中且不在其他name对应的集合的元素集合 cache.sdiff(['set1','set2','set3']) -> set
sdiffstore dest: str
keys: list
*args
获取第一个name对应的集合中且不在其他name对应的集合,再将其新加入到dest对应的集合中 -
sinter keys: list
*args
获取多个name对应集合的交集 key1 = {a,b,c,d}
key2 = {c}
key3 = {a,c,e} -> {c}
sinterstore dest:str
keys:list
*args
获取多个name对应集合的交集,并将其加入到dest对应的集合汇总 -
smembers name:str 获取name对应的集合的所有成员 -
spop name:str 从集合的(尾部)获取并移除一个元素 -
srandmember name:str
numbers:int
从name对应的集合中随机获取 numbers 个元素 -
sunion keys: list
*args
获取多个name对应集合的并集 -
sunionstore dest:str
keys:list
*args
获取多个name对应集合的并集,并将其加入到dest对应的集合汇总 -
有序集合...待续

其他(获取元素个数、删除、批量删除、检查是否在某集合、集合移动到集合)

函数 参数 说明 示例
scard name:str 获取name对应的集合中元素个数 -
ltrim name:str
start:int
end:int
索引结束位置(大于列表长度,则代表不移除任何)
在name对应的列表中移除没有在start-end索引之间的值 -
sismember name:str
value:str
检查value是否是name对应的集合的成员 -
smove src:str
dst:str
value:str
将某个成员从一个集合中移动到另外一个集合 -
srem name: str
*values
在name对应的集合中删除某些值 -

其他操作(删除、判断存在、模糊匹配、设置过期时间、重命名、数据改库位置、随机取、类型判断)

函数 参数 说明 示例
delete *names 删除任意类型符合name的数据 -
exists name 检测redis的name是否存在 -
keys pattern:str='*' 根据模型获取redis的name
1、* 匹配数据库中所有 key
2、h?llo 匹配 hello , hallo 和 hxllo 等
3、h*llo 匹配 hllo 和 heeeeello 等
4、h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo
5、....
-
expire name: str
time: int | timedelta
nx: bool = False
xx: bool = False
gt: bool = False
lt: bool = False
为某个redis的某个name设置超时时间 -
rename src
dst
对redis的name重命名为 -
move name:str
db:int
将redis的某个值移动到指定的db下 -
randomkey - 随机获取一个redis的name -
type name:str 获取name对应值的类型 -

管道(仅介绍)

日常使用中一般不会使用到管道,并且管道较为复杂,不在此篇内容涉及过多

这里引用pipeline详解到说法:

redis客户端执行一条命令分4个过程:发送命令-〉命令排队-〉命令执行-〉返回结果
这个过程称为Round trip time(简称RTT, 往返时间),mget mset有效节约了RTT,但大部分命令(如hgetall,并没有mhgetall)不支持批量操作,需要消耗N次RTT ,这个时候需要pipeline来解决这个问题
原文:pipeline详解

菜鸟教程:

Redis 管道技术
Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。这意味着通常情况下一个请求会遵循以下步骤:
客户端向服务端发送一个查询请求,并监听Socket返回,通常是以阻塞模式,等待服务端响应。
服务端处理命令,并将结果返回给客户端。
Redis 管道技术
Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。
原文:redis-pipelining

场景案例

会失效的token

需求:
需要从某平台的API接口中请求获取token,然后利用token去请求其他接口;已知每次请求token都会使前一次token失效,为了防止接口请求冲突,现在要将token保存进redis。
1、每次请求下来的token有效期可以从接口返回结果中获得。
2、接口返回结果是json格式

# 字典版本
import requests
from redis import Redis

cache = Redis(host='127.0.0.1', port=6379, db=1, decode_responses=True)


def request_token() -> requests.Session:
    """请求token

    :return: requests.Session
    """
    pass


def get_token_for_redis() -> dict:
    """从redis获取token

    :return: dict
    """
    if not cache.exists('api_token1'):
        token_response = request_token().json()  # 请求token
        cache.hset(name='api_token1', mapping=token_response)  # 保存dict进redis
        expiration_time = token_response.get('expiration_time')  # 获取过期时间
        cache.expire('api_token1', expiration_time)  # 设置api_token1过期时间
    else:
        token_response = cache.hgetall('api_token1')  # 从redis获取token
    return token_response
# 字符串版本
import requests
from redis import Redis

cache = Redis(host='127.0.0.1', port=6379, db=1, decode_responses=True)


def request_token() -> requests.Session:
    """请求token

    :return: requests.Session
    """
    pass


def get_token_for_redis() -> str:
    """从redis获取token

    :return: str
    """
    if not cache.exists('api_token1'):
        token_response = request_token().json()  # 请求token
        token = token_response.get('token')  # 获取token
        expiration_time = token_response.get('expiration_time')  # 获取过期时间
        cache.set(name='api_token1', value=token, ex=expiration_time)  # 保存dict进redis
    else:
        token_response = cache.get('api_token1')  # 从redis获取token
    return token_response

网站限流

需求:
现在公司网站为了防止别人暴力攻击,需要进行访问频率的限制;限制的逻辑是ip+cookie访问次数>=200次则限制访问,被限制后需要24h*1后接触解除。

from redis import Redis

cache = Redis(host='127.0.0.1', port=6379, db=1, decode_responses=True)

user_ip, user_cookie = ('xxx.xxx.xx.xxx', 'user_id=123456')


def get_user_ip_and_cookie(user_ip, user_cookie) -> str:
    """获取ip+cookie

    :return: str
    """
    pass


def auto_increment() -> bool:
    """访问自增

    :return: bool # True-可以访问 False-被限制
    """
    user_key = get_user_ip_and_cookie(user_ip, user_cookie)  # 用户标识
    if not cache.exists(user_key):
        cache.incr(user_key, amount=1)  # 自增数/新建自增
        return True
    else:
        count=int(cache.get('user_key'))
        if count == 200:
            cache.expire(user_key, 1 * 24 * 86400)  # 访问次数大于200,24h内限制访问
            return False
        elif count >= 200:
            return False
        else:
            cache.incr(user_key, amount=1)  # 访问次数+1
            return True

本文含有隐藏内容,请 开通VIP 后查看