Memcached是用于PHP的缓存系统,可以实现故障转移。

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%的密钥被发送到新的服务器的高概率。

目标是将所有请求发送到一个缓存服务器并仅在其中一个故障时切换?还是某些请求将发送到特定的服务器,但仍需要故障转移? - endyourif
我希望每个值都能均匀分布,但如果一个实例停止工作,也能优雅地处理这种情况。因此,在这种情况下,复制不是一个因素。 - Evert
你可以编写一个包装器,将数据发送到你的一系列memcached服务器。当请求信息时,你可以级联查找最新可用的服务器,获取你的数据并完成操作。 - wesside
4个回答

3

通常我会将可用服务器列表存储在APC中,这样我就可以随时修改它。你说得对,系统会尝试在其列出的同时继续使用已停机的服务器,但幸运的是,有了新的哈希方法,从轮换中取出它并不是什么大问题。

我建议避免使用全新的PHP扩展或尝试向部署栈添加新软件。你可能已经在使用某些监控工具(nagios?)。让它调用每个Web服务器上的简单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),因为早期服务器被删除等。然而,根据我快速的、完全非科学的测试,我只在10个特定的插槽中有一次得到了布尔假值。显然,我只是在我的测试盒上启动了10个服务器,每个服务器只分配了4mb的内存。

1
第二个问题是,如果确实有10个memcache服务器,并且中间的一个服务器被标记为不活动状态,那么一致性哈希系统将失败,因为这不仅仅是从数组中添加/删除1个项目,或者将1个项目标记为不活动。服务器数组将完全重新排序。 - Evert
根据http://docs.libmemcached.org/memcached_behavior.html?highlight=consistent,我认为一致性哈希值是基于服务器详细信息而不仅仅是它们在列表中的位置。虽然我也准备好接受错误。 - preinheimer
目前为止,根据进一步的阅读,我有95%的信心认为它正在对服务器信息进行哈希。另一种选择只是愚蠢的,并不是一个“哈希”。如果您按顺序添加服务器,则还需要哈希系统维护静态变量以记住已调用多少次。 - preinheimer
基本上,如果我们的服务器列表是一个简单的PHP数组,用于填充“circle”的数据是:array_keys(array_map('md5', $servers))这个简单的示例符合您上一个示例的要求。 - Evert
我已经复制了源代码,所以你可以很容易地尝试,让我知道你得到了什么。 - preinheimer
显示剩余14条评论

2
你可能想尝试使用PHP的Memcached::OPT_AUTO_EJECT_HOSTS选项常量。虽然它没有直接记录,但是在这里有一个注释here命名它。
(我还没有尝试过,所以无法告诉你它是否有效)

0

根据评论的答案,我建议按照以下方式进行:

您需要构建一个缓存类。

此类将包含以下信息:

  • 缓存服务器列表

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

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

每次执行这些函数时,您都需要检查键是否已经在缓存中以及它所在的服务器。

如果它不在服务器上,请选择请求最少的服务器,在检索实际的数据库值后将其保存。

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

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


你会建议将服务器和“密钥列表”信息存储在哪里? - Evert
也许是数据库服务器。否则,所有Web服务器都可以访问的磁盘上的共享位置。 - endyourif
我必须对你公正地说,这是一个糟糕的想法。Memcached 的整个重点在于它是基于键分布式的。你完全打败了这一点,而且两种解决方案(数据库、磁盘)都有很多问题与之相关。每次 Memcached 查找都将成为数据库查找,这是我们要避免的 :) - Evert
你提出的实际上是memcached已经解决的问题;因此,这个功能将被复制并变得更糟。在现实生活中不要这样做! - Evert

0

我有个小建议: 为Memcached开发一个强大的高可用(HA)模块并不容易。例如,考虑以下情况:

  • 你如何确定哪个服务器是活着的,哪个服务器已经宕机?你需要在所有运行Web/App服务器的HA模块之间进行同步。
  • 你如何在Web/App服务器之间发布这些信息?
  • 你打算使用一个编排器吗?

我建议你先看一下Redis Sentinel,它目前处于测试阶段,并且在过去几个月里专门为解决Redis中的这些问题而开发和测试了。在开始编写任何代码之前,你需要了解其中的许多特殊情况。

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

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

最后但并非最不重要的是,我建议您也检查Memcached作为服务的解决方案。例如,我们在Garantia Data已经解决了Memcached的HA问题。

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


感谢您的披露。我绝不会选择在客户端存储键的映射 ;) - Evert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接