python 整合使用 Redis

一、读写分离 Redis 封装

python 复制代码
# -*- coding: utf-8 -*-

REDIS = {
    'Master': {
        'host': '127.0.0.1',
        'port': 6379,
        'db': 5
    },
    'Slave': {
        'host': '127.0.0.1',
        'port': 6379,
        'db': 6
    }
}

import time
from redis import Redis as _Redis
from redis.client import Pipeline
from pickle import dumps, loads, UnpicklingError


class Redis(_Redis):
    '''
    Redis继承类

    接口与原生 Redis 保持一致,增加自动序列化、反序列化功能
    '''
    def __init__(self, *args, **kwargs):
        _Redis.__init__(self, *args, **kwargs)

    def keys(self, pattern='*'):
        'Returns a list of keys matching ``pattern``'
        return sorted(_Redis.keys(self, pattern))

    def set(self, key, value, timeout=0):
        if timeout > 0:
            return self.setex(key, dumps(value, 1), timeout)
        else:
            return _Redis.set(self, key, dumps(value, 1))

    def setnx(self, key, value, timeout=0):
        res = _Redis.setnx(self, key, dumps(value, 1))
        if res and timeout > 0:
            _Redis.expire(self, key, timeout)
        return res

    def get(self, key, default=None):
        value = _Redis.get(self, key)
        return default if value is None else value

    def mset(self, mapping):
        return _Redis.mset(self, {k: dumps(v, 1) for k, v in mapping.items()})

    def mget(self, keys, default=None):
        values = _Redis.mget(self, keys)
        return [default if v is None else v for v in values]

    def hset(self, name, key, value):
        return _Redis.hset(self, name, key, dumps(value, 1))

    def hget(self, name, key, default=None):
        value = _Redis.hget(self, name, key)
        return default if value is None else value

    def hmset(self, name, mapping):
        return _Redis.hmset(self, name, {k: dumps(v, 1) for k, v in mapping.items()})

    def hmget(self, name, keys, default=None):
        values = _Redis.hmget(self, name, keys)
        return [default if v is None else v for v in values]

    def pop(self, key, default=None):
        '''del specified key and return the corresponding value'''
        pipe = self.pipeline()
        pipe.get(key)
        pipe.delete(key)
        value, res = pipe.execute()
        return default if value is None or res != 1 else value

    def hpop(self, name, key, default=None):
        '''del specified key and return the value of key within the hash name'''
        pipe = self.pipeline()
        pipe.hget(name, key)
        pipe.hdel(name, key)
        value, res = pipe.execute()
        return default if value is None or res != 1 else value

    def hscan_iter(self, name, match=None, count=None):
        cursor = '0'
        found = []
        while cursor != 0:
            cursor, data = self.hscan(name, cursor=cursor,
                                      match=match, count=count)
            for k, v in data.items():
                if k not in found:
                    found.append(k)
                    yield k, v

    def unpickle(self, data):
        try:
            if isinstance(data, bytes):
                return loads(data)
            elif isinstance(data, (list, tuple)):
                return [self.unpickle(v) for v in data]
            elif isinstance(data, dict):
                return {k: self.unpickle(v) for k, v in data.items()}
            else:
                return data
        except (UnpicklingError, TypeError, ValueError, EOFError):
            return data

    def parse_response(self, connection, command_name, **options):
        '''Parses a response from the Redis server'''
        response = _Redis.parse_response(self, connection, command_name, **options)
        return self.unpickle(response)

    def pipeline(self, transaction=True, shard_hint=None, origin=False):
        if origin:
            return _Redis.pipeline(self, transaction, shard_hint)
        else:
            return Pipeline(self.connection_pool, self.response_callbacks,
                            transaction, shard_hint)


class Pipeline(Pipeline, Redis):
    '''覆盖原生Pipeline类'''
    def execute(self, raise_on_error=True):
        result = super(Pipeline, self).execute(raise_on_error)
        return [self.unpickle(r) for r in result]


class MSRedis(object):
    '''读写分离客户端 (只针对程序中用到的命令)'''
    def __init__(self, conf):
        self.master = Redis(**conf['Master'])
        self.slave = Redis(**conf['Slave'])
        self.read_commands = [
            'ttl', 'exists', 'expire', 'get', 'keys',
            'hget', 'hgetall', 'hkeys', 'hmget',
            'sismember', 'smembers', 'sdiff', 'sinter', 'sunion'
            'zrevrange', 'zrevrangebyscore', 'zrevrank', 'zscore'
        ]

    def __getattribute__(self, name):
        if name in ['master', 'slave', 'read_commands']:
            return object.__getattribute__(self, name)
        elif name in self.read_commands:
            return self.slave.__getattribute__(name)
        else:
            return self.master.__getattribute__(name)


# 创建全局 Redis 连接
rds = MSRedis(REDIS)

二、Redis 集群封装

python 复制代码
# redis配置
REDIS_NODES = [
    {'host': '172.25.102.70', 'port': '7000'},
    {'host': '172.25.102.71', 'port': '7000'},
    {'host': '172.16.179.72', 'port': '7000'},
    {'host': '172.16.179.75', 'port': '7000'},
    {'host': '172.16.179.76', 'port': '7000'},
]

class RedisApi(object):
    @classmethod
    def redis_conn(cls, startup_nodes=REDIS_NODES, max_connections=1000):
        redisObject = None
        try:
            redisObject = RedisCluster(startup_nodes=startup_nodes, max_connections=max_connections)
        except Exception as e:
            logger.error("redis conn error : %s" % e)
        return redisObject

    def read_string(self, key):
        """ 读取redis key值"""
        conn = self.redis_conn()
        val = conn.get(key)
        if not val:
            return None
        val = val.decode("utf8")
        return val

    def set_string(self, key, val, ex=REDIS_EX):
        """ 设置redis key的value值"""
        conn = self.redis_conn()
        res = conn.set(key, val, ex=ex)
        return res

三、python 实现布隆过滤器

python 复制代码
# pip install redis
# pip install bloom-filter
import redis
from bloom_filter import BloomFilter


class BloomCache:

    def __init__(self, host='localhots', port=6379, filter_capacity=1000000, error_rate=0.001):
        self.redis_conn = redis.Redis(host=host, port=port)
        self.bloom_filter = BloomFilter(max_elements=filter_capacity, error_rate=error_rate)

    def add_to_bloom(self, key):
        """
        将 key 添加到布隆布过滤器
        :param key:
        :return:
        """
        self.bloom_filter.add(key)

    def exists_in_bloom(self, key):
        """
        检查 key 是否可能存在于 布隆过滤器中
        :param key:
        :return:
        """
        return key in self.bloom_filter

    def get_from_cache(self, key):
        if not self.exists_in_bloom(key):
            # 如果布隆过滤器认为 key 不存在,则直接返回 None
            return None
        value = self.redis_conn.get(key)
        if value is not None:
            return value.decode('utf-8')
        # 如果 Redis 中也没有找到,则需要从数据库中获取
        # get_data_from_db(key):模拟从数据库中获取数据
        value = self.get_data_from_db(key)
        if value is not None:
            self.redis_conn.set(key, value)
            self.add_to_bloom(key)
        return value

    @classmethod
    def get_data_from_db(cls, key):
        """
        模拟从数据库获取数据
        :return:
        """
        print("get_db")
        if key == "example_key":
            return 'example_value'
        return None


if __name__ == '__main__':
    cache = BloomCache()
    print(cache.get_from_cache("example_key"))
    print(cache.get_from_cache("example_key"))
    print(cache.get_from_cache("nonexistent_key"))

四、python 使用 Redis 常用的方法

4.1 String 操作

set

(name, value, ex=None, px=None, nx=False, xx=False)

设置键的值
参数:

​ ex: 秒

​ px: 毫秒

​ nx: 如果设置为 True,则只有 name 不存在时,当前 set 操作才执行

​ xx: 如果设置为 True,则只有 name 存在时,当前 set 操作才执行

python 复制代码
result = redis_conn.set("lock", "888", 5)
print(result)

get

获取键的值

python 复制代码
value = redis_conn.get("lock")

setnx

设置值,只有 name 不存在时,执行才会操作

python 复制代码
result = redis_conn.setnx("lock_1", "666")
print(result)

incr

将存储在键中数字值增加 1

python 复制代码
result = redis_conn.incr("counter")
print(result)

decr

将存储在键中的数字值减 1

python 复制代码
result = redis_conn.decr("counter")
print(result)

4.2 Hash (哈希表)

hset

将哈希表中 key 的字段 field 的值设为 value

hget:获取存储在哈希表中指定字段的值

python 复制代码
redis_conn = redis.Redis(host='localhost', port=6379)
res = redis_conn.hset("student", "name", "jack")
value = redis_conn.hget("student", "name")

hmset

同时将多个 field-value (域-值) 对 设置到哈希表 key 中

hmget

获取所有给定字段的值

python 复制代码
redis_conn = redis.Redis(host='localhost', port=6379)
data = {
  "name": "Jack",
  "age": 25,
  "city": "New York"
}
redis_conn.hset("user:2", mapping=data)
# redis_conn.hmset("user:2", data)  新版本 hmset 废弃,可以使用 hset 函数
values = redis_conn.hmget("user:2", ['name', 'age', 'city'])
# 输出字段的值
for field, value in zip(data.keys(), values):
  print(f"{field}: {value.decode('utf-8')}")

hgetall

获取在哈希表中指定 key 的所有字段 和值

python 复制代码
redis_conn = redis.Redis(host='localhost', port=6379)
value = redis_conn.hgetall("user:1")
print(value)
"""
{b'name': b'Jack', b'age': b'25', b'city': b'New York'}
"""

hlen

获取哈希表中字段的数量

复制代码
redis_conn = redis.Redis(host='localhost', port=6379)
value = redis_conn.hlen("user:1")
print(value)

hkeys

获取 哈希表中 所有字段

python 复制代码
redis_conn = redis.Redis(host='localhost', port=6379)
value = redis_conn.hkeys("user:1")
print(value)

hvals

获取 哈希表中 所有的值

python 复制代码
redis_conn = redis.Redis(host='localhost', port=6379)
value = redis_conn.hvals("user:1")
print(value)

hexists

查看 哈希表中 指定的字段是否存在

python 复制代码
redis_conn = redis.Redis(host='localhost', port=6379)
value = redis_conn.hexists("user:1", "name")
print(value)

hdel

删除一个或多个哈希表字段

python 复制代码
redis_conn = redis.Redis(host='localhost', port=6379)
value = redis_conn.hdel("user:1", "name")
print(value)

4.3 List 操作

lindex

通过索引获取列表中的元素

python 复制代码
redis_conn = redis.Redis(host='localhost', port=6379)
value = redis_conn.lindex("student", 0)
print(value)

rpush

在列表末尾中添加元素

python 复制代码
redis_conn = redis.Redis(host='localhost', port=6379)
value = redis_conn.rpush("student", 'marry')
print(value)

lpush

在列表头部插入元素

python 复制代码
redis_conn = redis.Redis(host='localhost', port=6379)
value = redis_conn.lpush("student", 'tony')
print(value)

lpushx

将一个或多个值插入到已存在的列表头部

python 复制代码

linsert

在列表的元素前或者后插入元素

python 复制代码

lset

通过索引设置列表元素的值

python 复制代码

lpushx

将一个或多个值插入到列表头部

lrange

获取列表中指定范围的元素

python 复制代码
redis_conn = redis.Redis(host='localhost', port=6379)
value = redis_conn.lrange("student", 0, -1)
print(value)

lpop

移出并获取列表的第一个元素

python 复制代码

rpop

移除并获取列表最后一个元素

python 复制代码

bpoplpush

移除列表的最后一个元素,并将该元素添加到另一个列表返回

python 复制代码

blpop

移除并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止

python 复制代码

brpop

移除并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止

python 复制代码

brpoplpush

从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它;如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止

python 复制代码

lrem

移除列表元素

llen

获取列表长度

ltrim

对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除

4.4 Set 操作

Set 集合 就是不允许重复的列

sunion

返回所有给定集合的并集

scard

获取集合的成员数

srandmember

返回集合中的一个或多个随机数

smembers

返回集合中的所有成员

sinter

返回给定所有集合的交集

srem

移出集合中一个或多个成员

smove

将 member 元素从 source 集合移动到 destination 集合

sadd

向集合添加一个或多个成员

sismember

判断 member 元素是否是集合 key 的成员

sdiffstore

返回给定所有集合的差集并存储在 destination 中

sdiff

返回给定所有集合的差集

sscan

迭代集合中的元素

sinterstore

返回给定集合的交集并存储在 destination 中

sunionstore

所有给定集合的并集存储在 destination 中

spop

移除并返回集合中的一个随机元素

4.5 Sort Set 操作

有序集合,在集合的基础上,为每元素排序;元素的排序需要根据另外一个值进行比较,所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序

zadd

向有序集合添加一个或多个成员,或者更新已存在成员的分数

python 复制代码
result = r.zadd(name, mapping, nx=False, xx=False, ch=False, incr=False)
  • name: 是有序集合的键。
  • mapping: 是一个字典,键是成员,值是分数。
  • nxxx 是可选参数,nx=True 表示只添加不存在的成员,xx=True 表示只更新已存在的成员。
  • ch 如果设置为 True,则返回被改变的元素数量。
  • incr 如果设置为 True 并且成员已经存在,则对分数进行增量操作。
python 复制代码
redis_conn = redis.Redis(host='localhost', port=6379)
value = redis_conn.zadd("score", {"member1": 15, "member2": 20, "member3": 30, "member4": 40})
# value = redis_conn.zrevrange()
print(value)

zrevrange

在有序集合中计算指定字典区间内成员排名,有序集合成员按分数值递减(从大到小)排序

python 复制代码
def zrevrange(name: KeyT,start: int,end: int,withscores: bool = False)
redis_conn = redis.Redis(host='localhost', port=6379)
value = redis_conn.zrevrange("score", 0, -1, withscores=True)
print(value)

zrevrank

返回有序集合中指定成员的排名,有序集合成员按分数值递减(从大到小排序)

python 复制代码
redis_conn = redis.Redis(host='localhost', port=6379)
value = redis_conn.zrevrank("score", "member4")
print(value)

zunionstore

计算给定的一个或多个有序集的并集,并存储在新的 key 中

zremrangebyrank

移除有序集合中给定的排名区间的所有成员

zcard

获取有序集合的成员数

zrem

移除有序集合中的一个或多个成员

zrank

返回有序集合中指定成员的索引

zinterstore

计算给定的一个或多个有序集合的交集并将结果集存储在新的有序集合 key 中

zincrby

有序集合中对指定成员的分数加上增量 increment

zrangebyscore

通过分数返回有序集合指定区间内的成员

zrangebylex

通过字典区间返回有序集合的成员

zscore

返回有序集合中,成员的分数值

zremrangebyscore

移除有序集合中给定的分数区间的所有成员

zscan

迭代有序集合中的元素(包括元素成员和元素分值)

zrevrange

返回有序集合中指定区间内的成员,通过索引,分数从高到低

zrange

通过索引区间返回有序集合成指定区间内的成员

zcount

计算在有序集合中指定区间分数的成员数

4.6 管道

redis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次pipline 是原子性操作

python 复制代码
import redis
redis_conn = redis.Redis(host='localhost', port=6379)
pipe = redis_conn.pipeline(transaction=True)
pipe.set('name', 'alex')
pipe.set('role', 'sb')
pipe.execute()

4.7 发布订阅

订阅者
python 复制代码
import redis
redis_conn = redis.Redis(host='localhost', port=6379)
pub = redis_conn.pubsub()
    pub.subscribe("fm104.5")
    pub.parse_response()
    while 1:
        msg = pub.parse_response()
        print(msg)
发布者
python 复制代码
import redis
 
redis_conn = redis.Redis(host='localhost', port=6379)
redis_conn.publish("fm104.5", "Hi,yuan!")
相关推荐
im_AMBER1 小时前
学习日志19 python
python·学习
mortimer4 小时前
安装NVIDIA Parakeet时,我遇到的两个Pip“小插曲”
python·github
@昵称不存在4 小时前
Flask input 和datalist结合
后端·python·flask
赵英英俊5 小时前
Python day25
python
东林牧之5 小时前
Django+celery异步:拿来即用,可移植性高
后端·python·django
何双新5 小时前
基于Tornado的WebSocket实时聊天系统:从零到一构建与解析
python·websocket·tornado
AntBlack6 小时前
从小不学好 ,影刀 + ddddocr 实现图片验证码认证自动化
后端·python·计算机视觉
凪卄12136 小时前
图像预处理 二
人工智能·python·深度学习·计算机视觉·pycharm
巫婆理发2226 小时前
强化学习(第三课第三周)
python·机器学习·深度神经网络
seasonsyy7 小时前
1.安装anaconda详细步骤(含安装截图)
python·深度学习·环境配置