首页 数据库 mysql教程 libmemcached的MEMCACHED_MAX_BUFFER问题

libmemcached的MEMCACHED_MAX_BUFFER问题

Jun 07, 2016 pm 04:27 PM
max memcached

最近给服务增加了一个cache_put_latency指标,加了之后,吓了一跳。发现往memcached put一个10KB左右的数据,latency居然有7ms左右,难于理解,于是花了一些精力找原因。我分别写了一个shell和C++的测试程序。 1、shell脚本使用nc发送set命令。 #/bin/env ba

最近给服务增加了一个cache_put_latency指标,加了之后,吓了一跳。发现往memcached put一个10KB左右的数据,latency居然有7ms左右,难于理解,于是花了一些精力找原因。我分别写了一个shell和C++的测试程序。

1、shell脚本使用nc发送set命令。

#/bin/env bash
let s=1
let i=0
let len=8*1024
while true
do
	if (( i >= $len ))
	then
		break
	fi
	str=${str}1
	let i++
done
let i=0
begin_time=`date +%s`
while true
do
	if (( i >= 1000 ))
	then
		break
	fi
	printf "set $i 0 0 $len\r\n${str}\r\n" | nc 10.234.4.24 11211
	if [[ $? -eq 0 ]]
	then
		echo "echo key: $i"
	fi
	let i++
done
end_time=`date +%s`
let use_time=end_time-begin_time
echo "set time consumed: $use_time"
let i=0
begin_time=`date +%s`
while true
do
	if (( i >= 1000 ))
	then
		break
	fi
	printf "get $i\r\n" | nc 10.234.4.22 11211 > /dev/null 2>&1
	let i++
done
end_time=`date +%s`
let use_time=end_time-begin_time
echo "get time consumed: $use_time"
登录后复制

2、C++程序则通过libmemcached set。

#include <iostream>
#include <map>
#include <string>
#include <sys>
#include <time.h>
#include <stdlib.h>
#include "libmemcached/memcached.h"
using namespace std;
uint32_t item_size = 0;
uint32_t loop_num = 0;
bool single_server = false;
std::string local_ip;
std::map<:string uint32_t> servers;
int64_t getCurrentTime()
{
    struct timeval tval;
    gettimeofday(&tval, NULL);
    return (tval.tv_sec * 1000000LL + tval.tv_usec);
}
memcached_st* mc_init()
{
	memcached_st * mc = memcached_create(NULL);
	if (mc == NULL)
	{
		cout ::iterator iter;
    for (iter = servers.begin(); iter != servers.end(); ++iter)
    {
        if (single_server && iter->first != local_ip)
        {
            continue;
        }
	    memcached_return rc = memcached_server_add(mc, iter->first.c_str(), iter->second);
        if(rc != MEMCACHED_SUCCESS)
        {
            cout first first 
<p>测试发现二者的结果是相背的。shell脚本set 1000次8KB的item,只要3s左右,平均需要3ms。而C++版本则需要39s左右,平均耗时39ms。照理说shell脚本需要不断连接服务器和启动nc进程,应该更慢才对。我用ltrace跟踪了一下,发现8KB的数据需要发送两次,两次write都是非常快的,但是等memcached返回时用了很多时间,主要的时间就耗费在这个地方。</p>
<pre class="brush:php;toolbar:false">
23:32:37.069922 [0x401609]
memcached_set(0x19076200, 0x7fffdad68560, 32, 0x1907a570, 8192 <unfinished ...>
23:32:37.070034 [0x3f280c5f80]
SYS_write(3, "set 29 0 600
8192\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"..., 8196) = 8196 
23:32:37.071657 [0x3f280c5f80]
SYS_write(3, "aaaaaaaaaaaaaaa\r\n", 17) = 17 
23:32:37.071741 [0x3f280c5f00]
SYS_read(3, "STORED\r\n", 8196) = 8 (39ms)
</unfinished>
登录后复制

和剑豪讨论下之后,剑豪马上去grep了一把代码,发现原来libmemcached居然有MEMCACHED_MAX_BUFFER这样一个常量,其值为8196。并且它还没有对应的memcached_behavior_set函数。在memcached_constants.h中将其直接改成81960,然后就欣喜地发现cache_put_latency从7ms降低到1ms左右。

问题完美虽然地解决了,但是意犹未尽,于是想搞明白为什么会出现这种奇怪的现象。瓶颈貌似在服务器端,于是对memcached做了一些修改。在状态切换的时候加上一个精确到微秒的时间。

static int64_t getCurrentTime()
{
    struct timeval tval;
    gettimeofday(&tval, NULL);
    return (tval.tv_sec * 1000000LL + tval.tv_usec);
}
static void conn_set_state(conn *c, enum conn_states state) {
    assert(c != NULL);
    assert(state >= conn_listening && state state) {
        if (settings.verbose > 2) { 
            fprintf(stderr, "%d: going from %s to %s, time: %lu\n",
                    c->sfd, state_text(c->state),
                    state_text(state), getCurrentTime());
        }    
        c->state = state;
        if (state == conn_write || state == conn_mwrite) {
            MEMCACHED_PROCESS_COMMAND_END(c->sfd, c->wbuf, c->wbytes);
        }    
    }    
}
登录后复制

从打印的时间戳可以看出来,时间主要花在conn_nread状态处理代码中。最后定位到第二次read花费的时间非常多。

15: going from conn_waiting to conn_read, time: 1348466584440118
15: going from conn_read to conn_parse_cmd, time: 1348466584440155
 NOT FOUND 98
>15 STORED
15: going from conn_nread to conn_write, time: 1348466584480099(36ms)
15: going from conn_write to conn_new_cmd, time: 1348466584480145
15: going from conn_new_cmd to conn_waiting, time: 1348466584480152
登录后复制

value的数据可能在conn_read中读完了,这个时候只需要memmove一下就好了。如果没有在conn_read状态中读完,那么就需要conn_nread自己来一次read了(因为套接字被设置成了异步,所以还可能需要多次read),关键就是这个read太慢了。

        case conn_nread:
            if (c->rlbytes == 0) { 
                complete_nread(c);
                break;
            }    
            /* first check if we have leftovers in the conn_read buffer */
            if (c->rbytes > 0) { 
                int tocopy = c->rbytes > c->rlbytes ? c->rlbytes : c->rbytes;
                if (c->ritem != c->rcurr) {
                    memmove(c->ritem, c->rcurr, tocopy);
                }    
                c->ritem += tocopy;
                c->rlbytes -= tocopy;
                c->rcurr += tocopy;
                c->rbytes -= tocopy;
                if (c->rlbytes == 0) { 
                    break;
                }    
            }    
            /*  now try reading from the socket */
            res = read(c->sfd, c->ritem, c->rlbytes);
            if (res > 0) { 
                pthread_mutex_lock(&c->thread->stats.mutex);
                c->thread->stats.bytes_read += res; 
                pthread_mutex_unlock(&c->thread->stats.mutex);
                if (c->rcurr == c->ritem) {
                    c->rcurr += res; 
                }    
                c->ritem += res; 
                c->rlbytes -= res; 
                break;
            }
登录后复制

折腾了好久,在libmemcached的io_flush函数前后也打了不少时间戳,发现libmemcached发送数据是非常快的。突然灵感闪现,我想起来了TCP_NODELAY这个参数,于是在libmemcached memcached_connect.c文件中的set_socket_options函数中增加了这个参数(事实上set_socket_options函数里面可以设置TCP_NODELAY,没有仔细看)。

    int flag = 1;
    int error = setsockopt(ptr->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag) );
    if (error == -1) {
          printf("Couldn't setsockopt(TCP_NODELAY)\n");
            exit(-1);
    }else
    {   
          printf("set setsockopt(TCP_NODELAY)\n");
    }
登录后复制

在不改MEMCACHED_MAX_BUFFER的情况下,现在set 100KB的item也是一瞬间的事情了。不过新的困惑又出现了,Nagle算法什么情况会起作用呢?为什么第一个包没被缓存,第二个包一定会被缓存呢?

libmemcached发送一个set命令是分成三部分的,首先是header(set 0 0 600 8192\r\n,共18个字节),然后是value(8192个字节),最后是’\r\n’(两个字节),一共是8212个字节。memcached在conn_read状态一共能读取2048+2048+4096+8196=16KB的数据,因此对于8KB的数据是完全可以在conn_read状态读完的。通过在conn_read状态处理的代码中增加下面的打印语句可以发现有些情况下,conn_read最后一次只读取了4个字节(正常情况应该是2048+2048+4096+20),剩下的16个字节放到conn_nread中读了。

        res = read(c->sfd, c->rbuf + c->rbytes, avail);
        if (res > 0) {
            char buf[10240] = {0};
            sprintf(buf, "%.*s", res, c->rbuf + c->rbytes);
            printf("avail=%d, read=%d, str=%s\n", avail, res, buf);
登录后复制

未设置TCP_NODELAY选项时,使用netstat可以看到客户端socket的Send-Q一直会维持在8214和8215之间。

tcp        0   8215 10.232.42.91:59836          10.232.42.91:11211          ESTABLISHED 25800/t
登录后复制

设置TCP_NODELAY选项时,客户端socket的Send-Q就一直为0了。

tcp        0      0 10.232.42.91:59890          10.232.42.91:11211          ESTABLISHED 26554/t.quick
登录后复制
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

iPhone 15 Pro Max vs iPhone 14 Pro Max:它们之间的对比和区别是什么? iPhone 15 Pro Max vs iPhone 14 Pro Max:它们之间的对比和区别是什么? Sep 19, 2023 pm 08:29 PM

iPhone15Pro与iPhone14Pro:规格比较以下是iPhone15ProMax和iPhone14ProMax的规格比较:iPhone15ProMaxiPhone14ProMax显示尺寸6.7英寸6.7英寸显示技术超级视网膜XDROLED超级视网膜XDROLED分辨率2796x1290像素,460ppi2796x1290像素,460ppi刷新率120赫兹120赫兹峰值亮度2,000尼特2,000尼特尺寸6.29x3.02x0.32英寸6.33x3.06x0.31英寸重量221克240克

Memcached缓存技术对于PHP中的Session处理的优化 Memcached缓存技术对于PHP中的Session处理的优化 May 16, 2023 am 08:41 AM

Memcached是一种常用的缓存技术,它可以使Web应用程序的性能得到很大的提升。在PHP中,常用的Session处理方式是将Session文件存放在服务器的硬盘上。但是,这种方式并不是最优的,因为服务器的硬盘会成为性能瓶颈之一。而使用Memcached缓存技术可以对PHP中的Session处理进行优化,提高Web应用程序的性能。PHP中的Session处

如何在 iPhone 14 Pro 上使用 HEIF Max (48 MP)并优化存储空间 如何在 iPhone 14 Pro 上使用 HEIF Max (48 MP)并优化存储空间 Sep 21, 2023 pm 02:13 PM

最新的iPhonePro系列配备了强大的48MP传感器,可确保拍摄高度详细和水晶般清晰的照片,捕捉每一个珍贵的时刻。然而,一个潜在的缺点是全分辨率图像的大小,尤其是ProRAW格式的图像。尽管iPhone提供的最大存储空间为512GB,但捕获大量ProRAW图像(每张约75MP)和视频(每分钟440MB,60FPS)会快速占用您的存储空间。如果您打算将iPhone用作大型项目或旅行的主摄像头,这可能会带来问题。但是,如果您可以拍摄那些高分辨率的48MP照片而不用担心存储限制,那不是很棒吗?这很快

全系列 iPhone 15 持航力比拼 iPhone 15 Plus 击败 15 Pro Max 全系列 iPhone 15 持航力比拼 iPhone 15 Plus 击败 15 Pro Max Sep 30, 2023 pm 11:09 PM

虽然苹果会推出iPhone的视频播放时间来让用户知道iPhone电池差不多可用。但是正常的用户不会全天使用iPhone查看视频。7款iPhone进行日常应用的持久力测试。包含包括iPhone15ProMax、iPhone15Pro、iPhone15Plus、iPhone15、iPhone14ProMax、iPhone14及iPhone13ProMax共7款。横跑一些日常的应用,例如Spotify、Zoom、Tiktok、Headspace想想App、游戏等等,由此可见不同iPhone的持航力。此

PHP8.0中的缓存库:Memcached PHP8.0中的缓存库:Memcached May 14, 2023 am 08:16 AM

PHP8.0中的缓存库:Memcached随着互联网的快速发展,现代应用程序需要高效可靠的缓存技术来提高性能和处理大量数据。由于PHP的流行和开源特性,PHP缓存库已经成为了Web开发社区的一个必备工具。Memcached是一种广泛使用的开源高速内存缓存系统,它能处理数百万个同时连接的缓存请求,可以用于许多不同类型的应用程序,例如社交网络、在线

java8的stream怎么取max java8的stream怎么取max May 14, 2023 pm 03:43 PM

java8的stream取maxpublicstaticvoidmain(String[]args){Listlist=Arrays.asList(1,2,3,4,5,6);Integermax=list.stream().max((a,b)->{if(a>b){return1;}elsereturn-1;}).get();System.out.println(max);}注意点:这里判断大小是通过正负数和0值。而不是直接写成if(a>b){returna;}elseretur

三星 S25 Ultra 细节曝光:与 iPhone 16 Pro Max 同宽,屏幕更大 三星 S25 Ultra 细节曝光:与 iPhone 16 Pro Max 同宽,屏幕更大 Aug 23, 2024 pm 09:33 PM

8月22日消息,随着三星新一代旗舰手机S25Ultra的发布日益临近,越来越多的细节开始浮出水面。知名博主@i冰宇宙今日在微博上透露了S25Ultra的更多规格信息,其中最引人注目的是其机身宽度与苹果iPhone16ProMax相同,均为77.6mm。1.得益于三星在边框设计上的进一步优化,使得S25Ultra在保持与iPhone16ProMax相同宽度的同时,屏幕大小提升至6.86英寸,为用户带来更加沉浸的视觉体验。博主在评论区进一步指出,S25Ultra的黑边比iPhone16ProMax&

如何使用Memcached缓存技术优化PHP应用程序CPU的使用率? 如何使用Memcached缓存技术优化PHP应用程序CPU的使用率? Jun 21, 2023 pm 05:07 PM

随着互联网的发展,PHP应用程序在互联网应用领域中变得越来越常见。但是,PHP应用程序的高并发访问会导致服务器的CPU使用率高,从而影响应用程序的性能。为了优化PHP应用程序的性能,Memcached缓存技术成为了一种很好的选择。本文将介绍如何使用Memcached缓存技术优化PHP应用程序CPU的使用率。Memcached缓存技术简介Memcached是一

See all articles