Memcached服务器安装、配置、使用详解

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
basketball
END

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);
        }
});

参考链接

原文地址:https://www.cnblogs.com/zzwx/p/8820153.html