Riak VClock

关于向量时钟的概念。在这里就多讲了,大家能够參照一下Dynamo的论文了解一下,向量时钟在分布式主要用于解决一致性性问题。能够和CRDTs一起看。

以下的源码是參照riak中的,就是把它翻译为elixir格式而已。基本不变。

时钟主要出现的情况有网络分区和并行更新。

这样仅仅会丢掉一些向量时钟的信息,即数据更新过程的信息,可是不会丢掉实实在在的数据。仅仅有当一种情况会有问题,就是一个client保持了一个非常久之前的向量时钟,然后继承于这个向量时钟提交了一个数据,此时就会有冲突。由于服务器这边已经没有这个非常久之前的向量时钟信息了,已经被剪枝掉了可能,所以client提交的此次数据,在服务端无法找到一个祖先。此时就会创建一个sibling。

所以这个剪枝的策略是一个权衡tradeoff,一方面是无限增长的向量时钟的空间。还有一方面是偶尔的会有"false merge"。对,但肯定的是,不会悄无声息的丢数据。综上。为了防止向量时钟空间的无限增长,剪枝还是比用server标识向量时钟工作的更好。
  • 结构:

主要有3个元祖{node,
{opCount, TS}}
,分布为节点(协调器)。操作数和操作时间。

  • 基本的方法:

merge(合并):

合并的规则是,opCount>TS:当节同样时,谁的opCount大,谁赢;假设opCount一样时,谁的时间大谁赢。

@doc """
Combine all VClock in the input list into their least possible common descendant
"""
@spec merge(list, list) :: list
def merge([]), do: []
def merge([singevclock]), do: singevclock
## first is a list, eg [:a, {1, 1234}]
# rest is list of list, eg [[{:a, {1, 233}}, {:b, {3, 124}}]]
def merge([first|rest]) do
merge(rest, :lists.keysort(1, first))
end
def merge([], nclock), do: nclock
def merge([aclock|vclocks], nclock) do
merge(vclocks, merge(:lists.keysort(1, aclock), nclock, []))
end
def merge([], [], accclock), do: :lists.reverse(accclock)
def merge([], left, accclock), do: :lists.reverse(accclock, left)
def merge(left, [], accclock), do: :lists.reverse(accclock, left)
def merge(v = [{node1, {ctr1, ts1} = ct1} = nct1 | vclock],
n = [{node2, {ctr2, ts2} = ct2} = nct2 | nclock], accclock) do
cond do
node1 < node2 ->
merge(vclock, n, [nct1|accclock]);
node1 > node2 ->
merge(v, nclock, [nct2|accclock]);
true ->
({_ctr, _ts} = ct) = cond do
ctr1 > ctr2 ->
ct1;
ctr1 < ctr2 ->
ct2;
true ->
{ctr1, :erlang.max(ts1, ts2)}
end
merge(vclock, nclock, [{node1, ct}|accclock])
end
end

prune(裁剪):

裁剪的法则主要是空间时间双方面.

!()[../pic/riak_4.png]

终于的裁剪函数prune_vclock1(v,
now, bprops, headtime)
.

@doc """
Possibly shrink the size of a vclock, depending on current age and size
"""
@spec prune(v :: list, now :: integer, bucketprops :: any) :: list
def prune(v, now, bucketprops) do
## This sort need to be deterministic, to avoid spurious merge conflicts later,
# We achieve this by using the node ID as secondary key
sortv = :lists.sort(fn({n1, {_, t1}}, {n2, {_, t2}}) -> {t1, n1} < {t2, n2} end, v)
prune_vclock1(sortv, now, bucketprops)
end def prune_vclock1(v, now, bprops) do
case get_property(:small_vclock, bprops) >= :erlang.length(v) do
true -> v;
false ->
{_, {_, headtime}} = hd(v)
case (now - headtime) < get_property(:young_vclock, bprops) do
true -> v;
false -> prune_vclock1(v, now, bprops, headtime)
end
end
end def prune_vclock1(v, now, bprops, headtime) do
# has a precondition that v is longer than small and older than young
case (:erlang.length(v) > get_property(:big_vclock, bprops)) or ((now - headtime) > get_property(:old_vclock, bprops)) do
true -> prune_vclock1(tl(v), now, bprops);
false -> v
end
end def get_property(key, pairlist) do
case :lists.keyfind(key, 1, pairlist) do
{_key, value} ->
value;
false ->
:undefined
end
end

  • source
defmodule VClock do
@moduledoc """
this is !!!!!!!!
"""
@vsn 0.1 @spec fresh() :: []
def fresh do
[]
end # return true if va is a direct descendant of vb, else false -- remember, a vclock is its own descendant!
@spec descends(any, []) :: (true|false)
def descends(_, []) do
true
end @type va :: list()
@spec descends(any, any) :: (false|true)
def descends(va, vb) do
[{nodeb, {ctrb, _}} | resetb] = vb
case :lists.keyfind(nodeb, 1, va) do
false ->
false;
{_, {ctra, _tsa}} ->
(ctra >= ctrb) && descends(va, resetb)
end
end @doc """
Combine all VClock in the input list into their least possible common descendant
"""
@spec merge(list, list) :: list
def merge([]), do: []
def merge([singevclock]), do: singevclock
## first is a list, eg [:a, {1, 1234}]
# rest is list of list, eg [[{:a, {1, 233}}, {:b, {3, 124}}]]
def merge([first|rest]) do
merge(rest, :lists.keysort(1, first))
end
def merge([], nclock), do: nclock
def merge([aclock|vclocks], nclock) do
merge(vclocks, merge(:lists.keysort(1, aclock), nclock, []))
end
def merge([], [], accclock), do: :lists.reverse(accclock)
def merge([], left, accclock), do: :lists.reverse(accclock, left)
def merge(left, [], accclock), do: :lists.reverse(accclock, left)
def merge(v = [{node1, {ctr1, ts1} = ct1} = nct1 | vclock],
n = [{node2, {ctr2, ts2} = ct2} = nct2 | nclock], accclock) do
cond do
node1 < node2 ->
merge(vclock, n, [nct1|accclock]);
node1 > node2 ->
merge(v, nclock, [nct2|accclock]);
true ->
({_ctr, _ts} = ct) = cond do
ctr1 > ctr2 ->
ct1;
ctr1 < ctr2 ->
ct2;
true ->
{ctr1, :erlang.max(ts1, ts2)}
end
merge(vclock, nclock, [{node1, ct}|accclock])
end
end @doc """
get the counter value in vclock set from node
"""
@spec get_counter(node :: atom, vclock::list) :: (integer|:undefined)
def get_counter(node, vclock) do
case :lists.keytake(node, 1, vclock) do
{_, {c, _}} -> c;
false -> :undefined
end
end @doc """
Get the timestamp value in a VClock set from node
"""
@spec get_timestamp(node :: atom, vclock :: list) :: (integer | :undefined)
def get_timestamp(node, vclock) do
case :lists.keytake(node, 1, vclock) do
{_, {_, ts}} -> ts;
false -> :undefined
end
end
@doc """
increment VClock at node
"""
@spec increment(atom, list) :: integer
def increment(node, vclock) do
increment(node, timestamp(), vclock)
end
@spec increment(atom, integer, list) :: list
def increment(node, incts, vclock) do
IO.puts "#{inspect node}, #{inspect incts}, #{inspect vclock}"
{{_ctr, _ts} = c1, newv} = case :lists.keytake(node, 1, vclock) do
false ->
{{1, incts}, vclock};
{:value, {_n, {c, _t}}, modv} ->
{{c + 1, incts}, modv}
end
[{node, c1} | newv]
end # retrun the list of all nodes that have ever incremented VClock
@spec all_nodes(vclock :: list) :: (list)
def all_nodes(vclock) do
vclock |> Enum.map(fn({x, {_, _}}) -> x end)
end
@days_from_gergorian_base_to_epoch (1978 * 365 + 478)
@seconds_from_gergorian_base_to_epoch (@days_from_gergorian_base_to_epoch * 24 * 60 * 60)
@spec timestamp() :: integer
def timestamp do
{megaseconds, seconds, _} = :os.timestamp()
@days_from_gergorian_base_to_epoch + megaseconds * 1000000 + seconds
end @doc """
Compares two VClock for equality
"""
@spec equal(va :: list, vb :: list) :: (true | false)
def equal(va, vb) do
Enum.sort(va) === Enum.sort(vb)
end @doc """
Possibly shrink the size of a vclock, depending on current age and size
"""
@spec prune(v :: list, now :: integer, bucketprops :: any) :: list
def prune(v, now, bucketprops) do
## This sort need to be deterministic, to avoid spurious merge conflicts later,
# We achieve this by using the node ID as secondary key
sortv = :lists.sort(fn({n1, {_, t1}}, {n2, {_, t2}}) -> {t1, n1} < {t2, n2} end, v)
prune_vclock1(sortv, now, bucketprops)
end def prune_vclock1(v, now, bprops) do
case get_property(:small_vclock, bprops) >= :erlang.length(v) do
true -> v;
false ->
{_, {_, headtime}} = hd(v)
case (now - headtime) < get_property(:young_vclock, bprops) do
true -> v;
false -> prune_vclock1(v, now, bprops, headtime)
end
end
end def prune_vclock1(v, now, bprops, headtime) do
# has a precondition that v is longer than small and older than young
case (:erlang.length(v) > get_property(:big_vclock, bprops)) or ((now - headtime) > get_property(:old_vclock, bprops)) do
true -> prune_vclock1(tl(v), now, bprops);
false -> v
end
end def get_property(key, pairlist) do
case :lists.keyfind(key, 1, pairlist) do
{_key, value} ->
value;
false ->
:undefined
end
end end

Riak VClock的更多相关文章

  1. Linux/centos下安装riak

    必备的组件: gccgcc-c++glibc-develmakepam-devel 使用yum安装相关组件 sudo yum install gcc gcc-c++ glibc-devel make ...

  2. 向量时钟Vector Clock in Riak

    Riak 是以 Erlang 编写的一个高度可扩展的分布式数据存储,Riak的实现是基于Amazon的Dynamo论文,Riak的设计目标之一就是高可用.Riak支持多节点构建的系统,每次读写请求不需 ...

  3. DB监控-Riak集群监控

    公司的Riak版本是2.0.4,目前已根据CMDB三级业务部署了十几套集群,大部分是跨机房部署.监控采集分为两个大的维度,第一个维度是单机,也就是 「IP:端口」:第二个维度是集群,也就是所有节点指标 ...

  4. Centos6.5里安装Erlang 并安装riak

    一.Erlang安装: 1 首先进入www.erlang.org 下载页面,下载otp_src_17.5.tar.gz. IT网,http://www.it.net.cn 2 解压缩:tar -xzv ...

  5. 分布式系统中一些主要的副本更新策略——Dynamo/Cassandra/Riak同时采取了主从式更新的同步+异步类型,以及任意节点更新的策略。

    分布式系统中一些主要的副本更新策略. 1.同时更新 类型A:没有任何协议,可能出现多个节点执行顺序交叉导致数据不一致情况. 类型B:通过一致性协议唯一确定不同更新操作的执行顺序,从而保证数据一致性 2 ...

  6. HBase Cassandra Riak HyperTable

    Cassandra                                                              HBase 一致性 Quorum NRW策略 通过Goss ...

  7. IOT数据库选型——NOSQL,MemSQL,cassandra,Riak或者OpenTSDB,InfluxDB

    IoT databases should be as flexible as required by the application. NoSQLdatabases -- especially key ...

  8. 对比Cassandra、 Mongodb、CouchDB、Redis、Riak、 Membase、Neo4j、HBase

    转自:http://www.cnblogs.com/alephsoul-alephsoul/archive/2013/04/26/3044630.html 导读:Kristóf Kovács 是一位软 ...

  9. Riak

    出处:http://www.oschina.net/p/riak Riak是以 Erlang 编写的一个高度可扩展的分布式数据存储,Riak的实现是基于Amazon的Dynamo论文,Riak的设计目 ...

随机推荐

  1. JSTL学习笔记(核心标签)

    一.JSTL标签分类: 核心标签 格式化标签 SQL标签 XML标签 JSTL函数 二.核心标签       引用方式:<%@ taglib prefix="c" uri=& ...

  2. EffectiveC#3--选择is或者as操作符而不是做强制类型转换

    1.用as运算符进行类型转换.因为比起盲目的强制转换它更安全,而且在运行时效率更高. 安全体现在:as操作符就算是转化一个null的引用时,也会安全的返回一个null而不会像强制转换抛出异常. 2.a ...

  3. Javascript - IE8下parseInt()方法的取值异常

      公司的测试小妹妹跑来对我说,下拉框第9项始终无法正确提交的时候,我还以为见鬼了.   parseInt()会把'0'开头的数字以8进制来解析,当有大于7的数字时候就按10进制来解析.   // p ...

  4. Ubuntu自定义命令

    回到主文件夹 $ cd ~ 建立.bash_aliases $ touch .bash_aliases $ vim .bash_aliases 在此文件中加入一句话: alias cdlauncher ...

  5. c#程序为PDF文件填写表单内容

    众所周知,PDF文件一般情况下是无法修改的,如果你有一张现成的PDF表格,这时想通过编程实现从数据库或者动态生成内容去填写这张表格,就会有些问题了,首先我们要解决以下2个重要的问题: 1.如何将内容写 ...

  6. 已知TSP问题的最好解

    a280 : 2579ali535 : 202339att48 : 10628att532 : 27686bayg29 : 1610bays29 : 2020berlin52 : 7542bier12 ...

  7. Centos安装php提示virtual memory exhausted: Cannot allocate memory

    由于内存不够,需要在php配置的时候./configure最后添加上 --disable-fileinfo >>./configure --prefix= ...........   -- ...

  8. JS多选日期

    项目需要一个可以选择多个日期的日期选择框,从网上找到一个单选的选择框源码 (http://blog.5d.cn/user2/samuel/200503/61881.html),修改成可以多选. 使用方 ...

  9. jQuery源码学习:使用隐藏的new来创建对象

    在JQuery源码中发现,JQuery定义一个类,但不用new关键字去创建该类对象,而使用方法调用()方式去创建该对象. 很多时候我们是这样写类,然后使用new创建对象的: function Pers ...

  10. 如何使用Assetic进行文件管理

    安装和配置Assetic 从symfony2.8开始,Assetic就不再被包括在symfony标准版.使用任何Assetic的特性之前需要安装AsseticBundel,在命令行执行下面命令: $ ...