Redis, Memcache
Memcache
MemCache是一个自由、源码开放、高性能、分布式的内存对象缓存系统,用于动态Web应用以减轻数据库的负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高了网站访问的速度。
安装
https://github.com/memcached/memcached/wiki/Install
启动服务
memcached -d -m 10 -u root -l 10.211.55.4 -p 12000 -c 256 -P /tmp/memcached.pid
参数说明:
-d 是启动一个守护进程
-m 是分配给Memcache使用的内存数量,单位是MB
-u 是运行Memcache的用户
-l 是监听的服务器IP地址
-p 是设置Memcache监听的端口,最好是1024以上的端口
-c 选项是最大运行的并发连接数,默认是1024,按照你服务器的负载量来设定
-P 是设置保存Memcache的pid文件
测试时可以关闭防火墙,否则客户端无法访问:systemctl stop firewalld.service
python中操作MemCache
安装API
pip install python-memcached -i https://pypi.douban.com/simple
操作测试
这里在Django项目中测试
from django.conf.urls import url
from django.shortcuts import HttpResponse
import memcache
def test(request):
mc = memcache.Client(['192.168.14.16:12000'], debug=True)
mc.set('name', 'Lena', time=5) # 超时5秒
name = mc.get('name')
return HttpResponse(name)
urlpatterns = [
url(r'^test/', test),
]
分布式缓存
配置多个服务端,并设置权重,权重越高,命中几率越高。
mc = memcache.Client([('192.168.14.16:12000', 1), ('192.168.14.17:12000', 2), ('192.168.14.18:12000', 3)], debug=True) # 分别设置1,2,3权重
Memcache的分布式是由客户端程序的一致性哈希实现的,更多参考:http://www.csdn.net/article/2016-03-16/2826609
MemCache常用方法
注意:
- MemCache中存的值只支持字符串和数字
- 超时设置的时间单位是秒,超时后取出结果是
None
命 令 | 作 用 |
---|---|
get | 返回Key对应的Value值:mc.get(key) |
add | 添加一个Key值,没有则添加,有则报错:mc.add(key, val, time=10) 添加键值,并设置超时10秒 |
set | 设置一个Key值,没有就增加,有就覆盖:mc.set(key, val, time=10) |
replace | 按照相应的Key值替换数据,如果Key值不存在则会操作失败:mc.replace(key, val) |
delete | 删除一个键值对:mc.delete(key) |
stats | 返回MemCache通用统计信息 |
flush_all | 清空所有键值,但不会删除items,所以此时MemCache依旧占用内存:mc.flush_all() |
其它方法
set_multi 设置多个键值对
mc.set_multi({k1: v1, k2: v2})
del_multi删除多个键值对
mc.set_multi([k1, k2])
incr & decr
将key对应的值增加/减小
mc.set('a', 10)
mc.incr('a', 10) # 'a' = 20
mc.decr('a', 5) # 'a' = 15
append & prepend
在指定key值后面/前面追加
mc.set('name', 'Lena') # 'name'= 'Lena'
mc.append('name', 'Ayhan') # 'name' = 'LenaAyhan'
cas & gets
为了避免两个请求同时修改某个值,造成数据异常。gets
和cas
需要配合使用。
通过gets
获取值的时候(假设是val),会多返回一个数字,就叫做cas_id吧,当val改变时(比如期间有另一个请求修改了val),这个cas_id也会变。当执行cas
(check and set)重新设置值时,将检查cas_id是否和gets取出值时的cas_id匹配,如果匹配则修改,如果不匹配,表示期间有其它请求修改了val,则无法修改.
val = mc.gets(key)
# ...
# 如果在gets和cas之间有请求修改了key的值,那么,下面的cas将会检查到cas_id变了,抛出异常,避免修改造成数据异常
mc.cas(key, new_val)
Redis
同MemCache一样,Redis也是在服务器内存中保存的非关系型数据库(缓存),但是二者还是有些区别:
如果你对数据持久化和数据同步有所要求,那么推荐你选择Redis,因为这两个特性Memcached都不具备。即使你只是希望在升级或者重启系统后缓存数据不会丢失,选择Redis也是明智的。
Redis相比Memcached来说,拥有更多的数据结构和并支持更丰富的数据操作。
Redis不仅可以支持分布式,还具有高可用的特点:可以将数据定时刷入硬盘,解决了内存断电易失的问题。并且它支持更多的的数据类型:字符串,数字,列表,集合,字典,有序集合。
在python中使用Redis
centos服务端安装
yum install redis
如果安装不上,先安装epel仓库yum install epel-release
,再执行上述安装命令。
启动服务端:
systemctl start redis
配置:
vim/etc/redis.conf
# bind 127.0.0.1 这一行要注释掉,否则客户端连接不上
equirepass 123 # 设置密码
客户端安装:
pip install redis
连接
方式一
import redis
conn = redis.Redis(host=, port=, password='123')
不推荐,每次都会重新连接,创建新的对象。
方式二 推荐
推荐用连接池:
pool = redis.ConnectionPool(host=xx, port=xx, password='123')
conn = redis.Redis(connection_pool = pool)
进阶:单例模式
这里的host, port, password可以写到配置文件中。
import redis
class Singleton(object):
def __new__(cls, *args, **kwargs):
# 如果当前类没有_instance属性,那么就调用父类的__new__方法实例化对象,新增_instance属性并赋值
if not hasattr(cls, "_instance"):
cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance
class SingleRedis(Singleton):
def __init__(self, host='192.168.14.16', port=6379, password=123):
pool = redis.ConnectionPool(host=host, port=port, password=password)
self.conn = redis.Redis(connection_pool=pool)
# 使用
redis = SingleRedis()
redis.conn.set(key, val)
字符常用操作
conn.set(name, value, ex=None, px=None, nx=False, xx=False)
为name设置value(字符/数字类型);
ex/px
: 过期时间(秒/毫秒);
nx
: 如果为True,那么只有当name对应的value不存在时,该操作才能成功(即如果name在Redis中已经有value,将不会设置新的value,保留原来的值);默认False,无论如何设置新值。
xx
: 如果为True,那么只有当name对应的value已经存在时,该操作才能成功(即只能更新已存在的name)。
conn.mset(*args, **kwargs)
批量设置值
conn.mset(k1=v1, k2=v2)
# 或者
conn.mset({k1: v1, k2: v2})
conn.get(name)
返回name对应的value,如果不存在返回None
conn.get(keys, *args)
批量获取值
conn.mget(k1, k2)
# 或者
conn.mget([k1, k2])
conn.incr/decr(name, amount=1)
将name对应的value增加/减少amount,如果name不存在,将创建并初始化值为amount
列表常用操作
conn.lpush(name, *values)
往name对应的列表中添加元素(依次添加到最左边)
redis.conn.lpush('fruits', 'banana', 'orange')
# fruits: ['orange', 'banana']
conn.rpush(name, *values)
往k对应的列表中添加元素(从右边添加)
conn.llen(name)
name对应列表的长度
conn.lpop(name)
name对应列表的左边弹出一个值并返回
conn.rpop(name)
name对应列表的右边弹出一个值并返回
字典常用操作
conn.hset(name, key, value)
设置字典 name: {key: value};
conn.hset(name, key2, value2)
name: {key: value, key2: value2} 字典
注意如果value是字典或其它格式,需要先序列化为字符串
conn.hget(name, key)
获取name对应字典中key对应的value
conn.hdel(name, key)
删除name对应字典中的key
集合操作
将前缀s改为z,即可变为有序集合
conn.sadd(name, *values)
往name集合中添加元素
conn.scard(name)
返回name集合中元素的个数
conn.smembers(name)
获取name集合中的所有元素
conn.srem(name, *values)
删除name集合中某些元素
其它操作
delete(*names)
删除指定的keys
exists(name)
判断name是否存在,返回True/False
expire(name, time)
为某个name设置超时时间,单位是秒