Python自动化开发学习-Memcached

讲师的博客:https://www.cnblogs.com/wupeiqi/p/5132791.html
如果是要在django里使用Memcache缓存,那么下面都不用看了,直接看django里是怎么用的就好了。
这篇里缓存的章节:https://blog.51cto.com/steed/2104127

创新互联是一家以网络技术公司,为中小企业提供网站维护、成都做网站、成都网站建设、网站备案、服务器租用、国际域名空间、软件开发、微信平台小程序开发等企业互联网相关业务,是一家有着丰富的互联网运营推广经验的科技公司,有着多年的网站建站经验,致力于帮助中小企业在互联网让打出自已的品牌和口碑,让企业在互联网上打开一个面向全国乃至全球的业务窗口:建站咨询热线:18980820575

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就不会发生上面的那种冲突的情况。不过如果是字符串操作,比如在前后添加内容,就无法避免了。另外或许处理数字还有其他更复杂的情况,只是还没遇到。


分享文章:Python自动化开发学习-Memcached
当前链接:http://abwzjs.com/article/gehsec.html