16.2.2.5  memcached哈希/分布类型

Memcached客户端接口支持多种不同的分布算法, 这些算法用于多服务器配置,以确定在设置或从给定的Memcached实例获取数据时应使用哪个主机 。当您获取或设置一个值时,会根据提供的键构造一个散列,然后用于从已配置的服务器列表中选择一个主机。因为散列机制使用提供的密钥作为散列的基础,所以在设置和获取操作期间选择相同的服务器。

你可以把这个过程想象成下面这样。给定一个服务器数组(a、b 和 c),客户端使用一种散列算法,该算法根据存储或检索的密钥返回一个整数。然后使用结果值从客户端配置的服务器列表中选择一个服务器。memcache客户端中的大多数标准客户端散列使用简单的模数计算值与配置的memcached服务器的数量。您可以将伪代码中的过程总结为:

@memcservers = ['a.memc','b.memc','c.memc'];
$value = hash($key);
$chosen = $value % length(@memcservers);

用值替换上面的内容:

@memcservers = ['a.memc','b.memc','c.memc'];
$value = hash('myid');
$chosen = 7009 % 3;

在上面的示例中,客户端哈希算法选择索引为 1 ( 7009 % 3 = 1) 的服务器,并使用该服务器存储或检索键和值。

笔记

这个选择和散列过程由您使用的memcached客户端自动处理;您只需提供要使用的memcached 服务器列表。

您可以在下面的图 16.3 “ memcached哈希选择” 中看到这种情况的图形表示 。

图 16.3  memcached哈希选择

该图说明了在多服务器配置中使用的散列和选择过程,以确定在给定 memcached 实例设置或获取数据时应使用哪个主机。

在memcached客户端 中对指定键的任何操作期​​间都会发生相同的散列和选择过程 。

使用此方法有许多优点:

  • 要联系的服务器的散列​​和选择完全在客户端内处理。这消除了执行网络通信以确定要联系的正确机器的需要。

  • 因为 memcached服务器的确定完全发生在客户端内,所以可以自动选择服务器,而不管正在执行的操作(set、get、increment 等)。

  • 因为确定是在客户端内处理的,所以散列算法为给定的键返回相同的值;值不受服务器环境差异的影响或重置。

  • 选择非常快。键值的散列算法很快,服务器的结果选择来自一个简单的可用机器数组。

  • 使用客户端哈希简化了每个memcached服务器上的数据分布。散列算法返回的值的自然分布意味着密钥会自动分布在可用的服务器上。

假设客户端中配置的服务器列表保持不变,相同的存储键返回相同的值,因此选择相同的服务器。

但是,如果您不使用相同的哈希机制,那么相同的数据可能会通过不同的接口记录在不同的服务器上,这既浪费了您的 memcached空间,又导致了信息的潜在差异。

笔记

使用多接口兼容散列机制的一种方法是使用libmemcached库和关联的接口。因为不同语言(包括 C、Perl 和 Python)的接口使用相同的客户端库接口,所以它们总是从 ID 生成相同的哈希码。

客户端选择服务器的问题是服务器列表(包括它们的顺序) 必须在每个使用memcached服务器的客户端上保持一致,并且服务器必须可用。如果您在以下情况下尝试对键执行操作:

  • 一个新的memcached实例已添加到可用实例列表中

  • 已从可用实例列表中删除 一个memcached实例

  • memcached实例 的顺序已更改

当哈希算法用于给定的密钥,但服务器列表不同时,哈希计算可能会从列表中选择不同的服务器。

如果将新的memcached实例添加到服务器列表中,如下new.memc例所示,则使用相同键的 GET 操作 myid可能会导致缓存未命中。这是因为从键中计算出相同的值,它从服务器数组中选择相同的索引,但索引 2 现在指向新服务器,而不是c.memc 最初存储数据的服务器。这将导致缓存未命中,即使该键存在于另一个memcached实例的缓存中。

图 16.4 具有新memcached实例的memcached哈希选择

说明将新的 memcached 实例添加到服务器列表时 memcached 哈希选择的图表。

这意味着服务器c.memcnew.memc 都包含 key 的信息myid,但是针对每个服务器中的密钥存储的信息在每个实例中可能不同。一个更重要的问题是检索数据时缓存未命中的次数要多得多,因为添加新服务器会改变密钥的分布,这反过来又需要在memcached实例上重建缓存数据,从而导致数据库读取增加.

如果您主动管理客户端中配置的服务器列表,在每个实例被识别为可用时添加和删除配置的memcached实例,也会出现相同的效果。例如,当客户端注意到实例无法再联系时 删除memcached实例可能会导致服务器选择失败,如此处所述。

为防止这导致严重问题并使缓存无效,您可以选择用于选择服务器的哈希算法。有两种常见的哈希算法类型, 一致性数。

使用一致的散列算法,将相同的密钥应用于服务器列表时,始终使用相同的服务器来存储或检索密钥,即使配置的服务器列表发生变化也是如此。这意味着您可以在配置列表中添加和删除服务器,并且始终对给定密钥使用相同的服务器。有两种可用的一致性哈希算法,Ketama 和 Wheel。两种类型都受 支持libmemcached,并且实现可用于 PHP 和 Java。

任何一致的哈希算法都有一些局限性。当您将服务器添加到现有的已配置服务器列表时,密钥将作为正常分发的一部分分发到新服务器。当您从列表中删除服务器时,密钥将重新分配给列表中的另一台服务器,这意味着缓存需要重新填充信息。此外,一致的哈希算法不能解决您希望跨多个客户端一致选择服务器但每个客户端包含不同的服务器列表的问题。一致性仅在单个客户端中强制执行。

使用散列算法,客户端通过首先计算散列然后从已配置服务器列表中选择服务器来选择服务器。随着服务器列表的变化,使用模数哈希算法时选择的服务器也会发生变化。结果就是上面描述的行为;对服务器列表的更改意味着在检索数据时会选择不同的服务器,从而导致缓存未命中并在缓存中重新植入信息时增加数据库负载。

如果您只为每个客户端 使用一个memcached实例,或者您为客户端配置的memcached 服务器列表永远不会改变,那么哈希算法的选择是无关紧要的,因为它没有明显的影响。

如果您定期更换服务器,或者您使用一组在大量客户端之间共享的通用服务器,那么使用一致的哈希算法应该有助于确保您的缓存数据不重复并且数据均匀分布。