Memcached服务器安装、配置、使用详解
我使用的是CentOS 6.4系统,安装的Memcached版本为1.4.20。这里,记录一下安装配置的过程,以及如何使用一些常用的客户端来访问Memcached存储的数据。
安装配置
首先,编译、安装、配置libevent库,执行如下命令:
1
2
3
4
5
6
7
|
wget https: //github .com /downloads/libevent/libevent/libevent-1 .4.14b-stable. tar .gz tar xvzf libevent-1.4.14b-stable. tar .gz ln -s /usr/local/libevent-1 .4.14b-stable /usr/local/libevent cd /usr/local/libevent . /configure make make install |
然后,编译、安装、配置Memcached,执行如下命令行:
1
2
3
4
5
6
|
wget http: //www .memcached.org /files/memcached-1 .4.20. tar .gz tar xvzf memcached-1.4.20. tar .gz ln -s /usr/local/memcached-1 .4.20 /usr/local/memcached . /configure --with-libevent= /usr/local/libevent/ make make install |
如果没有出错,安装成功。
管理memcached服务
- 启动Memcached
一般情况下,简单地可以使用类似如下形式,启动Memcached服务:
1
|
/usr/local/bin/memcached -d -m 64 -I 20m -u root -l 192.168.4.86 -p 11211 -c 1024 -P /usr/local/memcached/memcached.pid |
上述命令行中,基于上面各个选项,以及其他一些选项的含义,说明如下表所示:
选项 | 含义说明 |
-d | 指定memcached进程作为一个守护进程启动 |
-m <num> | 指定分配给memcached使用的内存,单位是MB |
-u <username> | 运行memcached的用户 |
-l <ip_addr> | 监听的服务器IP地址,如果有多个地址的话,使用逗号分隔,格式可以为“IP地址:端口号”,例如:-l 指定192.168.0.184:19830,192.168.0.195:13542;端口号也可以通过-p选项指定 |
-p <num> | Memcached监听的端口,要保证该端口号未被占用 |
-c <num> | 设置最大运行的并发连接数,默认是1024 |
-R <num> | 为避免客户端饿死(starvation),对连续达到的客户端请求数设置一个限额,如果超过该设置,会选择另一个连接来处理请求,默认为20 |
-k | 设置锁定所有分页的内存,对于大缓存应用场景,谨慎使用该选项 |
-P | 保存memcached进程的pid文件 |
-s <file> | 指定Memcached用于监听的UNIX socket文件 |
-a <perms> | 设置-s选项指定的UNIX socket文件的权限 |
-U <num> | 指定监听UDP的端口,默认11211,0表示关闭 |
-M | 当内存使用超出配置值时,禁止自动清除缓存中的数据项,此时Memcached不可以,直到内存被释放 |
-r | 设置产生core文件大小 |
-f <factor> | 用于计算缓存数据项的内存块大小的乘数因子,默认是1.25 |
-n | 为缓存数据项的key、value、flag设置最小分配字节数,默认是48 |
-C | 禁用CAS |
-h | 显示Memcached版本和摘要信息 |
-v | 输出警告和错误信息 |
-vv | 打印信息比-v更详细:不仅输出警告和错误信息,也输出客户端请求和响应信息 |
-i | 打印libevent和Memcached的licenses信息 |
-t <threads> | 指定用来处理请求的线程数,默认为4 |
-D <char> | 用于统计报告中Key前缀和ID之间的分隔符,默认是冒号“:” |
-L | 尝试使用大内存分页(pages) |
-B <proto> | 指定使用的协议,默认行为是自动协商(autonegotiate),可能使用的选项有auto、ascii、binary。 |
-I <size> | 覆盖默认的STAB页大小,默认是1M |
-F | 禁用flush_all命令 |
-o <options> | 指定逗号分隔的选项,一般用于用于扩展或实验性质的选项 |
- 停止Memcached
可以通过Linux的如下命令查询到Memcached的进程号:
1
|
ps -ef | grep memcached |
然后杀掉Memcached服务进程:
1
|
kill -9 <PID> |
-9表示强制杀掉进程。
Memcached启动以后,可以通过客户端来操作缓存中的数据,我们说明一些常用的客户端,及其使用方法。
Telnet客户端
Telnet客户端可以通过命令行的方式来监控查看Memcached服务器存储数据的情况。例如,Memcached的服务地址为192.168.4.86:11211,可以telnet到该服务端口:
1
|
telnet 192.168.4.86 11211 |
如果连接成功,可以使用如下一些命令:
- stats命令
该命令用于显示服务器信息、统计数据等,结果示例数据(来自www.2cto.com网站),例如:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
STAT pid 22362 //memcache服务器的进程ID www.2cto.com STAT uptime 1469315 //服务器已经运行的秒数 STAT time 1339671194 //服务器当前的unix时间戳 STAT version 1.4.9 //memcache版本 STAT libevent 1.4.9-stable //libevent版本 STAT pointer_size 64 //当前操作系统的指针大小(32位系统一般是32bit,64就是64位操作系统) STAT rusage_user 3695.485200 //进程的累计用户时间 STAT rusage_system 14751.273465 //进程的累计系统时间 STAT curr_connections 69 //服务器当前存储的items数量 STAT total_connections 855430 //从服务器启动以后存储的items总数量 STAT connection_structures 74 //服务器分配的连接构造数 STAT reserved_fds 20 // STAT cmd_get 328806688 //get命令(获取)总请求次数 STAT cmd_set 75441133 //set命令(保存)总请求次数 www.2cto.com STAT cmd_flush 34 //flush命令请求次数 STAT cmd_touch 0 //touch命令请求次数 STAT get_hits 253547177 //总命中次数 STAT get_misses 75259511 //总未命中次数 STAT delete_misses 4 //delete命令未命中次数 STAT delete_hits 565730 //delete命令命中次数 STAT incr_misses 0 //incr命令未命中次数 STAT incr_hits 0 //incr命令命中次数 STAT decr_misses 0 //decr命令未命中次数 STAT decr_hits 0 //decr命令命中次数 STAT cas_misses 0 //cas命令未命中次数 STAT cas_hits 0 //cas命令命中次数 STAT cas_badval 0 //使用擦拭次数 STAT touch_hits 0 //touch命令未命中次数 STAT touch_misses 0 //touch命令命中次数 STAT auth_cmds 0 //认证命令处理的次数 STAT auth_errors 0 //认证失败数目 STAT bytes_read 545701515844 //总读取字节数(请求字节数) STAT bytes_written 1649639749866 //总发送字节数(结果字节数) STAT limit_maxbytes 2147483648 //分配给memcache的内存大小(字节) STAT accepting_conns 1 //服务器是否达到过最大连接(0/1) STAT listen_disabled_num 0 //失效的监听数 STAT threads 4 //当前线程数 STAT conn_yields 14 //连接操作主动放弃数目 STAT hash_power_level 16 // STAT hash_bytes 524288 STAT hash_is_expanding 0 STAT expired_unfetched 30705763 STAT evicted_unfetched 0 STAT bytes 61380700 //当前存储占用的字节数 STAT curr_items 28786 //当前存储的数据总数 STAT total_items 75441133 //启动以来存储的数据总数 STAT evictions 0 //为获取空闲内存而删除的items数(分配给memcache的空间用满后需要删除旧的items来得到空间分配给新的items) STAT reclaimed 39957976 //已过期的数据条目来存储新数据的数目 END |
上面给出了各个统计项的含义说明,不再累述。
stats命令有几个二级子项,说明如下表所示:
命令 | 含义说明 |
stats slabs | 显示各个slab的信息,包括chunk的大小、数目、使用情况等 |
stats items | 显示各个slab中item的数目和最老item的年龄(最后一次访问距离现在的秒数) |
stats detail [on|off|dump] | 设置或者显示详细操作记录; 参数为on,打开详细操作记录; 参数为off,关闭详细操作记录; 参数为dump,显示详细操作记录(每一个键值get、set、hit、del的次数) |
stats malloc | 打印内存分配信息 |
stats sizes | 打印缓存使用信息 |
stats reset | 重置统计信息 |
下面的命令,我们通过表格的形式说明,如下表所示:
命令 | 用法格式 | 含义说明 | 示例 |
get | get <key>* | 用于获取缓存的数据,键为key。 | get name VALUE name 0 7 shirdrn END |
gets | gets <key>* | 用于获取缓存的数据,键为一组key。 | gets name hobby VALUE name 1 7 1234567 VALUE hobby 0 25 tenis basketball football END |
set | set <key> <flags> <exptime> <bytes> [noreply] <value> | 向缓存中存储数据,不管key对应的值存在与否,都设置key对应的值。 | set name 0 1800 7 shirdrn STORED get name VALUE name 0 7 shirdrn END |
touch | touch <key> <exptime> [noreply] | 更新缓存中key对应的值的过期时间。 | touch name 1800 |
delete | delete <key> [<time>] [noreply] | 给定键key,删除缓存中key对应的数据。 | delete name 60 |
add | add <key> <flags> <exptime> <bytes> [noreply] <value> | 向缓存中存储数据,只有key对应的值不存在时,才会设置key对应的值。 | add hobby 0 1800 10 basketball STORED get hobby VALUE hobby 0 10 |
replace | replace <key> <flags> <exptime> <bytes> [noreply] <value> | 覆盖一个已经存在Key及其对应的Value,替换一定要保证替换后的值的长度原始长度相同,否则replace失败。 | get name VALUE name 0 7 shirdrn END replace name 0 1800 7 youak47 STORED get name VALUE name 0 7 youak47 END |
append | append <key> <flags> <exptime> <bytes> [noreply] <value> | 在一个已经存在的数据值(value)上追加,是在数据值的后面追加。 | get hobby VALUE hobby 0 10 basketball END append hobby 0 1800 9 football STORED get hobby VALUE hobby 0 19 basketball football END |
prepend | prepend <key> <flags> <exptime> <bytes> [noreply] <value> | 在一个已经存在的数据值(value)上追加,是在数据值的前面追加。 | get hobby VALUE hobby 0 19 basketball football END prepend hobby 0 1800 6 tenis STORED get hobby VALUE hobby 0 25 tenis basketball football END |
incr | incr <key> <value> [noreply] | 计数命令,可以在原来已经存在的数字上进行累加求和,计算并存储新的数值。 | set active_users 0 1000000 7 1000000 STORED get active_users VALUE active_users 0 7 1000000 END incr active_users 99 1000099 |
decr | decr <key> <value> [noreply] | 计数命令,可以在原来已经存在的数字上进行减法计算,计算并存储新的数值。 | get active_users VALUE active_users 0 7 1000099 END decr active_users 3456 996643 |
flush_all | flush_all [<time>] [noreply] | 使缓存中的数据项失效,可选参数是在多少秒后失效。 | flush_all 1800 |
version | version | 返回Memcached服务器的版本信息。 | version |
quit | quit | 退出telnet终端。 | quit |
Java客户端
可以使用Java语言编写代码来访问Memcached缓存。目前,可以使用的Java客户端很多,这里简单介绍几个。
- spymemcached客户端
示例代码,如下所示:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
package org.shirdrn.spymemcached; import net.spy.memcached.AddrUtil; import net.spy.memcached.BinaryConnectionFactory; import net.spy.memcached.MemcachedClient; import net.spy.memcached.internal.OperationFuture; public class TestSpymemcached { public static void main(String[] args) throws Exception { String address = "192.168.4.86:11211" ; MemcachedClient client = new MemcachedClient( new BinaryConnectionFactory(), AddrUtil.getAddresses(address)); String key = "magic_words" ; int exp = 3600 ; String o = "hello" ; // set OperationFuture<Boolean> setFuture = client.set(key, exp, o); if (setFuture.get()) { // get System.out.println(client.get(key)); // append client.append(key, " the world!" ); System.out.println(client.get(key)); // prepend client.prepend(key, "Stone, " ); System.out.println(client.get(key)); // replace o = "This is a test for spymemcached." ; OperationFuture<Boolean> replaceFuture = client.replace(key, exp, o); if (replaceFuture.get()) { System.out.println(client.get(key)); // delete client.delete(key); System.out.println(client.get(key)); } } client.shutdown(); } } |
更多用法,可以参考后面的链接。
- XMemcached客户端
示例代码,如下所示:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
package org.shirdrn.xmemcached; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.net.InetSocketAddress; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; import net.rubyeye.xmemcached.CASOperation; import net.rubyeye.xmemcached.GetsResponse; import net.rubyeye.xmemcached.MemcachedClient; import net.rubyeye.xmemcached.XMemcachedClientBuilder; import net.rubyeye.xmemcached.command.BinaryCommandFactory; import net.rubyeye.xmemcached.exception.MemcachedException; import net.rubyeye.xmemcached.utils.AddrUtil; public class UsingXMemcachedClient { public static void main(String[] args) throws IOException { String servers = "192.168.4.86:11211" ; // build and create a client XMemcachedClientBuilder builder = new XMemcachedClientBuilder( AddrUtil.getAddresses(servers)); builder.setCommandFactory( new BinaryCommandFactory()); final MemcachedClient client = builder.build(); // examples using client to operate final String key = "ghost" ; try { // add client.add(key, 0 , "Ghost wind blows!" ); System.out.println( "add & get: " + client.get(key)); // append client.append(key, " It's a lie." ); System.out.println( "append & get: " + client.get(key)); // prepend client.prepend(key, "Who's said?! " ); System.out.println( "prepend & get: " + client.get(key)); // replace client.replace(key, 0 , "Everything is nothing!" ); System.out.println( "replace & get: " + client.get(key)); // delete client.delete(key); System.out.println( "delete & get: " + client.get(key)); // gets List<String> keys = Arrays.asList( new String[] { "key1" , "key2" , "key3" }); for (String k : keys) { client.set(k, 3600 , "v:" + System.nanoTime()); } Map<String, GetsResponse<Object>> values = client.gets(keys); for (Map.Entry<String, GetsResponse<Object>> entry : values.entrySet()) { System.out.println( "key=" + entry.getKey() + ", value=" + entry.getValue().getValue()); } // cas final AtomicLong seq = new AtomicLong(System.nanoTime()); ExecutorService pool = Executors.newCachedThreadPool(); for ( int i= 0 ; i< 10 ; i++) { pool.execute( new Runnable() { @Override public void run() { while ( true ) { CacheResult o = new CacheResult(); o.file = new File( "/opt/status/servers.lst" ); o.lastmodified = seq.incrementAndGet(); System.out.println( "#" + Thread.currentThread().getId() + "=>o: " + o); try { client.set(key, 0 , o); Thread.sleep( 100 ); } catch (TimeoutException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (MemcachedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }); } Thread.sleep( 3000 ); for ( int i= 0 ; i< 10 ; i++) { client.cas(key, new CASOperation<CacheResult>() { @Override public int getMaxTries() { return 3 ; } @Override public CacheResult getNewValue( long arg0, CacheResult result) { CacheResult old = result; CacheResult nu = new CacheResult(); nu.file = old.file; nu.lastmodified = seq.incrementAndGet(); System.out.println( "cas: old=" + old + ", new=" + nu); return result; } }); } pool.shutdown(); // flush_all client.flushAll(); // stats List<InetSocketAddress> addresses = AddrUtil.getAddresses(servers); for (InetSocketAddress addr : addresses) { Map<String, String> stats = client.stats(addr); System.out.println(stats); } } catch (TimeoutException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } catch (MemcachedException e) { e.printStackTrace(); } synchronized (client) { try { client.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } static class CacheResult implements Serializable { private static final long serialVersionUID = 3349686173080590047L; private File file; private long lastmodified; @Override public String toString() { return "file=[" + file + ", lastmodified=" + lastmodified + "]" ; } } } |
Node.js客户端
Memcached客户端代码的逻辑都非常类似,这里对Node.js简单举例说明,代码如下所示:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#!/usr/bin/env node var MemcachedClient = require( 'memcached' ); // configure memcached client var servers = [ '192.168.4.86:11211' ]; var client = new MemcachedClient(servers); // access memcached servers var key = 'ghost' ; // set var value = 'Ghost wind blows!' ; client.set(key, 0, value, function (err) { var data = 'key=' + key + ', value=' + value; if (err) { console.error( 'Fail to set: ' + data); } else { console.log( 'Added: ' + data); } }); // get var valueGot = client.get(key, function (err, data) { var dataGot = 'key=' + key + ', valueGot=' + data; if (err) { console.error( 'Fail to get: ' + dataGot); } else { console.log( 'Got: ' + dataGot); } }); |
参考链接
- http://www.memcached.org
- https://code.google.com/p/memcached/wiki/NewStart
- http://www.memcached.org/files/memcached-1.4.20.tar.gz
- https://code.google.com/p/memcached/wiki/Clients
- https://github.com/downloads/libevent/
- https://github.com/downloads/libevent/libevent/libevent-1.4.14b-stable.tar.gz
- https://code.google.com/p/spymemcached/
- https://code.google.com/p/xmemcached/
- https://github.com/killme2008/xmemcached
- https://code.google.com/p/memcached/wiki/NewCommands
- http://www.2cto.com/os/201303/193264.html
- http://tech.idv2.com/tag/memcached/
- http://programcreek.com/java-api-examples/index.php?api=net.spy.memcached.MemcachedClient
- http://lzone.de/articles/memcached.htm
- http://blog.elijaa.org/?post/2010/05/21/Memcached-telnet-command-summary
- https://github.com/3rd-Eden/node-memcached
- https://www.npmjs.org/package/memcached