• redis简单使用


    Redis

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

      简单介绍下redis,一个高性能key-value的存储系统,支持存储的类型有string、list、set、zset和hash。在处理大规模数据读写的场景下运用比较多。

      Redis有以下特点:

        -- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
    
        -- Redis支持五种数据类型。
    
        -- Redis支持数据库备份。
    特点

      Redis的优势:

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

      Redis 配置

        -- 可以通过redis-cli 进入交互模式,使用config命令查看或设置配置项。也可以进入配置文件用vim编辑器进行修改。

    # 获取所有配置项
    reids 127.0.0.1:6379> config get *
    # 获取单个配置项
    redis 127.0.0.1:6379> config get loglevel
    # 编辑配置
    redis 127.0.0.1:6379> config set loglevel "notice"
    配置

      Redis 数据类型

     -- String 字符串 
    
          -- redis的string可以包含任何数据,包括图片以及序列化的对象,一个键最大能存储512MB。
    
        -- Hash 哈希
    
          -- redis的hash是一个String类型的key和value的映射表,hash特别适合存储对象,类比python字典。
    
        -- List 列表
    
          -- redis的list是简单的字符串列表,按照插入顺序排序,可以从两端进行添加,类似于双向链表,列表还可以进行阻塞。
    
          -- Set 集合
    
          -- redis的set是字符串类型的无序且不重复集合。集合是通过哈希表实现的,所以添加,删除,查找的时间复杂度都是O(1)。
    
        -- Zset 有序集合
    
          -- redis的zset和set一样,不同的是每个元素都会关联一个double类型的分数,redis正是通过对分数的排序对集合进行有序存储。
    数据类型

    1.连接Redis数据库:

      -- 下载  pip install redis

      -- 连接

        Redis提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令,

        Redis是StrictRedis的子类,用于向后兼容旧版本的redis-py。

        Redis连接实例是线程安全的,可以直接将redis连接实例设置为一个全局变量,直接使用。

        如果需要另一个Redis实例(or Redis数据库)时,就需要重新创建redis连接实例来获取一个新的连接

        连接redis,加上decode_responses=True,写入的键值对中的value为str类型,不加这个参数写入的则为字节类型。

    import redis
    conn = redis.Redis(host="localhost", port=6379, password="", decode_responses=True)

      连接池链接

           使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。

      默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,

      然后作为参数传给Redis实例,这样就可以实现多个Redis实例共享一个连接池。

    conn = redis.Redis(connection_pool=redis.ConnectionPool(
    host="localhost",
    port=6379,
    # password="",
    decode_responses=True, # 设置为str,不然可能会是bytes
    db=1))
    print(conn) # Redis<ConnectionPool<Connection<host=localhost,port=6379,db=1>>> 连接对象

    2.String类型存取:

      set(name, value, ex=None, px=None, nx=False, xx=False)
      在Redis中设置值,默认,不存在则创建,存在则修改
        参数:
        ex,过期时间(秒)过期后值None
        px,过期时间(毫秒)
        nx,如果设置为True,则只有name不存在时,当前set操作才执行
        xx,如果设置为True,则只有name存在时,当前set操作才执行
      
      mset(self, mapping) 批量设置值
       getset(name, value) # 获取旧值,并设置新值
    
    
    print(conn.set("name", "xiao_ming"))  # 返回True  写入str成功
    print(conn.get('name'))  # get(self, name)  选择对应name的str
    
    print(conn.set('name', 'bai', nx=True))  # 只有name不存在时,执行设置操作(添加),如果存在,不会修改
    
    get_name = conn.set('name', '111', px=1000)  # 1000毫秒失效
    time.sleep(1.5)
    print(conn.get("name"))  # None
    
    print(conn.set('extend', '11111', xx=True))  # None 则只有key存在时,当前set操作才执行
    print(conn.get('extend'))  # None
    
    # #  setex 设置超时str   参数为秒
    print(conn.setex('token', 2, 'token_value'))  # True
    time.sleep(2)
    print(conn.get("token"))  # None
    
    # 设置过期时间(豪秒)
    print(conn.psetex("token", 1000, "token_value"))
    time.sleep(1)
    print(conn.get("token"))
    View Code

    3.hash类型存取:

      hset(name, key, value)  增加单个 不存在则创建
    
      hget(name, key)  获取单个
    
      hmset(name, mapping)  批量增加 mapping为字典
    
      hgetall(name) 获取name对应hash的所有键值
    
      hlen(name)  获取name对应的hash中键值对的个数
    
      hkeys(name)  获取name对应的hash中所有的key的值
    
      hvals(name)  获取name对应的hash中所有的value的值
    
      hexists(name, key)  检查name对应的hash是否存在当前传入的key
    
      hdel(name,*keys)   将name对应的hash中指定key的键值对删除
    
      hscan_iter(name, match=None, count=None)
        利用yield封装hscan创建生成器,实现分批去redis中获取数据
        参数:
        match,匹配指定key,默认None 表示所有的key
        count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
    # hset(self, name, key, value)  # name对应的hash中设置一个键值对(不存在,则创建,否则,修改)
    print(conn.hset("name", "key", "value"))  # 设置时候返回结果0 or 1  存在返回0,不存在返回1
    print(conn.hset("name", "key_1", "value_1"))  # 设置时候返回结果0 or 1  存在返回0,不存在返回1
    print(conn.hset("name", "key_2", "value_2"))  # 设置时候返回结果0 or 1  存在返回0,不存在返回1
    print(conn.hmset("name", {"key_2": "value-2", "key_3": "value_3"}))  # True
    print(conn.hget("name", "key"))  # value  单个获取
    print(conn.hgetall("name"))  # {'key': 'value', 'key_1': 'value_1', 'key_2': 'value-2', 'key_3': 'value_3'} 全部获取
    print(conn.hlen("name"))  # 4 获取name对应的hash中键值对的个数
    print(conn.hkeys("name"))  # ['key', 'key_1', 'key_2', 'key_3']  name中全部的key
    print(conn.hvals("name"))  # ['value', 'value_1', 'value-2', 'value_3']  # name中全部的value
    print(conn.hdel("name", "key_3"))  # 返会 1或者0    删除name中key_3的键值对
    print(conn.hexists("name", 'key_3'))  # False  检查name对应的hash是否存在当前key
    print(conn.hincrby("a", "key", amount=1))  # 返回:1
    print(conn.hincrby("a", "key", amount=1))  # 返回:2
    print(conn.hincrby("a", "key", amount=10))  # 返回:12  #  可用于计数或者分页等
    print(conn.hdel("a", "key"))
    mset相关

    3.list类型存取:

    lpush(name,values)  # 在name对应的list中左边添加元素 可以添加多个
    llen(name) # 获取name对应的列表长度
    lrang(name, index1, index2) # 按照index切片取出name对应列表里值
    lpushx(name, value) # 当name存在,元素左边添加
    rpushx(name, value) # 当value存在,元素右边添加

    linsert(name, where, refvalue, value))
      在name对应的列表的某一个值前或后插入一个新值
      参数:
      name,redis的name
      where,BEFORE或AFTER
      refvalue,标杆值,即:在它前后插入数据
      value,要插入的数据
    lset(name, index, value) 给指定索引修改值

    lrem(name, value, num)
      在name对应的list中删除指定的值
      参数:
      name,redis的name
      value,要删除的值
      num, num=0,删除列表中所有的指定值;
      num=2,从前到后,删除2个; num=1,从前到后,删除左边第1个
      num=-2,从后向前,删除2个

    lindex(name, index) 在name对应的列表中根据索引获取列表元素

    blpop(key) 没有值阻塞等待 直到有第一个值可以弹出
    brpop(key) 没有值阻塞等待 直到有最后一个值可以弹出
    print(conn.lpush('name', '1', '2'))  # 返回2 # 1,2 从list的左边添加,可以添加多个建
    print(conn.lpush('name', *['左1', '左2']))  # 返回4 # 左1 左2  从list的左边添加,加入列表
    print(conn.rpush('name', *['右1', '右2']))  # 返回6 #右1,右2  从list的右边添加,加入列表
    
    print(conn.lpushx('no_existent', '左边加不进去'))  # 返回0  key存在左添加 不存在就添加失败
    print(conn.rpushx('no_existent', '右边也加不进去'))  # 返回0  key存在右添加 不存在就添加失败
    
    print(conn.llen('name'))  # 返回6 元素个数即list长度
    print(conn.llen('no_existent'))  # 返回0 没有key相当于 len([])
    
    print(conn.linsert("name", "BEFORE", "左1", "插入1"))  # 返回插入值所在索引  # 左1 前边  插入1
    print(conn.linsert("name", "AFTER", "右1", "插入2"))  # 返回插入值所在索引   # 右1 后边插入  插入2
    
    print(conn.lrange('name', 0, -1))  # ['左2', '插入1', '左1', '2', '1', '右1', '插入2', '右2']
    print(conn.lrange("name", 1, -2))  # index切片 取出name对应列表里值 #  ['插入1', '左1', '2', '1', '右1', '插入2']
    
    print(conn.lindex("name", -2))  # 获取该索引的值  # 插入2
    
    print(conn.lset("name", 1, '我被修改了'))  # index为1的值修改
    print(conn.lrange('name', 0, -1))  # ['左2', '我被修改了', '左1', '2', '1', '右1', '插入2', '右2']
    
    print(conn.lrem("name", 2, "2"))  # 从前到后 删除 value为"2"的   两个  # 1 删除成功
    print(conn.lrem("name", -2, "2"))  # 从后到前 删除 value为"2"的   两个  # 0 没有了  删除失败
    print(conn.lrange('name', 0, -1))  # ['左2', '我被修改了', '左1', '1', '右1', '插入2', '右2']
    
    print(conn.lpop('name'))  # 左删除一个  返回删除元素  # 左2
    print(conn.rpop('name'))  # 右删除一个  返回删除元素  # 右2
    
    print(conn.ltrim("name", 0, 2))  # 删除没在 索引内的值
    print(conn.lrange("name", 1, -1))  # ['左1', '1']
    list相关

    3.set类型存取:

    # 1. Sadd 命令将一个或多个成员元素加入到集合中,已经存在于集合的成员元素将被忽略。
    # 假如集合 key 不存在,则创建一个只包含添加的元素作成员的集合。当集合 key 不是集合类型时,返回一个错误。
    print(r.sadd("1", 1))  # 输出的结果是1
    print(r.sadd("1", 2))  # 输出的结果是1
    print(r.sadd("1", 2))  # 因为2已经存在,不能再次田间,所以输出的结果是0
    print(r.sadd("1", 3, 4))  # 输出的结果是2
    print(r.sinter("1"))  # 输出的结果是set(['1', '3', '2', '4'])
    
    
    # 2.Scard 命令返回集合中元素的数量。集合的数量。 当集合 key 不存在时,返回 0 。
    print(r.sadd("2", 1))  # 输出的结果是1
    print(r.sadd("2", 2, 3, 4, 5))  # 输出的结果是1
    print(r.scard("2"))  # 输出的结果是5
    
    
    # 3.Sdiff 命令返回给定集合之间的差集。不存在的集合 key 将视为空集。
    print(r.sadd("31", 1, 2, 3, 4, 5, 6))  # 输出的结果是6
    print(r.sadd("32", 4, 5, 6, 7, 8, 9,10))  # 输出的结果是6
    print(r.sdiff(31, 32))  # 输出的结果是set(['1', '3', '2'])
    print(r.sdiff(32, 31))  # 输出的结果是set(['9', '8', '7'])
    print(r.sdiff(31, 31))  # 输出的结果是set([])

    #
    4.Sdiffstore 命令将给定集合之间的差集存储在指定的集合中。如果指定的集合 key 已存在,则会被覆盖。 print(r.sadd("41", 1, 2, 3, 4, 5, 6)) # 输出的结果是6 print(r.sadd("42", 4, 5, 6, 7, 8, 9)) # 输出的结果是6 print(r.sadd("43", 0)) # 输出的结果是1 print(r.sdiffstore("43", "41", "42")) # 输出的结果是3 print(r.sinter("43")) # 输出的结果是 set(['1', '3', '2'])

    #
    5.Sinter 命令返回给定所有给定集合的交集。 不存在的集合 key 被视为空集。 当给定集合当中有一个空集时,结果也为空集(根据集合运算定律)。 print(r.sadd("51", 3, 4, 5, 6)) # 输出的结果是4 print(r.sadd("52", 1, 2, 3, 4)) # 输出的结果是4 print(r.sinter(51, 52)) # 输出的结果是set(['3', '4']) print(r.sadd("53", 1, 2, 3, 4, 5, 6)) # 输出的结果是6 print(r.sadd("54", 3, 4, 5, 6, 7, 8, 9)) # 输出的结果是7 print(r.sinter(53, 54)) # 输出的结果是set(['3', '5', '4', '6']) print(r.sinter(53, 56)) # 输出的结果是set([])
    #
    6.Sinterstore 命令将给定集合之间的交集存储在指定的集合中。如果指定的集合已经存在,则将其覆盖。 print(r.sadd("61", 3, 4, 5, 6)) # 输出的结果是4 print(r.sadd("62", 1, 2, 3, 4)) # 输出的结果是4 print(r.sadd("63", 0)) # 输出的结果是1 print(r.sinterstore(63, 61, 62)) # 输出的结果是2 print(r.sinter(63)) # 输出的结果是set(['3', '4'])

    #
    7.Sismember 命令判断成员元素是否是集合的成员。 # 如果成员元素是集合的成员,返回 1 。 如果成员元素不是集合的成员,或 key 不存在,返回 0 。 print(r.sadd("71", 1, 2, 3, 4, 5, 6)) # 输出的结果是6 print(r.sismember("71", 1)) # 输出的结果是True print(r.sismember("71", 2)) # 输出的结果是True print(r.sismember("71", 7)) # 输出的结果是False print(r.sismember("71", 8)) # 输出的结果是False

    #
    8.Smembers 命令返回集合中的所有的成员。 不存在的集合 key 被视为空集合。 print(r.sadd("81", 1, 2, 3, 4, 5, 6)) # 输出的结果是6 print(r.smembers(81)) # 输出的结果是set(['1', '3', '2', '5', '4', '6']) print(r.smembers(82)) # 输出的结果是set([])

    #
    9.Smove 命令将指定成员 member 元素从 source 集合移动到 destination 集合。 # SMOVE 是原子性操作。 # 如果 source 集合不存在或不包含指定的 member 元素,则 SMOVE 命令不执行任何操作,仅返回 False 。否则, member 元素从 source 集合中被移除,并添加到 destination 集合中去。 # 当 destination 集合已经包含 member 元素时, SMOVE 命令只是简单地将 source 集合中的 member 元素删除。 # 当 source 或 destination 不是集合类型时,返回一个错误。 # 如果成员元素被成功移除,返回 True。 如果成员元素不是 source 集合的成员,并且没有任何操作对 destination 集合执行,那么返回 False print(r.sadd("91", 1, 2, )) # 输出的结果是2 print(r.sadd("92", 3, 4, )) # 输出的结果是2 print(r.smove(91, 92, 1)) # 把91中的1移动到92中去,输出的结果是True print(r.smembers("91")) # 输出的结果是set(['2']) print(r.smembers("92")) # 输出的结果是set(['1', '3', '4']) print(r.smove(91, 92, 5)) # 91不存在5,输出的结果是False print(r.smembers("91")) # 输出的结果是set(['2']) print(r.smembers("92")) # 输出的结果是set(['1', '3', '4']) # 10. Spop 命令用于移除并返回集合中的一个随机元素。 print(r.sadd("10", 1, 2, 3, 4, 5, 6)) # 输出的结果是6 print(r.spop("10")) # 输出的结果是3 print(r.smembers("10")) # 输出的结果是set(['1', '2', '5', '4', '6']) print(r.spop("10")) # 输出的结果是1 print(r.smembers("10")) # 输出的结果是set(['2', '5', '4', '6'])

    #
    11.Srandmember 命令用于返回集合中的一个随机元素。 # 从 Redis 2.6 版本开始, Srandmember 命令接受可选的 count 参数: # 如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合。 # 如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。 # 该操作和 SPOP 相似,但 SPOP 将随机元素从集合中移除并返回,而 Srandmember 则仅仅返回随机元素,而不对集合进行任何改动。 print(r.sadd("11", 1, 2, 3, 4, 5, 6)) # 输出的结果是6 print(r.srandmember(11)) # 输出的结果是4 print(r.smembers(11)) # 输出的结果是set(['1', '3', '2', '5', '4', '6']) print(r.srandmember(11, 3)) # 输出的结果是['6', '3', '1'] print(r.smembers(11)) # 输出的结果是set(['1', '3', '2', '5', '4', '6'])

    #
    12. Srem 命令用于移除集合中的一个或多个成员元素,不存在的成员元素会被忽略。 # 当 key 不是集合类型,返回一个错误。 # 被成功移除的元素的数量,不包括被忽略的元素。 print(r.sadd("12", 1, 2, 3, 4, 5, 6, 7)) # 输出的结果是7 print(r.srem("12", 1)) # 输出的结果是1 print(r.smembers("12")) # 输出的结果是set(['3', '2', '5', '4', '7', '6']) print(r.srem("12", 8)) # 输出的结果是0 print(r.smembers("12")) # 输出的结果是set(['3', '2', '5', '4', '7', '6'])

    #
    13.Sunion 命令返回给定集合的并集。不存在的集合 key 被视为空集。 print(r.sadd("131", 1, 2, 3, 4, 5, 6, 7)) # 输出的结果是7 print(r.sadd("132", 0, 1, 2, 7, 8, 9)) # 输出的结果是6 print(r.sunion(131, 132)) # 输出的结果是set(['1', '0', '3', '2', '5', '4', '7', '6', '9', '8']) print(r.sunion(131, 134)) # 输出的结果是set(['1', '3', '2', '5', '4', '7', '6'])

    #
    14.Sunionstore 命令将给定集合的并集存储在指定的集合 destination 中。 print(r.sadd("141", 1, 2, 3, 4, 5, 6, 7)) # 输出的结果是7 print(r.sadd("142", 0, 1, 2, 3, 4)) # 输出的结果是5 print(r.sunionstore(143, 141, 142)) # 输出的结果是8 print(r.smembers(143)) # 输出的结果是set(['1', '0', '3', '2', '5', '4', '7', '6'])

    #
    15.Sscan 命令用于迭代集合键中的元素。 print(r.sadd("151", 1, 2, 3, 4, 5, 6, 7)) # 输出的结果是7 print(r.sscan(151, cursor=2, match=1, count=1)) # 输出的结果是 (0L, ['1'])

    有序集合的命令 请移步官方文档~~~~

    4.pipeline管道批量处理

    Pipeline指的是管道技术,指的是客户端允许将多个请求依次发给服务器,过程中而不需要等待请求的回复,在最后再一并读取结果即可。
    管道技术使用广泛,例如许多POP3协议已经实现支持这个功能,大大加快了从服务器下载新邮件的过程。
    Redis很早就支持管道(pipeline)技术。(因此无论你运行的是什么版本,你都可以使用管道(pipelining)操作Redis)

    import redis
    
    # 连接池链接
    r = redis.Redis(connection_pool=redis.ConnectionPool(
        host="localhost",
        port=6379,
        # password="",
        decode_responses=True,  # 设置为str,不然可能会是bytes
        db=4))
    
    li = [
        {"id": "111", "class": 1, "on_line": "1"},
        {"id": "111", "class": 2, "on_line": "0"},
        {"id": "222", "class": 1, "on_line": "0"},
    ]
    
    # 操作量大的情况   有点慢
    # for i in li:
    #     r.hset(i.get('id'), i.get('class'), i.get("on_line"))
    
    pipe = r.pipeline()  # 管道批量操作  # 大量节省io调高效率
    [pipe.hset(i.get('id'), i.get('class'), i.get("on_line")) for i in li]  # 要执行的命令, 放在管道但没执行
    pipe.execute()  # 执行所有的命令
    [pipe.hget(i.get('id'), i.get('class')) for i in li]
    print([li[i] for i, x in enumerate(pipe.execute()) if x == "1"])  # 筛选出结果为'1'的序号

    pipeline 可以链式操作

    pipe.set('hello', 'redis').sadd('faz', 'baz').incr('num').execute()
    print(r.get("num"))  # incr统计次数使用

    5.delete使用

    py-redis中有个delete接口,既可以删除单个key,也可以批量删除key,后者的正确用法网上很少,我在实践中遇到该问题,特此记录一下。

    删除单个key

    r.delete('age')
    r.delete('sex', 'age')

    删除所有的key

    keys = r.keys()
    r.delete(*keys)  # 执行成功,返回删除key的个数,否则返回0

    5.过期使用

    pipe.expire()
  • 相关阅读:
    SpringBoot整合Mybatis之进门篇
    Tomcat和Java Virtual Machine的性能调优总结
    一次浴火重生的MySQL优化(EXPLAIN命令详解)
    简单聊聊不可或缺的Nginx反向代理服务器--实现负载均衡【上篇】
    Java设计模式之适配器设计模式(项目升级案例)
    前端错误监控
    三栏布局的5种方案
    prototype原型链详解
    关于mysql修改密码 set password for root@localhost = password('xxx');报错解决方法
    页面布局之三栏布局的5种方案
  • 原文地址:https://www.cnblogs.com/clbao/p/11793292.html
Copyright © 2020-2023  润新知