RIAK得到http:// host / riak / people / key" not found"

时间:2014-12-03 12:50:20

标签: riak

riak从2台服务器上处理集群。

riak-admin status | grep ring_members
ring_members : ['riak@server1.local','riak@server2.local',

在server1中写入数据

for i in {1..1000}; do
 curl -i -XPOST 'http://server1.local:8098/riak/people/'$i'' -H 'Content-Type:application/json' -d '{"name":"aaron_'$i'"}'
done

关闭server1上的riak:/etc/init.d/riak stop

并使用server2获取数据

for i in {1..1000}; do
 curl -v -i http://server2.local:8098/riak/people/$i
done

第一遍未读取10-30%的数据。第二遍读取数据。

第一次通过

curl -i http://server2.local:8098/riak/people/196

About to connect() to server2.local port 8098 (#0)
*   Trying 2.2.2.2... connected
* Connected to server2.local (2.2.2.2) port 8098 (#0)
> GET /riak/people/196 HTTP/1.1
> Host: server2.local:8098
> Accept: */*
< HTTP/1.1 404 Object Not Found
< Server: MochiWeb/1.1 WebMachine/1.10.0 (never breaks eye contact)
< Date: Thu, 27 Nov 2014 11:22:25 GMT
< Content-Type: text/plain
< Content-Length: 10

server2.local left intact

* Closing connection #0

  not found

第二次传递

curl -i http://server2.local:8098/riak/people/196

* About to connect() to server2.local port 8098 (#0)
*   Trying 2.2.2.2... connected
* Connected to server2.local (2.2.2.2) port 8098 (#0)
> GET /riak/people/196 HTTP/1.1
> Host: server2.local:8098
> Accept: */*
< HTTP/1.1 200 OK
< X-Riak-Vclock: a85hYGBgzGDKBVIcypz/foYkbmfKYEpkzGNlCGh9fZYvCwA=
< Vary: Accept-Encoding
< Server: MochiWeb/1.1 WebMachine/1.10.0 (never breaks eye contact)
< Link: </riak/people>; rel="up" 
< Last-Modified: Thu, 27 Nov 2014 11:21:52 GMT
< ETag: "2C4oPFcSctzBX1mwHjjfQ1" 
< Date: Thu, 27 Nov 2014 11:25:47 GMT
< Content-Type: application/json
< Content-Length: 20

* Closing connection #0

 {"name":"aaron_196"}

为什么会这样?

1 个答案:

答案 0 :(得分:1)

这是由最终一致性,分区容差和小簇大小的组合引起的。

<强> Preflists

当您存储密钥时,Riak会将副本存储在3个不同的虚拟节点(vnode)中,这些虚拟节点统称为该密钥的“预先列表”。

vnode数量的默认值为64.因此每个节点都有32个vnode。创建集群时,将vnode分配给物理节点的算法会尝试在同一物理节点上驻留任何preflist中不超过1个vnode - 当只有2个节点存在时,这是一项不可能完成的任务。

因此,当您存储值时,会将2个副本写入一个节点,将一个副本写入另一个节点。

失败状态

当节点发生故障,关闭或以其他方式变得不可用时,其余节点会根据需要启动回退vnode,以接受丢失节点的vnode已处理的操作。当原始节点再次可用时,会触发一个提示切换过程,使得回退vnode将其所有数据发送到其原始位置的主vnode。

<强>仲裁

Riak中的请求受vnode法定数量的限制。一些常用的法定人数是:

  • read r - 在可以向客户端返回值之前必须回复读取请求的最小vnode数。
  • write w - 在客户端被告知成功之前必须将成功返回到写入的最小数量的vnode
  • 主要阅读pr - 与r相同,但可能不会考虑回退vnodes
  • 主要写入pw - 与w相同,但可能不会考虑回退vnode

rw法定人数的默认值为(n_val / 2) + 1,默认值为n_val=3时为2。

发生了什么

将值写入2节点群集时,每个值的2个副本写入一个节点,1个写入另一个节点。收到2份副本的节点在预切片之间会有所不同。我希望这个分区大约是你写的两半,一次写入节点1,一次写入节点2,反之亦然。

然后,当您停止节点1时,其所有vnode都同时不可用。您对节点2的第一个读取请求将导致它启动1或2个回退vnode以替换preflist中缺少的一个(s)。这些回退在完成启动之前无法响应,因此对get进程的第一个回复将来自节点2上的主vnode,该vnode具有该值。然后,get进程将等待另一个回复(以满足默认的r = 2仲裁),这可能是从新启动的(并且非常空的)回退vnode中找不到的,之后它会将值返回给客户端。 / p>

回复后,get进程不会立即退出。它等待来自所有vnode的响应,如果它们不匹配,它会比较返回的值,根据它们的vclocks选择最新的值,并将解析后的值发送回vnodes。该过程称为read repair,是恢复一致性的关键。

随着每次连续读取,前一次读取已经需要所需的回退,因此已经开始。

假设您在Bitcask中保留了默认后端,那些没有值的后备vnode将扫描RAM内的密钥目录,发现请求的密钥不存在,并返回notfound。具有数据的vnode将扫描keydir,使用引用找到所请求密钥的条目,并将偏移量放入包含数据的磁盘文件中,执行磁盘读取以获取数据,然后将其发送到get进程。您可以从中推断出,只有因为不涉及磁盘访问,才能比数据响应更快地生成未发现的响应。

因此,如果满足以下所有条件:

  • 您的阅读请求所需的所有vnode正在运行
  • 自回退开始以来尚未请求密钥
  • 密钥被写入节点1两次,一次写入节点2
  • 使用默认的n_val = 3和r = 2

然后,获取流程将首先收到2个notfound响应,满足r=2法定人数,并向客户端回复notfound。它将在适当的时候收到数据的第三个响应,并将值写入回退vnodes。

然后,下一个读取请求将找到填充的所有3个vnode并返回该值。

如何预防

要防止这种情况发生:

  • 使用要求preflist中所有vnode响应的仲裁(r=all
  • 使用需要来自主要vnode(pr=1
  • 的答案的法定人数
  • 将节点添加到群集,直到没有preflist在同一节点上有2个成员。

将节点加入群集时,在plan阶段,您应该会看到对#34;效果的警告;并非所有副本都位于不同的节点上#34;如果任何节点将包含2个preflist成员。

将vnodes分配给节点的算法使用target_n_val设置(通常高于n_val),以确保如果节点发生故障,则回退vnode的第一个选择不是已经拥有其他成员的节点预购清单。 target_n_val的默认值为4,通常建议群集中有target_n_val + 1个节点,以确保无需重叠即可完成vnode分配。