一 简介
Redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
特点:
-
异常快速 : Redis是非常快的,每秒可以执行大约110000设置操作,81000个/每秒的读取操作。
-
支持丰富的数据类型 : Redis支持最大多数开发人员已经知道如列表,集合,可排序集合,哈希等数据类型。
这使得在应用中很容易解决的各种问题,因为我们知道哪些问题处理使用哪种数据类型更好解决。 -
操作都是原子的 : 所有 Redis 的操作都是原子,从而确保当两个客户同时访问 Redis 服务器得到的是更新后的值(最新值)。
-
MultiUtility工具:Redis是一个多功能实用工具,可以在很多如:缓存,消息传递队列中使用(Redis原生支持发布/订阅),在应用程序中,如:Web应用程序会话,网站页面点击数等任何短暂的数据
二 Redis安装
1. Ubuntu安装redis命令:
$ sudo apt-get update
$ sudo apt-get install redis-server
2. 启动redis:
$ redis-server
3. 进入redis命令提示行:
$ redis-cli
127.0.0.1:6379>
其中6379时redis服务运行的默认端口号。
4. 测试redis是否安装成功:
127.0.0.1:6379> ping
PONG
5. Redis配置用户验证
(1)配置访问Redis的密码
进入Redis配置文件,取消注释并设置密码。
$ sudo vim /etc/redis/redis.conf # requirepass foobared
保存配置,重启服务:
$ sudo /etc/init.d/redis-server restart [ ok ] Restarting redis-server (via systemctl): redis-server.service.
再次进入redis-cli命令行界面执行操作时,需要验证:
$ redis-cli 127.0.0.1:6379> keys * (error) NOAUTH Authentication required. 127.0.0.1:6379> auth 123456 #认证 OK 127.0.0.1:6379> keys * 1) "info" 2) "name1" 3) "name" 4) "foo" 5) "age" 127.0.0.1:6379> config get requirepass #查询密码 1) "requirepass" 2) "123456" 127.0.0.1:6379>
三 安装Redis桌面管理器
通过界面来管理redis键值。
下载并安装:https://redisdesktop.com/download
四 Python操作Redis
1. 安装redis模块:
1 sudo pip install redis 2 or 3 sudo easy_install redis 4 or 5 源码安装 6 7 详见:https://github.com/WoLpH/redis-py
2. 通过redis模块连接Redis
redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令,Redis是StrictRedis的子类,用于向后兼容旧版本的redis-py。
import redis
r = redis.Redis(host='127.0.0.1', port=6379, db=0) #db=0 表示连接第0个数据库;如果需要密码,添加password='密码'
r.set('foo', 'Bar')
print(r.get('foo'))
#b'Bar'
3. 连接池
redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池。
#连接池
pool = redis.ConnectionPool(host='127.0.0.1', port=6379, db=0) #;如果需要密码,添加password='密码'
r = redis.Redis(connection_pool=pool)
r.set('foo','Bar')
print(r.get('foo'))
#b'Bar'
五 命令参考
1 String操作
Redis中的String在在内存中按照name→value来存储。
(1)set(name, value, ex=None, px=None, nx=False, xx=False) 将字符串值 value 关联到 key 。 如果 key 已经持有其他值,SET 就覆写旧值,无视类型 # ex:过期时间(秒) # px:过期时间(毫秒) # nx:如果设置为True,则只有name不存在时,当前set才执行 # xx:如果设置为True,则只有name存在时,当前set才执行 (2)setex(name, time, value) 将值 value 关联到 key,并将 key 的生存时间设为 seconds (以秒为单位) 如果 key 已经存在,SETEX 命令将覆写旧值。 # time:生存时间,秒或python的timedelta对象 (3)setnx(name, value) 将 key 的值设为 value ,当且仅当 key 不存在 若给定的 key 已经存在,则 SETNX 不做任何动作 (4)psetex(name, time_ms, value) 这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。 (5)mset(*args, **kwargs) 同时设置一个或多个 key-value 对. # mset(k1='v1', k2='v2') #或 #mset({'k1': 'v1', 'k2': 'v2'}) 如果某个给定 key 已经存在,那么 MSET 会用新值覆盖原来的旧值 如果这不是你所希望的效果,请考虑使用 MSETNX 命令:它只会在所有给定 key 都不存在的情况下进行设置操作。 (6)setrange(name, offset, value) 用 value 参数覆写(overwrite)给定 key 所储存的字符串值,从偏移量 offset 开始。 不存在的 key 当作空白字符串处理。 # offset,字符串的索引,字节(一个汉字三个字节) # value,要设置的值 (7)setbit(name, offset, value) 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。 # name,redis的name # offset,位的索引(将值变换成二进制后再进行索引) # value,值只能是 1 或 0 #应用场景:用最省空间的方式,存储在线用户数及分别是哪些用户在线 (8)get(name) 返回 key 所关联的字符串值 如果 key 不存在那么返回特殊值 nil 假如 key 储存的值不是字符串类型,返回一个错误,因为 GET 只能用于处理字符串值 (9)getbit(name, offset) 对 key 所储存的字符串值,获取指定偏移量上的位(bit) 当 offset 比字符串值的长度大,或者 key 不存在时,返回 0 # offset,字符串的索引 (10)mget(keys, *args) 返回所有(一个或多个)给定 key 的值。 (11)getset(name, value) 将给定 key 的值设为 value ,并返回 key 的旧值(old value) 当 key 存在但不是字符串类型时,返回一个错误 (12)getrange(key, start, end) 截取字符串 # start,起始位置(字节) # end,结束位置(字节) (13)bitcount(key, start=None, end=None) 获取key对应的值的二进制表示中 1 的个数 # key,Redis的name # start,位起始位置 # end,位结束位置 (14)strlen(name) 返回name对应值的字节长度(一个汉字3个字节) (15)incr(name, amount=1) 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增 # name,Redis的name # amount,自增数(必须是整数) (16)incrbyfloat(name, amount=1.0) 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增 # name,Redis的name # amount,自增数(浮点型) (17)decr(name, amount=1) 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减 # name,Redis的name # amount,自减数(整数) (18)append(key, value) 在redis name对应的值后面追加内容 # key, redis的name # value, 要追加的字符串
2 Hash操作
hash表现形式上有些像pyhton中的dict,可以存储一组关联性较强的数据
(1)hset(name, key, value) name对应的hash中设置一个键值对(不存在,则创建;否则,修改) # name,redis的name # key,name对应的hash中的key # value,name对应的hash中的value (2)hmset(name, mapping) 在name对应的hash中批量设置键值对 # name,redis的name # mapping,字典,如:{'k1':'v1', 'k2': 'v2'} (3)hget(name,key) 在name对应的hash中获取根据key获取value (4)hmget(name, keys, *args) 在name对应的hash中获取多个key的值 # name,reids对应的name # keys,要获取key集合,如:['k1', 'k2', 'k3'] # *args,要获取的key,如:k1,k2,k3 (5)hgetall(name) 获取name对应hash的所有键值 (6)hlen(name) 获取name对应的hash中键值对的个数 (7)hkeys(name) 获取name对应的hash中所有的key的值 (8)hvals(name) 获取name对应的hash中所有的value的值 (9)hexists(name, key) 检查name对应的hash是否存在当前传入的key (10)hdel(name,*keys) 将name对应的hash中指定key的键值对删除 (11)hincrby(name, key, amount=1) 自增name对应的hash中的指定key的值,不存在则创建key=amount # name,redis中的name # key, hash对应的key # amount,自增数(整数) (12)hincrbyfloat(name, key, amount=1.0) 自增name对应的hash中的指定key的值,不存在则创建key=amount # name,redis中的name # key, hash对应的key # amount,自增数(浮点数) (13)hscan(name, cursor=0, match=None, count=None) 增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据, 并非一次性将数据全部获取完,从而放置内存被撑爆 # name,redis的name # cursor,游标(基于游标分批取获取数据) # match,匹配指定key,默认None 表示所有的key # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数 (14)hscan_iter(name, match=None, count=None) 利用yield封装hscan创建生成器,实现分批去redis中获取数据 # match,匹配指定key,默认None 表示所有的key # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
3 List操作
List操作,redis中的List在在内存中按照一个name对应一个List来存储
(1)lpush(name,values) 在name对应的list中添加元素,每个新的元素都添加到列表的最左边 (2)lpushx(name,value) 在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边 (3)llen(name) name对应的list元素的个数 (4)linsert(name, where, refvalue, value)) 在name对应的列表的某一个值前或后插入一个新值 # name,redis的name # where,BEFORE或AFTER # refvalue,标杆值,即:在它前后插入数据 # value,要插入的数据 (5)lset(name, index, value) 对name对应的list中的某一个索引位置重新赋值 # name,redis的name # index,list的索引位置 # value,要设置的值 (6)lrem(name, value, num) 在name对应的list中删除指定的值 # name,redis的name # value,要删除的值 # num, num=0,删除列表中所有的指定值; # num=2,从前到后,删除2个; # num=-2,从后向前,删除2个 (7)lpop(name) 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素 (8)lindex(name, index) 在name对应的列表中根据索引获取列表元素 (9)lrange(name, start, end) 在name对应的列表分片获取数据 (10)ltrim(name, start, end) 在name对应的列表中移除没有在start-end索引之间的值 (11)rpoplpush(src, dst) 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边 # src,要取数据的列表的name # dst,要添加数据的列表的name (12)blpop(keys, timeout) 将多个列表排列,按照从左到右去pop对应列表的元素 # keys,redis的name的集合 # timeout,超时时间,当元素所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0 表示永远阻塞 (13)brpoplpush(src, dst, timeout=0) 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧 # src,取出并要移除元素的列表对应的name # dst,要插入元素的列表对应的name # timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞
4 Set操作
Set集合就是不允许重复的列表
(1)sadd(name,values) name对应的集合中添加元素 (2)scard(name) 获取name对应的集合中元素个数 (3)sdiff(keys, *args) 在第一个name对应的集合中且不在其他name对应的集合的元素集合 (4)sdiffstore(dest, keys, *args) 获取第一个name对应的集合中且不在其他name对应的集合,再将其新加入到dest对应的集合中 (5)sinter(keys, *args) 获取多一个name对应集合的并集 (6)sinterstore(dest, keys, *args) 获取多一个name对应集合的并集,再讲其加入到dest对应的集合中 (7)sismember(name, value) 检查value是否是name对应的集合的成员 (8)smembers(name) 获取name对应的集合的所有成员 (9)smove(src, dst, value) 将某个成员从一个集合中移动到另外一个集合 (10)spop(name) 从集合的右侧(尾部)移除一个成员,并将其返回 (11)srandmember(name, numbers) 从name对应的集合中随机获取 numbers 个元素 (12)srem(name, values) 在name对应的集合中删除某些值 (13)sunion(keys, *args) 获取多一个name对应的集合的并集 (14)sunionstore(dest,keys, *args) 获取多一个name对应的集合的并集,并将结果保存到dest对应的集合中 (15)sscan(name, cursor=0, match=None, count=None) (16)sscan_iter(name, match=None, count=None) 同字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大
5 Sort Set操作
有序集合,在集合的基础上,为每元素排序;元素的排序需要根据另外一个值来进行比较,所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。
(1)zadd(name, *args, **kwargs) 在name对应的有序集合中添加元素 (2)zcard(name) 获取name对应的有序集合元素的数量 (3)zcount(name, min, max) 获取name对应的有序集合中分数 在 [min,max] 之间的个数 (4)zincrby(name, value, amount) 自增name对应的有序集合的 name 对应的分数 (5)zrange( name, start, end, desc=False, withscores=False, score_cast_func=float) 按照索引范围获取name对应的有序集合的元素 # name,redis的name # start,有序集合索引起始位置(非分数) # end,有序集合索引结束位置(非分数) # desc,排序规则,默认按照分数从小到大排序 # withscores,是否获取元素的分数,默认只获取元素的值 # score_cast_func,对分数进行数据转换的函数 (6)zrank(name, value) 获取某个值在 name对应的有序集合中的排行(从 0 开始) # zrevrank(name, value),从大到小排序 (7)zrem(name, values) 删除name对应的有序集合中值是values的成员 (8)zremrangebyrank(name, min, max) 根据排行范围删除 (9)zremrangebyscore(name, min, max) 根据分数范围删除 (10)zscore(name, value) 获取name对应有序集合中 value 对应的分数 (11)zinterstore(dest, keys, aggregate=None) 获取两个有序集合的交集,如果遇到相同值不同分数,则按照aggregate进行操作 # aggregate的值为: SUM MIN MAX (12)zunionstore(dest, keys, aggregate=None) 获取两个有序集合的并集,如果遇到相同值不同分数,则按照aggregate进行操作 # aggregate的值为: SUM MIN MAX (13)zscan(name, cursor=0, match=None, count=None, score_cast_func=float) (14)zscan_iter(name, match=None, count=None,score_cast_func=float)
6 其它操作
(1)delete(*names) 根据删除redis中的任意数据类型 (2)exists(name) 检测redis的name是否存在 (3)keys(pattern='*') 根据模型获取redis的name # KEYS * 匹配数据库中所有 key 。 # KEYS h?llo 匹配 hello , hallo 和 hxllo 等。 # KEYS h*llo 匹配 hllo 和 heeeeello 等。 # KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo (4)expire(name ,time) 为某个redis的某个name设置超时时间 (5)rename(src, dst) 对redis的name重命名为 (6)move(name, db)) 将redis的某个值移动到指定的db下 (7)randomkey() 随机获取一个redis的name(不删除) (8)type(name) 获取name对应值的类型 (9)scan(cursor=0, match=None, count=None) (10)scan_iter(match=None, count=None) 同字符串操作,用于增量迭代获取key
六 管道
redis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次pipline 是原子性操作。
import redis pool = redis.ConnectionPool(host='127.0.0.1', port=6379) r = redis.Redis(connection_pool=pool) # pipe = r.pipeline(transaction=False) pipe = r.pipeline(transaction=True) pipe.set('name', 'alex') pipe.set('role', 'sb') pipe.execute()
七 发布订阅
1 import redis 2 3 4 class RedisHelper: 5 6 def __init__(self): 7 self.__conn = redis.Redis(host='10.211.55.4') 8 self.chan_sub = 'fm104.5' 9 self.chan_pub = 'fm104.5' 10 11 def public(self, msg): 12 self.__conn.publish(self.chan_pub, msg) 13 return True 14 15 def subscribe(self): 16 pub = self.__conn.pubsub() 17 pub.subscribe(self.chan_sub) 18 pub.parse_response() 19 return pub
1 from monitor.RedisHelper import RedisHelper 2 3 obj = RedisHelper() 4 redis_sub = obj.subscribe() 5 6 while True: 7 msg= redis_sub.parse_response() 8 print(msg)
1 from monitor.RedisHelper import RedisHelper 2 3 obj = RedisHelper() 4 obj.public('hello')
八 关系型数据库 与 NoSQL如何选择
略
九 参考文档
1. http://doc.redisfans.com/
2. https://github.com/andymccurdy/redis-py/