缓存数据库介绍
NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库。NoSQL数据库的产生就是为了解决大规模数据集和多重数据种类带来的挑战,尤其是大数据应用难题。
NoSQL数据库的四大分类
这类数据库注意使用到一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据。
列存储数据库。
这部分数据库通常是用来应对分布式存储的海量数据,键任然存在,但是它们的特点是指向了多个列。这些列是由列家族来安排的。
文档型数据库
文档型数据库的灵感是来自于Lotus Notes办公软件,而且它同第一种键值存储类似。该类型的数据模型是版本化的文档,半结构化的文档以特定的格式存储,比如JSON.文档型数据库可以看作是键值数据库的升级版,允许键值之间嵌套键值。而且文档型数据库比键值数据库的查询效率高。
图形(Graph)数据库
图形结构的数据库同其他行列以及刚性结构的SQL数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务器上。NoSQL数据库没有标准的查询语言SQL.因此进行数据库查询需要制定数据模型。许多NoSQL数据库都有REST式的数据接口或者查询API.如:Neo4J, InfoGrid, Infinite Graph.
总结:
NoSQL数据库在以下的这几种情况下比较适用:
1数据模型比较简单;
2需要灵活性更强的IT系统;
3对数据库性能要求较高;
4不需要高度的数据一致性;
5对于给定key,比较容易映射复杂值的环境。
NoSQL数据库的四大分类表格分析
redis
redis是业界主流的key-value nosql 数据库之一.
和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。
这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的.
在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis优点
- 异常快速 : Redis是非常快的,每秒可以执行大约110000设置操作,81000个/每秒的读取操作。
- 支持丰富的数据类型 : Redis支持最大多数开发人员已经知道如列表,集合,可排序集合,哈希等数据类型。
- 操作都是原子的 : 所有 Redis 的操作都是原子,从而确保当两个客户同时访问 Redis 服务器得到的是更新后的值(最新值)。
- MultiUtility工具:Redis是一个多功能实用工具,可以在很多如:缓存,消息传递队列中使用(Redis原生支持发布/订阅),在应用程序中,如:Web应用程序会话,网站页面点击数等任何短暂的数据;
安装Redis环境
$sudo apt-get update $sudo apt-get install redis-server
启动Redis
$redis-server
redis 127.0.0.1:6379>
redis 127.0.0.1:6379> ping PONG
这说明现在你已经成功地在计算机上安装了Redis
Python操作Redis
sudo pip install redis
or
sudo easy_install redis
or
源码安装
详见:https://github.com/WoLpH/redis-py
在Ubuntu上安装Redis桌面管理器
要在Ubuntu 上安装 Redis桌面管理,可以从 http://redisdesktop.com/download 下载包并安装它。
Redis API使用
redis-py 的API的使用可以分类为:
- 连接方式
- 连接池
- 操作
- String 操作
- Hash 操作
- List 操作
- Set 操作
- Sort Set 操作
- 管道
- 发布订阅
连接方式
import redis r = redis.Redis(host='10.211.55.4', port=6379) r.set('foo', 'Bar') print r.get('foo')
2、连接池
操作
1. String操作
redis中的String在内存中按照一个name对应一个value来存储。
set
在redis中设置值,不存在则创建,存在则修改,参数:ex:过期时间(秒),px:过期时间(毫秒)
setnx
设置值,只有name不存在时,才执行设置操作
setex
设置带失效时间的值,秒
psetex
设置带失效时间的值,毫秒
mset ,mget
批量设置获取值
getset
设置新值,并获取原来的值
getrange
获取子序列(根据字节获取,非字符)
setrange
修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)
setbit
对name对应值的二进制表示的位进行操作
注:如果在Redis中有一个对应: n1 = "foo",
那么字符串foo的二进制表示为:
01100110
01101111
01101111
所以,如果执行 setbit(
'n1'
,
7
,
1
),则就会将第
7
位设置为
1
,
那么最终二进制则变成
01100111
01101111
01101111
,即:
"goo"
getbit
获取name对应的值的二进制表示中的某位的值(0或者1)
bitcount
获取name对应的值的二进制表示中1的个数
strlen
返回name对应值的字节长度,一个汉字三个字节
incrbyfloat
自增,name对应的值,当name不存在时,则创建name=amount,否则自增
decr
自减
append
在name对应的值后面追加内容
2. Hash操作
hash表现形式上有些像pyhton中的dict,可以存储一组关联性较强的数据 , redis中Hash在内存中的存储格式如下图:
hset,hget
在name对应的hash中设置一个键值对,不存在则创建,否则修改
hmset,hmget
批量设置获取设置的键值对
hgetall\
获取name对应的hash的所有键值
hlen
获取name对应hash的键值对的个数
hkeys
获取name对应的hash中所有的key的值
hvals
获取name对应的hash中所有的value的值
hexists
检查name对应的hash是否存在当前传入的key
hdel
删除指定键值对
hincrby
自增name对应的hash中的指定key的值,不存在则创建key=amount
hscan_iter(name, match=None, count=None)
1
2
3
4
5
6
7
8
9
|
# 利用yield封装hscan创建生成器,实现分批去redis中获取数据
# 参数:
# match,匹配指定key,默认None 表示所有的key
# count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数
# 如:
# for item in r.hscan_iter('xx'):
# print item
|
HSCAN myhash 0 MATCH order_* COUNT 1000
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 增量式迭代获取,对于数据大的数据非常有用,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时,表示数据已经通过分片获取完毕
|
List
List操作,redis中的List在在内存中按照一个name对应一个List来存储。如图:
lpush,lrange,rpush
在name对应的list中添加元素,每个新的元素都添加到列表的最左边
,
llen
name对应的list元素的个数
linsert
在name对应的列表的某一个值前后插入一个新值
lpop
在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素
rpop(name) 表示从右向左操作
lindex
在name对应的列表中根据索引获取列表元素
ltrim
在name对应的列表中移除没有在start-end索引之间的值
rpoplpush
从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边
blpop
# 将多个列表排列,按照从左到右去pop对应列表的元素 # 参数: # keys,redis的name的集合 # timeout,超时时间,当元素所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0 表示永远阻塞 # 更多: # r.brpop(keys, timeout),从右向左获取数据
brpoplpush(src, dst, timeout=0)
# 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧 # 参数: # src,取出并要移除元素的列表对应的name # dst,要插入元素的列表对应的name # timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞
set集合操作
Set操作,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) zcard(name) # 获取name对应的有序集合元素的数量 zcount(name, min, max) # 获取name对应的有序集合中分数 在 [min,max] 之间的个数 zincrby(name, value, amount) # 自增name对应的有序集合的 name 对应的分数 r.zrange( name, start, end, desc=False, withscores=False, score_cast_func=float) # 按照索引范围获取name对应的有序集合的元素 # 参数: # name,redis的name # start,有序集合索引起始位置(非分数) # end,有序集合索引结束位置(非分数) # desc,排序规则,默认按照分数从小到大排序 # withscores,是否获取元素的分数,默认只获取元素的值 # score_cast_func,对分数进行数据转换的函数 # 更多: # 从大到小排序 # zrevrange(name, start, end, withscores=False, score_cast_func=float) # 按照分数范围获取name对应的有序集合的元素 # zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float) # 从大到小排序 # zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float) zrank(name, value) # 获取某个值在 name对应的有序集合中的排行(从 0 开始) # 更多: # zrevrank(name, value),从大到小排序 zrem(name, values) # 删除name对应的有序集合中值是values的成员 # 如:zrem('zz', ['s1', 's2']) zremrangebyrank(name, min, max) # 根据排行范围删除 zremrangebyscore(name, min, max) # 根据分数范围删除 zscore(name, value) # 获取name对应有序集合中 value 对应的分数 zinterstore(dest, keys, aggregate=None) # 获取两个有序集合的交集,如果遇到相同值不同分数,则按照aggregate进行操作 # aggregate的值为: SUM MIN MAX zunionstore(dest, keys, aggregate=None) # 获取两个有序集合的并集,如果遇到相同值不同分数,则按照aggregate进行操作 # aggregate的值为: SUM MIN MAX zscan(name, cursor=0, match=None, count=None, score_cast_func=float) zscan_iter(name, match=None, count=None,score_cast_func=float) # 同字符串相似,相较于字符串新增score_cast_func,用来对分数进行操作
其他常用操作
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, dst) # 对redis的name重命名为 move(name, db)) # 将redis的某个值移动到指定的db下 randomkey() # 随机获取一个redis的name(不删除) type(name) # 获取name对应值的类型 scan(cursor=0, match=None, count=None) scan_iter(match=None, count=None) # 同字符串操作,用于增量迭代获取key
管道
redis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次pipline 是原子性操作。
#!/usr/bin/env python # -*- coding:utf-8 -*- 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', 'alex') pipe.set('role', 'sb') pipe.execute()
发布订阅
发布者:服务器
订阅者:Dashboad和数据处理
Demo如下:
import redis class RedisHelper: def __init__(self): self.__conn = redis.Redis(host='10.211.55.4') self.chan_sub = 'fm104.5' self.chan_pub = 'fm104.5' def public(self, msg): self.__conn.publish(self.chan_pub, msg) return True def subscribe(self): pub = self.__conn.pubsub() pub.subscribe(self.chan_sub) pub.parse_response() return pub redis helper
订阅者:
#!/usr/bin/env python # -*- coding:utf-8 -*- from monitor.RedisHelper import RedisHelper obj = RedisHelper() redis_sub = obj.subscribe() while True: msg= redis_sub.parse_response() print msg
发布者:
#!/usr/bin/env python # -*- coding:utf-8 -*- from monitor.RedisHelper import RedisHelper obj = RedisHelper() obj.public('hello')