进阶 docs.memcached.org 2026-10-13 18:51:48 · 22 阅读

第33章 缓存故事

一个关于缓存的故事

注:本文概述了 memcached 的基本使用场景及其客户端的工作原理。

两位勇敢的冒险者,程序员(Programmer)和系统管理员(Sysadmin),踏上了一段旅程。他们共同构建网站,包括 Web 服务器和数据库。来自互联网各地的用户与 Web 服务器交互,请求生成页面,而 Web 服务器则向数据库索取生成页面所需的资源。程序员负责写代码,系统管理员则负责部署 Web 服务器和数据库服务器。

有一天,系统管理员发现他们的数据库“病了”!它到处吐着呕吐物和红色的东西!系统管理员宣布它发烧了,负载平均值为 20!程序员问系统管理员:“那我们该怎么办?”系统管理员说:“我听说过一个很棒的东西叫 memcached。它对 LiveJournal 帮助很大!”“好吧,让我们试试!”程序员说。

我们勇敢的 sysadmin 审视着他手下的六台 Web 服务器。他决定使用其中三台来运行 'memcached' 服务器。Sysadmin 给每台 Web 服务器增加了一千兆字节(1 GB)的内存,并启动 memcached,限制每台最多使用 1 GB。于是,他拥有了三个 memcached 实例,每个实例最多可存储 1 GB 的数据。就这样,程序员和系统管理员退后一步,欣赏他们辉煌的 memcached 集群!

“那现在呢?”他们说道,“它什么都没做啊!” 这些 memcached 没有在和任何东西通信,当然也没有数据。而现在,他们的数据库负载达到了 25!

我们爱冒险的程序员拿起了 pecl/memcache 客户端库手册,这是勇敢的系统管理员贴心地在所有六台 Web 服务器上安装好的。“别怕!”他说。“我有一个主意!”他获取了三个 memcached 的 IP 地址和端口号,并将它们添加到 PHP 数组中。

$MEMCACHE_SERVERS = array( "10.1.1.1", //web1 "10.1.1.2", //web2 "10.1.1.3", //web3 );

然后,他创建了一个对象,巧妙地将它命名为 '$memcache'。

$memcache = new Memcache(); foreach($MEMCACHE_SERVERS as $server){ $memcache->addServer ( $server ); }

现在,程序员开始思考。他想了又想。 “我知道了!”他说。“主页上有这么个东西,运行着 SELECT * FROM hugetable WHERE timestamp > lastweek ORDER BY timestamp ASC LIMIT 50000; 这个查询,它耗时五秒!”“让我们把它放进 memcached 吧,”他说。于是,他将这段 SELECT 查询的代码包装起来,并使用了他的 $memcache 对象。他的代码逻辑如下:

这个 select 查询的结果在 memcache 里有吗? 如果没有,执行查询,获取结果,并将其放入 memcache!

代码如下:

$huge_data_for_front_page = $memcache->get("huge_data_for_front_page"); if($huge_data_for_front_page === false){ $huge_data_for_front_page = array(); $sql = "SELECT * FROM hugetable WHERE timestamp > lastweek ORDER BY timestamp ASC LIMIT 5000"; $res = mysql_query($sql, $mysql_connection); while($rec = mysql_fetch_assoc($res)){ $huge_data_for_front_page[] = $rec; } // 缓存 10 分钟 $memcache->set("huge_data_for_front_page", $huge_data_for_front_page, 0, 600); } // 您可以随意使用 $huge_data_for_front_page

程序员推送了代码。系统管理员紧张地流汗。砰!数据库负载降到了 10!网站现在速度相当快。于是,系统管理员感到困惑,“刚才到底发生了该死的什么回事!?“我在我的 memcached 上放了监控图表!我用的是 cacti,这是我看到的情况!我看到只有一个 memcached 有流量,但我明明配置了三个 :(。” 因此,系统管理员迅速学会了 ascii 协议,telnet 到每个 memcached 的 11211 端口,并询问它:

嘿,'get huge_data_for_front_page',你在吗?

第一个 memcached 没有回应…

第二个 memcached 没有回应…

然而,第三个 memcached 却往他的 telnet 会话中吐出了一大团数据!数据在那里!只有当 memcached 拥有程序员缓存的那个键时才会发生这种情况!

困惑之下,他向邮件列表提问。大家异口同声地回答:“这是一个分布式缓存!这就是它的工作方式!” 但这意味着什么?仍然感到困惑,并且有点担心自己的职业生涯,系统管理员让程序员再缓存一些其他的东西。“让我们看看会发生什么。我们很好奇。我们可以弄明白这一点的,” 系统管理员说。

“嗯,还有另一个查询,它不快,但每秒运行 100 次。也许那个会有帮助,” 程序员说。于是,他像之前一样把它包装起来。果然,服务器负载降到了 8!

因此,程序员编写了更多代码,缓存了更多事物。他使用了新技术。“我在邮件列表和 FAQ 上找到的!这些真不错,”他说。数据库负载下降了;7、5、3、2、1!

“好吧,” 系统管理员说,“让我们再试一次。” 现在他查看图表。所有 memcached 都在运行!它们都在接收请求!这太棒了!它们都在被使用!

于是,他再次取出程序员使用的键,在他的 memcached 服务器上查找它们。'get this_key' 'get that_key' 但每次这样做,他只在一个 memcached 上找到每个键!他心想,为什么要这样做呢?并且整夜苦苦思索。这太愚蠢了!难道你不想让键存在于所有 memcached 上吗?

“但是等等”, 他心想,“我给每个 memcached 分配了 1 GB 内存,这意味着,总共,我可以缓存数据库的 3 GB 数据,而不是仅仅 1 GB!哦天哪,这太棒了,” 他想着。“这将为我节省一大笔钱。Brad Fitzpatrick,我爱你!”

“但是嗯,下一个问题,这个让人头疼,这里这台 Web 服务器,这台运行 memcached 的机器很旧,它病了,需要升级。但为了做到这一点,我不得不将它下线!我可怜的 memcache 集群会发生什么?好吧,让我们看看,” 他说,然后关掉了这台机器。现在他查看他的图表。“噢不,数据库负载,它一下子跳起来了!负载不再是一,现在是二。嗯,但仍然可以忍受。所有的其他 memcached 仍在接收流量。情况还不算糟。只是几次缓存未命中,而我几乎就完成了工作。于是他重启了机器,让 memcached 重新工作。几分钟后,数据库负载再次降回 1,这是它应该一直保持在的位置。

“缓存恢复了!我现在明白了。如果它不可用,只是意味着我的部分请求会未命中。但这不足以击垮我。这相当棒。”

于是,程序员和系统管理员继续构建网站。他们继续缓存。当他们有疑问时,他们就询问邮件列表或再次阅读 FAQ。他们看着他们的图表。从此过着幸福的生活。

作者:Dormando via IRC. 由 Brian Moon 出于乐趣进行了编辑。 Emufarmers 进行了进一步的趣味编辑。

评论 (0)