• Python操作Redis从入门到精通附代码(全)


    前言

    对于Redis的相应原理以及知识点对应的java版本,可看我之前的文章:(其知识点都是互通的)
    Redis框架从入门到学精(全)

    对于以上的前置知识如果了解了更好,如果不了解,就当是一种数据库,上手也很快。
    本身Redis就是一种可持久的Key-Value的数据库,有丰富的数据结构等

    通过pip install redis进行安装导入redis的模块包即可
    如果在安装以及使用的过程中遇到这些bug
    可看我之前的文章:

    1. Redis (error) NOAUTH Authentication required.解决方法
    2. Redis Could not connect to Redis at 127.0.0.1:6379: Connection refused解决方法
    3. linux中设置redis的密码
    4. Redis出现 Creating Server TCP listening socket *:6379: bind: No error 的解决方法(全)

    1. 连接Redis

    本身就是一种数据库,操作数据库第一就是连接Redis,本身有两个类可以连接,官方提供了Redis(StrictRedis的子类)以及StrictRedis。

    连接也有两种方式:

    第一种方式是正常连接:

    # 导入redis的包
    import redis
     
    # 通过redis连接用户名、端口、密码以及db数据库
    redis_conn = redis.Redis(host='127.0.0.1', port= 6379, password= '?', db= 0,decode_responses=True)
    
    redis_conn = redis.StrictRedis(host='127.0.0.1', port= 6379, password= '?', db= 0,decode_responses=True)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    关于以上的连接参数中:
    decode_responses=True输出的时候变为字符串,默认输出是字节(decode_responses可不用)

    连接方式的第二种是:(使用连接池)
    连接池的好处是通过连接池来管理其所有的连接,避免对建立释放连接等开销

    import redis
     
    redis_pool = redis.ConnectionPool(host='127.0.0.1', port= 6379, password= '?', db= 0)
    
    # 通过调用连接池
    redis_conn = redis.Redis(connection_pool= redis_pool)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2. 基本类型

    在基本类型中介绍其方法的使用
    正常连接之后 大部分都可通过如下查看其共性的东西:
    通过set设置键值对:redis.set('manong',18)
    获取键值:redis.get('manong')
    判断键值是否存在:redis.exists('manong')
    查看键值的类型:redis.type('manong')
    获取当前数据库中键值对数目:redis.dbsize()
    以及其他的函数,可参照官网进行学习

    基本的使用类型以及方法会用即可

    2.1 String

    其实Sting类在上面的展示中已经有使用到

    函数:

    描述函数用法
    设置单个键值对set(name, value, ex=None, px=None, nx=False, xx=False, keepttl=False, get=False, exat=None, pxat=None)
    如果key不存在就添加成功setnx(name, value)
    设置key的过期时间setex(name, time, value)以秒为单位,psetex(name, time_ms, value)以毫秒为单位
    获取单个键值对get(name)
    设置多个键值对mset(*args, **kwargs)
    获取多个键值对mget(keys, *args)
    值不存在设新键值对getset(name, value)
    根据索引修改值setrange(name, offset, value)
    根据索引获取值getrange(key, start, end)
    根据建值追加值append(key, value)
    获取值长度strlen(name)

    String还有一个特点是:可以设置自增类型(不同类型设置不同类型的自增,注意区分)

    • incr(name, amount=1)
    • incrbyfloat(name, amount=1.0)

    对于上面各个函数的用法如下:
    单个键值对的用法

    r.set("name", "码农研究僧")
    r.get("name")  
    r.append("name", "大帅哥")  # key后追加,不存在则不成功
    """
    对于set的参数比较多,不同参数不同的延迟
    """
    r.setex("name", 10086, "码农研究僧")  # 10086秒后过期 
    # 类似
    r.set("name", "码农研究僧", ex = 10086)
    
    r.psetex("name", 10086, "码农研究僧")  # 10086毫秒后过期
    # 类似
    r.set("name","码农研究僧",px = 10086) 
    
    r.setnx("name", "码农研究僧")  # key不存在设置成功,也就是 新建 键值
    # 类似
    r.set("name", "码农研究僧",nx=True)  
    
    r.setxx("name", "码农研究僧")  # key存在设置成功,也就是 修改 键值
    # 类似
    r.set("name", "码农研究僧",xx=True)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    多个键值对的用法:

    r.mset({'k1': 'v1', 'k2': 'v2'}) # 设置多个值,传入字典
    r.mget("k1", "k2")  # 返回一个列表,一次取出多个值
    r.getset("name", "关注我")  # 设置新值,并返回之前的值
    
    # 类似
    dict = {
        'k1' : 'v1',
        'k2' : 'v2'
    }
    r.mset(dict)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    范围以及自增的用法:

    r.setrange("name", 2, "开发")  # 从第2个位置开始替换(0开始数),原来是码农研究僧,替换后是码农开发
    r.getrange("name", 2, 3)  # 取中间子串,为开发
    r.strlen("name")  # 长度,不存在为0
    r.incr("name", amount=3)  # 以3为自增,不存在则以3为初值,不指定amount则默认为1
    r.incrbyfloat("name", amount=0.3) # 以0.3自增, 同理
    r.decr("name", amount=3)  # 递减同理
    
    r.delete("name1")  # 删除
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.2 List

    描述函数用法
    lpush(name,values)对应的name中往左添加list元素
    rpush(name,values)对应的name中往右添加list元素
    lpushx(name, value)往已有name的列表添加左元素,没有无法创建
    rpushx(name, value)往已有name的列表添加左元素,没有无法创建
    linsert(name, where, refvalue, value))通过refvalue的where(可以before或者after)插入value
    r.lset(name, index, value)修改对应list列表中的某个索引值
    r.lrem(name, num, value)删除list列表指定值
    lpop(name)删除列表左边的第一个元素,返回删除的元素
    rpop(name)删除列表右边的第一个元素,返回删除的元素
    ltrim(name, start, end)删除索引之外的值
    lindex(name, index)根据索引获取列表值
    rpoplpush(list1,anotherlist1)list1的列表中最右边的值插入anotherlist1列表的最左边
    brpoplpush(list1,anotherlist1, timeout=0)list1的列表中最左边的值插入anotherlist1列表的最右边
    blpop(keys, timeout)一次移除多个列表

    插入元素具体的使用用法:

    r.lpush("list1", 1,2,3)  # 从左插入,通过r.lrange('list1',0,2)得到321。list1不存在会创建
    r.rpush("list2", 1,2,3)  # 从右插入,通过r.lrange('list2',0,2)得到123
    
    r.lpushx("list1", 4,5)  # 从左插入,通过r.lrange('list1',0,5)得到45321
    r.rpushx("list1", 4,5)   # 从右插入,通过r.lrange('list1',0,6)得到453216
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其他的增删改查的一些指标:(查看最后的值,通过r.lrange('list4',0,-1) ).
    python的输出格式有多种:可通过如下format格式print("list4:{}".format(r.lrange('list4',0,-1)))

    r.lrange("list1",0,-1) # 默认是输出全部值,返回所有列表
    r.llen("list1")  # 返回元素个数
    r.lindex("list1", 1)  # 通过下标1的取值
    
    r.linsert("list1", "before", 5,3)  # 5之前插入3,本身值为453216,最后为4353216
    r.lset("list1", 2, 6)  # 修改第2个元素为6,最后值为456216
    
    
    r.rpush("list4", 2,2,1,2,3,4,5,6,1,1)  # 从右插入,通过r.lrange('list4',0,-1)得到1212345611
    r.lrem("list4", 1, 2)  # 删除左边的第一个2
    r.lrem("list4", -1, 1) # 删除右边的第一个1,负数为从后往前
    r.lrem("list4", 0, 1)  # 删除所有的1
    
    r.lpop("list4")  # 删除左边第一个值并返回删除值
    r.rpop("list4")  # 删除右边第一个值并返回删除值
    
    r.ltrim("list4", 2, 3)  # 除了下标2到3的元素,其他都被删除
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2.3 Hash

    描述函数用法
    hset(name, key, value)设置键值对。有即修改无即创建
    hget(name, key)取出单个key的值
    hmget(name, key,…)取出多个key的值
    hgetall(name)取出对应的所有键值对
    hkeys(name)取出所有的key
    hvals(name)取出所有的value
    hlen(name)取出键值对个数
    hexists(name, key)查看是否存在这个key

    自增以及分片的函数
    主要为了防止多次取值,撑爆内存
    具体的思想通过

    1. 通过cursor的标志位置获取数据
    2. match参数过滤,如果指定None则为所有key,如果指定key则过滤key出来
    3. count每次最少取key的个数,为None则为默认分片的个数

    在应用场景中比较常用,所以单独放在一个表格中:

    描述函数用法
    hincrby(name, key, amount=?)对key自增正或者负
    hincrbyfloat(name, key, amount=?.0)对key自增正浮点数或者负浮点数
    hscan(name, cursor=0, match=None, count=None)指定游标获取数据
    hscan_iter(name, match=None, count=None)同上,只不过将其封装在迭代器

    具体操作如下:

    r.hset("names", "manong", "yanjiuseng")  # 设置键值对
    r.hmset("names", {"manong": "yanjiuseng", ....})  # 设置多个键值对,redis4.0版本前
    r.hset("names", mapping={"manong": "yanjiuseng", ...})  # 设置多个键值对,redis4.0版本后
    
    r.hget("names", "manong")  # 取出key中对应的value值,返回其value值
    r.hmget("names", ["manong", "age"])  # 取出多个key中对应的value值,返回其value值
    
    r.hgetall("names")  # 取出多有的键值对(字典)
    r.hkeys("names")  # 取出所有key(列表)
    r.hvals("names")  # 取出所有value(列表)
    hlen("names") # 获取键值对个数
    r.hexists("names", "ma")  # 查看是否存在这个key
    
    r.hincrby("names", "age", amount=1)  
    # 自增1,age本身这个就没设置,则以amount为初始,也可为负数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    分片读取的具体操作:

    cursor1, data1 = r.hscan('names', cursor=0, match=None, count=None)
    cursor2, data2 = r.hscan('names', cursor=cursor1, match=None, count=None)
    r.hscan_iter("names", match=None, count=None)  # 取出所有key,注意这里要用遍历,将其值进行遍历出来
    
    # 具体如下
    for item in r.hscan_iter('xx'):
    print item
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.4 Set

    set元素本身是无序不可重复

    描述函数用法
    sadd(name,values)集合中添加一个或多个元素
    sismember(name, value)判断集合name中是否有对应value元素,返回false或者true
    smove(src, dst, value)将src集合中的value元素移动到dst集合中
    sdiff(keys, *args)返回只存在第一个集合names中,之后的names集合都排除掉(差集)
    sdiffstore(dest, keys, *args)只存在第二个集合的names,之后的names集合都排除掉,放在第一个dest集合中
    sinter(keys, *args)多个集合names取交集
    sinterstore(dest, keys, *args)多个names取交集,存放在第一个dest集合中
    sunion(keys, *args)多个集合names取并集
    sunionstore(dest,keys, *args)多个集合names取并集,存放在第一个dest集合中
    spop(name)随机删除元素,并且返回输出该元素
    srem(name, values)删除集合中指定的value值,并返回已经删除的元素个数

    新增元素以及查询等操作:

    r.sadd("names", "ma", "nong", "yan", "jiu", "seng")  # 返回集合长度个数5
    r.sismember("names", "ma")  # 查询 ma 元素是否存在,返回false或者true操作
    r.scard("names")  # 获取集合元素个数
    
    # 补充测试输出的时候,可以通过如下:
    print('集合元素个数:{}'.format(r.scard("names")))
    
    r.smembers("names")  # 返回所有集合元素
    r.sscan('names') #返回集合的所有元素(以元祖形式展示)
    r.scan_iter("names")  # 返回集合所有元素(需要通过遍历的形式输出)
    # 例如
    for i in r.sscan_iter("names"):
        print(i)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    差集的操作:

    r.sdiff("names1", "names2")  # 返回存在names1不存在names2的集合元素
    r.sdiffstore("names3", "names1", "names2")  # 存在names1不存在names2的集合元素,存放在names3中,并且输出这个集合
    
    • 1
    • 2

    交集的操作:

    r.sinter("names1", "names2")  # 返回names1和names2的交集元素
    r.sinterstore("names3", "names", "names2")  # 把names和names2的交集存到names3,返回names3的元素
    
    • 1
    • 2

    并集的操作:

    r.sunion("names1", "names2")  # 返回names1和names2的并集元素
    r.sunionstore("names3", "names1", "names2")  # 把names和names2的并集存到names3,返回names3的元素
    
    • 1
    • 2

    差集的操作:

    r.spop("names")  # 随机删除一个元素,并且将这个元素返回输出
    r.srem("names", "ma")  # 删除指定元素ma,返回已经删除的个数
    
    • 1
    • 2

    2.5 Zset

    这个zset与set的区别在于
    zset为有序集合,会为元素进行排序

    描述函数用法
    zadd(name, {‘k1’:v1,‘k2’:v2})有序集合中添加元素
    zcard(name)获取有序集合元素个数
    r.zrange( name, start, end, desc=False, withscores=False, score_cast_func=float)获取有序集合的元素。desc 为排序规则, 默认是value从小到大排序。withscores是否获取value,默认不获取value只有key。score_cast_func对value进行转换的函数
    zrevrange(name, start, end, withscores=False, score_cast_func=float)同上,只不过集合默认为从大到小排序
    zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float)对应value值获取其对应key以及value元素,默认value从小到大
    zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float)同上,只不过value默认是从大到小
    zscan(name, cursor=0, match=None, count=None, score_cast_func=float)获取所有的value,默认value从小到大
    zcount(name, min, max)统计value在min以及max的元素个数
    zincrby(name, amount, value)对应name的集合中的value分数,每次都自增amount
    zrank(name, value)获取集合name中的value排在第几,导出从小到大的正序的索引号
    zrevrank(name, value)同上,只不过是从大到小的正序索引号
    zscore(name, key)获取集合name中key对应的value值
    zrem(name, key)删除集合name中的单个key值
    zremrangebyrank(name, min, max)删除对应的索引元素(将其value进行从小到大进行排序)
    zremrangebyscore(name, min, max)删除value范围在min以及max之间的(将其value进行从小到大进行排序)

    获取元素的key以及对应value,具体操作:

    r.zadd("names", {"ma": 1, "nong": 2, "yan": 3})  # 新增元素,而且设置对应的key以及value
    r.zcard("names") # 返回有序集合个数长度
    
    r.zrange("names", 0, -1) # 取出集合names中所有元素的key
    r.zrange("names", 0, -1, withscores=True)  # 取出集合names中所有元素 正序 排序,有key以及value
    r.zrevrange("names", 0, -1, withscores=True) # 同上,倒序操作
    r.zrangebyscore("names", 1, 3, withscores=True) # 取出集合中的value范围在1-3之间的,value按照从小到大输出对应的key以及value
    r.zrevrangebyscore("names", 1, 3, withscores=True) # 同上,从大到小的输出
    
    r.zscan("names")  # 取出所有有序集合的元素
    r.zscan_iter("names")  # 获取所有元素,这是一个迭代器
    #具体操作如下:
    for i in r.zscan_iter("names"): # 遍历迭代器
        print(i)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    计数、自增、获取索引以及删除等具体操作:

    r.zcount("names", 1, 3)  # 统计value在1-3之间的元素个数,并且返回
    r.zincrby("names", 2, "ma")  # 将ma的value自增2
    
    r.zscore("names", "ma")  # 返回key对应的value
    
    r.zrank("names", "ma")  # 返回元素ma的下标(从小到大排序)
    r.zrerank("names", "ma")  # 返回元素ma的下标(从大到小排序)
    
    r.zrem("names", "ma")  # 删除单个key
    r.zremrangebyrank("names", 0, 2)  # 删除下标在0到2间的元素
    r.zremrangebyscore("names", 1, 3)  # 删除value在1-3之间的元素
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3. 实战

    结合python web开发
    通过表单获取redis的数据
    通过普通连接或者连接池连接
    连接成功之后获取想要的数据传回到表单即可

    3.1 普通连接

    调用一般的redis函数,或者自已封装一个连接函数

    def get_redis(config):
        sentinels = config.get("sentinels", "")
        cluster_tag = config.get("cluster_tag", "")
        if all([sentinels, cluster_tag]):
            sentinel = Sentinel(sentinels, socket_timeout=5)
            REDIS = sentinel.master_for(cluster_tag, socket_timeout=5)
        else:
            REDIS = StrictRedis(host=config.get("host", "127.0.0.1"),
                                port=config.get("port", 6379))
        return REDIS
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.2 连接池连接

    一般企业都会有很清晰的流水线
    如果划分了dev以及生产环境,不同的redis集群配置放不同配置
    这部分是setting的公共配置

    REDIS_CACHES = {
        'manongyanjiuseng': {
            'type': 'sentinel',
            'cluster_tag': 'manongyanjiuseng_test',
            'host': [
                ("ip", 端口号),
                ("https://blog.csdn.net/weixin_47872288", 端口号),
                ("https://blog.csdn.net/weixin_47872288", 端口号),
            ],
            'socket_timeout': 10,
            'master': 1,
            'db': 3
        },
    	# 同理如上创建
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    公共类的函数都放在common.py中

    import redis
    from redis.sentinel import Sentinel
    from redis import StrictRedis
    
    # 初始化一个线程池
    CONNECTION_POOL = {}
    
    # 定义连接的参数
    def get_redis_connection(server_name='default'):
    
    	# 函数内部可改变函数外部的变量,所以定义global
        global CONNECTION_POOL
    
        if not CONNECTION_POOL:
            CONNECTION_POOL = _setup_redis()
    
        pool = CONNECTION_POOL[server_name]
    
        if settings.REDIS_CACHES[server_name].get("type") == "sentinel":
            return pool
        return redis.Redis(connection_pool=pool)
    
    
    def _setup_redis():
        for name, config in settings.REDIS_CACHES.items():
            if config.get("type") == "sentinel":
                sentinel = Sentinel(config['host'], socket_timeout=config['socket_timeout'])
                if config.get('master'):
                    pool = sentinel.master_for(
                        config["cluster_tag"],
                        redis_class=redis.Redis,
                        socket_timeout=config['socket_timeout']
                    )
                else:
                    pool = sentinel.slave_for(
                        config["cluster_tag"],
                        redis_class=redis.Redis,
                        socket_timeout=config['socket_timeout']
                    )
            else:
                pool = redis.ConnectionPool(
                    host=config['host'],
                    port=config['port'],
                    db=config['db'],
                    socket_timeout=1
                )
            CONNECTION_POOL[name] = pool
        return CONNECTION_POOL
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    在调用连接的时候通过如下:

    # manong这个值是通过form表单传入进去的
    redis = get_redis_connection(manong)
    
    • 1
    • 2
  • 相关阅读:
    深入理解 C 语言的内存管理
    Centos 安装MySQL 5.7.38
    openGauss学习笔记-98 openGauss 数据库管理-管理数据库安全-客户端接入认证之配置客户端接入认证
    sshpass传输文件提示Host key verification failed.
    #paypay付款测试#
    低代码是个用处不大的“玩具”?可别小看它的威力!
    高性能本地缓存Ristretto(一)——存储策略
    详聊设计模式之动态代理
    biquad滤波器的设计
    React 中 react-i18next 切换语言( 项目国际化 )
  • 原文地址:https://blog.csdn.net/weixin_47872288/article/details/125383014