Salibra

It's the best of time and it's the worst of time.

导航

1. Memcache

1.1 简介

官网:http://memcached.org/

Github:https://github.com/memcached/memcached

官方定义:Free & open source, high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database load.

Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。Memcached基于一个存储键/值对的hashmap。其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信。

Memcache只支持一种数据格式,就是key:value的格式

1.2 特征
协议简单
   它是基于文本行的协议,直接通过telnet在memcached服务器上可进行存取数据操作
基于libevent事件处理
    Libevent是一套利用C开发的程序库,它将BSD系统的kqueue,Linux系统的epoll等事件处理功能封装成一个接口,与传统的select相比,提高了性能。
内置的内存管理方式
    所有数据都保存在内存中,存取数据比硬盘快,当内存满后,通过LRU算法自动删除不使用的缓存,但没有考虑数据的容灾问题,重启服务,所有数据会丢失。
分布式
   各个memcached服务器之间互不通信,各自独立存取数据,不共享任何信息。服务器并不具有分布式功能,分布式部署取决于memcache客户端。

自身不支持持久化

 由于自身机制限制,memcache不支持持久化,但是可以利用其他方式将数据读出写入到需要保存的地方,当memcache宕机后重新加入到系统中时,再进行预热,将数据重新写入memcache中。

 

在 Memcached中可以保存的item数据量是没有限制的,只要内存足够 。
Memcached单进程在32位系统中最大使用内存为2G,若在64位系统则没有限制,这是由于32位系统限制单进程最多可使用2G内存,要使用更多内存,可以分多个端口开启多个Memcached进程 ,
最大30天的数据过期时间,设置为永久的也会在这个时间过期,常量REALTIME_MAXDELTA=60*60*24*30控制 
最大键长为250字节,大于该长度无法存储,常量KEY_MAX_LENGTH=250控制 
单个item最大数据是1MB,超过1MB数据不予存储,常量POWER_BLOCK=1048576进行控制,它是默认的slab大小 
最大同时连接数是200,通过 conn_init()中的freetotal进行控制,最大软连接数是1024,通过settings.maxconns=1024 进行控制 
跟空间占用相关的参数:settings.factor=1.25, settings.chunk_size=48, 影响slab的数据占用和步进方式

memcached是一种无阻塞的socket通信方式服务,基于libevent库,由于无阻塞通信,对内存读写速度非常之快。
memcached分服务器端和客户端,可以配置多个服务器端和客户端,应用于分布式的服务非常广泛。
memcached作为小规模的数据分布式平台是十分有效果的。

memcached是键值一一对应,key默认最大不能超过128个字 节,value默认大小是1M,也就是一个slabs,如果要存2M的值(连续的),不能用两个slabs,因为两个slabs不是连续的,无法在内存中 存储,故需要修改slabs的大小,多个key和value进行存储时,即使这个slabs没有利用完,那么也不会存放别的数据。

memcached已经可以支持C/C++、Perl、PHP、Python、Ruby、Java、C#、Postgres、Chicken Scheme、Lua、MySQL和Protocol等语言客户端。

1.3 安装和基本使用

  安装memcache

wget http://memcached.org/latest

tar -zxvf memcached-1.x.x.tar.gz
cd memcached-1.x.x
./configure && make && make test && sudo make install
 
PS:依赖libevent
       CentOS:yum install libevent-devel
       Ubuntu:apt-get install libevent-dev

  启动memcache

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文件

   存储命令

存储命令的格式:
<command name> <key> <flags> <exptime> <bytes>
<data block>

   格式参数说明  

 命令使用教程:http://www.cnblogs.com/jeffwongishandsome/archive/2011/11/06/2238265.html

Python操作memcache常用方法:

2.1 简单操作Python

import memcache
#导入python memcache模块

mc = memcache.Client(['10.211.55.4:12000'], debug=True)
#创建memcache连接,IP:port 用引号引起来,再用中括号括起来。
#debug=True,设置出错时显示信息,线上环境无需

mc.set("key1", "value1")

ret = mc.get('foo')

print ret

2.2 memcache模块集群模式支持

Python-memcached模块默认支持memcache的集群操作,其原理是在内存维护一个memcache主机列表,且集群中主机的权重值和主机列表中重复出现的次数成正比。

理解代码如下:

   主机     权重
  1.1.1.1      1
     1.1.1.2     2
     1.1.1.3     1
 
那么在内存中主机列表为:
    host_list = ["1.1.1.1", "1.1.1.2", "1.1.1.2", "1.1.1.3", ]
可见,权重高的主机相当于在列表中写了多次。

如果用户根据如果要在内存中创建一个键值对(如:k1 = "v1"),那么要执行一下步骤:

  • 根据算法将 k1 转换成一个数字
  • 将数字和主机列表长度求余数,得到一个值 N( 0 <= N < 列表长度 )
  • 在主机列表中根据 第2步得到的值为索引获取主机,例如:host_list[N]
  • 连接 将第3步中获取的主机,将 k1 = "v1" 放置在该服务器的内存中

 实现代码:

 

mc = memcache.Client([('1.1.1.1:12000', 1), ('1.1.1.2:12000', 2), ('1.1.1.3:12000', 1)], debug=True)
#用小括号将 ('IP:PORT',weight) 为一个单位
mc.set('k1', 'v1')
#存储时根据上边的算法计算到某台机器写入,取出时同样的算法算出机器,取出数据即可。

 

2.3 add

添加一条键值对

1.如内存中不存在该键值对,则 添加到内存中

2.如果已经存在的 key,执行add操作则提示异常

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import memcache
 
mc = memcache.Client(['10.211.55.4:12000'], debug=True)
mc.add('k1', 'v1')
# mc.add('k1', 'v2') # 报错,对已经存在的key重复添加,失败!!!

2.4 replace

replace 修改某个key的值

1.如果key存在,则正常替换

2.如果key不存在,则报出异常

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import memcache
 
mc = memcache.Client(['10.211.55.4:12000'], debug=True)
# 如果memcache中存在kkkk,则替换成功,否则异常
mc.replace('kkkk','999')

2.5 set 和 set_multi

set            设置一个键值对,如果key不存在,则创建(相当于add);如果key存在,则修改(相当于replace);
set_multi   设置多个键值对,如果key不存在,则创建(相当于add);如果key存在,则修改(相当于replace);

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import memcache
 
mc = memcache.Client(['10.211.55.4:12000'], debug=True)
 
mc.set('key0', 'wupeiqi')
 
mc.set_multi({'key1': 'val1', 'key2': 'val2'})
#一次性添加多个,格式为字典键值对格式

2.6 delete 和 delete_multi

delete             在Memcached中删除指定的一个键值对
delete_multi    在Memcached中删除指定的多个键值对

由于memcache中数据key都是唯一的,所以删除时只需给出key值即可。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import memcache
 
mc = memcache.Client(['10.211.55.4:12000'], debug=True)
 
mc.delete('key0')
mc.delete_multi(['key1', 'key2'])

2.7 get 和 get_multi

get             获取一个键值对
get_multi   获取多一个键值对

get 和 get_multi 得到的数据返回值的格式为字典格式

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import memcache
 
mc = memcache.Client(['10.211.55.4:12000'], debug=True)
 
val = mc.get('key0')
item_dict = mc.get_multi(["key1", "key2", "key3"])
#被get_multi的key用引号引起来,且是列表格式

2.8 append 和 prepend

append    修改指定key的值,在该值 后面 追加内容
prepend   修改指定key的值,在该值 前面 插入内容

注:不是追加key-value键值对,而是直接修改value,追加到value中。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import memcache
 
mc = memcache.Client(['10.211.55.4:12000'], debug=True)
# k1 = "v1"
 
mc.append('k1', 'after')
#结果为 k1 = "v1after"
 
mc.prepend('k1', 'before')
#结果为 k1 = "beforev1after"

2.9、decr 和 incr  

incr  自增,将Memcached中的某一个值增加 N ( N默认为1 )
decr 自减,将Memcached中的某一个值减少 N ( N默认为1 )

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import memcache
 
mc = memcache.Client(['10.211.55.4:12000'], debug=True)
mc.set('k1', '777')
 
mc.incr('k1')
# k1 = 778
 
mc.incr('k1', 10)
# k1 = 788,后边的10为步长
 
mc.decr('k1')
# k1 = 787
 
mc.decr('k1', 10)
# k1 = 777

2.10、gets 和 cas

如商城商品剩余个数,假设改值保存在memcache中,product_count = 900
A用户刷新页面从memcache中读取到product_count = 900
B用户刷新页面从memcache中读取到product_count = 900

如果A、B用户均购买商品

A用户修改商品剩余个数 product_count=899
B用户修改商品剩余个数 product_count=899

如此一来缓存内的数据便不在正确,两个用户购买商品后,商品剩余还是 899
如果使用python的set和get来操作以上过程,那么程序就会如上述所示情况!

如果想要避免此情况的发生,只要使用 gets 和 cas 即可

原理说明:本质上每次执行gets修改数据时,会从memcache中获取一个自增的数字cas_id,通过cas去修改gets的值时,会将之前获取的自增值和memcache中的自增值进行比较,如果相等,则可以提交;如果不想等(其它用户进行了cas操作),那表示在gets和cas执行之间,又有其他人执行了gets(获取了缓冲的指定值), 如此一来有可能出现非正常数据,则不允许修改。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import memcache,time
mc = memcache.Client(['192.168.56.136:11211'], debug=True, cache_cas=True)

mc.set('k1','01')

##################
v = mc.gets('k1')
print "value:",v
print mc.cas_ids

####################
mc.cas('k1', "02") v = mc.gets('k1') print "value:",v print mc.cas_ids

结果为:

value: 01    #第一次获取到的value
{'k1': 83}    #第一次的内部id

value: 02    #第二次修改后获取道德value
{'k1': 84}    #经过一次cas修改后的内部id