13

我们正在为我们的应用程序部署 memcached,我想让它尽可能地具有抵抗力。

我们计划使用更新的memcacheD扩展。

我还没有完全弄清楚的一件事是,如果其中一台服务器死了会发生什么。至少,memcached 客户端似乎只是“放弃”了该服务器,并且没有在其中存储任何内容。

这种行为我很好。我们可以处理一堆缓存未命中。但是,在其中一台服务器被视为“失败”之后,后续的集合并被重新分配给剩余的服务器会很好。

因为这似乎不会自动发生;我想解决这个问题的唯一方法是让外部系统对 memcached 系统进行健康检查,并适当地更新服务器列表。

但是,如果有一个包含 10 个服务器的列表,并且假设第 5 个服务器死掉了……即使使用 Ketama 哈希,这似乎也会引发密钥的大量重新分配(这只是基于常识)。

所以理想情况下,我只想让 PHP 扩展找出服务器已关闭,将其标记为关闭指定的时间量(10 分钟),并在这 10 分钟内回退到其他服务器(分布良好)以进行设置和获取.

其他人如何解决这个问题?

编辑:澄清我的 libketama 点。

假设我们有 10 台服务器:

1,2,3,4,5,6,7,8,9,10

其中一人死亡。然后,Libketama 将提供一个非常高的可能性,即丢失服务器的命中被平均分配到其余服务器:

1,2,3,4,inactive,6,7,8,9,10

但是:如果我们手动提供和管理此列表,则情况并非如此:

1,2,3,4,6,7,8,9,10 // There are now 9 servers!

6 现在将获得 5 以前的密钥,7 将获得 6。8个得到7个,9个得到8个,10个得到9个。第 10 台服务器获得的所有点击量不会平均分配到其余服务器中。导致几乎 50% 的所有密钥被发送到新服务器的可能性很高。

4

4 回答 4

3

我通常将可用服务器列表存储在 APC 中,因此我可以即时修改它。你是对的,系统会在它被列出时尝试继续使用关闭的服务器,幸运的是,使用新的散列方法,将它从轮换中拉出来并不是什么大问题。

我会避免使用全新的 PHP 扩展,或者尝试将新软件添加到您的部署堆栈中。您可能已经在使用某些东西进行监控(nagios?)。让它在每个网络服务器上调用一个简单的 PHP 脚本来调整内存列表似乎是最好的选择。

值得注意的是,在Ketama 散列系统下,将服务器从轮换中移除将导致其密钥在环上的其他位置(连续体)被重新散列,其他服务器将看不到它们在其他地方分配的密钥。将其可视化为一个圆圈,为每个服务器分配圆圈上的多个点(100-200)。密钥被散列到圆圈中并顺时针继续,直到找到服务器。从环中删除服务器只会导致这些值继续更进一步以找到新服务器。运气好的话,值的分布将平等地影响剩余的服务器。

演示哈希系统:

<?php


$m = new Memcached();
$m->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);


$m->addServer('localhost', 11211);
$m->addServer('localhost', 11212);
$m->addServer('localhost', 11213);
$m->addServer('localhost', 11214);
$m->addServer('localhost', 11215);
$m->addServer('localhost', 11216);
$m->addServer('localhost', 11217);
$m->addServer('localhost', 11218);
$m->addServer('localhost', 11219);
$m->addServer('localhost', 11210);

$key = uniqid(); //You may change this to md5(uniqid()); if you'd like to see a greater variation in keys. I don't think it necessary.
$m->set($key, $key, 5);


var_dump($m->get($key));

unset($m);


$m = new Memcached();
$m->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
//one server removed. If assignment to the continuum is dependent based on add order, we would expect the get call here to fail 90% of the time, as there will only be a success if the value was stored on the first server. If the assignment is based on some hash of the server details we'd expect success 90% of the time. 
$m->addServer('localhost', 11211);
//$m->addServer('localhost', 11212);
$m->addServer('localhost', 11213);
$m->addServer('localhost', 11214);
$m->addServer('localhost', 11215);
$m->addServer('localhost', 11216);
$m->addServer('localhost', 11217);
$m->addServer('localhost', 11218);
$m->addServer('localhost', 11219);
$m->addServer('localhost', 11210);

var_dump($m->get($key));

unset($m);

$m = new Memcached();
$m->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
//2 servers removed
$m->addServer('localhost', 11211);
$m->addServer('localhost', 11212);
//$m->addServer('localhost', 11213);
//$m->addServer('localhost', 11214);
$m->addServer('localhost', 11215);
$m->addServer('localhost', 11216);
$m->addServer('localhost', 11217);
$m->addServer('localhost', 11218);
$m->addServer('localhost', 11219);
$m->addServer('localhost', 11210);

var_dump($m->get($key));

unset($m);

$m = new Memcached();
$m->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
//Out of order
$m->addServer('localhost', 11210);
$m->addServer('localhost', 11211);
$m->addServer('localhost', 11219);
$m->addServer('localhost', 11212);
$m->addServer('localhost', 11217);
$m->addServer('localhost', 11214);
$m->addServer('localhost', 11215);
$m->addServer('localhost', 11216);
$m->addServer('localhost', 11218);
$m->addServer('localhost', 11219);
$m->addServer('localhost', 11213);

var_dump($m->get($key));

unset($m);

如果散列系统关心顺序,或者我们希望bool(false)在大多数次要示例上得到省略的服务器,因为早期的服务器已被删除等。但是基于我快速、完全非科学的测试,我只得到一个 bool false 10 次中的任何特定插槽。我显然刚刚在我的测试盒上启动了 10 台服务器。只给他们每个人 4mb 的内存

于 2012-09-11T14:48:54.090 回答
2

您可能想尝试Memcached::OPT_AUTO_EJECT_HOSTSPHP 的选项常量。它没有直接记录,但这里有一条评论命名它。

(我没试过,所以我不能告诉你它是否有效)

于 2012-09-11T14:49:29.303 回答
0

根据对评论的回答,我建议如下:

您需要构建一个缓存类。

此类将包含以下信息:

  • 缓存服务器列表

    • 在线或离线状态
    • 对此服务器的请求计数
  • 当前存储的密钥列表及其所在的服务器

接下来,您将需要标准函数来添加、更新和删除密钥。

每次执行这些功能之一时,您都需要检查密钥是否已经在缓存中以及它在哪个服务器上。

如果它不在服务器中,请在检索到实际 DB 值后选择请求最低的服务器以将其保存。

如果这些函数中的任何一个从缓存服务器返回错误,我会将该服务器标记为脱机,重置计数,并从该服务器上的列表中删除任何键。

此时,您可以轻松地将它们自动移动到新服务器或删除它们以便再次查询它们。

于 2012-09-11T14:52:57.640 回答
0

我的 2 美分:为 Memcached 开发一个健壮的 HA 模块并不容易。例如考虑以下情况:

  • 您将如何确定哪个服务器还活着,哪个服务器死了?您应该以某种方式在运行 Web/应用程序服务器的所有 HA 模块之间同步
  • 您如何在您的网络/应用服务器之间发布此信息
  • 你会有一个编排器吗?

我建议您看一下Redis Sentinel,它现在处于 Beta 版,在过去几个月中专门为解决 Redis 中的这些问题而开发和测试。在开始开发单行代码之前,您会发现许多必须注意的极端情况。

至于这里讨论的其他问题:

  • 当您丢失节点时,您将丢失 1/N 的密钥,其中 N 是您最初拥有的节点数,即包括失败的节点。这就是 Ketama 的工作原理
  • 使用新的 Memcached 类在 Memcached 客户端上存储密钥绝对不是可行的方法(IMO):(1_ 你要将所有这些密钥保存在哪里?(2)你如何在你的 web/app 节点之间同步?(3 ) 访问这些数据结构需要多长时间才能了解每个键驻留在哪个节点上?——这就是为什么 Memcached 完全基于散列函数,以使其快速和简单。

最后但同样重要的是,我建议您也检查 Memcached as-a-service 解决方案。例如,我们Garantia Data已经解决了 Memcached 的 HA 问题。

披露:我是 Garantia Data 的联合创始人兼首席技术官。

于 2012-09-18T20:00:56.297 回答