Memcached 一致性哈希不适用于 4 台服务器中的 3 台宕机
Memcached consistent hashing not working with 3 of 4 servers down
故事
我有 3 个 memcached 服务器 运行ning,我关闭其中一个或另一个以调查 PHP-memcached 在无法访问的服务器上的行为。
我在PHP中定义了4台服务器,其中1台模拟一个大部分离线的服务器(备用服务器)。当我关闭 1 个服务器时(=> 2 个仍然在线),第三个 ->get()
给我一个结果。
当我关闭另一台服务器时(=> 1 台仍然在线),它不会找到推送到最后一台服务器的对象。
示例输出
第一个 运行,4 个服务器中的 3 个:
Entity not found in cache on 1st try: NOT FOUND
Entity not found in cache on 2nd try: NOT FOUND
Entity not found in cache on 3rd try: NOT FOUND
Entity not found in cache on 4th try: NOT FOUND
第二个 运行,4 个服务器中的 3 个:
Entity found in Cache: SUCCESS
第三个 运行,4 个服务器中的 2 个:
Entity not found in cache on 1st try: CONNECTION FAILURE
Entity not found in cache on 2nd try: SERVER IS MARKED DEAD
Entity not found in cache on 3rd try: NOT FOUND
Entity not found in cache on 4th try: NOT FOUND
第四个 运行,4 个服务器中的 1 个:
Entity not found in cache on 1st try: CONNECTION FAILURE
Entity not found in cache on 2nd try: SERVER IS MARKED DEAD
Entity not found in cache on 3rd try: CONNECTION FAILURE
Entity not found in cache on 4th try: SERVER IS MARKED DEAD
尽管只有一台服务器在线,而且每次在缓存中找不到任何对象时我都会将对象推送到 memcached,但它再也找不到密钥了。
我认为它应该也可以只剩下一个服务器。
你能给我解释一下这种行为吗?
即使我关闭了 20 台服务器中的 19 台,安全的东西似乎也无法实现。
附带问题:libketama真的不再维护了,还好用吗? lib 背后的逻辑相当不错,也用在 varnish 缓存服务器中。
附录
我的脚本:
<?php
require_once 'CachableEntity.php';
require_once 'TestEntity.php';
echo PHP_EOL;
$cache = new Memcached();
$cache->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
$cache->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
$cache->setOption(Memcached::OPT_SERVER_FAILURE_LIMIT, 1);
$cache->setOption(Memcached::OPT_REMOVE_FAILED_SERVERS, true);
$cache->setOption(Memcached::OPT_AUTO_EJECT_HOSTS, true);
$cache->setOption(Memcached::OPT_TCP_NODELAY, true);
//$cache->setOption(Memcached::OPT_RETRY_TIMEOUT, 10);
$cache->addServers([
['localhost', '11212'],
['localhost', '11213'],
['localhost', '11214'],
['localhost', '11215'], // always offline
]);
$entityId = '/test/test/article_123456789.test';
$entity = new TestEntity($entityId);
$found = false;
$cacheKey = $entity->getCacheKey();
$cacheResult = $cache->get($cacheKey);
if (empty($cacheResult)) {
echo 'Entity not found in cache on 1st try: ' . $cache->getResultMessage(), PHP_EOL;
$cacheResult = $cache->get($cacheKey);
if (empty($cacheResult)) {
echo 'Entity not found in cache on 2nd try: ' . $cache->getResultMessage(), PHP_EOL;
$cacheResult = $cache->get($cacheKey);
if (empty($cacheResult)) {
echo 'Entity not found in cache on 3rd try: ' . $cache->getResultMessage(), PHP_EOL;
$cacheResult = $cache->get($cacheKey);
if (empty($cacheResult)) {
echo 'Entity not found in cache on 4th try: ' . $cache->getResultMessage(), PHP_EOL;
$entity
->setTitle('TEST')
->setText('Hellow w0rld. Lorem Orem Rem Em M IpsuM')
->setUrl('http://www.google.com/content-123456789.html');
$cache->set($cacheKey, $entity->serialize(), 120);
}
}
else { $found = true; }
}
else { $found = true; }
}
else { $found = true; }
if ($found === true) {
echo 'Entity found in Cache: ' . $cache->getResultMessage(), PHP_EOL;
$entity->unserialize($cacheResult);
echo 'Title: ' . $entity->getTitle(), PHP_EOL;
}
echo PHP_EOL;
冗余
从Memcached 3.0.0开始,有冗余配置。
- 可以在扩展配置文件中设置。
/etc/php/7.0/mods-available/memcached.ini(不同操作系统可能不同)
memcache.redundancy=2
- 与 ini_set('memcache.redundancy', 2)
这个参数没有真正的记录,你可以用服务器的数量替换“2”,这会增加额外写入的轻微开销。
失去 19/20 个服务器
有了冗余,您可能会丢失一些服务器并保留 "read success"。
备注:
- 失去 95% 的服务器池会给剩余的服务器造成压力。
- 缓存服务器是为性能而生的,"a large number of servers can slow down a client from a few angles"
libketama
Github 存储库自 2014 年以来未收到任何提交。Libketama 正在寻找新的维护者 https://github.com/RJ/ketama
- 您遇到的行为是一致的。当服务器不可用时,它首先被标记为失败,然后被标记为已死。
问题是,显然只有当您将 Memcached::OPT_SERVER_FAILURE_LIMIT
值设置为 2 而将其设置为 1 时,它才会连贯。这可以解释为什么每个无法访问的服务器有两个错误行(CONNECTION FAILURE
, SERVER IS MARKED AS DEAD
)
这似乎与超时有关。在具有匹配的 OPT_RETRY_TIMEOUT
值的失败后添加 usleep()
将使服务器能够从列表中删除(参见 following bug comment)
该值不会复制到下一个服务器,因为只分发密钥。
注意 OPT_LIBKETAMA_COMPATIBLE
不使用 libketama,而只是重现相同的算法,这意味着如果 libketama 不再处于活动状态并不重要,而这是推荐的配置 in PHP documentation:
It is highly recommended to enable this option if you want to use consistent hashing, and it may be enabled by default in future releases.
编辑:
根据我对您的 post 的理解,消息 "Entity found in Cache: SUCCESS" 仅出现在第二个 运行(1 个服务器离线)上,因为与上一个命令相比没有变化并且托管此密钥的服务器仍然可用(因此 memcached 从键中考虑该值存储在第一台、第二台或第三台服务器上)。我们称这些服务器为 John、George、Ringo 和 Paul。
在第三个 运行 中,在开始时,memcached 从密钥中推断出四个服务器之一拥有该值(例如 John)。它在放弃之前询问了 John 两次,因为它现在已关闭。它的算法然后只考虑 3 个服务器(不知道 Paul 已经死了)并推断出 George 应该包含该值。
乔治回答两次它不包含该值然后存储它。
但是在第四天 运行,约翰、乔治和保罗不在。 Memcached 尝试 John 两次,然后尝试 George 两次。然后存储在 Ringo 中。
这里的问题是不可用的服务器不会在不同的 运行 之间被记住,并且在同一个 运行 中你必须在删除之前询问服务器两次。
故事
我有 3 个 memcached 服务器 运行ning,我关闭其中一个或另一个以调查 PHP-memcached 在无法访问的服务器上的行为。
我在PHP中定义了4台服务器,其中1台模拟一个大部分离线的服务器(备用服务器)。当我关闭 1 个服务器时(=> 2 个仍然在线),第三个 ->get()
给我一个结果。
当我关闭另一台服务器时(=> 1 台仍然在线),它不会找到推送到最后一台服务器的对象。
示例输出
第一个 运行,4 个服务器中的 3 个:
Entity not found in cache on 1st try: NOT FOUND
Entity not found in cache on 2nd try: NOT FOUND
Entity not found in cache on 3rd try: NOT FOUND
Entity not found in cache on 4th try: NOT FOUND
第二个 运行,4 个服务器中的 3 个:
Entity found in Cache: SUCCESS
第三个 运行,4 个服务器中的 2 个:
Entity not found in cache on 1st try: CONNECTION FAILURE
Entity not found in cache on 2nd try: SERVER IS MARKED DEAD
Entity not found in cache on 3rd try: NOT FOUND
Entity not found in cache on 4th try: NOT FOUND
第四个 运行,4 个服务器中的 1 个:
Entity not found in cache on 1st try: CONNECTION FAILURE
Entity not found in cache on 2nd try: SERVER IS MARKED DEAD
Entity not found in cache on 3rd try: CONNECTION FAILURE
Entity not found in cache on 4th try: SERVER IS MARKED DEAD
尽管只有一台服务器在线,而且每次在缓存中找不到任何对象时我都会将对象推送到 memcached,但它再也找不到密钥了。
我认为它应该也可以只剩下一个服务器。
你能给我解释一下这种行为吗?
即使我关闭了 20 台服务器中的 19 台,安全的东西似乎也无法实现。
附带问题:libketama真的不再维护了,还好用吗? lib 背后的逻辑相当不错,也用在 varnish 缓存服务器中。
附录
我的脚本:
<?php
require_once 'CachableEntity.php';
require_once 'TestEntity.php';
echo PHP_EOL;
$cache = new Memcached();
$cache->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
$cache->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
$cache->setOption(Memcached::OPT_SERVER_FAILURE_LIMIT, 1);
$cache->setOption(Memcached::OPT_REMOVE_FAILED_SERVERS, true);
$cache->setOption(Memcached::OPT_AUTO_EJECT_HOSTS, true);
$cache->setOption(Memcached::OPT_TCP_NODELAY, true);
//$cache->setOption(Memcached::OPT_RETRY_TIMEOUT, 10);
$cache->addServers([
['localhost', '11212'],
['localhost', '11213'],
['localhost', '11214'],
['localhost', '11215'], // always offline
]);
$entityId = '/test/test/article_123456789.test';
$entity = new TestEntity($entityId);
$found = false;
$cacheKey = $entity->getCacheKey();
$cacheResult = $cache->get($cacheKey);
if (empty($cacheResult)) {
echo 'Entity not found in cache on 1st try: ' . $cache->getResultMessage(), PHP_EOL;
$cacheResult = $cache->get($cacheKey);
if (empty($cacheResult)) {
echo 'Entity not found in cache on 2nd try: ' . $cache->getResultMessage(), PHP_EOL;
$cacheResult = $cache->get($cacheKey);
if (empty($cacheResult)) {
echo 'Entity not found in cache on 3rd try: ' . $cache->getResultMessage(), PHP_EOL;
$cacheResult = $cache->get($cacheKey);
if (empty($cacheResult)) {
echo 'Entity not found in cache on 4th try: ' . $cache->getResultMessage(), PHP_EOL;
$entity
->setTitle('TEST')
->setText('Hellow w0rld. Lorem Orem Rem Em M IpsuM')
->setUrl('http://www.google.com/content-123456789.html');
$cache->set($cacheKey, $entity->serialize(), 120);
}
}
else { $found = true; }
}
else { $found = true; }
}
else { $found = true; }
if ($found === true) {
echo 'Entity found in Cache: ' . $cache->getResultMessage(), PHP_EOL;
$entity->unserialize($cacheResult);
echo 'Title: ' . $entity->getTitle(), PHP_EOL;
}
echo PHP_EOL;
冗余
从Memcached 3.0.0开始,有冗余配置。
- 可以在扩展配置文件中设置。
/etc/php/7.0/mods-available/memcached.ini(不同操作系统可能不同)
memcache.redundancy=2
- 与 ini_set('memcache.redundancy', 2)
这个参数没有真正的记录,你可以用服务器的数量替换“2”,这会增加额外写入的轻微开销。
失去 19/20 个服务器
有了冗余,您可能会丢失一些服务器并保留 "read success"。
备注:
- 失去 95% 的服务器池会给剩余的服务器造成压力。
- 缓存服务器是为性能而生的,"a large number of servers can slow down a client from a few angles"
libketama
Github 存储库自 2014 年以来未收到任何提交。Libketama 正在寻找新的维护者 https://github.com/RJ/ketama
- 您遇到的行为是一致的。当服务器不可用时,它首先被标记为失败,然后被标记为已死。
问题是,显然只有当您将 Memcached::OPT_SERVER_FAILURE_LIMIT
值设置为 2 而将其设置为 1 时,它才会连贯。这可以解释为什么每个无法访问的服务器有两个错误行(CONNECTION FAILURE
, SERVER IS MARKED AS DEAD
)
这似乎与超时有关。在具有匹配的 OPT_RETRY_TIMEOUT
值的失败后添加 usleep()
将使服务器能够从列表中删除(参见 following bug comment)
该值不会复制到下一个服务器,因为只分发密钥。
注意
OPT_LIBKETAMA_COMPATIBLE
不使用 libketama,而只是重现相同的算法,这意味着如果 libketama 不再处于活动状态并不重要,而这是推荐的配置 in PHP documentation:
It is highly recommended to enable this option if you want to use consistent hashing, and it may be enabled by default in future releases.
编辑: 根据我对您的 post 的理解,消息 "Entity found in Cache: SUCCESS" 仅出现在第二个 运行(1 个服务器离线)上,因为与上一个命令相比没有变化并且托管此密钥的服务器仍然可用(因此 memcached 从键中考虑该值存储在第一台、第二台或第三台服务器上)。我们称这些服务器为 John、George、Ringo 和 Paul。
在第三个 运行 中,在开始时,memcached 从密钥中推断出四个服务器之一拥有该值(例如 John)。它在放弃之前询问了 John 两次,因为它现在已关闭。它的算法然后只考虑 3 个服务器(不知道 Paul 已经死了)并推断出 George 应该包含该值。
乔治回答两次它不包含该值然后存储它。
但是在第四天 运行,约翰、乔治和保罗不在。 Memcached 尝试 John 两次,然后尝试 George 两次。然后存储在 Ringo 中。
这里的问题是不可用的服务器不会在不同的 运行 之间被记住,并且在同一个 运行 中你必须在删除之前询问服务器两次。