缓存系统
从以下几个维度,对 redis、memcache、mongoDB 做了对比。
1、性能
都比较高,性能对我们来说应该都不是瓶颈。
总体来讲,TPS 方面 redis 和 memcache 差不多,要大于 mongodb。
2、操作的便利性
memcache 数据结构单一。(key-value)
redis 丰富一些,数据操作方面,redis 更好一些,较少的网络 IO 次数,同时还提供 list,set,
hash 等数据结构的存储。
mongodb 支持丰富的数据表达,索引,最类似关系型数据库,支持的查询语言非常丰富。
3、内存空间的大小和数据量的大小
redis 在 2.0 版本后增加了自己的 VM 特性,突破物理内存的限制;可以对 key value 设置过
期时间(类似 memcache)
memcache 可以修改最大可用内存,采用 LRU 算法。Memcached 代理软件 magent,比如建立
10 台 4G 的 Memcache 集群,就相当于有了 40G。 magent -s 10.1.2.1 -s 10.1.2.2:11211 -b
10.1.2.3:14000 mongoDB 适合大数据量的存储,依赖操作系统 VM 做内存管理,吃内存也比较厉害,服务
不要和别的服务在一起。
4、可用性(单点问题)
对于单点问题,
redis,依赖客户端来实现分布式读写;主从复制时,每次从节点重新连接主节点都要依赖整
个快照,无增量复制,因性能和效率问题,
所以单点问题比较复杂;不支持自动 sharding,需要依赖程序设定一致 hash 机制。
一种替代方案是,不用 redis 本身的复制机制,采用自己做主动复制(多份存储),或者改成
增量复制的方式(需要自己实现),一致性问题和性能的权衡
Memcache 本身没有数据冗余机制,也没必要;对于故障预防,采用依赖成熟的 hash 或者环
状的算法,解决单点故障引起的抖动问题。
mongoDB 支持 master-slave,replicaset(内部采用 paxos 选举算法,自动故障恢复),auto sharding 机制,对客户端屏蔽了故障转移和切分机制。
5、可靠性(持久化)
对于数据持久化和数据恢复,
redis 支持(快照、AOF):依赖快照进行持久化,aof 增强了可靠性的同时,对性能有所影
响
memcache 不支持,通常用在做缓存,提升性能;
MongoDB 从 1.8 版本开始采用 binlog 方式支持持久化的可靠性
6、数据一致性(事务支持)
Memcache 在并发场景下,用 cas 保证一致性redis 事务支持比较弱,只能保证事务中的每个操作连续执行
mongoDB 不支持事务
7、数据分析
mongoDB 内置了数据分析的功能(mapreduce),其他不支持
8、应用场景
redis:数据量较小的更性能操作和运算上
memcache:用于在动态系统中减少数据库负载,提升性能;做缓存,提高性能(适合读多写
少,对于数据量比较大,可以采用 sharding)
MongoDB:主要解决海量数据的访问效率问题。
表格比较:
memcache redis 类型 内存数据库 内存数据库
数据类型 在定义 value 时就要固定数据类型 不需要
有字符串,链表,集 合和有序集合
虚拟内存 不支持 支持
过期策略 支持 支持
分布式 magent master-slave,一主一从或一主多从
存储数据安全 不支持 使用 save 存储到 dump.rdb 中
灾难恢复 不支持 append only file(aof)用于数据恢复
性能
1、类型——memcache 和 redis 都是将数据存放在内存,所以是内存数据库。当然,memcache 也可用于缓存其他东西,例如图片等等。
2、 数据类型——Memcache 在添加数据时就要指定数据的字节长度,而 redis 不需要。
3、 虚拟内存——当物理内存用完时,可以将一些很久没用到的 value 交换到磁盘。
4、 过期策略——memcache 在 set 时就指定,例如 set key1 0 0 8,即永不过期。Redis 可以通
过例如 expire 设定,例如 expire name 10。
5、 分布式——设定 memcache 集群,利用 magent 做一主多从;redis 可以做一主多从。都可
以一主一从。
6、 存储数据安全——memcache 断电就断了,数据没了;redis 可以定期 save 到磁盘。
7、 灾难恢复——memcache 同上,redis 丢了后可以通过 aof 恢复。
redis 实际上是单线程的,通过epoll实现的多并发,效率相当高
Linux 下redis 命令
命令行
redis-server 启动redis-server服务
redis-cli 运行
set name wch 设置key name value wch
keys * 查询所有key
set name wch ex 2 代表wch这个key只存在两秒钟
Redis 测试1
2
3
4
5# Author: Diedline
import redis
r = redis.Redis(host="192.168.167.128", port="6379")
r.set('foo','bar')
print(r.get('foo'))
连接池1
2
3
4
5
6# Author: Diedline
import redis
pool = redis.ConnectionPool(host="192.168.167.128", port="6379")
r = redis.Redis(connection_pool=pool)
r.set('foo','bar')
print(r.get('foo'))
在redis中设置值,默认,不存在则创建,存在则修改
参数
ex,过期时间(秒)
px过期时间(毫秒)
nx 如果设置为true,则只有name不存在的时候,当前Set操作才执行
xx 如果设置为ture,则只有name存在时,当前Set操作才执行
setnx(name, value)
设置值只有在name 不存在的时候,执行操作(添加)
setex(name,value,time)
相当于就是加了 ex这个参数,能设置过期时间秒计算
psetex(name,value,time)
相当于加了px 这个参数,设置过期时间 毫秒 计算
mset(*args, **kwargs)
批量设置值
如:
mset(k1 = “v1”,k2 = “v2”)
或mget({“k1”:“v1”,”k2”:”v2”})
getset(name,value)
设置新值,并返回原来的值。
getrange(key,start,end)
如getrange(n1,0,2)
输出“jac” 即取前三个字符
对字符串的切片操作
setrange(name,offset,value)
如setrange n2 0 p
即将n2为key对应的值的第0个位置的字符替换为P
setbit n4 111 1
设置n4为key的111位二进制为1
BITCOUNT n1
统计n1为key 的字符串中有多少二进制1
getbit n4 111
查找n4 111位的二进制位值并输出
strlen(name)
返回name对应值的字符串长度
incr(self,name,amount=1)
decr(self,name,amount =1)
可以相当于以name为key一个计数器
incr增加decr减少
如:
incr login
decr login
INCRBYFLOAT(self,name,amount=1.2)
如 INCRBYFLOAT login 1.6
APPEND 追加
APPEND name li
在以name的key 的value后边追加li。
redis hash 操作
hest 添加信息
如:添加一条hset info name wch
HGETALL info显示所有信息
HKEYS info 显示所有key信息
HVALS info 显示所有值的信息
HMSET info2 k1 1 k2 2 批量设置key k1=1 k2=2
hkeys info2 输出info2中key的值
hgetall info2 输出info2的所有信息
hlen(name)
获取name中所对应的hash键值对的个数
hexists info2 k3
判断info2中是否有k3有返回1没有返回0
HINCRBY info2 k2 1 像这样简单的key能存200多亿个
将info中的k2的值加1
增量式迭代获取
hscan info 0 match n
从info中找出包含n的key 值和对应的value
hscan info 0 match *a
从hscan中获取开头为a的key 值和对应的value
hscan_iter(name, match =None, count = None )
利用yield封装hscan创建生成器,实现分配去除redis中获取数据
参数:
match匹配指定Key,默认none,表示所有的key。
count ,每次分片最少获得次数,默认none采用radis的默认分片个数
如
for item in r,hscan_iter(‘xx’):
print item
列表操作
lpush names alex hanyan zhangyang
先入后出的列表names中 顺序为zhang hanyan alex
RPUSH names wangseng
先入先出的列表names中,在后面加wangseng
lrange names 0 -1 取出列表中的所有值
linsert(name, where, refvalue, value)
在name对应的列表的某一个值前或后插入一个新值
参数:
name,redis的name
where,BEFORE或AFTER
refvalue,标杆值,即:在它前后插入数据
value 即你要插入的数据
如:
LINSERT names BEFORE alex TEXT
在names列表中 alex前的值插入 TEXT
如:
LSET names 3 ALEX
修改names 位置为3的值为 ALEX
r.lren(name, value, num)
对name所对应的 list 中删除指定的值
参数:
name, redis的name
value , 要删除的值
num, num=0删除列表所有的指定值
num=2 从前到后,删除两个。
num=-2 ,从后向前,删除两个。
如 LREM names 1 TEXT
删除names 1 中de text值
lpop(name)
在name对应 的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素。
更多:
rpop(name)表示从右向左操作
lindex(name, index)
在name对应的列表中根据索引获取列表元素
ltrim(name, start, end)
在name对应的列表中移除没有在start-end的索引之间的值
参数:
name:redis 的name
start,索引的起始位置
end,索引的结束位置
rpoplpush(src ,dst)
从一个参数取出列表最右边的元素,同时将其添加到另一个列表的最左侧
参数:
src,要取数据的列表的name
dst, 要添加的数据的列表的name
blpop(keys, timeout)
将多个列表排列,按照从左到右去pop对应的元素
参数:
keys:radis name的集合
timeout:超时时间,当元素所有列表都获取完毕时,阻塞等待列表中有数据的 时间(s),0表示永远阻塞。
更多:
r.brpop(keys, timeout)从右向左获取数据
brpoplpush(src, dst,timeout =0)
从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧
参数:
src: 取出并要移除元素的列表对应的name
dst: 要插入元素的列表的name
timeout: 当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(s),0表示永远阻塞。
redis中的集合
SADD names3 alex p jack 3
在names3的集合中增加 alex p jack 3 这四个元素
SMEMBERS names3
获取names3中所有元素的值
SCARD names3
获取names3中元素的个数
SDIFF names3 name4
获取names3中 在name4不同的一部分
如:
names3
127.0.0.1:6379> SMEMBERS names3
1) “3”
2) “alex”
3) “p”
4) “jack”
name4
127.0.0.1:6379> SMEMBERS name4
1) “alex”
2) “543”
3) “dnisd”
4) “ppp”
输出:
127.0.0.1:6379> SDIFF names3 name4
1) “3”
2) “p”
3) “jack”
SDIFFSTORE
获取第一个name对应的集合且不再其他name 对应的集合,再将其新加入到dest对 应的集合中
例:
127.0.0.1:6379> SDIFFSTORE n6 names3 name4
(integer) 3
127.0.0.1:6379> SMEMBERS n6
1) “3”
2) “p”
3) “jack”
SINTER
获取两个集合的交集
127.0.0.1:6379> SINTER names3 name4
1)”alex”
sinterstore(dest , keys, *args)
获取一个name对应集合的并集,在讲其加入到dest对应的集合中。
sismember(name, value)
检查value 是否是name 所对应的集合的成员,有返回1
如:
127.0.0.1:6379> SISMEMBER names3 alex
(integer) 1
smembers(name)
获取name对应的集合的所有成员
smove(src, dst, value)
将某个成员从一个集合中移到另一个集合
spop(name)
从集合的右侧(尾部)移除一个成员并将其返回
srandmember(name, numbers)
从name对应的集合中随机获取numbers个元素
srem(name, value)
在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)
字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大。
如:
127.0.0.1:6379> SUNIONSTORE n7 names3 name4
(integer) 4
127.0.0.1:6379> sscan n7 0 match a*
1) “0”
2) 1) “alex”
和上面的列表迭代差不多输出结果的”0”是光标的位置”alex是输出”
有序集合
zadd添加有序集合
zrange 切片显示值
127.0.0.1:6379> zadd z1 10 alex 5 jack 8 rain
(integer) 3
127.0.0.1:6379> ZRANGE z1 0 -1
1) “jack”
2) “rain”
3) “alex”
集合是从小到大排列的
当你增加一个已有的值时,但是对应的分数不一样,
后面的会把分数给替换掉,所以排名会变
127.0.0.1:6379> ZADD z1 7 alex
(integer) 0
127.0.0.1:6379> ZRANGE z1 0 -1
1) “jack”
2) “alex”
3) “rain”
你可以在使用zrange来查询的时候加上 withscores来显示每个值的分数,
所以最直接的应用就是直接列出全班的分数。你的成绩就是你的分数,你的名字就是那个值。
127.0.0.1:6379> ZRANGE z1 0 -1 withscores
1) “jack”
2) “5”
3) “alex”
4) “7”
5) “rain”
6) “8”
zcard(name)
获取name对应的有序集合的数量
zcount(name, min, max)
获取name 在【min,max】之间的所有的分数个数。
zrank(name, value)
获取某个值在name对应的有序集合中的排行(从0开始)
zrangebyles(name, min, max, start = None, num = None)
当有序集合中的所有成员都具有相同的分数时,
有序集合的元素会根据成员的值来进行排序
对集合中的每个成员进行逐个字节的对比,并按照从低到高的顺序返回排序后的集合。
参数:
name:redis的name
min: 左区间 +表示正无限 - 表示负无限(表示开区间 【表示闭区间
max: 右区间
Start: 对结果进行分片处理, 索引位置
num:对结果进行分片处理,索引后面的num个元素
zrem(name, values)
删除name对应集合中值是values的成员
zremrangebyrank(name,min,max)
根据排行范围删除
zremrangebyscore(name,min,max)
根据分数范围删除
zremrangebylex(name,min,max)
根据值返回删除
zscore(name, value)
获取name对应有序集合中value对应的分数
zinterstore(desk,key,aggregate=None)
获取两个有序集合的交集,如果遇到相同值不同分数,则按照aggregate进行操作同值会相加
aggregate的值为: SUM MIN MAX
zunionstore(desk, keys, aggregate=None)
获取两个有序集合的并集,如果遇到相同值不同分数,则按照aggregate进行操作同值会相加
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,用来 对分数进行操作
redis其他的常用操作
del(*names)
根据names删除redis中任意的数据类型
exist(name)
检测redis的name是否存在
keys(pattern=””)
根据模型获取redis中的name
更多:
keys 匹配数据库中所有key
keys h?llo 匹配hello ,hxllo ,hpllo等
keys h*llo 匹配hllo, heeeeeello等
keys h[ae]llo 匹配hello或者hallo 但不匹配haello
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 Publish 命令基本语法如下:
redis 127.0.0.1:6379> PUBLISH channel message
返回值
接收到信息的订阅者数量。
管道
redis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,,则可以使用pipline实现一次请求多个命令,并且默认情况下pipline是原子性操作。
连接远程Linux使用redis操作1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# Author: Diedline
import redis
import time
pool = redis.ConnectionPool(host = "192.168.167.128", port="6379", db=5)
r = redis.Redis(connection_pool=pool)
pipe = r.pipeline(transaction=True)
# r.set("name2","alex")
"""
使用pipe可以同时存取几个操作一起执行
"""
# time.sleep(50)
# r.set("age2","20")
pipe.brpoplpush("names", "names232", timeout=30 )
pipe.set("name7", "huxudong23")
pipe.execute()
使用redis发布和订阅
即实现多个广播多个用户选择听广播的消息
redis_helper
1 | # Author: Diedline |
redis_sub1
2
3
4
5
6
7
8
9
# Author: Diedline
from redis_helper import RedisHelper
obj = RedisHelper()
redis_sub = obj.subscribe()
while True:
msg = redis_sub.parse_response()
print(msg)
可以在Linux中的redis命令行中
127.0.0.1:6379[5]> PUBLISH fm104.5 “ how are you”
(integer) 2
多个订阅 fm104.5的用户都能收到消息。“how are you”
更多关于redis操作的网站
https://github.com/andymccurdy/redis-py/
http://doc.redisfans.com/