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"}
为什么会这样?
答案 0 :(得分:1)
这是由最终一致性,分区容差和小簇大小的组合引起的。
<强> Preflists 强>
当您存储密钥时,Riak会将副本存储在3个不同的虚拟节点(vnode)中,这些虚拟节点统称为该密钥的“预先列表”。
vnode数量的默认值为64.因此每个节点都有32个vnode。创建集群时,将vnode分配给物理节点的算法会尝试在同一物理节点上驻留任何preflist中不超过1个vnode - 当只有2个节点存在时,这是一项不可能完成的任务。
因此,当您存储值时,会将2个副本写入一个节点,将一个副本写入另一个节点。
失败状态
当节点发生故障,关闭或以其他方式变得不可用时,其余节点会根据需要启动回退vnode,以接受丢失节点的vnode已处理的操作。当原始节点再次可用时,会触发一个提示切换过程,使得回退vnode将其所有数据发送到其原始位置的主vnode。
<强>仲裁强>
Riak中的请求受vnode法定数量的限制。一些常用的法定人数是:
r
- 在可以向客户端返回值之前必须回复读取请求的最小vnode数。w
- 在客户端被告知成功之前必须将成功返回到写入的最小数量的vnode pr
- 与r
相同,但可能不会考虑回退vnodes pw
- 与w
相同,但可能不会考虑回退vnode r
和w
法定人数的默认值为(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进程。您可以从中推断出,只有因为不涉及磁盘访问,才能比数据响应更快地生成未发现的响应。
因此,如果满足以下所有条件:
然后,获取流程将首先收到2个notfound
响应,满足r=2
法定人数,并向客户端回复notfound
。它将在适当的时候收到数据的第三个响应,并将值写入回退vnodes。
然后,下一个读取请求将找到填充的所有3个vnode并返回该值。
如何预防
要防止这种情况发生:
r=all
)pr=1
)将节点加入群集时,在plan
阶段,您应该会看到对#34;效果的警告;并非所有副本都位于不同的节点上#34;如果任何节点将包含2个preflist成员。
将vnodes分配给节点的算法使用target_n_val
设置(通常高于n_val),以确保如果节点发生故障,则回退vnode的第一个选择不是已经拥有其他成员的节点预购清单。 target_n_val
的默认值为4,通常建议群集中有target_n_val + 1
个节点,以确保无需重叠即可完成vnode分配。