memcached全面剖析–2.理解memcached的内存存储

2019-03-26 22:00|来源: 网络

我是mixi株式会社研究开发组的前坂徹。 上次的文章介绍了memcached是分布式的高速缓存服务器。 本次将介绍memcached的内部构造的实现方式,以及内存的管理方式。 另外,memcached的内部构造导致的弱点也将加以说明。

Slab Allocation机制:整理内存以便重复使用

最近的memcached默认情况下采用了名为Slab Allocator的机制分配、管理内存。 在该机制出现以前,内存的分配是通过对所有记录简单地进行malloc和free来进行的。 但是,这种方式会导致内存碎片,加重操作系统内存管理器的负担,最坏的情况下, 会导致操作系统比memcached进程本身还慢。Slab Allocator就是为解决该问题而诞生的。

下面来看看Slab Allocator的原理。下面是memcached文档中的slab allocator的目标:

the primary goal of the slabs subsystem in memcached was to eliminate memory fragmentation issues totally by using fixed-size memory chunks coming from a few predetermined size classes.

也就是说,Slab Allocator的基本原理是按照预先规定的大小,将分配的内存分割成特定长度的块, 以完全解决内存碎片问题。

Slab Allocation的原理相当简单。 将分配的内存分割成各种尺寸的块(chunk), 并把尺寸相同的块分成组(chunk的集合)(图1)。

memcached-0002-01.png

图1 Slab Allocation的构造图

而且,slab allocator还有重复使用已分配的内存的目的。 也就是说,分配到的内存不会释放,而是重复利用。

Slab Allocation的主要术语

Page

分配给Slab的内存空间,默认是1MB。分配给Slab之后根据slab的大小切分成chunk。

Chunk

用于缓存记录的内存空间。

Slab Class

特定大小的chunk的组。

在Slab中缓存记录的原理

下面说明memcached如何针对客户端发送的数据选择slab并缓存到chunk中。

memcached根据收到的数据的大小,选择最适合数据大小的slab(图2)。 memcached中保存着slab内空闲chunk的列表,根据该列表选择chunk, 然后将数据缓存于其中。

memcached-0002-02.png

图2 选择存储记录的组的方法

实际上,Slab Allocator也是有利也有弊。下面介绍一下它的缺点。

Slab Allocator的缺点

Slab Allocator解决了当初的内存碎片问题,但新的机制也给memcached带来了新的问题。

这个问题就是,由于分配的是特定长度的内存,因此无法有效利用分配的内存。 例如,将100字节的数据缓存到128字节的chunk中,剩余的28字节就浪费了(图3)。

memcached-0002-03.png

图3 chunk空间的使用

对于该问题目前还没有完美的解决方案,但在文档中记载了比较有效的解决方案。

The most efficient way to reduce the waste is to use a list of size classes that closely matches (if that’s at all possible) common sizes of objects that the clients of this particular installation of memcached are likely to store.

就是说,如果预先知道客户端发送的数据的公用大小,或者仅缓存大小相同的数据的情况下, 只要使用适合数据大小的组的列表,就可以减少浪费。

但是很遗憾,现在还不能进行任何调优,只能期待以后的版本了。 但是,我们可以调节slab class的大小的差别。 接下来说明growth factor选项。

使用Growth Factor进行调优

memcached在启动时指定 Growth Factor因子(通过-f选项), 就可以在某种程度上控制slab之间的差异。默认值为1.25。 但是,在该选项出现之前,这个因子曾经固定为2,称为“powers of 2”策略。

让我们用以前的设置,以verbose模式启动memcached试试看:

$ memcached -f 2 -vv

下面是启动后的verbose输出:

slab class   1: chunk size    128 perslab  8192

slab class   2: chunk size    256 perslab  4096

slab class   3: chunk size    512 perslab  2048

slab class   4: chunk size   1024 perslab  1024

slab class   5: chunk size   2048 perslab   512

slab class   6: chunk size   4096 perslab   256

slab class   7: chunk size   8192 perslab   128

slab class   8: chunk size  16384 perslab    64

slab class   9: chunk size  32768 perslab    32

slab class  10: chunk size  65536 perslab    16

slab class  11: chunk size 131072 perslab     8

slab class  12: chunk size 262144 perslab     4

slab class  13: chunk size 524288 perslab     2

可见,从128字节的组开始,组的大小依次增大为原来的2倍。 这样设置的问题是,slab之间的差别比较大,有些情况下就相当浪费内存。 因此,为尽量减少内存浪费,两年前追加了growth factor这个选项。

来看看现在的默认设置(f=1.25)时的输出(篇幅所限,这里只写到第10组):

slab class   1: chunk size     88 perslab 11915

slab class   2: chunk size    112 perslab  9362

slab class   3: chunk size    144 perslab  7281

slab class   4: chunk size    184 perslab  5698

slab class   5: chunk size    232 perslab  4519

slab class   6: chunk size    296 perslab  3542

slab class   7: chunk size    376 perslab  2788

slab class   8: chunk size    472 perslab  2221

slab class   9: chunk size    592 perslab  1771

slab class  10: chunk size    744 perslab  1409

可见,组间差距比因子为2时小得多,更适合缓存几百字节的记录。 从上面的输出结果来看,可能会觉得有些计算误差, 这些误差是为了保持字节数的对齐而故意设置的。

将memcached引入产品,或是直接使用默认值进行部署时, 最好是重新计算一下数据的预期平均长度,调整growth factor, 以获得最恰当的设置。内存是珍贵的资源,浪费就太可惜了。

接下来介绍一下如何使用memcached的stats命令查看slabs的利用率等各种各样的信息。

查看memcached的内部状态

memcached有个名为stats的命令,使用它可以获得各种各样的信息。 执行命令的方法很多,用telnet最为简单:

$ telnet 主机名 端口号

连接到memcached之后,输入stats再按回车,即可获得包括资源利用率在内的各种信息。 此外,输入”stats slabs”或”stats items”还可以获得关于缓存记录的信息。 结束程序请输入quit。

这些命令的详细信息可以参考memcached软件包内的protocol.txt文档。

$ telnet localhost 11211

Trying ::1...

Connected to localhost.

Escape character is '^]'.

stats

STAT pid 481

STAT uptime 16574

STAT time 1213687612

STAT version 1.2.5

STAT pointer_size 32

STAT rusage_user 0.102297

STAT rusage_system 0.214317

STAT curr_items 0

STAT total_items 0

STAT bytes 0

STAT curr_connections 6

STAT total_connections 8

STAT connection_structures 7

STAT cmd_get 0

STAT cmd_set 0

STAT get_hits 0

STAT get_misses 0

STAT evictions 0

STAT bytes_read 20

STAT bytes_written 465

STAT limit_maxbytes 67108864

STAT threads 4

END

quit

另外,如果安装了libmemcached这个面向C/C++语言的客户端库,就会安装 memstat 这个命令。 使用方法很简单,可以用更少的步骤获得与telnet相同的信息,还能一次性从多台服务器获得信息。

$ memstat --servers=server1,server2,server3,...

libmemcached可以从下面的地址获得:

  • http://tangent.org/552/libmemcached.html

查看slabs的使用状况

使用memcached的创造着Brad写的名为memcached-tool的Perl脚本,可以方便地获得slab的使用情况 (它将memcached的返回值整理成容易阅读的格式)。可以从下面的地址获得脚本:

  • http://code.sixapart.com/svn/memcached/trunk/server/scripts/memcached-tool

使用方法也极其简单:

$ memcached-tool 主机名:端口 选项

查看slabs使用状况时无需指定选项,因此用下面的命令即可:

$ memcached-tool 主机名:端口

获得的信息如下所示:

#  Item_Size   Max_age  1MB_pages Count   Full?

1     104 B  1394292 s    1215 12249628    yes

2     136 B  1456795 s      52  400919     yes

3     176 B  1339587 s      33  196567     yes

4     224 B  1360926 s     109  510221     yes

5     280 B  1570071 s      49  183452     yes

6     352 B  1592051 s      77  229197     yes

7     440 B  1517732 s      66  157183     yes

8     552 B  1460821 s      62  117697     yes

9     696 B  1521917 s     143  215308     yes

10     872 B  1695035 s     205  246162     yes

11     1.1 kB 1681650 s     233  221968     yes

12     1.3 kB 1603363 s     241  183621     yes

13     1.7 kB 1634218 s      94   57197     yes

14     2.1 kB 1695038 s      75   36488     yes

15     2.6 kB 1747075 s      65   25203     yes

16     3.3 kB 1760661 s      78   24167     yes

各列的含义为:

含义
#slab class编号
Item_SizeChunk大小
Max_ageLRU内最旧的记录的生存时间
1MB_pages分配给Slab的页数
CountSlab内的记录数
Full?Slab内是否含有空闲chunk

从这个脚本获得的信息对于调优非常方便,强烈推荐使用。

内存存储的总结

本次简单说明了memcached的缓存机制和调优方法。 希望读者能理解memcached的内存管理原理及其优缺点。

下次将继续说明LRU和Expire等原理,以及memcached的最新发展方向—— 可扩充体系(pluggable architecher))。


本文链接:memcached全面剖析–2.理解memcached的内存存储,转自:http://tech.idv2.com/2008/07/11/memcached-002/

相关问答

更多
  • 什么是memcached[2023-11-18]

    memcached是一种高性能的内存式缓存系统,通过将数据存储在内存中减少读取数据库的次数
  • Memcached就是在内存中维护一张巨大的hash表,通过自己的一套算法来维 护数据的操作,这个算法比较复杂,这里我们只做简单的介绍,对我们PHP程序员而言,重点掌握如何在项目如何使 Memcached技术. 本章节要求同学们深刻理解Memcached运行原理,并能够正确使用Memcached对网站优化提速。
  • 这是不可能的, 这是解释原因: 没有.memcached不支持它,它不是一个计划的功能。 这将是一个相对缓慢和阻塞的操作...... UPDATE 常见问题如何列出所有密钥? 使用memcached,您无法列出所有密钥。 有一个调试接口,但这不是一个明智的用法。 It's not possible, here's the explanation why: No. memcached doesn't support that and it's not a planned feature. It would b ...
  • 也许正确的解决方案是在您的Web服务器上使用Apache自己的内置缓存。 毕竟,这不是说有很多不同的变化,或者变化频繁。 如果您的服务器场中的每台服务器都会不时生成其自己的副本,那么这不会是(我猜)会大受打击。 Apache的缓存在输出中遵守“Expires”头文件等,因此只需将这些URI打开并输出适当的头文件就足够简单了。 memcached的“优点”是你的memcached服务器形成一个池,它看起来像一个缓存,并可以被你的所有网络节点共享; 如果你只有很少的项目(在这种情况下,它听起来像你有一个或几个) ...
  • 我不建议使用Memcached。 缓存可以随时失效,最终会丢失数据。 我建议你使用像Redis这样的解决方案。 I wouldn't suggest using Memcached. The cache can invalidate at any time and you'll end up loosing data. I would recommend you use a solution such as Redis.
  • 不,没有多大意义。 是的,你需要告诉它要缓存什么。 但是有一个中间件告诉它缓存所有内容。 不,它不会知道。 No, there's not much point. Yes, you need to tell it what to cache. There is a middleware that tells it to cache everything, though. No, it won't know.
  • 我对两者都有一些经验。 我对你的两个问题说'是'。 我一直在和Hibernate一起使用ehcache。 它将在中央数据库中找到的数据缓存在群集中的多个服务器节点上的内存中。 原因当然是减少数据库访问。 如果一个节点写入缓存值,则该值应在其他节点上失效,以强制它们重新读取数据库中的值,然后重新缓存它。 Memcached我用作数据源。 这是一个简单的key = value数据库。 在每个服务节点上,您可以定义可以使用的memcached节点列表。 然后它会写入某个节点(通过循环我猜)。 密钥具有关于存储值的 ...
  • 我会说,如果你运行一个需要memcached的网站,你不应该在共享主机上运行它。 这是我轻浮的答案。 这是一个真正的答案: Memcached与MEMORY存储引擎相比具有一些优点。 存储分布在多个服务器上。 MEMORY存储引擎仅限于单个主机,并受到该主机的CPU和内存的限制。 快速访问个人条目。 MEMORY存储引擎仅具有表级锁定功能,因此并发性受到影响。 非关系密钥/值存储。 MEMORY存储引擎更加结构化,这对于缓存类型的使用并不有用。 此外,MEMORY将varchar展开为全长,因此存储效率较低 ...
  • 这是因为django传递给memcached的密钥并不是你在调用cache.set使用的密钥。 用户提供的缓存密钥不是逐字使用的 - 它与缓存前缀和密钥版本组合以提供最终缓存密钥。 默认情况下,使用冒号连接这三个部分以生成最终字符串 https://docs.djangoproject.com/en/1.10/topics/cache/#cache-key-transformation 调整设置,或创建自己的KEY_FUNCTION以确保PHP键与django键匹配。 That's because the ...
  • 这里的问题是$ lifetime和Zend_Cache_Backend_Memcached中的相对错误。 Memcached的最大生命周期为30天(2592000),因此我使用它的限制太大,这就是数据未存储的原因。 可悲的是: Zend_Cache_Backend_Memcached :: save()不检查限制是否正确<2592000 memcached对象上的set()方法返回true,即使它没有存储数据 $ result = @ $ this - > _ memcache-> set($ id,arr ...