什么是Redis?
Redis是一个TCP服务器,支持请求/响应协议。 在Redis中,请求通过以下步骤完成:
- 客户端向服务器发送查询,并从套接字读取,通常以阻塞的方式,用于服务器响应。
- 服务器处理命令并将响应发送回客户端。
Redis是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库,缓存和消息中间件。它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
那么问题来了,既然我们要说的是缓存数据库redis,而只是了解了redis,那么什么又是缓存数据库,Redis和缓存数据库又是什么关系?
缓存数据库
NoSQL(nosql = not only SQL),意为不仅仅是数据库。
NoSQL,泛指非关系型的数据库,随着互联网web2.0网站的兴起,传统的关系型数据库应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模集合多重数据种类带来的挑战,尤其是大数据应用难题。
所以如果有人说NoSQL就是Redis,那么就大错特错,Redis只是NoSQL四大类中的一种。
NoSQL数据库的四大分类
键值(Key-Value)存储数据库
这一类数据库主要会使用到一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据。Key/value模型对于IT系统来说的优势在于简单、易部署。但是如果DBA只对部分值进行查询或更新的时候,Key/value就显得效率低下了。[3] 举例如:Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB.
列存储数据库。
这部分数据库通常是用来应对分布式存储的海量数据。键仍然存在,但是它们的特点是指向了多个列。这些列是由列家族来安排的。如:Cassandra, HBase, Riak.
文档型数据库
文档型数据库的灵感是来自于Lotus Notes办公软件的,而且它同第一种键值存储相类似。该类型的数据模型是版本化的文档,半结构化的文档以特定的格式存储,比如JSON。文档型数据库可 以看作是键值数据库的升级版,允许之间嵌套键值。而且文档型数据库比键值数据库的查询效率更高。如:CouchDB, MongoDb. 国内也有文档型数据库SequoiaDB,已经开源。
图形(Graph)数据库
图形结构的数据库同其他行列以及刚性结构的SQL数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务器上。NoSQL数据库没有标准的查询语言(SQL),因此进行数据库查询需要制定数据模型。许多NoSQL数据库都有REST式的数据接口或者查询API。[2] 如:Neo4J, InfoGrid, Infinite Graph.
因此,我们总结NoSQL数据库在以下的这几种情况下比较适用:
- 1、数据模型比较简单;
- 2、需要灵活性更强的IT系统;
- 3、对数据库性能要求较高;
- 4、不需要高度的数据一致性;
- 5、对于给定key,比较容易映射复杂值的环境。
分类 | Examples举例 | 典型应用场景 | 数据模型 | 优点 | 缺点 |
键值 (key-value) |
Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB |
内容缓存, 主要用于处理大量数据的高访问负载, 也用于一些日志系统等等。 |
Key 指向 Value 的键值对,通常用 hash table来实现 |
查找速度快 |
数据无结构化, 通常只被当作字 符串或者二进制 数据 |
列存储数据库 |
Cassandra, HBase, Riak |
分布式的文件系统 |
以列簇式存储, 将同一列数据存在 一起 |
查找速度快, 可扩展性强, 更容易进行 分布式扩展 |
功能相对局限 |
文档型数据库 |
CouchDB,MongoDb |
Web应用(与Key-Value类似, Value是结构化的,不同的是数 据库能够了解Value的内容) |
Key-Value对应 的键值对,Value 为结构化数据 |
数据结构要求 不严格,表结构 可变,不需要像 关系型数据库一 样要预先定义表 结构 |
查询性能不高, 而且缺乏统一 的查询语法。 |
图形(Graph) 数据库 |
Neo4J, InfoGrid, Infinite Graph |
社交网络,推荐系统等。 专注于构建关系图谱 |
图结构 |
利用图结构相关算 法。比如最短路径 寻址,N度关系查 找等 |
很多时候需要对整 个图做计算才能得 出需要的信息,而 且这种结构不太好 做分布式的集群方 案。 |
适用场景
- 1、数据模型比较简单;
- 2、需要灵活性更强的IT系统;
- 3、对数据库性能要求较高;
- 4、不需要高度的数据一致性;
- 5、对于给定key,比较容易映射复杂值的环境。
Redis
redis是业界主流的key-value nosql 数据库之一。和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应用程序会话,网站页面点击数等任何短暂的数据。
使用前,需要对其环境安装配置
在Ubuntu下,只需要输入apt-get install redis-server即可。安装完成后,redis-server启动服务,然后redis-cli就能对其进行操作了。
我们可以对其设置密码,以保证其安全性 进入 vi /etc/redis.conf下改为 requirepass ‘密码‘ 即可.
远程链接时redis-cli -h 127.0.0.1 -p 6379 -a "mypassword"
在python环境下,需要 pip install redis
如果在python上链接不上则需要修好配置文件的下的bind,进入 vi /etc/redis.conf下改为 bind 0.0.0.0 即可。
连接方式,没有密码时,则不用填写password
import redis r = redis.Redis(host=‘10.211.55.4‘, port=6379,password=‘mypassword‘)
Redis的常用操作
String
redis中的String在在内存中按照一个name对应一个value来存储
查看set 命令什么用法
get(name): 获取值
mget(keys, *args)
批量获取 如: mget(‘ylr‘, ‘wupeiqi‘) 或 r.mget([‘ylr‘, ‘wupeiqi‘])
127.0.0.1:6379> help set SET key value [EX seconds] [PX milliseconds] [NX|XX] summary: Set the string value of a key since: 1.0.0 group: string
在Redis中设置值,默认,不存在则创建,存在则修改 参数: ex,过期时间(秒) px,过期时间(毫秒) nx,如果设置为True,则只有name不存在时,当前set操作才执行 xx,如果设置为True,则只有name存在时,岗前set操作才执行
setnx(name, value)
设置值,只有name不存在时,执行设置操作(添加)
setex(name, time,value)
# 设置值 # 参数:# time,过期时间(数字秒 或 timedelta对象)
psetex(name, time_ms, value)
# 设置值 # 参数:# time_ms,过期时间(数字毫秒 或 timedelta对象)
mset(*args, **kwargs)
批量设置值 如: mset(k1=‘v1‘, k2=‘v2‘) 或 mget({‘k1‘: ‘v1‘, ‘k2‘: ‘v2‘})
getset(name, value)
设置新值并获取原来的值
getrange(key, start, end)
# 获取子序列(根据字节获取,非字符) # 一个汉子3个字节
setrange(name, offset, value)
# 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加) # 参数: # offset,字符串的索引,字节(一个汉字三个字节) # value,要设置的值
setbit(name, offset, value)
# 对name对应值的二进制表示的位进行操作 # 参数: # name,redis的name # offset,位的索引(将值变换成二进制后再进行索引) # value,值只能是 1 或 0
getbit(name, offset)
# 获取name对应的值的二进制表示中的某位的值 (0或1)
bitcount(key, start=None, end=None)
# 获取name对应的值的二进制表示中 1 的个数 # 参数: # key,Redis的name # start,位起始位置 # end,位结束位置
strlen(name)
# 返回name对应值的字节长度(一个汉字3个字节)
incr(self, name, amount=1)
# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。 # 参数: # name,Redis的name # amount,自增数(必须是整数) # 注:同incrby
decr(self, name, amount=1) : 自减
incrbyfloat(self, name, amount=1.0)
# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。 # 参数: # name,Redis的name # amount,自增数(浮点型)
append(key, value)
# 在redis name对应的值后面追加内容 # 参数: key, redis的name value, 要追加的字符串
Hash操作
hash表现形式上有些像pyhton中的dict,可以存储一组关联性较强的数据,在Redis中,每个哈希(散列)可以存多达4亿个键值对
hscan(name, cursor=0, match=None, count=None)
# 增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆 # 参数: # name,redis的name # cursor,游标(基于游标分批取获取数据) # match,匹配指定key,默认None 表示所有的key # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数 # 如: # 第一次:cursor1, data1 = r.hscan(‘xx‘, cursor=0, match=None, count=None) # 第二次:cursor2, data1 = r.hscan(‘xx‘, cursor=cursor1, match=None, count=None) # ... # 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕
hscan_iter(name, match=None, count=None)
# 利用yield封装hscan创建生成器,实现分批去redis中获取数据 # 参数: # match,匹配指定key,默认None 表示所有的key # count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
常用的命令如下
命令 | 解释 |
HDEL key field2 [field2] | 删除一个或多个哈希字段。 |
HEXISTS key field | 判断是否存在散列字段。 |
HGET key field | 获取存储在指定键的哈希字段的值。 |
HGETALL key | 获取存储在指定键的哈希中的所有字段和值 |
HINCRBY key field increment | 将哈希字段的整数值按给定数字增加 |
HINCRBYFLOAT key field increment | 将哈希字段的浮点值按给定数值增加 |
HKEYS key | 获取哈希中的所有字段 |
HLEN key | 获取散列中的字段数量 |
HMGET key field1 [field2] | 获取所有给定哈希字段的值 |
HMSET key field1 value1 [field2 value2 ] | 为多个哈希字段分别设置它们的值 |
HSET key field value | 设置散列字段的字符串值 |
HSETNX key field value | 仅当字段不存在时,才设置散列字段的值 |
HVALS key | 获取哈希中的所有值 |
List
List操作,redis中的List在在内存中按照一个name对应一个List来存储
lpush(name,values)
# 在name对应的list中添加元素,每个新的元素都添加到列表的最左边 # 如: # r.lpush(‘oo‘, 11,22,33) # 保存顺序为: 33,22,11 # 扩展: # rpush(name, values) 表示从右向左操作
其余命令详情列表如下
命令 | 解释 |
BLPOP key1 [key2 ] timeout | 删除并获取列表中的第一个元素,或阻塞,直到有一个元素可用 |
BRPOP key1 [key2 ] timeout | 删除并获取列表中的最后一个元素,或阻塞,直到有一个元素可用 |
BRPOPLPUSH source destination timeout | 从列表中弹出值,将其推送到另一个列表并返回它; 或阻塞,直到一个可用 |
LINDEX key index | 通过其索引从列表获取元素 |
LINSERT key BEFORE/AFTER pivot value | 在列表中的另一个元素之前或之后插入元素 |
LLEN key | 获取列表的长度 |
LPOP key | 删除并获取列表中的第一个元素 |
LPUSH key value1 [value2] | 将一个或多个值添加到列表 |
LPUSHX key value | 仅当列表存在时,才向列表添加值 |
LRANGE key start stop | 从列表中获取一系列元素 |
LREM key count value | 从列表中删除元素 |
LSET key index value | 通过索引在列表中设置元素的值 |
LTRIM key start stop | 修剪列表的指定范围 |
RPOP key | 删除并获取列表中的最后一个元素 |
RPOPLPUSH source destination | 删除列表中的最后一个元素,将其附加到另一个列表并返回 |
RPUSH key value1 [value2] | 将一个或多个值附加到列表 |
RPUSHX key value | 仅当列表存在时才将值附加到列表 |
Set集合
就是不允许重复的列表
sadd(name,values)
# name对应的集合中添加元素
scard(name)
获取name对应的集合中元素个数
sdiff(keys, *args)
在第一个name对应的集合中且不在其他name对应的集合的元素集合
sdiffstore(dest, keys, *args)
获取第一个name对应的集合中且不在其他name对应的集合,再将其新加入到dest对应的集合中
sinter(keys, *args)
获取多一个name对应集合的并集
sinterstore(dest, keys, *args)
获取多一个name对应集合的并集,再讲其加入到dest对应的集合中
sismember(name, value)
检查value是否是name对应的集合的成员
smembers(name)
获取name对应的集合的所有成员
smove(src, dst, value)
将某个成员从一个集合中移动到另外一个集合
spop(name)
从集合的右侧(尾部)移除一个成员,并将其返回
srandmember(name, numbers)
从name对应的集合中随机获取 numbers 个元素
srem(name, values)
在name对应的集合中删除某些值
sunion(keys, *args)
获取多一个name对应的集合的并集
sunionstore(dest,keys, *args)
获取多一个name对应的集合的并集,并将结果保存到dest对应的集合中
sscan(name, cursor=0, match=None, count=None)
递增地迭代集合中的元素
sscan_iter(name, match=None, count=None)
同字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大
有序集合,在集合的基础上,为每元素排序;元素的排序需要根据另外一个值来进行比较,所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。
zadd(name, *args, **kwargs)
# 在name对应的有序集合中添加元素 # 如: # zadd(‘zz‘, ‘n1‘, 1, ‘n2‘, 2) # 或 # zadd(‘zz‘, n1=11, n2=22)
zrank(name, value)
获取某个值在 name对应的有序集合中的排行(从 0 开始) # 更多: # zrevrank(name, value),从大到小排序
其余大致和无序集合用法相同。
其它常用操作
delete(*names)
根据删除redis中的任意数据类型
exists(name)
检测redis的name是否存在
keys(pattern=‘*‘)
根据模型获取redis的name
更多:
# KEYS * 匹配数据库中所有 key 。
# KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
# KEYS h*llo 匹配 hllo 和 heeeeello 等。
# KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo
expire(name ,time)
为某个redis的某个name设置超时时间
rename(src, newsrc)
对redis的name重命名为
move(name, db))
将redis的某个值移动到指定的db下
randomkey()
随机获取一个redis的name(不删除)
type(name)
获取name对应值的类型
Redis管道
管道的基本含义是,客户端可以想服务端发送多个请求,而不必等待回复,并最终在一个步骤中等待回复。
在python中管道的用法如下
由于edis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次pipline 是原子性操作。
import redis pool = redis.ConnectionPool(host=‘10.211.55.4‘, port=6379) r = redis.Redis(connection_pool=pool) # pipe = r.pipeline(transaction=False) pipe = r.pipeline(transaction=True) pipe.set(‘name‘, ‘aa‘) pipe.set(‘role‘, ‘bb‘) pipe.execute()
管道的好处
大大的提高的协议性能,通过管道从连接到本地主机速度增加5倍,英特网的连接至少快100倍。
Redis发布订阅
Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
Redis 发布订阅(pub/sub)实现了消息系统,发送者(在redis术语中称为发布者)在接收者(订阅者)接收消息时发送消息。传送消息的链路称为信道。
在python中实现代码如下
from monitor.RedisHelper import RedisHelper obj = RedisHelper() obj.public(‘hello‘)
发布者
from monitor.RedisHelper import RedisHelper obj = RedisHelper() redis_sub = obj.subscribe() while True: msg= redis_sub.parse_response() print msg
订阅者