讲师的博客:https://www.cnblogs.com/wupeiqi/p/5132791.html
如果是要在django里使用Memcache缓存,那么下面都不用看了,直接看django里是怎么用的就好了。
这篇里缓存的章节:http://blog.51cto.com/steed/2104127
Memcached
Memcached是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。Memcached基于一个存储键/值对的hashmap。其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信。
和redis比较
貌似Redis要比Memcached好。不过这两个都不错,也都有人在用。仔细比较的话也确实是各有优劣的,比如Redis只使用单核,而Memcached可以使用多核。
具体看这篇吧:https://www.oschina.net/news/26691/memcached-timeout
安装Memcached
Memcached官网:http://memcached.org/
关于安装,可以看这里:https://github.com/memcached/memcached/wiki/Install
可以直接用yum安装:
$ yum install libevent-devel
可能还要安装这个,但是上面的yum安装时并不在依赖关系里:
$ yum install libevent-devel
上面这个我还没有安装。
启动运行
$ memcached -d -m 10 -u root -l 10.211.55.4 -p 12000 -c 256 -P /tmp/memcached.pid
启动选项:
- -p
<num>
: TCP监听端口(default: 11211) - -U
<num>
: UDP监听端口(default: 11211, 0 is off) - -s
<file>
: UNIX套接字路径侦听 - -a
<mask>
: UNIX套接字的访问掩码,八进制(默认值:0700) - -l
<addr>
: 侦听接口地址默认为所有地址,可以指定主机加端口可以使用逗号分隔多个地址 - -d : 作为守护进程运行
- -r : 最大文件描述符
- -u
<username>
: 指定运行用户 - -m
<num>
: 最大内存(默认为64 MB) - -M : 内存耗尽时返回错误而不删除项目
- -c
<num>
: 最大同时连接数默认为1024 - -k : 锁定所有分页内存
- -v : 显示错误或警告事件
- -vv : 详细错误
- -vvv : 详细错误信息及内部状态转换
- -h : 打印此帮助
- -i : 打印内存缓存和许可证
- -P
<file>
: 指定PID文件,只与-d选择一起使用 - -f
<factor>
: 块大小生长因子,默认值为1.25 - -n
<bytes>
: 为键值标志的最小空间,默认为48 - -L : 使用大内存页,增加的内存页大小可以减少TLB命中数提高性能
- -D
<char>
: 使用<char>
作为密钥前缀和IDS之间的分隔符 - -t
<num>
: 使用的线程数,默认为4 - -R : 每个事件的最大请求数默认为20
- -C : 禁用CAS的使用
- -b
<num>
: 设置积压队列限制默认值1024 - -B : 绑定协议——ASCII、二进制或AUTO(默认)之一
- -I : 重写每个板页的大小。调整最大项目大小(默认值:1MB,MI:1K,MAX:128M)
- -S : 打开SASL认证
- -o : 逗号分隔的扩展或实验选项列表
上面如果用后台启动了之后无法关闭,可以先找到守护进程的PID,然后kill掉:
$ ps -aux
$ kill -9 PID
开放防火墙:
$ firewall-cmd --permanent --add-port=11211/tcp
$ firewall-cmd --reload
安装python-memcached模块
直接使用pip安装:
pip install python-memcached
第一次使用:
import memcache
mc = memcache.Client([‘192.168.3.108:11211‘], debug=True)
mc.set(‘k1‘, ‘v1‘)
ret = mc.get(‘k1‘)
print(ret)
这里有个参数,debug=True表示运行出现错误时,显示错误信息,上线后移除该参数。
运行上面的代码前先确认启动了服务端的Memcached,用如下的命令开启吧:
$ memcache -u root -vv
此时,在运行上面的代码,就可以进行设置值和取值的操作了。
天生支持集群
python-memcached模块原生支持集群操作。这里单讲上面的生成memcache.Client实例的这条命令。
实例化的时候传入的第一个参数,是一个列表。既然是列表,就可以传入多个,这样是实现了集群的操作:
mc = memcache.Client([‘192.168.3.108:11211‘, ‘192.168.3.109:11211‘, ‘192.168.3.110:11211‘])
另外,列表中的元素可以是一个元组,元组的第一个元素就是上面的IP地址和端口,第二个元素是数字表示权重:
mc = memcache.Client([(‘192.168.3.108:11211‘, 1), (‘192.168.3.109:11211‘, 3), (‘192.168.3.110:11211‘, 4)])
根据算法将用户要存的key转换成一个数字,将数字和主机列表的长度求值,如此就知道该连接哪一台主机了。加了权重值,就是增加这个主机在主机列表里出现的次数。
基本操作
这里操作的命令比较简单,所以简单了解一下有哪些命令可以操作就好了。
add
添加一条键值对,如果是已经存在的key,会返回False:
res = mc.add(‘k2‘, ‘v2‘)
print(res) # 设置成功,返回True
res = mc.add(‘k2‘, ‘value2‘)
print(res) # 设置失败,返回False
这里重复add同一个key,应该是会产生异常的,不过应该是被底层捕获的,不会导致程序崩溃。如果开启了debug,那么会把异常打印出来。
replace
修改某个key的值,如果key不存在,则异常:
mc.replace(‘k1‘, ‘value1‘)
这里的异常和上面add的情况是一样的。set操作就相当于是 add + replace 。
set 和 set_multi
set : 设置一个键值对,如果key不存在,则创建,如果key存在,则修改
set_multi : 设置多个键值对,此时要传入一个字典
res = mc.set(‘k1‘, ‘v1‘)
print(res) # 设置成功,会返回True
mc.set_multi({‘k11‘: ‘v11‘, ‘k12‘: ‘v12‘, ‘k13‘: ‘v13‘})
set的时候可以直接传数字,应该是会自动帮我们传成字符串保存的:
mc.set(‘n1‘, 100)
print(mc.get(‘n1‘))
get 和 get_multi
get : 获取一个键值对
get_multi : 获取多个键值对,传入一个列表,返回字典
items = mc.get_multi([‘k11‘, ‘k12‘, ‘k13‘])
print(items)
delete 和 delete_multi
delete : 删除指定的一个键值对,可以删除不存在的键值对
delete_multi : 删除指定的多个键值对,如果key不存在,会异常
mc.delete(‘k1‘)
mc.delete_multi([‘k11‘, ‘k12‘, ‘k13‘])
append 和 prepend
append : 修改指定key的值,在该字符串后面追加内容
prepend : 修改指定key的值,在该字符串前面插入内容
在Memcached里只有一个数据格式,就是字符串,所以这里是对字符串的操作:
mc.set(‘s1‘, ‘TEST‘)
print(mc.get(‘s1‘))
mc.append(‘s1‘, ‘ after‘)
print(mc.get(‘s1‘))
mc.prepend(‘s1‘, ‘before ‘)
print(mc.get(‘s1‘))
decr 和 incr
incr : 自增,将某个值增加N,默认加1
decr : 自减,将某个值减少N,默认减1
这里能操作的只能是能是数字形式的字符串,否则程序会抛出异常。这个异常应该是客户端计算是产生的,直接会导致程序崩溃:
mc.set(‘n1‘, ‘123‘)
print(mc.get(‘n1‘))
mc.incr(‘n1‘)
print(mc.get(‘n1‘))
mc.incr(‘n1‘, 100)
print(mc.get(‘n1‘))
设置超时时间
上面的设置值的操作:set、 set_multi、add、replace,都是可以设置超时时间的。通过time参数,单位是秒:
mc.set(‘k1‘, ‘v1‘, time=2)
time.sleep(1)
print(mc.get(‘k1‘)) # 返回设置的值,v1
time.sleep(1)
print(mc.get(‘k1‘)) # 超时,返回None
gets 和 cas
这里提供的方法,是为了解决同时修改某个数据的时候,导致数据一致性问题。
比如A取出了某个值假设是1000,B也取出了这个值1000。A和B都要把数据减1然后回写回去。此时A和B都会尝试把999写回去。这样最终这个数字就不对了。应该是A和B都把数字减少了1,此时最终的数值应该是998。
用这里的gets和cas操作,就可以发生上面的问题。要让这两个命令有效果,需要在创建客户端实例的时候再加一个参数cache_cas=True:
mc = memcache.Client([‘192.168.3.108:11211‘], debug=True, cache_cas=True)
下面是演示这种冲突情况的代码:
# s2a.py
import memcache
mc = memcache.Client([‘192.168.3.108:11211‘], debug=True, cache_cas=True) # 注意参数
mc.set(‘count‘, 1000) # 设置一个默认值
n = mc.gets(‘count‘)
input("%s>>>" % n) # 获取到值之后,先暂停
res = mc.cas(‘count‘, n-1) # 把计算后的新的值写回
print(res, mc.get(‘count‘))
# s2b.py
import memcache
mc = memcache.Client([‘192.168.3.108:11211‘], debug=True, cache_cas=True)
n = mc.gets(‘count‘)
input("%s>>>" % n)
res = mc.cas(‘count‘, n-1)
print(res, mc.get(‘count‘))
先运行s2a.py,然后再运行s2b.py。此时2个程序都会在获取到值之后暂停。随便让一个程序继续,写回最新的数据。当第二个程序写回的时候就会发生错误,返回False。此时你就知道这里的操作失败了。知道失败了,就再重新执行一个gets和cas的操作应该就可以了。失败返回如下:
1000>>>
MemCached: while expecting ‘STORED‘, got unexpected response ‘EXISTS‘
False 999
这里应该是一个乐观锁的实现。总之就是cas之后检查返回值判断是否成功。如果不成功,表示之前执行的时候有冲突,那么再执行一次。
另外其实数字的加减,是不需要这样做的。对于数字,使用decr 和 incr就不会发生上面的那种冲突的情况。不过如果是字符串操作,比如在前后添加内容,就无法避免了。另外或许处理数字还有其他更复杂的情况,只是还没遇到。
原文地址:http://blog.51cto.com/steed/2342146