redis一览
Redis 简介
Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
Redis 与其他 key - value 缓存产品有以下三个特点:
- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
- Redis支持数据的备份,即master-slave模式的数据备份。
Redis 优势
- 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
- 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
1、安装redis
Redis与其他key-value存储有什么不同?
-
Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
-
Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。
Window 下安装
下载地址:https://github.com/MSOpenTech/redis/releases。
下载的为.msc后缀的,只需一顿下一步即可!
目前只是将redis安装在win环境下的,因为现在的对linux不是很熟悉!
2、Python操作redis
1 pip3 install redis
a、创建连接
1、普通连接
1 import redis 2 3 4 conn = redis.Redis(host='127.0.0.1', port=6379) 5 conn.set('animal', 'cat') 6 val = conn.get('animal') 7 print(val)
2、基于连接池
1 import redis 2 3 POOL = redis.ConnectionPool(host='127.0.0.1', port=6379, max_connections=1000) # 创建连接池 4 conn = redis.Redis(connection_pool=POOL) 5 6 conn.set("key", "cat") 7 val = conn.get("key") 8 print(val)
b、redis常用操作
在涉及到操作之前可以先来了解下redis支持的数据结构
- String:字符串
- Hash:散列(字典)
- List:列表
- Set:集合(去重)
- Sorted Set:有序集合
1 # redis 就相当与内存中的一个字典。(session??) 2 redis = { 3 k1: 'hello', # String 4 k2: [1, 2, 3, 5, 4, 5, 2], # List 5 k3: {1, 2, 3, 4}, # Set 6 k4: {name: 'pontoon', age: 19}, # Dict/Hash 7 k5: {('pontoon': 65), ('god_v': 75), ('dandy': 85)} # SortedSet 8 }
1、字典的操作
1 import redis 2 3 POOL = redis.ConnectionPool(host='127.0.0.1', port=6379, max_connections=1000) 4 conn = redis.Redis(connection_pool=POOL) 5 6 # conn.set("key", "cat") 7 # val = conn.get("key") 8 # print(val) 9 10 # 1、hset(name, key, value) # 设置单个值 11 conn.hset("k1", "animal", "dog") 12 13 # 2、hget(name,key) # 取单个值 14 val = conn.hget("k1", "animal") 15 print(val) # b'dog' 16 17 # 3、hmset(name, mapping) # 设置多个值 18 conn.hmset("k2", {"animal1": "cat", "animal2": "dog"}) 19 20 # 4、hmger(name, keys, *args) # 取多个值 21 val = conn.hmget("k2", "animal1", "animal2") 22 print(val) # [b'cat', b'dog'] 23 24 # 5、hgetall(name) # 获取name对应的所有值 25 val = conn.hgetall("k2") 26 print(val) # {b'animal1': b'cat', b'animal2': b'dog'} 27 28 # 6、hlen(name) # 获取个数 29 val = conn.hlen("k2") 30 print(val) # 2 31 32 # 7、hkeys(name) # 获取key 33 34 # 8、hvals(name) # 获取value 35 36 # 9、hexists(name, key) # 判断是否存在 37 val = conn.hexists("k2", "animal1") 38 print(val) # True 39 40 # 10、hdel(name,*keys) # 删除指定key中的键值对 41 42 # 11、hincrby(name, key, amount=1) # 自增name对应的hash中的指定key的值,不存在则创建key=amount 43 44 # 12、hscan(name, cursor=0, match=None, count=None) 45 """ 46 增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆 47 参数: 48 name,redis的name 49 cursor,游标(基于游标分批取获取数据) 50 match,匹配指定key,默认None 表示所有的key 51 count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数 52 如: 53 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None) 54 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None) 55 ... 56 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕 57 """ 58 59 # 13、hscan_iter(name, match=None, count=None) 60 """ 61 利用yield封装hscan创建生成器,实现分批去redis中获取数据 62 63 参数: 64 match,匹配指定key,默认None 表示所有的key 65 count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数 66 67 如: 68 for item in r.hscan_iter('xx'): 69 print item 70 """
关于第13条的一个补充:
1 # 对于13的补充: 2 假设redis中有name=k4的一个字典,这个字典中有10个G的数据,我们该如何取值? 3 ret = conn.hscan_iter("k4", count=10000) # 创建一个生成器对象,每次取1W条数据 4 for i in range(1000): 1000x10000 ≈ 10G 5 for item in ret: 6 print(item)
hscan_iter的源码
1 def hscan_iter(self, name, match=None, count=None): 2 """ 3 Make an iterator using the HSCAN command so that the client doesn't 4 need to remember the cursor position. 5 6 ``match`` allows for filtering the keys by pattern 7 8 ``count`` allows for hint the minimum number of returns 9 """ 10 # cursor表示游标 指明了count就相当于指明了cursor的数量 11 cursor = '0' 12 # 假如count=10000, 那么下一次迭代cursor='10000' 13 while cursor != 0: 14 cursor, data = self.hscan(name, cursor=cursor, match=match, count=count) 15 for item in data.items(): 16 yield item
2、列表的操作
1 import redis 2 3 4 pool = redis.ConnectionPool(host='127.0.0.1', port=6379, max_connections=1000) # 设置最大连接数量为1000 5 conn = redis.Redis(connection_pool=pool) # 连接连接池 6 7 # 1、lpush(name,values) # 列表的左侧添加数据 8 9 # 2、rpush(name, values) # 列表的右侧添加数据 10 conn.lpush("l1", "v1") 11 conn.lpush("l1", "v2") 12 conn.rpush("l1", "v3") 13 14 ret = conn.lrange("l1", 0, 100) # 范围取值,取前100条数据 15 print(ret) # [b'v2', b'v1', b'v3'] 16 17 # 3、lpushx(name, value) # 当name存在时,向列表左侧添加数据 18 19 # 4、rpushx(name, value) # 当name存在时,向列表右侧添加数据 20 21 # 5、lpop(name) # 列表的左侧弹出数据 22 23 # 6、rpop(name) # 列表的右侧弹出数据 24 val = conn.lpop("l1") 25 print(val) # b'v1' 26 27 val = conn.rpop("l1") 28 print(val) # b'v3' 29 30 31 # 7、llen(name) # name对应的list元素的个数 32 33 # 8、linsert(name, before/after, refvalue, value)) # 在name对应的列表的某一个值前或后插入一个新值 34 conn.linsert("l1", "before", "v2", "v3") # 在列表内找到第一个元素v2,在它前面插入v3 35 36 ret = conn.lrange("l1", 0, 100) 37 print(ret) 38 39 # 9、lset(name, index, value) # 对list中的某一个索引位置重新赋值 40 conn.lset("l1", 0, "v4") # 将索引为0的值重新赋值v4 41 42 ret = conn.lrange("l1", 0, 100) 43 print(ret) 44 45 # 10、lrem(name, num, value) 46 ''' 参数: 47 name: redis的name 48 num: num=0 删除列表中所有的指定值; 49 num=2 从前到后,删除2个; 50 num=-2 从后向前,删除2个 51 value: 要删除的值 52 ''' 53 # 这里要注意lrem在最新版本的redis中用法改了,将num与value调换位置了。(难怪我对着网上的博客写一致会报错~) 54 55 # 11、ltrim(name, start, end) # 在name对应的列表中移除没有在start-end索引之间的值 56 conn.ltrim("l1", 0, 0) # 移除第一个之后的所有值 57 58 ret = conn.lrange("l1", 0, 100) 59 print(ret) 60 61 # 12、rpoplpush(src, dst) # 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边 62 """ 63 参数: 64 src,要取数据的列表的name 65 st,要添加数据的列表的name 66 """ 67 conn.lpush("l2", '11', '22', '33') 68 conn.lpush("l3", '44', '55', '66') 69 conn.rpoplpush("l2", "l3") 70 71 ret = conn.lrange("l3", 0, 100) 72 print(ret) 73 74 # redis都是玩的一些什么操作啊?花里胡哨~还有一部分反向操作的就不列出来了。要吐血了
3、集合的操作
1 # 1、sadd(name,values) # ame对应的集合中添加元素 2 3 # 2、smembers(name) # 获取name对应的集合的所有成员 4 pool = redis.ConnectionPool(host='127.0.0.1', port=6379, max_connections=1000) # 设置最大连接数量为1000 5 conn = redis.Redis(connection_pool=pool) # 连接连接池 6 7 conn.sadd("s1", "v1") 8 conn.sadd("s1", "v1", "v2", "v3", "v4", "v5") 9 10 print(conn.smembers("s1")) 11 >>>{b'v4', b'v5', b'v2', b'v1', b'v3'} # 得到一个无序集合 12 13 3、scard(name) # 获取name对应的集合中的元素个数 14 15 4、sdiff(keys, *args) # 在第一个name对应的集合中且不在其他name对应的集合的元素集合(差集) 16 conn.sadd("s2", "aa", "bb") 17 conn.sadd("s3", "bb", "cc") 18 conn.sadd("s4", "bb", "cc", "dd") 19 20 print(conn.sdiff("s2", "s3", "s4")) 21 >>>{b'aa'} 22 23 5、sinter(keys, *args) # 获取多个name对应集合的交集 24 conn.sadd("s2", "aa", "bb") 25 conn.sadd("s3", "bb", "cc") 26 conn.sadd("s4", "bb", "cc", "dd") 27 28 print(conn.sinter("s2", "s3", "s4")) 29 >>>{b'bb'} 30 31 6、sinterstore(dest, keys, *args) # 获取两个name对应集合的交集,并将结果存入第三个集合中 32 conn.sadd("s3", "bb", "cc") 33 conn.sadd("s4", "bb", "cc", "dd") 34 35 print(conn.sinterstore("s2", "s3", "s4")) # s2为一个空的store存储s3,s4的交集,返回数字 36 print(conn.smembers("s2")) # 返回集合中的值 37 38 7、sunion(keys, *args) # 并集,获取多个name对应的集合的并集 39 40 8、smove(src, dst, value) 41 conn.smove("s2", "s3", 'aa') 42 print(conn.smembers("s3")) 43 >>>{b'cc', b'aa', b'bb'} 44 45 9、sismember(name, value) # 判断是否是集合的成员 类似in 46 47 10、spop(name) # 删除--随机删除并且返回被删除值 48 49 11、srem(name, values) # 删除--指定值删除 50 51 # 集合的操作相对最简单。
3.1、有序集合的操作
1 # 注意我的redis版本为3.2.1,python在操作有序集合中的部分源码已经改了,网上的一些博客抄来抄去,自己也不动手实践下,错的地方都一样。 2 3 1、zadd(name, mapping, nx=False, xx=False, ch=False, incr=False) # zadd的源码改了,网上大部分的博客都是旧的。所以输出的时候会报错 4 pool = redis.ConnectionPool(host='127.0.0.1', port=6379, max_connections=1000) # 设置最大连接数量为1000 5 conn = redis.Redis(connection_pool=pool) # 连接连接池 6 7 conn.zadd("z1", {"v11": 1, "v2": 2}) 8 print(conn.zrange("z1", 0, -1)) # 获取有序集合中所有元素 9 >>>[b'v11', b'v2'] # 返回一个列表 10 11 2、zcard(name) # 获取name对应的有序集合元素的数量 12 print(conn.zcard("z1")) 13 >>>2 14 15 3、zcount(name, min, max) # 获取name对应的有序集合中分数 在 [min,max] 之间的个数 16 17 4、zrange( name, start, end, desc=False, withscores=False, score_cast_func=float) # 按照索引范围获取name对应的有序集合的元素 18 """ 19 参数: 20 name,redis的name 21 start,有序集合索引起始位置(非分数) # 注意是索引 22 end,有序集合索引结束位置(非分数) 23 desc,排序规则,默认按照分数从小到大排序 24 withscores,是否获取元素的分数,默认只获取元素的值 25 score_cast_func,对分数进行数据转换的函数 26 """ 27 28 4.1、zrevrange(name, start, end, withscores=False, score_cast_func=float) # 从大到小排序(同zrange,集合是从大到小排序的) 29 4.2、zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float) # 按照分数范围获取name对应的有序集合的元素 30 4.3、zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float) # 按照分数范围获取有序集合的元素并排序(默认从大到小排序) 31 32 5、zscan(name, cursor=0, match=None, count=None, score_cast_func=float) # 获取所有元素--默认按照分数顺序排序 33 print(conn.zscan("z1")) 34 >>>(0, [(b'n1', 1.0), (b'v11', 1.0), (b'v2', 2.0)]) 35 36 5.1、zscan_iter(name, match=None, count=None,score_cast_func=float) # 获取所有元素--迭代器(这个非常有用) 37 print(conn.zscan_iter("z1")) 38 for i in conn.zscan_iter("z1"): 39 print(i) 40 41 >>><generator object Redis.zscan_iter at 0x00000010F26047D8> 42 (b'n1', 1.0) 43 (b'v11', 1.0) 44 (b'v2', 2.0) 45 46 6、zrem(name, values) # 删除--指定值删除 47 48 7、zremrangebyrank(name, min, max) # 删除--根据排行范围删除,按照索引号来删除 49 50 8、zscore(name, value) # 获取值对应的分数