【问题标题】:Memcached for PHP and failover用于 PHP 和故障转移的 Memcached
【发布时间】:2012-09-11 14:28:28
【问题描述】:

我们正在为我们的应用程序部署 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% 的所有密钥被发送到新服务器的可能性很高。

【问题讨论】:

  • 目标是将所有请求发送到一个缓存服务器,并且仅在一个缓存服务器关闭时才切换?还是某些请求会发送到某些服务器,但仍需要故障转移?
  • 我希望每个值都能均匀分布,但如果一个实例出现故障,请优雅地处理这种情况。因此,在这种情况下,复制不是一个因素。
  • 您可以编写一个包装器,然后将数据发送到您的 memcached 服务器列表。当请求提供信息时,您会逐级向下查找可用的最新数据,获取数据并完成处理。

标签: php memcached


【解决方案1】:

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

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

值得注意的是,在Ketama hashing system 下,将服务器从轮换中移除会导致其密钥在环上的其他位置(连续体)被重新散列,其他服务器将看不到其分配的密钥别处。将其可视化为一个圆圈,每个服务器在圆圈上分配多个点(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),因为早期的服务器已被删除等。但是根据我的快速、完全非科学的测试,我仅在 10 次中的任何特定插槽中获得 bool false 一次。我显然刚刚在我的测试盒上启动了 10 个服务器。只给他们每个人 4mb 的内存

【讨论】:

  • 这确实有道理;回退到真实的配置文件。不过,我们非常面向 EC2,因此这也将迫使我们保留要通知的活动网络服务器列表。
  • 第二个问题是.. 如果确实有 10 个 memcache 服务器,并且中间的一个服务器被标记为不活动,则一致性哈希系统将失败,因为它不仅仅是从数组中添加/删除 1 个,或标记为非活动的项目。服务器阵列完全重新排序。
  • Evert,按数字排序列表还是什么?那么您的订单将保持一致。不过很好。
  • 如果我们可以调用 'addServers' 并给 1 个服务器一个 'inactive' 标志,但 Ketama 散列仍然有效。
  • 在添加或删除服务器期间,有序的服务器列表将如何失败?
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    根据对 cme​​ts 的回答,我建议如下:

    您需要构建一个缓存类。

    这个类将包含以下信息:

    • 缓存服务器列表

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

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

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

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

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

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

    【讨论】:

    • 您建议将服务器和“密钥列表”信息存储在哪里?
    • 可能是数据库服务器。否则,磁盘上的共享位置供所有 Web 服务器访问。
    • 我必须对你公平......这是一个糟糕的主意。 memcached 的全部意义在于它是分布式的,基于一个键。您完全击败了这一点,并且任何一种解决方案(db、disk)都存在与它们相关的大量问题。每个 memcached 查找都是数据库查找,这是我们想要避免的 :)
    • 您提出的实际上是 memcached 已经解决的问题;因此,此功能将被复制并变得更糟。不要在现实生活中这样做!
    【解决方案4】:

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

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

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

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

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

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

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

    【讨论】:

    • 感谢您的披露。地狱没有办法我会选择在客户端上存储密钥映射;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-05
    • 2010-11-06
    相关资源
    最近更新 更多