最终的一致性-如何避免幻影

时间:2018-07-11 04:58:40

标签: elasticsearch cassandra hazelcast ignite high-availability

我是这个话题的新手。阅读了其中的几篇文章,并问了几个人之后,我仍然不明白你们的人对一个问题的看法。

有些UI客户端向多个后端实例发出请求(目前,会话是否粘滞是无关紧要的),并且这些实例已连接到某个高度可用的数据库集群(可能是Cassandra或什至是Elasticsearch)。假设后端实例不是专门与一台或集群的计算机绑定的,而是它对DB的每个请求都可以由另一台计算机满足。 一个客户端创建一些记录,将其异步同步存储到群集的一台计算机上,然后最终复制到其余的DB计算机上。然后另一个客户端请求该列表或记录,该请求最终由尚未接收到复制的更改的远程计算机服务,因此该客户端看不到该记录。好吧,这很糟糕,但是还不难看。

但是,请考虑第二个客户端命中具有记录的计算机,将其显示在列表中,然后刷新列表,这次命中远程计算机,再次没有看到该记录。要观察,这是很奇怪的行为,不是吗?甚至可能变得更糟:客户端成功地请求了记录,对其进行了一些编辑,然后尝试将更新存储到DB,这一次命中了远程机器,该机器说“我不知道您要更新的记录”。这是用户在做完全合法的事情时会看到的错误。

那么,防止这种情况的常见惯例是什么? 到目前为止,我只看到三种解决方案。

1)实际上不是解决方案,而是一项策略:忽略该问题,而是以足够快的速度加速群集,以确保在整个群集上以0.5秒的时间复制99.999%的更改(这很难想象)一些用户将尝试在同一时间对一条记录进行多次连续请求;他当然可以发出多个读取请求,但是在那种情况下,他可能不会注意到结果之间的不一致。即使有时出现问题并且用户面临问题,我们也接受了。如果失败者感到不满并向我们投诉(可能每周一次或每小时一次),我们会道歉并继续。

2)介绍用户会话与特定DB计算机之间的关联。这会有所帮助,但需要数据库的显式支持,并且还会损害负载平衡,并在数据库机故障且会话需要重新绑定到另一台计算机时引起麻烦(但是,在数据库的适当支持下,我认为这是可能的) ;; Elasticsearch可以接受路由密钥,并且我相信如果目标分片掉线,它将只是将关联链接切换到另一个分片-虽然我不确定,但即使发生重新绑定,另一台机器也可能包含较旧的数据:))。

3)依靠单调一致性,即某种方法来确保来自客户端的下一个请求将获得不早于前一个请求的结果。但是,据我所知,这种方法还需要数据库的显式支持,例如能够将一些“全局版本时间戳”传递给集群的平衡器,然后将其与所有计算机时间戳上的最新数据进行比较,以确定哪些计算机可以服务请求。

还有其他好的选择吗?还是认为这三个足以使用?

P.S。我现在的具体问题是Elasticsearch; AFAIK那里不支持单调读取,尽管看起来选项2可用。

4 个答案:

答案 0 :(得分:2)

Apache Ignite具有用于密钥的主分区和备份分区。除非您设置了notifyItemRemoved选项,否则您将始终从预期其内容可靠的主分区读取。

如果节点消失,则事务(或操作)应该由其余节点传播或回滚。

请注意,Apache Ignite不执行最终一致性,而是执行强一致性。这意味着您可以观察到节点丢失期间的延迟,但不会观察到不一致的数据。

答案 1 :(得分:2)

在Cassandra中,如果至少对读取和写入均使用仲裁一致性,则将获得单调读取。 1.0之前不是这种情况,但是很久以前就是这样。如果使用服务器时间戳,则有些麻烦,但是默认情况下不是这样,因此如果使用C * 2.1+,就不会成为问题。

有趣的是,由于C *使用时间戳是在“相同时间”发生的事情。由于Cassandra是Last Write Win,因此时间和时钟漂移确实很重要。但是对记录的并发更新将始终具有竞争条件,因此,如果在写保证之前需要强势读取,则可以使用轻量级事务(本质上是使用paxos的CAS操作)来确保读取更新之间没有其他人进行更新,尽管这样做很慢,所以我除非有必要,否则会避免。

答案 2 :(得分:1)

在真正的分布式系统中,只要您的客户端连接到该远程群集,记录在远程群集中的存储位置就无关紧要。在Hazelcast中,记录始终存储在一个分区中,并且一个分区归集群中的一台服务器拥有。集群中可能有X个分区(默认为271个),并且所有这些分区均等地分布在整个集群中。因此,一个3个成员的群集将具有类似91-90-90的分区分布。

现在,当客户端发送记录以存储在Hazelcast群集中时,它已经通过使用一致的哈希算法来知道该记录属于哪个分区。这样,它也知道哪个服务器是该分区的所有者。因此,客户端将其操作直接发送到该服务器。此方法适用于所有客户端操作-放置或获取。因此,在您的情况下,您可能有多个UI客户端连接到群集,但是针对特定用户的记录存储在群集中的一台服务器上,并且您的所有UI客户端都将接近该服务器以执行与该记录相关的操作。

关于一致性,Hazelcast默认情况下是高度一致的分布式缓存,这意味着您对特定记录的所有更新都在同一线程中同步发生,并且应用程序等待,直到收到所有者服务器(和备份)的确认为止服务器(如果启用了备份)。

将数据库层(可以是并行运行的一种或多种不同类型的数据库)连接到群集时,Hazelcast群集通过从数据库读取数据来返回数据,即使该数据当前不在群集中也是如此。因此,您永远不会获得空值。更新时,将群集配置为同步或异步向下游发送更新。

答案 3 :(得分:0)

啊哈,在对ES讨论进行了更深入的研究之后,我发现:https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-preference.html

请注意他们如何特别强调“自定义值”案例,建议完全使用它来解决我的问题。

因此,鉴于这是他们的官方建议,我们可以将其总结如下。

  1. 为了应对不稳定的读取,我们应该使用“首选项”, 使用“自定义”或其他方法。
  2. 也可以“阅读您的 写”的一致性,我们可以让所有客户使用 “ preference = _primary”,因为主要分片首先获得所有 写道。但是,这可能会比 由于没有分配,因此采用“自定义”模式。这与这里的其他人所说的有关Ignite和Hazelcast的说法非常相似。

对吗?

当然,这是专门针对ES的解决方案。回到我最初的问题,这个问题更为通用,原来,对于许多分布式系统,选项#2和#3确实被认为足够好,其中#3可以通过#2实现(即使没有对#3的直接支持)。 DB)。