项目中有一个对实时响应性比较高的服务,引入了 Memcached 以减少延迟和减少数据库压力。但是期间遇到了一些问题,这里记录一些调优细节。
客户端选择
- 最开始我使用的是 Memcached Java Client,但是最后放弃了,放弃原因包括:
- 有时会出现的 “No Thread For Socket” 异常,我记录在这里;
- 它不支持 NOREPLY 模式(在这种模式下,更新缓存的 set 操作可以不需要 Memcached 服务端响应,这使得 set 操作非常非常快)。
- 现在我使用的是 XMemcached。
统计信息
可以通过 nc 命令向 Memcached 服务端发送消息来获取统计信息,例如:
echo "stats settings" | nc localhost 20200 | sort
但是,我更需要客户端的统计信息,尤其是缓存命中率,set 操作成功率等等。所以在客户端添加了一个简单的统计模块。每次处理用户请求的过程中,通常有两次向 Cache 服务端的提交 get 请求,很多情况下还有两次 set 请求,合计消耗 17ms,在把 set 请求改成 NOREPLY 模式以后,这个数减少到 10ms 以内。因此,对于实时性要求比较高的情形,请打开这个模式,或者干脆使用异步的 set。
服务端参数
- 可以使用-U 来使用 UDP 传输,但是收效不大。
- -k 参数可以阻止换页操作发生,在内存足够的情况下对提高性能有益。
- -C 参数可以禁用 CAS。
- -t 指定使用的线程数,如果你是多 CPU、多核 CPU,可以把这个值配成和总 CPU 核数一致。
- -f 参数,增长因子,存储大对象把它配大一点可以提高效率,配小一点可以减少浪费。
客户端参数
- 在使用 Memcached Java Client 的时候:
- 由于它会使用 direct memory,一定不能加上 DisableExplicitGC 这个参数,否则就等着 OOM 吧;
- 配置大一些的 heap size 可以提高 L1 cache 的命中率;
- 把 alive check 置为 false。
- 对于实时性和响应性要求比较高的项目,需要做 GC 调优,主要是 GC 时延,比如配置 MaxGCPauseMillis 参数到一个可以接受的值,但是不是越小越好,减低时延的同时会降低吞吐量。
- 有同事提了个建议,在客户端存放一个 cache key 的集合,可以在去 cache server 查询之前,先在本地查看一下是否有缓存记录(比如用 Bloom filter 来实现),如果有,再去 cache server 查询。这个集合可以和实际的 cache key 有出入,也许一个小时同步一次就可以。但是实际上实现起来比较困难,本身 key set 的总量非常大,而且 Memcached 最初提供获取 key iterator 的接口返回的是一个限定大小 key set 的 iterator,缺乏实际意义(这个接口在后来 Memcached 的版本中已经被废弃)。至于 stats 方法,它会把所有 cache 对象 dump 出来,只能小规模调试的时候使用。
- 关于 Nagle 算法:Nagle 的好处是可以批量处理请求,提高 TCP 包有效部分的大小,从而提高网络利用率,但是如果对每个请求处理时延要求很高的话请关闭。
- 一定要指定 socket timeout 或者 get/set timeout。
最后,有人做了一个几个 Memcached 客户端的综合的性能试验:链接。
文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》