Python Redis

# pip install redis

from redis import Redis
from redis import ConnectionPool

def init01():
    """通过redis对象连接redis"""
    # decode_responses=False 存入的是字节数据
    # decode_responses=True 存的是字符串
    r = Redis(host="redis-master.operation.svc.cluster.local", port=6379,
              db=10, password="xxxxx", socket_timeout=300, decode_responses=True)

    print(r.ping())  # True 表示连接成功

    print(r.info()['redis_version'])

def init02():
    """推荐: 通过连接池获取redis对象连接服务器"""
    redis_pool = ConnectionPool(host="redis-master.operation.svc.cluster.local",
                                port=6379, db=10, password="xxxxx", socket_timeout=300, decode_responses=True)

    r = Redis(connection_pool=redis_pool)

    print(r.ping())
    print(r.info()['redis_version'])

    r.set("init02", "redis", ex=None, px=None, nx=False, xx=False)
    # ex 失效时间 秒
    # px 失效时间 毫秒
    # nx True 则只有在name不存在时,当前set操作才执行
    # xx True 则只有name存在时,当前set操作才执行

    print(r.get("init02"))

    # redis默认在执行每次请求都会创建和断开一次连接操作,redis-server会关闭超时空闲的连接,使用连接池不需要手动关闭连接
    # 不推荐手动释放连接池资源
    # r.connection_pool.disconnect()

# Python操作redis五种数据
redis_pool = ConnectionPool(host="redis-master.operation.svc.cluster.local",
                            port=6379, db=10, password="xxxxx", socket_timeout=300, decode_responses=True)

r = Redis(connection_pool=redis_pool)

# 操作字符串
def string_test():
    """操作string"""
    r.set("name", "key", ex=5)
    r.set("username", "张三", nx=True)
    r.set("username", "李四", nx=True)  # username 已存在,不会执行
    print(r.get("username"))  # 张三
    r.set("username", "李四", xx=True)  # username 已存在,执行
    print(r.get("username"))  # 李四

    # setnx 设置值 只有name不存在时,执行设置操作
    print(r.setnx("username", "张三"))  # False
    print(r.setnx("age", "18"))  # True

    # setex 设置值 参数time,过期时间,数字秒或timedelta对象
    print(r.setex("age", 10, "19"))  # True  10秒过期

    # psetex  毫秒
    print(r.psetex("age", 5000, "20"))  # True  5000毫秒过期

    # mset 批量设置
    r.mset({"k1": "v1", "k2": "v2", "k3": "v3"})
    print(r.mget("k1", "k2", "k3"))   # ['v1', 'v2', 'v3']
    print(r.mget(["k1", "k2", "k3"]))   # ['v1', 'v2', 'v3']

    # getset(name, value)  设置新值并获取原来的值
    print(r.getset("k1", "v111"))  # v1
    print(r.get("k1"))  # v111

    # getrange(key, start, end) 字节位置
    r.set("cn_test", "哈哈哈哈哈哈")
    print(r.getrange("cn_test", 0, 2))  # 哈  取索引前2位的字节,一个汉字3个字节,一个字母一个字节
    # print(r.getrange("cn_test", 0, 4))  # UnicodeDecodeError: 'utf-8' codec can't decode bytes in position 3-4: unexpected end of data
    print(r.getrange("cn_test", 0, 5))  # 哈哈
    print(r.getrange("cn_test", 0, -1))  # 哈哈哈哈哈哈 切片操作,取所有
    r.set("en_test", "hahahahahaha")
    print(r.getrange("en_test", 0, 5))  # hahaha
    print(r.getrange("en_test", 0, -1))  # hahahahahaha   切片操作,取所有

    # setrange(name, offset, value)  修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
    r.setrange("cn_test", 3, "嘿嘿")  # 一个汉字3个字节
    # r.setrange("cn_test", 2, "嘿嘿")  # UnicodeDecodeError: 'utf-8' codec can't decode bytes in position 0-1: invalid continuation byte
    print(r.get("cn_test"))  # 哈嘿嘿哈哈哈
    r.setrange("en_test", 2, "oo")
    print(r.get("en_test"))  # haoohahahaha
    r.setrange("en_test", 2, "oooooooooooooooooooooooo")
    print(r.get("en_test"))  # haoooooooooooooooooooooooo

    # strlen(name)  返回name对应值的字节长度,一个汉字三个字节
    print(r.strlen("cn_test"))  # 18
    print(r.strlen("en_test"))  # 26

    # incr(self, name, amount=1) 自增name对应的值,当name不存在时,创建name=amount,否则自增加
    print(r.get("age"))  # 20
    r.incr("age", amount=2)
    print(r.get("age"))  # 22
    r.incr("age", amount=2)
    print(r.get("age"))  # 24
    r.incr("age_test", amount=2)
    print(r.get("age_test"))  # 2
    r.incrby("age_test", amount=2)

    # incrbyfloat(self, name, amount=1.0) 自增name对应的值,当name不存在时,创建name=amount,否则自增加 浮点型
    r.set("foo1", "123.0")
    r.set("foo2", "222.2")
    print(r.mget("foo1", "foo2"))   # ['123.0', '222.2']
    r.incrbyfloat("foo1", amount=2.1)
    r.incrbyfloat("foo2", amount=3.3)
    print(r.mget("foo1", "foo2"))   # ['125.1', '225.5']

    # decr(self, name, amount=1) 自减name对应的值,当name不存在时,创建name=amount,否则自减
    r.decr("age", amount=2)
    print(r.get("age"))  # 22
    r.decr("age", amount=2)
    print(r.get("age"))  # 20

    # append(key, value) 在name对应的值后面追加内容
    r.append("cn_test", "追加内容")
    print(r.get("cn_test"))  # 哈嘿嘿哈哈哈追加内容

# 操作HASH
def hash_test():
    """HASH操作"""

    # hset(name, key, value)  name:redis的key, key:hash的key, value:hash中的value
    r.hset("hash1", "k1", "v1")
    r.hset("hash1", "k2", "v2")
    print(r.hkeys("hash1"))  # ['k1', 'k2']   获取hash1中所有的key
    print(r.hget("hash1", "k1"))  # v1 获取hash1中k1的value
    print(r.hmget("hash1", "k1", "k2"))  # ['v1', 'v2']  批量获取
    r.hsetnx("hash1", "k3", "v3")  # 如果不存在则插入
    print(r.hsetnx("hash1", "k4", "v4"))  # k4不存在,则新增,输出1
    print(r.hsetnx("hash1", "k4", "v5"))  # k4存在则输出0,不执行
    print(r.hget("hash1", "k4"))  # v4

    # hmset(name, mapping)  批量增加
    # DeprecationWarning: Redis.hmset() is deprecated. Use Redis.hset() instead.
    r.hmset("hash2", {"k1": "v1", "k2": "v2", "k3": "v3"})
    print(r.hkeys("hash2"))   # ['k1', 'k2', 'k3']
    # hmget(name, keys,*args)  批量获取
    print(r.hmget("hash1", "k1", "k2"))  # ['v1', 'v2']
    print(r.hmget("hash1", ["k1", "k2"]))  # ['v1', 'v2']

    # hgetall(name)  取出所有的键值
    # {'k1': 'v1', 'k2': 'v2', 'k3': 'v3 ', 'k4': 'v4 '}
    print(r.hgetall("hash1"))

    # hlen(name)  获取name对应的hash中键值对的个数
    print(r.hlen("hash1"))  # 4

    # hkeys(name)  获取所有key
    print(r.hkeys("hash1"))  # ['k1', 'k2', 'k3', 'k4']

    # hvals(name)  获取所有values
    print(r.hvals("hash1"))  # ['v1', 'v2', 'v3', 'v4']

    # hincrby(name, key, amount=1)   自增整数 如果name不存在时 value=amount
    # hincrby(name, key, amount=-1)   自减整数 如果name不存在时 value=amount
    r.hset("hash1", "k1", 100)
    r.hincrby("hash1", "k1", amount=1)
    print(r.hget("hash1", "k1"))  # 101
    r.hincrby("hash1", "k1", amount=-2)
    print(r.hget("hash1", "k1"))  # 99

    # hincrbfloat(name, key, amount=1.0)  自增/减浮点数 如果name不存在时 value=amount
    r.hset("hash1", "k2", 10.0)
    r.hincrbyfloat("hash1", "k2", amount=-1.1)
    print(r.hget("hash1", "k2"))  # 8.9
    r.hincrbyfloat("hash1", "k2", amount=2.3)
    print(r.hget("hash1", "k2"))  # 11.2

    # hdel(name, *keys)   删除
    # {'k1': '99', 'k2': '11.2', 'k3': 'v3', 'k4': 'v4'}
    print(r.hgetall("hash1"))
    r.hdel("hash1", "k4")
    print(r.hgetall("hash1"))  # {'k1': '99', 'k2': '11.2', 'k3': 'v3'}

    # HSCAN(name, cursor=0, match=None, count=None)  # 获取所有hash数据
    print(r.hscan("hash1"))  # (0, {'k1': '99', 'k2': '11.2', 'k3': 'v3'})

    # hscan_iter(name, match=None, count=None)  利用yield封装hscan创建生成器,实现分批去redis中获取数据
    # match 匹配指定key,默认None 表示所有key
    # count 每次分片最少获取个数,默认None表示采用Redis的默认分片个数
    for item in r.hscan_iter("hash1"):
        print(item)
    print(r.hscan_iter("hash1"))   # 生成器内存地址

# 操作list
def list_test():
    """操作list"""

    # lpush(name, values)  从左边增加 在name对应的list中添加元素,每个新的元素都添加到列表的最左边
    if r.exists("list1"):
        r.delete("list1")
    if r.exists("list2"):
        r.delete("list2")
    r.lpush("list1", 11, 22, 33)
    print(r.lrange("list1", 0, -1))  # ['33', '22', '11']
    r.lpush("list1", 666)
    print(r.lrange("list1", 0, -1))  # ['666', '33', '22', '11']

    # rpush(name, values)  从右边增加
    r.rpush("list1", 10, 9, 8)
    # ['666', '33', '22', '11', '10', '9', '8']
    print(r.lrange("list1", 0, -1))

    # lpushx(name, value) 往已有的name的列表的左边添加元素,没有的话不创建
    print(r.lpushx("list2", 777))   # 0  list2不存在。不添加
    r.lpushx("list1", 777)
    # ['777', '666', '33', '22', '11', '10', '9', '8']
    print(r.lrange("list1", 0, -1))

    # linsert(name, where, refvalue, value) 在name对应的列表的某个值前或者后插入一个新值
    # where BEFORE/AFTER   refvalue 标杆值,即在它的前后插入新值
    r.lpush("list1", 11, 22, 33)
    # ['33', '22', '11', '777', '666', '33', '22', '11', '10', '9', '8']
    print(r.lrange("list1", 0, -1))
    r.linsert("list1", "before", 11, 10)  # 在列表从左至右匹配到的第一个11前面新增 10
    # ['33', '22', '10', '11', '777', '666', '33', '22', '11', '10', '9', '8']
    print(r.lrange("list1", 0, -1))
    print(r.linsert("list1", "before", 88, 10))   # -1 标杆值不存在,添加失败

    # lset(name, index, value) 在name对应的list中的某个索引位置重新赋值
    r.lset("list1", 2, 18)
    # ['33', '22', '18', '11', '777', '666', '33', '22', '11', '10', '9', '8']
    print(r.lrange("list1", 0, -1))

    # lrem(name: str, count: int, value: str)  指定值进行删除  count=1 从左至右,第一个出现的删除,count=-2 从右至左 删除2个匹配的值
    r.lrem("list1", -1, 33)
    # ['33', '22', '18', '11', '777', '666', '22', '11', '10', '9', '8']
    print(r.lrange("list1", 0, -1))
    r.lrem("list1", 2, 11)
    # ['33', '22', '18', '777', '666', '22', '10', '9', '8']
    print(r.lrange("list1", 0, -1))

    # lpop(name)  从左删除并返回该值
    # rpop(name)  从右删除并返回该值
    print(r.lpop("list1"))  # 33
    print(r.lpop("list1"))  # 22
    print(r.rpop("list1"))  # 8

    # ltrim(name, start, end)  # 在name对应的列表中移除没有在start-end索引之间的值
    r.ltrim("list1", 0, 2)  # 保留索引 0 - 2的值,其余值删除
    print(r.lrange("list1", 0, -1))  # ['18', '777', '666']

    # llen(name)  列表元素个数
    print(r.llen("list1"))  # 3

    # lindex(name, index)   取值,根据索引号取值
    print(r.lindex("list1", 2))  # 666
    print(r.lindex("list1", 0))  # 18

    # rpoplpush(src, dst) 移动 元素从一个列表取出最右边的元素移动到另外一个列表的最左边
    r.lpush("list2", 11, 22)
    print(r.lrange("list2", 0, -1))  # ['22', '11']
    r.rpoplpush("list1", "list2")
    print(r.lrange("list1", 0, -1))  # ['18', '777']
    print(r.lrange("list2", 0, -1))  # ['666', '22', '11']

    # brpoplpush(src, dst, timeout=0)  从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
    # timeout 当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞
    r.brpoplpush("list2", "list1", timeout=2)
    print(r.lrange("list2", 0, -1))  # ['666', '22']
    print(r.lrange("list1", 0, -1))  # ['11', '18', '777']

    # blpop(keys, timeout)  将多个列表排列,按照从左到右去pop对应列表的元素
    r.blpop(["list1", "list2"], timeout=2)
    print(r.lrange("list1", 0, -1))  # ['18', '777']
    print(r.lrange("list2", 0, -1))  # ['666', '22']

    r.blpop(["list1", "list2"], timeout=2)
    print(r.lrange("list1", 0, -1))  # ['777']
    print(r.lrange("list2", 0, -1))  # ['666', '22']

# 操作set
def set_test():
    """操作set集合"""

    # sadd(name, values)   name对应的集合中添加元素
    r.sadd("set1", 11, 22, 33, 44, 55, 66)

    # scard(name) 获取集合中元素个数
    print(r.scard("set1"))   # 6

    # smembers(name)  获取集合中所有的成员
    print(r.smembers("set1"))   # {'22', '55', '33', '11', '66', '44'}

    # scan(cursor=0, match=None, count=None)  查找数据库所有key
    # sscan(key, cursor=0, match=None, count=None)   查找集合中所有数据
    # (5, ['username', 'list1', 'hash2', '459/data1', 'cn_test', 'k3', 'en_test', 'age_test', 'foo1', 'k1'])
    print(r.scan(0))
    print(r.sscan("set1"))  # (0, ['11', '22', '33', '44', '55', '66'])

    # sscan_iter(name, match=None, count=None)   获取集合中所有的成员--迭代器的方式
    for i in r.sscan_iter("set1"):
        print(i)

    # sdiff(keys, *args)  差集  第一个name对应的集合中去除相同的取不同的
    r.sadd("set2", 55, 66, 77, 88, 99)
    print(r.smembers("set1"))  # {'66', '11', '22', '55', '44', '33'}
    print(r.smembers("set2"))  # {'66', '99', '88', '55', '77'}
    print(r.sdiff(["set1", "set2"]))  # {'11', '33', '22', '44'}
    print(r.sdiff(["set2", "set1"]))  # {'77', '88', '99'}

    # sdiffstore(dest, keys, *args)  差集保存至一个新的集合
    r.sdiffstore("set3", ["set1", "set2"])
    print(r.smembers("set3"))  # {'11', '44', '33', '22'}

    # sinter(keys, *args)   交集,第一个name对应的集合中取相同的
    print(r.sinter(["set1", "set2"]))   # {'55', '66'}

    # sunion(keys, *args)  并集,合并不同的
    # {'44', '22', '33', '11', '88', '77', '99', '66', '55'}
    print(r.sunion(["set2", "set1"]))

    # sunionstore(dest, keys, *args)   并集,并集保存至一个新的集合
    r.sunionstore("set4", ["set2", "set1"])
    # {'22', '55', '77', '99', '44', '66', '88', '33', '11'}
    print(r.smembers("set4"))

    # sismember(name, value)   判断是否是集合的成员 类似in
    print(r.sismember("set4", 88))  # 1
    print(r.sismember("set4", 999))  # 0

    # smove(src, dst, value)   将某个成员从一个集合中移动到另一个集合
    r.smove("set2", "set1", 99)    # 把set2中的99 移动至set1
    print(r.smembers("set1"))  # {'11', '44', '66', '33', '55', '22', '99'}
    print(r.smembers("set2"))  # {'88', '55', '77', '66'}

    # srem(name, values)   删除指定的集合中的元素
    print(r.srem("set1", 100))  # 0 删除的元素不存在
    print(r.srem("set1", 99))  # 1  删除成功
    print(r.smembers("set1"))  # {'66', '44', '22', '11', '33', '55'}

# 操作sorted set
def sorted_set_test():
    """操作sorted set集合"""

    # zadd(name, *args, **kwargs)  新增
    r.zadd("zset1", {'one': 1, 'two': 2, 'three': 3})

    # zcard(name)  获取有序集合的元素个数,类似于len
    print(r.zcard("zset1"))  # 3

    # zrange(name, start, end, desc=False, withscores=False, score_cast_func=float)
    # start,end 有序集合索引起始结束位置,desc,排序规则,默认按照分数大小到大排序
    # withscores 是否获取元素的分数,默认只获取元素的值
    # score_cast_func 对分数进行数据转换的函数
    print(r.zrange("zset1", 0, -1))   # ['one', 'two', 'three']   获取有序集合中所有的元素
    # [('one', 1.0), ('two', 2.0), ('three', 3.0)]
    print(r.zrange("zset1", 0, -1, withscores=True))

    # zrevrange(name ,start, end, withscores=False, score_cast_func=float)  从大到小排序
    print(r.zrevrange("zset1", 0, -1))   # ['three', 'two', 'one']
    # [('three', 3.0), ('two', 2.0), ('one', 1.0)]
    print(r.zrevrange("zset1", 0, -1, withscores=True))

    # zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float)
    # 按照分数范围获取name对应的有序集合的元素
    r.delete("zset2")
    for i in range(1, 30):
        element = 'n' + str(i)
        r.zadd("zset2", {element: i})
    # ['n10', 'n11', 'n12', 'n13', 'n14', 'n15', 'n16', 'n17', 'n18', 'n19', 'n20']
    print(r.zrangebyscore("zset2", 10, 20))
    # [('n5', 5.0), ('n6', 6.0), ('n7', 7.0), ('n8', 8.0), ('n9', 9.0), ('n10', 10.0), ('n11', 11.0), ('n12', 12.0), ('n13', 13.0), ('n14', 14.0), ('n15', 15.0)]
    print(r.zrangebyscore("zset2", 5, 15, withscores=True))

    # zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float)
    # 按照分数范围获取有序集合的元素并排序(默认从大到小排序)
    # [('n25', 25.0), ('n24', 24.0), ('n23', 23.0), ('n22', 22.0), ('n21', 21.0), ('n20', 20.0), ('n19', 19.0), ('n18', 18.0), ('n17', 17.0), ('n16', 16.0), ('n15', 15.0)]
    print(r.zrevrangebyscore("zset2", 25, 15, withscores=True))

    # zscan_iter(name, match=None, count=None, score_cast_func=float)
    # 获取所有元素--迭代器
    for i in r.zscan_iter("zset1"):
        print(i)

    # zcount(name, min, max)   获取有序集合中,分数在[min,max]范围内的个数
    print(r.zcount("zset2", 10, 20))  # 11

    # zincrby(name, amount, value)  自增name对应的有序集合value对应的分数
    # [('one', 1.0), ('two', 2.0), ('three', 3.0)]
    print(r.zrange("zset1", 0, -1, withscores=True))
    r.zincrby("zset1", 2, 'one')
    # [('two', 2.0), ('one', 3.0), ('three', 3.0)]
    print(r.zrange("zset1", 0, -1, withscores=True))

    # zrank(name, value)   获取索引号
    print(r.zrank("zset1", 'three'))  # 2     从小到大排序
    print(r.zrevrank("zset1", 'three'))  # 0    从大到小排序

    # zrem(name, values)  删除指定值
    r.zrem("zset1", 'one')
    # [('two', 2.0), ('three', 3.0)]
    print(r.zrange("zset1", 0, -1, withscores=True))

    # zremrangebyrank(name, min, max)   根据索引号范围来删除
    r.zremrangebyrank("zset2", 0, 20)
    # ['n22', 'n23', 'n24', 'n25', 'n26', 'n27', 'n28', 'n29']
    print(r.zrange("zset2", 0, -1))

    # zremrangebyscore(name, min, max)   根据分数范围删除
    r.zremrangebyscore("zset2", 21, 25)
    print(r.zrange("zset2", 0, -1))  # ['n26', 'n27', 'n28', 'n29']

    # zscore(name, value)   获取值对应的分数
    # [('n26', 26.0), ('n27', 27.0), ('n28', 28.0), ('n29', 29.0)]
    print(r.zrange("zset2", 0, -1, withscores=True))
    print(r.zscore("zset2", 'n28'))  # 28.0

# 其他操作
def other_test():
    """其他操作"""

    # redis中以层级关系,目录形式存储数据
    r.set("user:01", 'zhangsan')
    print(r.get("user:01"))

    # delete(*names)  删除redis中任意数据类型
    r.delete("user:01")
    print(r.get("user:01"))   # None

    # exists(name)   检查name是否存在
    print(r.exists("set1"))   # 1
    print(r.exists("user:01"))  # 0

    # keys(pathern='')   模糊匹配
    # keys *  匹配所有key
    # keys h?llo   匹配hello,hallo等,?为一个字符
    # keys h*llo  匹配hllo 和 heeeeeello等  *为任意字符
    # keys h[ae]llo  匹配hallo hello
    print(r.keys('*'))  # 所有
    print(r.keys("f*o1"))  # ['foo1']

    # expire(name, time)   设置超时时间
    r.lpush("list5", 11, 22)
    r.expire("list5", time=3)   # 三秒后list5超时清除

    # rename(src, dst)   重命名
    print(r.lpush("list5", 11, 22))
    r.rename("list5", "list6")
    print(r.lrange("list5", 0, -1))  # []
    print(r.lrange("list6", 0, -1))  # ['22', '11', '22', '11']

    # randomkey()   随机获取name
    print(r.randomkey())  # zset1

    # type(name)   获取类型
    print(r.type("zset1"))  # zset

    # scan(cursor=0, math=None, count=None)   查看所有元素
    print(r.hscan("hash1"))  # (0, {'k1': '99', 'k2': '11.2', 'k3': 'v3'})
    print(r.sscan("set1"))  # (0, ['11', '22', '33', '44', '55', '66'])
    print(r.zscan("zset1"))  # (0, [('two', 2.0), ('three', 3.0)])
    print(r.getrange("cn_test", 0, -1))  # 哈嘿嘿哈哈哈追加内容
    print(r.lrange("list1", 0, -1))  # ['777']
    print(r.smembers("set1"))  # {'55', '22', '66', '44', '33', '11'}
    print(r.zrange("zset2", 0, -1))  # ['n26', 'n27', 'n28', 'n29']
    print(r.hgetall("hash2"))  # {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}

    # scan_iter(match=None, count=None)   查看所有元素--迭代器
    for i in r.hscan_iter("hash1"):
        pass
    for i in r.sscan_iter("set1"):
        pass
    for i in r.zscan_iter("zset1"):
        pass

    # dbsize()  当前redis包含多少条数据(key)
    print(r.dbsize())  # 24

    # keys()   查询所有key
    print(r.keys())

    # flushdb()   清空redis中的所有数据
    # r.flushdb()

# 操作byte
def byte_test():
    """操作byte"""
    redis_pool = ConnectionPool(host="redis-master.operation.svc.cluster.local",
                                port=6379, db=10, password="xxxxx", socket_timeout=300, decode_responses=False)

    r = Redis(connection_pool=redis_pool)

    r.set("username", "tang")
    print(r.get("username").decode(encoding='utf-8'))   # tang

# redis 事务及操作redis事务
# 事务(Transaction)是访问并可能更新数据库中各种数据顶的一个程序执行单元(unit),就是把多件事当做一件事情来处理,全部成功或全部失败。
# Redis 实现事务的4个重要命令:multi、exec、discard、watch(乐观锁)
# Redis 实现事务:使用multi开启事务,通过exec提交事务,discard用于回滚事务,事务回滚后,数据无变化。
# 事务中,错误的操作不会被执行,已经正确的操作会被执行,且不会回滚,所以原子性不太好。
# 悲观锁:Pessimistic Lock 悲观,每次去拿数据时都会给该数据上锁,这样别人想拿这个数据就会block直到它拿到锁,传统关系型数据库用到了很多这种锁机制,如行锁、表锁、读锁、写锁等,都是在做操作之前先上锁
# 乐观锁:Optimistic Lock 乐观,每次去拿数据时不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制,乐观锁适用于多读的应用类型,这样可以提高吞吐量。
def transaction_pipeline_test():
    """使用管道操作事务"""
    try:
        pipe = r.pipeline(transaction=True)   # 默认启用事务
        pipe.multi()   # 开启事务

        # 可以插入多种数据类型
        pipe.set("username", 'zhangsan')
        pipe.hset("user", "username", "lisi")
        pipe.sadd('letters', 'a', 'b')

        # 管道的命令可以写在一起
        # pipe.set("username", 'zhangsan').hset("user", "username", "lisi").sadd('letters', 'a', 'b').execute()

        # 提交事务
        pipe.execute()

        print(r.get("username"))  # zhangsan
        print(r.hget("user", 'username'))  # lisi
        print(r.smembers("letters"))  # {'b', 'a'}
    except Exception as e:
        print(e)
        pipe.reset()    # 回滚事务

if __name__ == "__main__":
    # init01()
    # init02()
    # string_test()
    # hash_test()
    # list_test()
    # set_test()
    # sorted_set_test()
    # other_test()
    # byte_test()
    transaction_pipeline_test()