试着开源LiteCloud项目

所谓LiteCloud,无非就是前些天提到的LightCloud的php版本实现。这个和原来的python版本有一些区别,会造成不兼容,如下:

    1. 把Consistent Hashing算法换成了ketama,在pecl的memcached扩展里有简单方法可以实现,效率比单纯的php好很多,能快个10倍吧。没有重复造轮子,因此我很是得意。
    2. 静态方法调用时,不再支持原来的system参数,原版是用这个来支持多个LiteCloud集群。
    3. 第一个版本,利用memcached扩展来读取tokyo tyrant,所以目前仅支持简单的操作比如get, set, delete, increment。其实再努力一下,也可以支持更多的功能,比如redis。
    4. 去掉了原版的local_cache功能,我觉得这个功能完全可以放在外面,更灵活。

项目主页

目前托管在github上 —— LiteCloud ,使用git以及github的时间不太长,但是很喜欢,欢迎fork。

使用示例

用静态方法调用:

require 'LiteCloud.php';

$config = array(
    'lookup1_A' => '127.0.0.1:41201',
    'lookup1_B' => '127.0.0.1:51201',

    'storage1_A' => '127.0.0.1:44201',
    'storage1_B' => '127.0.0.1:54201',
);

list($lookupNodes, $storageNodes) = LiteCloud::generateNodes($config);
LiteCloud::init($lookupNodes, $storageNodes);

LiteCloud::set('hello', 'world');
print LiteCloud::get("hello"); # => world
LiteCloud::delete("hello");

print LiteCloud::get("hello"); # => nil

或者采用实例化的方式调用,这种方式能够支持多个LightCloud集群

 require 'LiteCloud.php';

$config = array(
    'lookup1_A' => '127.0.0.1:41201',
    'lookup1_B' => '127.0.0.1:51201',

    'storage1_A' => '127.0.0.1:44201',
    'storage1_B' => '127.0.0.1:54201',
);

$cloud = new LiteCloud($config);

$cloud->set('hello', 'world');
print $cloud->get("hello"); # => world
$cloud->delete("hello");

print $cloud->get("hello"); # => nil

看上去和python版本差不多,对吧?

性能测试

这个部分是个重点,之前找到的lightcloud的php版本,性能比原版要慢一个数量级,我可不想在这个地方丢了份。

暂时做了个最简单的性能测试,测试脚本在test目录下。测试条件如下:

    1. 底层采用tokyo tyrant作为存储。
    2. 单个进程重复操作一万次,比如写一万次hello => world,这个测试条件和python版本完全一致
    3. 关闭了local_cache

python版本的测试结果


Finished "Tyrant set" 10000 times in 2.50 sec [4002.0 operations pr.sec]
Finished "Tyrant get" 10000 times in 1.04 sec [9583.7 operations pr.sec]
Finished "Tyrant delete" 10000 times in 7.39 sec [1352.4 operations pr.sec]

LiteCloud的测试结果


Using 1.8229308128357 time to set 10000 values. QPS:5485.67
Using 0.71097207069397 time to get 10000 values. QPS:14065.25
Using 2.1550960540771 time to delete 10000 values. QPS:4640.16

看上去还比较乐观,尤其是delete的性能翻了好几番,几乎要怀疑我在代码实现部分出了差错,在其它方面的表现也是全面超出,因为python版在hash_ring的实现上拖了后腿。

LightCloud的设计原理

LightCloud是最近看到的一个比较轻巧的分布式key-value数据库,尽管这类软件已经让人觉得审美疲劳,但我仍然觉得它的设计思路值得一提。

特色

除开其项目主页上列出来的特点不提,我觉得还能数得上的特色有:

    1. 理论上可以用任意key-value数据库做为底层存储,现在支持以tokyo tyrant或者redis作为底层的存储,如果使用redis可以获得更好的性能(大概提升30%~50%)
    2. 没有定制服务器端,基本上靠客户端语言来实现键值查找。优点是部署起来比较简单,缺点也是显而易见的,效率会有损失。
    3. 可以很方便的移植到其它语言上,我已经在github上找到一个ruby版本,甚至还有个php版本的实现。
    4. 可以方便的增加节点。
    5. 结构简单,方便hack

LightCloud的设计原理

Hash ring

LightCloud不能免俗的使用了一致性hash算法(Consistent Hashing),这是为了避免新增数据节点时发生集体拆迁事件。Consistent Hashing算法的原理请参考这里

last.fm的工作人员写的ketama算法算是比较常见的一致性算法,在libmemcached里大量使用。而LightCloud的作者当时还没发现合适的ketama python版,所以干脆自己捋起袖子写了个python版本的hash_ring,不到50行。这个是量身定制的,所以效率也还过得去,但是兼容ketama就别想了。

献上hash圈圈一个以明志:

LightCloud的hash环有什么与众不同?

其它分布式key-value数据库采用的办法是复制数据到多个节点上,例如Amazon Dynamo的复制策略图:

Dynamo用了许多办法解决consistent问题,系统相当复杂。而LightCloud直接使用tokyo tyrant的master-master复制功能,大幅简化了这部分的逻辑。所以在它的hash环上,单个节点其实是一对master-master的tokyo tyrant,焦不离孟不离焦。

在新增数据节点时,如果没有路由服务找到正确的服务器,可能会损失数据。那么LightCloud继续采用流氓手段解决这个问题,他又给上了个环,保证不会发生意外。这两个hash环里的节点仍然是之前提到的tokyo tyrant双人组,一个环叫lookup,记录了每一个key保存在哪个storage节点上;另外一个环叫storage,这是真正存放数据的地方。于是它的结构图变成了下面这个样子:

这部分比较难以理解,试着就上图阐述一下:

    1. 一个名叫A的家伙,住在storage_SB地区(storage ring)。同时,我们还告诉记性好的lookup_B(lookup ring),A君的地址为storage_SB。
    2. 很不幸或很幸运,咱们的数据膨胀到需要扩容了,于是新增了一个违章建筑storge_X,这个该死的建筑正好影响了我们找到A君。这时候,我们还可以问起记性好的lookup_B,A君住在哪个角落里啊? —— lookup_B日道:他就住在sotrage_SB一带~
    3. lookup这群家伙记性虽然好,但是也架不住人多,再也记不住这么多人的住址,所以又新来了几个记性好的lookup。这个会影响咱们找到storage住哪吗?答案是不会,因为没有新增别的违章storage建筑,咱们不需要问路也能找着人。

按照以上推论,一定要避免同时增加lookup和storage节点,这很可能会损失数据。

参考网页