10 2008存档

Dynamo学习

| | Comments (4) | TrackBacks (0)
      看了介绍Dynamo的论文,虽然英文不济,理解可能有误,但还是交流一下学习心得。Dynamo是亚马逊的key-value模式的存储平台,可用性和扩展性都很好,性能也不错:读写访问中99.9%的响应时间都在300ms内。

数据划分
      按分布式系统常用的哈希算法切分数据,分放在不同的node上。Read操作时,也是根 据key的哈希值寻找对应的node。Dynamo使用了Consistent Hashing算法【参考 http://tech.idv2.com/2008/07/24/memcached-004/】,node对应的不再是一个确定的hash值,而是一 个hash值范围,key的hash值落在这个范围内,则顺时针沿ring找,碰到的第一个node即为所需。
      Dynamo对Consistent Hashing算法的改进在于:它放在环上作为一个node的是一组机器(而不是memcached把一台机器作为node),这一组机器是通过同步机制保证数据一致的。
以上图为例,node1其实包含了多台机器,在一个node里宕了一台机或增加一台机,并不影响整个Dynamo对key的寻找。
      如果一个ring内的访问量大了,则可以在两个node间加入一个新node以缓解压力,这时会影响到其后继node的hash范围,需要调整数据。假设一 个ring中原本只有node2、node3、node4,在加入新的node1之后,原先从node2查询的部分key将改为从node1查 询,node1和node2中的数据就需要调整,主要是node1从node2中提取出属于它的数据,这样做需要选取性能压力不高的时候。(至于具体的调 整方法,从论文中没找到 -_-b)

数据同步
      Dynamo的一个node中的同步是由client端来“解决”的,使用所谓的(N, R, W)模型,其中,N表示node中机器的总数,R表示一个读请求需要的机器参与总数,W代表一个写请求需要的机器参与总数,这些值由client端配置。
      例如,一个node有5台机器(N=5),client发出写请求——广播到5台机,如果收到3个“写完成”的返回消息,即认为写成功 (W=3);client发出读请求——还是广播到5台机,如果收到2个“读完成”的返回消息,即认为读成功(R=2)。对于数据十分重要的应用(如金融),配置可以为(5, 5, 5),即要求node中所有机器的写都成功;而对于数据读写访问量极高的应用,配置可以为(5, 1, 1)。
      通常W不等于N,于是,在某些情况下一个node内的机器上的数据可能会有不一致,这时Dynamo是通过将多个Read的返回结果“合并”来得出最终结果 的,使用了所谓Object Version和Vector clock的技术,即跟踪一个Object在不同机器上的版本变化,以确保当多个Read请求结果返回不一致时,能够更具其版本信息得出正确的结果。 Dynamo的这种做法是一种折衷,即为了同时保证读和写的效率,写操作不要求绝对同步,而把不同步可能产生的后果推给了读操作。

数据恢复
      Dynamo的一个node中一台机器建有一个Merkle Tree,当两台机器不一致时(如一台机器宕机一段时间),通过这个tree结构,可以快速定位不一致的Object来恢复数据。Merkle Tree又叫Hash Tree,它把key分成几个range,每个range算出一个hash值,作为叶子,再一层层合并计算上去,这样,从root开始比较hash值,就可以快速找到哪几段range中的hash值变化了。

      个人感觉Dynamo的同步策略有点别扭,读采用广播,怎么保证读的效率呢?当然,也许人家有高招。
      程序出现了coredump,用gdb发现了出错的地方。

vector<size_t> seq_offset;
......
if(seq_id < seq_offset.size())
{
    seq_offset[seq_id];        //此处coredump
}

      没有超过vector的大小,访问是不应该有问题的,而且这一现象是偶然出现,所以怀疑是多线程的原因。果然在另一个线程里发现对seq_offset有 insert操作,insert本身不会影响访问,但是当vector大小不够的时候,insert会引发内存重新分配。STL代码(linux gcc-3.4.3 /usr/include/c++/3.4.3/bits)里,vector的insert会调用_M_fill_insert,而当空间不够时 _M_fill_insert会重新分配空间,并将相关数据结构指向新空间。

文件 vector.tcc
324        std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish);        //销毁vector里的元素,即调用各元素的析构函数
325         _M_deallocate(this->_M_impl._M_start,                                           //收回存放各元素的空间,即free
326               this->_M_impl._M_end_of_storage - this->_M_impl._M_start);       
327         this->_M_impl._M_start = __new_start.base();                                //数据结构指向新的空间
328         this->_M_impl._M_finish = __new_finish.base();             
329         this->_M_impl._M_end_of_storage = __new_start.base() + __len;

      麻烦就在于当326行结束后,_M_start指向的是一块已经释放了的空间,这时候再有vector的访问操作(如 seq_offset[seq_id])当然就会coredump。其实STL完全可以先把_M_start赋给一个临时变量,然后_M_start指向 新空间,最后再释放临时变量指向的空间,这样可以保证在多线程下顺利运行。不过STL文档说了——它不支持多线程——所以这样做也没错。
      加锁也可以解决这个问题,不过那样太低效了,不予考虑。最后的解决方案是,用vector的reserve方法预先分配好内存,免得在使用中动态增长。

植物兄弟

| | Comments (0) | TrackBacks (0)
      十一在花市看到有一小株一小株的珍珠菠萝(这是姑公告诉我的名字,今天才知道应该叫珍珠芦荟,不过姑且这么叫吧)卖,跟当年姑公送给我的那盆简直一模一 样,看着十分亲切,忍不住买了一小株。上周五家里又寄来了三小株吊兰,想想以前在老家还觉得这东西太普通都没在意过,而现在那一点嫩绿在这北方苦寒之地都 显得那么难得。阳台都快成花园了。我不是在种植物,我是在寻味飘散的回忆。
      古希腊哲人说每颗树都是一个神明,确实,看着哪怕是一颗拳头大小的榕树,也让我觉得平静、觉得高兴,如果不是神,又怎会如此?
      看到代码里有
      printf("%.*s", msgLen, msg)
      觉得奇怪,还以为是笔误,查了查资料,才知道有 %.*s 这种用法:即在字符串指针前再跟上想打印的长度参数。
      const char* msg="hello,world";
      printf("%.*s", 3, msg);                   //输出: hel
      printf("%.*s", 6, msg);                   //输出: hello,


      启动程序的时候发现一个日志都不输出,什么都不执行,但ps可以看到进程,strace跟一下才发现程序在open一个有名管道(fifo)的时候停住了。原来有名管道如果没有读的一方,open是阻塞的,详细规则如下:
      “如果同时用读写方式(O_RDWR)方式打开,则不会引起阻塞。
      如果用只读方式(O_RDONLY)方式打开,则open()会阻塞一直到有写方打开管道, 除非你指定了O_NONBLOCK,来保证打开成功。
      同样以写方式(O_WRONLY)打开也会阻塞到有读方打开的管道,不同的是如果O_NONBLOCK被指定open()会以失败告终。 ”


      程序一直运行正常,突然不转了,再重启也不行,十分怪异:如果是程序有问题,为何先前能运行?如果程序没问题,为什么现在又不能启动了?后来才发现是日志写到2G大小了,重启也写不进日志。linux下2G的文件大小限制,可以在编译时加参数来解决:
      gcc -D_FILE_OFFSET_BITS=64

最man的男人

| | Comments (2) | TrackBacks (0)
夏辉: 小钢炮收到个包裹咧,是什么?

haodong: 是刮胡刀,而且是三刀片的

夏辉: 为什么用三刀片的?

haodong: 因为....只有最man的男人才用三刀片的

小钢炮: 什么叫man?


第二天


董昊: 程xx是新来的吗?

夏辉: 是的,算法组的

董昊: 男的女的?

夏辉: 男的,而且是很man的那种

董昊: 哼,有小钢炮man吗?

夏辉: 应该还是没有小钢炮man....

小钢炮: 你们!
      有几台机器用SecureCRT登录上去以后,每隔大概5分钟左右就会断开连接,很麻烦。发现这个好像不能通过修改服务端的配置解决,只好配SecureCRT了,好在也很简单:
secure crt

      每隔60秒发一个“无操作”消息,服务端就不会断开连接了。
      十一期间看了倪金刚写的《CFM56方程》,还有点意思。
      法国人在战后建立了独立的航空工业,包括达索飞机制造公司(产品:“超军旗”战斗机、“幻影”系列)和斯奈克玛航空发动机公司,毫无疑问,所有的军用发动 机都由斯奈克玛这家“国企”生产。上世纪70年代初,斯奈克玛开始图谋民用发动机市场,在被普.惠公司拒绝后,和通用电气公司(GE)开始了合作。先是按 照GE提供的图纸生产,后来在法美两国总统的帮助下,斯奈克玛和GE开始合作研发F101发动机(用于美国B1轰炸机),不过,核心机(高压压气机、高压 涡轮等)技术还是在GE手里,斯奈克玛只是负责风扇、低压压气机、低压涡轮、附件机匣等,不过跟超级大国合作,能从军用发动机研发中分一杯羹,已经非常不 容易了。由于以上合作都较为愉快,斯奈克玛和GE合资建立了CFM国际公司(因为GE发动机的编号是字母“CF”开头,而斯奈克玛的是“M”开头,所以合 起来),两家各占一半股份,70年代中期,著名的民用航空发动机CFM56终于问世。30年过去了,全部新型波音737和空客A340、60%的A320 都装配的是CFM56。
      书里的故事讲得确实有趣,但对专业知识涉及较少,航空爱好者可以借来看看(别买,与内容相比,感觉太贵),书中本来是想以这两家公司为范例,讲讲商业合作 和谈判的技巧,但我看完书还是不知道斯奈克玛和GE的合作妙在哪里,觉得它们的合作也没啥稀奇,两家对自己的技术都是高度保密的,无法从对方那里学到高级 技术,说白了就是“强强拼合”,而不是“强强融合”。生产的时候你负责你的、我负责我的,最后按接口规范把组件拼在一块儿,一个产品就完成了。最终造成CFM56成功的是两家的“硬实力”,而不是什么商业技巧。话说远一点,那么多讲MBA、讲商战案例的书,读完了你也不会觉得有什么启发,感觉学成一样的 模式容易,做到一样的成功就难了。书本和现实之间还是隔很远的。
      struct ThreadParam
      {
        bool  *exit;
        ......
      };
     
      ......

      ThreadParam my;
      my.exit = false;

      pthread_create(thread, NULL, threadProc, &my);

      ......

      void threadProc(void* p)
      {
        ThreadParam *param = (ThreadParam*)p;
        if(param->exit)
        {
        ......

      昨天的一段程序,却发现param->exit的判断总是成功,心想怪了:我赋值为false了啊,难道my析构了?调了半天才发现是 if 判断的问题,应该判断bool的指 *(param->exit) ,判断 param->exit 就成了判断指针是否为空了(当然不为空了)!
      以前听过有人建议所有对bool量的判断应该用 if(param->exit == true) ,这样虽然麻烦,倒是可以通过编译器的类型检查提早发现上面的问题。

关于存档

This page is an archive of entries from 10 2008 listed from newest to oldest.

09 2008 is the previous archive.

11 2008 is the next archive.

Find recent content on the main index or look in the 存档 to find all content.