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"))
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"))
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']
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()