当前位置:Gxlcms > mysql > memcached优化始末

memcached优化始末

时间:2021-07-01 10:21:17 帮助过:38人阅读

我们线上服务一直使用memcached作为cache服务器,客户端使用的是libmemcached,示意图如下。 问题现象 我们发现item平均大小仅为10KB左右,结果memcached set的平均延时就有7ms之多。我们之前已经在libmemcached里面设置了TCP_NODELAY。 libmemcached在set/g

我们线上服务一直使用memcached作为cache服务器,客户端使用的是libmemcached,示意图如下。

问题现象

我们发现item平均大小仅为10KB左右,结果memcached set的平均延时就有7ms之多。我们之前已经在libmemcached里面设置了TCP_NODELAY。

libmemcached在set/get之后会接收服务器返回的数据,如果一次没有收到,就会循环调用poll等待服务器返回数据。

      while (1) 
      {   
        int enable = 1;
        setsockopt( ptr->fd, IPPROTO_TCP, TCP_QUICKACK, (void *)&enable, sizeof(enable));
        data_read= recv(ptr->fd, ptr->read_buffer, MEMCACHED_MAX_BUFFER, 0); 
        setsockopt( ptr->fd, IPPROTO_TCP, TCP_QUICKACK, (void *)&enable, sizeof(enable));
        if (data_read > 0)
        {   
          break;
        }   
        else if (data_read == SOCKET_ERROR)
        {   
          ptr->cached_errno= get_socket_errno();
          memcached_return_t rc= MEMCACHED_ERRNO;
          switch (get_socket_errno())
          {   
          case EWOULDBLOCK:
#ifdef USE_EAGAIN
          case EAGAIN:
#endif
          case EINTR:
#ifdef TARGET_OS_LINUX
          case ERESTART:
#endif
            if ((rc= io_wait(ptr, MEM_READ)) == MEMCACHED_SUCCESS)
              continue;
            /* fall through */
          default:
            {   
              memcached_quit_server(ptr, true);
              *nread= -1; 
              return rc; 
            }   
          }   
        }

剑豪使用systemstap发现经常有超过40ms的poll,这是一个很重要的线索,我使用strace也可以看到很多超过40ms的poll。

经过一系列测试发现,流量越小,延时反而越高。但是为什么延时又随着QPS的波动而波动呢?我想应该是这样的,memcached_st对象池使用stack保存,使用的顺序是后进先出。当流量小的时候,会一直使用栈最上面的一两个memcached_st对象,所以对某个memcached服务器的压力可能不必流量高峰低。

问题缘由

经过来回折腾,我发现客户端和服务器端都存在DELAYACK的情况,客户端会对服务器的响应做DELAYACK,不会影响性能。真正奇怪的是客户端设置了NODELAY,但是有时候它会等待服务器发送ACK,才接着继续发数据。并且由于服务器都开启了TSO、GRO等特性,使用tcpdump看不到网卡做的事情,tcp经常push一个远大于MSS的包出去,似乎也根本不考虑服务端的接收窗口大小。

下图中487、488、489三行在做一个get,没有命中cache之后,从490行开始做set,结果服务器的ack delay了,导致客户端发送第二个包的时候等了40ms。

我甚至怀疑TCP_NODELAY没有设置上,但是在代码中使用getsockopt将TCP_NODELAY的值打印出来,发现确实是1。

NOREPLY

无意中看到memcached支持NOREPLY选项(这个选项libmemcached同样也支持,要不然一个巴掌拍不响),真是如获重宝啊,实际线上的效果如下,现在set的latency已经可以忽略不计了。我想世界上所有非关键的服务都应该支持NOREPLY这个选项。

一点小结

1、TCP_NODELAY之所以没起作用,应该和gro有关系。我做了一个测试,发现关闭gro之后,poll超过40ms的情况减少了很多。如果不能理解其中的奥义,就很难解释为什么流量大,延时反而会降下来这个现象。

2、libmemcached这种每个memcached_st对象对每个memcached服务器都有自己连接的方式,特别容易导致每个连接压力都很小,导致延时变得特别高。这种压力能小到什么程度呢?假设有30台memcached服务器,客户端set的QPS是1K,多线程导致一共有30个memcached_st对象,那么一个socket的set QPS就是1K/(30*30)=1。

淘宝自己的tbnet/anet网络通讯库是复用socket的,每个包通过channel id来标识自己,可以使socket保持高压力,不容易出现上面这个问题。

人气教程排行