代码行统计工具-CLOC

在工作中有时会有需要统计代码的行数,一般会用 wc 给出一个大致的结果。只不过在源代码文件分布比较分散,且存在多种不同类型语言的源代码时,wc 就不是特别适合了。

在公司内部也见过一些同事实现类似功能的脚本,但我想这应该是一个通用的需求,于是就找到了这个工具 - CLOC。其实就是一个 perl 脚本,很好用,统计报告也很清晰。在这里推荐一下。下面是一个统计 leveldb 源代码行数的例子。

$ cloc .
     128 text files.
     123 unique files.                                          
     353 files ignored.

http://cloc.sourceforge.net v 1.55  T=0.5 s (238.0 files/s, 46718.0 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
C++                             60           2012           1258          13124
C/C++ Header                    52            968           1458           2690
HTML                             3             84              0           1094
C                                1             33              7            255
make                             1             43             17            153
CSS                              1             10              1             78
Bourne Shell                     1              9             19             46
-------------------------------------------------------------------------------
SUM:                           119           3159           2760          17440
-------------------------------------------------------------------------------

关于自动分裂的思考

自动分裂是分布式系统中的一项重要技术,通常与自动迁移和负载均衡一起考虑,提供了系统的可扩展性和良好的性能。例如 Google 的 BigTable 和 Yahoo 的 PNUTS 都实现了类似的功能,我之前也认为这应该是一个好的分布式系统标配。

读了 Facebook 关于实时 Hadoop 的文章后,结合我自己在工程上的实践,我开始反思这一想法,认识到了这个功能的一些局限性。

Facebook 在打造实时 HBase 系统时,放弃了 HBase 提供的自动分裂,而专门开发了手工分裂功能。对此, Facebook 的解释是:

  1. 由于业务数据的均匀增长性,所有子表可能在相近的时间触发自动分裂,导致分裂风暴;合理安排的手工分裂可以避免这一情况,减少对生产环境的影响。
  2. 手工分裂时在某个时间,子表的数目是稳定的,有利于进行调试和调优;自动分裂时很难把握住系统中子表的变化。
  3. 在对日志文件问题进行后期处理时,子表没有分裂比有分裂要容易处理很多。因为应用日志到子表上时不用考虑是否已经分裂。

Facebook 给出的三个原因是非常合理的,我也很赞同,但我想补充一下我对自动分裂局限性的两个考虑:

  1. 较难进行事故影响评估。对于一个严肃服务来说,发生系统事故时不仅要求尽快恢复,更为紧迫的要求是迅速给出影响评估。手工分裂时运维人员对系统中子表的分布情况有着更好的了解,能够更快地做出评估(而且一般影响面也可控一些)。
  2. 较难进行数据恢复。当子表数据出现问题,或者数据源本身就有问题,要进行数据恢复时,手工分裂一方面能够准确地定位错误数据的位置,另一方面便于进行错误数据的处理(后台直接替换错误文件等,不单指 HBase)。而自动分裂时寻找错误数据位置本身就比较麻烦,由于子表可能一直在变动中,对错误数据进行处理也不容易。

从上面列出的几点来看,使用、改造或者实现一个分布式系统时,不能仅仅考虑方案是否漂亮,还要考虑到该系统的具体应用场景。脱离了应用场景的系统实现,如同漂亮的水果,吃起来不一定甜。但令人感到讽刺的是,漂亮的水果一般比较贵。

青岛行记

在种种客观、主观条件下,我和媳妇儿同时决定上周休假一周散散心。选在十一之后,主要是为了人少点儿、住店便宜点儿和玩的舒服点儿。玩的舒服点儿主要是指,十一我还得值班处理服务器告警,但十一之后别人都在上班,我就可以放心地玩了。

之所以选择去青岛,主要是因为某个人太懒,对爬山和走远路很抵触,只好到海边溜达。我在去哪儿上团购了个旅馆,一天才七十块,这样能够比较放心地多住几天。

在青岛的日子是比较闲适的,每天睡到中午才起床。起来后就背个小包去逛,逛到哪儿算哪儿。没有奢求逛完所有的地方,但是由于时间充裕,基本上有意思的地方也都去看了下,唯一没去的就是崂山...

上班以后,工作赚钱成了主要目的,即使是下班回到家,也时不时地要处理一些工作上的事情,因而平时感觉生活完全被工作给充斥了。真正抛开所有事情,手机调成静音,才回归到了比较简单的状态,心情畅快不少。

我从青岛回来之后,看了一个名为《硬汉2》的电影。整个故事是在青岛取景拍摄的,一开头就是青岛天主教堂,熟悉的画面让我感觉好生激动。想要休闲的话,在家里宅着也未尝不可。但到一个陌生的地方总是一个更优的选择。在那里,困扰你的不再是日常的琐碎小事,而是完全不同的问题域。除此之外,还有增长见闻的好处,这一点对我是颇有吸引力的。特别是在当地能够发生一些有趣的、甜蜜的、或者忧伤的故事,就再好不过了。

我不太善于描述旅行中的人和事,再加上到青岛玩纯粹是休息,没办法作为攻略分享。只好挑几张图片出来,随便看看吧。

《三体三部曲》赠书计划

上周我又重读了一遍《三体》系列,在路上,用 Kindle。

还记得原来《三体I》好像是在《科幻世界》上连载读完的;《三体Ⅱ》曾经买过一本书,不知道放哪里了;《三体Ⅲ》是读的 txt 电子版。这次重读,看的是网友精心排版过的三体系列 PDF 电子版。

第一次读时主要是跟着情节往前追,急切地想知道后面会发生什么,看重的是酣畅淋漓。这次重读,更注意细节和推理,故事情节发展的脉络,人物感情和心理的变化。虽然重读时对书中一些地方产生了疑惑,但我觉得和整个故事的图景来比瑕不掩瑜,况且还有可能是我没有想透呢?

《三体》三部曲是一个好的科幻故事,好到当想到它描述的宇宙有可能是真实的时,忍不住会打个冷战。刘慈欣是一个非常杰出的科幻小说家。在我眼里,相信也是在很多科幻迷眼里,他代表了中国当代长篇科幻小说的最高水平。我很惭愧阅读电子版小说侵犯了大刘的权益,但我实在对收藏实体书无爱,书架已经够沉了。因而我想赠出一套《三体三部曲》给我的博客读者,一来我的良心能够稍觉安稳,二来希望对扩大国产科幻的影响力有所助益。

获得这套赠书的方法很简单,你只需要在评论中留言说明你想要这套书的理由:

我想要《三体三部曲》,因为...

我会选择喜欢的一个理由,然后根据你留下的电子信箱联系你,通过某个电商(卓越、京东之类)直接发货给你。这不是广告,不是福利,也不是读后感评比,没有任何客观性,一切都凭我的主观感觉,所以对于没拿到的同学我最后只能表示遗憾。

理由可以随便写,我相信应该不会有人为了这几十块钱的书而费劲脑筋来编故事。此外,我不会考虑认识的朋友,世界已经很小了,我们有必要把它搞得更小吗?还有,仅限大陆的朋友,不然运费都可以买好几套了...

考虑到我博客的影响力,应该不会有太多理由让我挑选,那么等待的时间就稍微长一点吧。最后的时间放到 2011 年 10 月 25 日,如果到时还没有我喜欢的理由出现,我就从已有的留言中随机挑选一个送出。

★★★★★
书已经送给来自浙江杭州的 Arcthur 同学
★★★★★

Leveldb 编译错误背后的C++标准变化

在编译 Levedb 时,我遇到了这个错误:

g++ -c -I. -I./include -fno-builtin-memcmp -DLEVELDB_PLATFORM_POSIX -pthread -DOS_LINUX -O2 -DNDEBUG db/version_set.cc -o db/version_set.o
db/version_set.cc: In member function `void leveldb::VersionSet::Builder::Apply(leveldb::VersionEdit*)':
./db/version_edit.h:100: error: `std::vector, std::allocator > > leveldb::VersionEdit::compact_pointers_' is private
db/version_set.cc:461: error: within this context
...

在网上容易搜到解决方案,由于归根结底是访问控制问题,方法是把所有涉及到的的 private 变量或类型修改为 public。由于不是所有的编译器都会报错,我就很好奇产生这个错误的根本原因。

BTW: 一种不修改代码的 work around 方法是,在编译这个文件时加上 -fno-access-control 参数,这样 g++ 就不会进行访问控制检查,自然也就没问题了。这个参数同样可以用于对 private 成员函数进行单元测试。

简单地分析一下这个错误。发生错误的地方是在 VersionSet::Builder 这个类的成员函数中,而错误则是其成员函数无法访问 VersionEdit 和 Version 类的私有成员变量。VersionSet 是 VersionEdit 和 Version 类的友元类,Builder 是 VersionSet 的嵌套类。简化一下,代码如下所示:

class VersionSet;

class VersionEdit {
    friend class VersionSet;
    static int compact_pointers_;
};

class VersionSet {
    class Builder {
        int foo()
        {  
            return VersionEdit::compact_pointers_;
        }  
    }; 
};

把这段代码拿给编译器去编译,g++ 3.4.4/5 会报类似的 `int VersionEdit::compact_pointers_' is private 错误,但是 g++ 4.5.3 则能够编译通过。

由于 VersionSet 是 VersionEdit 的友元类,那么 VersionSet 是能够访问 VersionEdit 私有成员的,这样问题就集中在 Builder 是否能够获得与 VersionEdit 的友元关系。如果语法规定嵌套类 Builder 能够从 VersionSet “获得”友元关系,那么 Builder就能够访问 VersionEdit::compact_pointers_,反之就不能访问。

在 C++98 标准中,关于嵌套类的权限有如下描述:

$11.8/1 [class.access.nest],

The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (clause 11) shall be obeyed. The members of an enclosing class have no special access to members of a nested class; the usual access rules (clause 11) shall be obeyed.

Example:

class E {
    int x;
    class B { };
    class I {
        B b;                 // error: E::B is private
        int y;
        void f(E* p, int i) {
           p->x = i;         // error: E::x is private
        }
   };
   int g(I* p)
   {
       return p->y;          // error: I::y is private
   }
};

但是在 C++11 中,这段描述变更为:

$11.7/1 Nested classes [class.access.nest]

A nested class is a member and as such has the same access rights as any other member. The members of an enclosing class have no special access to members of a nested class; the usual access rules (Clause 11) shall be obeyed.

Example:

class E {
    int x;
    class B { };
    class I {
        B b;                  // OK: E::I can access E::B
        int y;
        void f(E* p, int i) {
            p->x = i;         // OK: E::I can access E::x
        }  
    }; 
    int g(I* p) {
        return p->y;          // error: I::y is private
    }  
};

从上面的描述和示例代码对比中我们可以明显看出,在旧标准中嵌套类和“被嵌套类”没有什么特殊的关系,就像两个普通类一样;但是在新标准中嵌套类已经完全视为“被嵌套类”的成员,那么自然也获得了“被嵌套类”成员应该有的访问控制权限。这也就意味着“被嵌套类”的普通成员拥有的访问“被友元类”私有成员变量的权限,嵌套类也能够获得,那么 Leveldb 在新版本的编译器下能够编译通过也不足为奇了。

不过 gcc3.4 的编译错误问题还不能单单归究于标准的变化。因为 gcc3.4 已经能够支持嵌套类访问“被嵌套类”的私有成员(因为在很早以前这就被确认为一个缺陷),只是不能够支持友元关系到嵌套类的传递。友元关系的传递可能是在 4.1 或者 4.2 版本中实现的,应该属于上述标准变化的衍生特性。

通过科目三路考

前天晚上通过科目三路考,我为期半年的驾校学习总算结束了。

想起来这半年也是折腾不断。我是 2 月底参加水木团购版的东方时尚驾校报名团购,3 月中旬考科目一。之后因为懒得约模拟机,光模拟机 6 个小时花了三周时间才上完。后来从淘宝上买了个约车软件,专门用来约周末的散段,效率还挺高的,也就三周就把散段上完了。后来科目二考试也算挺顺利。只是5月该考科目三的时候忽然忙起来了,工作上各种事情,再加上天气热,就把科目三考试拖了下来,一直拖到 9 月。

工作以后考驾照呢,我觉得主要还是得看执行力。执行力高的,两个月以内就能搞定,像我这样执行力差的,光拖就能拖四个月。我自己的一个特性是有压力的时候不希望分心。前段时间在做一个比较大的升级,对我个人的设计能力、架构把握和编码能力都是一个挑战。再加上合作半年的一个同事离职,新加入的同事也不太熟悉,我只好一个人吭哧吭哧把剩下的事情做完。

不过这个项目让我很有成就感!重构了一个 2 万多行的模块,精简到不到 1 万行;重整了 4 万多行代码,架构上进行了很大的调整,脉络上清晰很多。另外也加了些新功能。但是由于我负责的系统涉及产品线太多,平时工作中新需求、沟通、线上问题处理等日常事务占到一大半,很多时候只能加班写代码。这样大的工作量让人有些筋疲力尽。现在项目完成了,我也想休息休息调整一下,就把后续的编码工作让给同事去做,我下面这个月主要任务就是跟进测试和校园招聘的工作了。

算了,又跑题了,最后做个广告吧:如果 2012 应届生同学对百度提供的工作机会感兴趣的话,欢迎邮件简历给我!优秀的朋友我可以帮助内部推荐,相信你知道在哪里能找到我的邮箱地址 :)

双人桌游体验

比较惭愧地说,最近有点儿迷上了玩桌游。上下班地铁上有时候都会一直玩手机上的安卓游戏《Catan》(卡坦岛)。xixi 自从体验过桌游《三国杀》、《只言片语》后,也成天嚷嚷着要参加我们的同事聚会玩游戏。只是从家到单位的距离实在太远,只有进城 building 的时候才有机会一起玩玩游戏。

由于比较经典的桌游大部分需要三人或以上才可以玩,除非是合租的朋友,不然在家很难凑齐人。于是我就略微探索了一下适合双人玩的游戏,淘宝买了三款,《Lost Cities》(失落的城市、遗失的古迹)、《Carcassonne》(卡卡颂、卡卡城)、《Hailli Galli》(德国心脏病)。下面简单介绍一下初步的体验,供同样喜欢桌游的小情侣儿们参考 :)

《Lost Cities》是一款卡牌游戏,主线是两个人去竞争开发五座古城,只能两人玩。卡牌分两种,一种是赌注牌,每个古城对应三张相同的赌注牌,以颜色区分;一种是开发牌,每个古城对应 9 张从 2~10 的开发牌,以颜色区分。限制有 3 个:1. 一旦决定开发某座古城,首先付出 20 点的成本;2. 每张赌注牌能够提高一倍的收入/损失,但是赌注牌必须先于开发牌去开发古城;3. 开发牌必须按照点数从小到大用于开发某座古城。

规则就不详细描述了,主要谈谈体验。《Lost Cities》的双人对抗性还是很强的,每个人必须规划好自己的出牌顺序。如果刚开始出的点数大,那么后面点数小的牌只能弃掉了,弃牌可能被对方拿走;赌注牌也是这样,如果先出开发牌,赌注牌也只能弃掉;牌不好的话,开发的城市还不能太多,否则可能会亏本;追求的有一个目标是某座城的赌注牌+开发牌数大于等于 8 张,这样可以有 20 点的奖励。可能对结局影响比较大的状态是摸到开发牌点数的大小,如果某个人摸到的大部分是大于 5 的开发牌,那么赢的概率就会高很多。总的来讲,这个游戏的可玩性还是很强的,看似强势的一方到最后结算时发现分数反而更少也是有可能的。

《Carcassonne》是一款纸牌建造游戏,和拼图类似,适合2~6人。所有的纸牌都是正方形硬纸板,上面有河流、城市、寺庙、草原等场景。两个人轮流抽牌进行拼图,每个人有 7 个人偶(Meeple)。人偶可以放置在未建造成功的场景上,一旦建造成功,就可以根据场景不同结算分数,人偶被释放走去占领其它未建造成功的场景。

这款游戏的计分规则还是比较复杂的。如果多人玩,由于人偶太多,可能必须考虑复杂规则。但是两人玩的时候我觉得可以简化一些,比如我们玩的时候根本没有考虑农场场景和不允许二人占领同一场景。除了进展比较慢,游戏还是挺有意思的,而且值得一提的是,这种拼图类的游戏一般比较讨女孩子喜欢...

《Hailli Galli》也是一款纸牌游戏,规则更简单,适合2~4人。先均分所有纸牌,每个人轮流出牌,放到面前的自己弃牌堆上。一旦某个出牌触发了某个规则,所有人去抢按中间的铃铛,先抢到的就拥有所有人的弃牌堆,最终赢光所有人牌的人获胜。规则简单到什么程度呢,纸牌内容就是水果组合和动物,两张花色一样的牌需要抢铃、出现猴子时,台面上没有柠檬时抢铃、出现大象时台面上没有草莓时抢铃等等。好幼稚啊!我觉得把它称为家庭桌游更合适。

不过,话说回来,如果你想光明正大地摸摸姑娘的小手,强烈推荐这款游戏!因为大家都去抢按一个铃的时候,嘿嘿,你懂的!此外,估计也适合同事之间的破冰活动,大家有了肌肤之亲,自然距离感就会少一点儿~~~

承德行记

来北京几年,周边去的地方也不多。今年大组 building 选择了承德,那么自然愿意去到彼一游了。由于是跟团去,游记攻略可说的不多,就以照片为引,流水一番吧。

这是避暑山庄大门,从这样的景象中可以看到暑假里的承德是多么的人山人海,据导游介绍说避暑山庄每天要接待 10 万人。反正心理预期是早就有的,对这样的情况也没什么可抱怨的。

避暑山庄大门
避暑山庄大门

下面这张应该是正门,康熙爷写的“避”字多了一横。

避暑山庄牌匾
避暑山庄牌匾

在纷纷扰扰之中,偶尔也会有几片宁静。

小伙子你肿么了,是在练习蹲坑吗?

等待游览车中
等待游览车中
二马道上遥望小布达拉宫

烟雨楼附近避暑山庄的湖光山色真是太漂亮了,即使在那么多游客的情况下也掩饰不住它的美丽。什么故宫、北海、颐和园都是浮云啊!遥想当年,我不禁发出一句响亮的评论:“操!”

避暑山庄烟雨楼

小布达拉宫地方小,游客密度较避暑山庄更胜一筹。拍出来能看的照片不多,下面这个是大红台的近景。

普陀宗乘之庙(小布达拉宫)大红台近景

从外面看大红台和一堵墙似的,以为里面会是很窄小的空间,但事实上却是别有洞天。

大红台内院西北最高处
一窗一世界

虽然与小布达拉宫相比略小,但我觉得班禅行宫的金顶还是挺霸气的,因为上面有八条龙!

须弥福寿之寺大殿金顶
须弥福寿之寺红墙一角

最后再从另一个角度看看小布达拉宫,看看你能发现什么!

照片正中那棵树,放大了看,有没有觉得很眼熟?哈哈 :)

虽然被赶着只能走马观花地看,虽然游人多到恨不得马上买票回家,但总的来说,这次承德游还是长了不少见识。我自己的体悟主要在园林和建筑上,皇家园林和寺庙的确是费了不少的心机在里面。廊庑婉转、雉堞衔连,起承转合,相当精妙。很多地方都有让人柳暗花明、豁然开朗的感觉。风景实在是太好了!若有机会穿越回清朝,在里面倘佯几回,岂不快哉?

技术人员的眼界

意识到眼界的重要性,最初是在大学时学长的交流会上。南大数学系有一个传统,每年总有那么两三次组织高年级的同学开经验交流会。这些交流会可能有明确主题,例如留学或是找工作,也可能没有明确主题。幼稚如我,在大一阶段拒绝参加任何形式的社团或者活动,认为踏踏实实做好眼前的事情足矣,闲暇时间基本花费在小说上。后来的种种经验证明,这是多么傻的一种做法!

我在数学方面是一个资质一般的学生,是几位师长和朋友打开了我在计算机行业的眼界,得以投身到信息技术的洪流中。写到这里忽然觉得有变成回忆长文的迹象,就此打住,几位是谁就不介绍了。唯一值得一提的是,李开复先生也是其中之一,这也是我从不参于对他的争议和讽刺话题的原因。在帮助和启发中国学生方面,我觉得他是一个值得尊敬的人。

但是在进研究生院之后,我自己闭塞了眼界的发展。一方面有当时身处的环境使然的原因,另一方面有成绩羞见江东父老的自闭自卑成分。非常后悔的有两点,一是虽然在某个研究专题方面有所收获,但在计算机科学系统性知识方面的进展不够大;二是身处远离实业的象牙塔,虽然亲身经历了一次严重的经济危机,却没能趁机深入地去学习和观察隐藏在这场危机之后的经济和科技发展规律。

我用来勉励自己,有时候也用来鼓励他人的一句话是:“只要你读的是自己不知道的东西,那就是在进步。”因而我经常不择食地去读一些书,了解一些知识。但渐渐地认识到了一点,当你着手去学习时,眼界决定了你汲取知识的效率和效果。具体到计算机科学,眼界可能决定了你读的书是经典还是垃圾,花时间在过时的还是新兴的技术上。

这个周末在家看吴军先生的《浪潮之巅》,爱不释手。作为一个曾经拜读过 Google 中国黑板报上“浪潮之巅”系列连载的读者,我本以为这会是本信息企业史,但阅读后发现新增的三分之一内容更精彩。我不曾想过一个技术人员的眼界会如此之广,能够从各个角度去观察和分析身处的这个行业、这个时代。我不否认对于书中的一些观点我并不完全赞同,但作者的眼界和思考能力着实让我佩服。

现在我已经初步开始自己的职业生涯,未来该何去何从,心中有一些惶恐。该打造哪方面的能力,亦不知该如何着手。有一些短期的规划,但并无具体的长远目标,我知道自己又到了一个眼界的瓶颈期。都说中国的年青人被房子票子绑架了,我希望自己能在忧愁这些之外,分些时间来读读好书,交交优秀的朋友,想想深刻的问题,聊以自勉。

2011年13月32日24点60分60秒

这是一个测试时候发现的问题,2011 年 13 月 32 日 24 点 60 分 60 秒,这个时间存在吗?

我觉得这应该是个错误的时间,但很不幸地是,如果往 struct tm 中填入这个时间,mktime() 时并不会报错。下面是一段简单的测试代码:

#include <stdio.h>
#include <string.h>
#include <time.h>

int main()
{
    struct tm t;
    memset(&t, 0, sizeof(t));
    t.tm_year = 2011-1900;
    t.tm_mon = 13-1;
    t.tm_mday = 32;
    t.tm_hour = 24;
    t.tm_min = 60;
    t.tm_sec = 60;
    printf("tm_mon=%d, tm_mday=%d, tm_hour=%d, tm_min=%d, tm_sec=%d\n",
            t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
    time_t tt = mktime(&t);
    printf("%s", ctime(&tt));
    printf("tm_mon=%d, tm_mday=%d, tm_hour=%d, tm_min=%d, tm_sec=%d\n",
            t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
}

这个程序执行的结果是:

$ ./a.out  
tm_mon=12, tm_mday=32, tm_hour=24, tm_min=60, tm_sec=60
Thu Feb  2 01:01:00 2012
tm_mon=1, tm_mday=2, tm_hour=1, tm_min=1, tm_sec=0

查看 man mktime,的确有对这个特性的解释:

...if structure members are outside their valid interval, they will be normalized
 (so that, for example,  40  October  is  changed into  9  November);...

这就意味着,对输入时间做严格的检查无法依赖于标准库中的 mktime() 函数,只能自己来进行。这应该也是 date 命令曾经改进过的地方。

$ date -d "2011-13-32" # date (coreutils) 5.2.1
Wed Feb  1 00:00:00 CST 2012
$ date -d "2011-13-32" # date (GNU coreutils) 8.5
date: invalid date `2011-13-32' 

也谈地铁迷药

这是篇临时起意的文章。最近经常看到有人讨论“地铁迷药”或者“地铁迷药辟谣”,很多人抱着宁可信其有,不可信其无的态度。而且还有人在微博上、论坛上吐槽,怀疑是被下迷药了,吐槽当时怎样怎样头晕,怎样怎样难受。

简单来说,我的基本态度是“信其无”,但却无法证明“其无”。所以我不讨论它到底有没有,只是就我自己的“丰富”经历,谈谈头晕、恶心、难受、两眼发黑和迷药不一定有关系。

我站着不动久了容易头晕,这是小时候就知道的事情,但至今不晓得是什么原因导致的。高中之前都是我父亲用推子给我理发,他个子比较高,所以我必须得站着让他理发才舒服。我一般站个二十分钟上下,就会头晕恶心,必须得蹲一会儿才能继续站。因而我从小怀疑自己心脏有问题,但后来发现自己足球、篮球、羽毛球各种剧烈运动都不怕,检查也没出过问题,这个怀疑只能不了了之。

长大之后,好了一些。不过大一结束时军训站军姿的时候,我晕倒过两次。晕倒的过程很奇妙,最开始是胃疼,但不是疼得受不了那种,就是悠悠地疼,然后头开始晕,觉得四周景物有点儿晃,然后觉得呼吸困难,接着是两眼开始冒金星(很奇妙,真的!),最后一黑就倒了。值得一提的是这种晕倒不是毫无知觉那种,即使是倒地,仍然有意识觉得自己要倒了,因而没有发生“咚”的一声以头抢地的情况。晕倒以后被同学扶到树荫下,喝点儿水,大概十几分钟就能缓过劲儿来。

来北京之后的第一个夏天,在 619 路玉泉路到中关村的公交车上晕过一次。当年的 619 是很破的公交车,而且很难占到座位。我站着站着忽然觉得军训时候晕倒的症状来了,而且还伴有恶心的症状,久病成良医,马上蹲下靠着椅子大口喘气。坐在椅子上的女孩儿看我状况不对,连忙把座位让给了我,我坐在那趴了好大一会儿才缓过来。

后来还有一次,是和女友从北海公园回来的公交车上晕的。几路不记得了,应该是北海公园西面某站到知春里东站的线路,过新街口的。那辆车也是那种两节的老车,大热天的闷得透不过气,我就站在两节车厢的连接处。上车感觉还挺正常的,大半程没啥问题,快到了开始头晕。也是赶紧蹲下,女友扶着,忍到下车。下车后直奔中关村海关旁边的麦当劳去买冷饮,在里面坐了好久才敢出来。

前面这几次都应该是天热闹的,后来有一次估计是因为空调太冷导致的。研究生毕业最后一天搬家,天很热,把行李搬到西二旗智学苑租的房子后,到中科院奥运园区找同学吃饭。吃了驴肉火锅,喝了啤酒,略微有点儿上头,就直接去坐地铁回家了。8号线大家应该知道,奥运支线,人特别少。一进地铁我就觉得冷风呼呼灌得我不舒服,等倒了两趟到知春路换乘,两腿都已经软了。我忍着头晕难受坐到西二旗站,头重脚轻,两腿飘飘回到家中,闷头躺床上就睡,第二天一点事儿都没有。

我的这几次遭遇里面,真正晕倒的是在军训场上,因为没法采取什么措施。其它情况下都是蹲下或者坐下,扶着什么东西尽量让自己舒服一些。

我仔细回想过,我头晕基本发生在暑期,或者太阳直射的情况下,因而我认为是体质不耐热导致的中暑症状。至于迷药,虽然好多次头晕发生在公共交通工具上,但我从来没怀疑过,因为根据我的经历根本不可能想到迷药上去。

Facebook的实时Hadoop系统

Facebook 在今年六月 SIGMOD 2011 上发表了一篇名为“Apache Hadoop Goes Realtime at Facebook”的会议论文 (pdf),介绍了 Facebook 为了打造一个实时的 HBase 系统使用到的独门秘技。由于该论文提到的应用场景与小弟负责的系统要解决的问题域有相似之处,因而抽时间仔细阅读了这篇论文。下面便是结合论文的内容,谈一谈我的一些看法和感想,如有谬误,敬请指正。

这篇 10 页的长文主要的内容是 Facebook 在 Hadoop 系统上的工程实践,这些工程实践的目标则是题目所点出的——实时。虽然缺乏 Hadoop 系统的开发或使用经验,但是我觉得并没有妨碍我对这篇论文的理解。在我的脑子里,HDFS 就是 GFS,HBase 就是 BigTable。它们实现上可能有差异之处,但主要的思想应该是相通的。如果熟悉 GFS 和 BigTable 那两篇文章,这篇文章就可以视为 GFS 和 BigTable “进阶”。

1. 应用场景和需求

文章的最初是一些背景介绍,主要给出了三类应用场景:Facebook Messaging、Facebook Insight 和 Facebook Metrics System(ODS)。Messaging 就是 Facebook 的新型消息服务,Insight 是提供给开发者和网站主的数据分析工具,ODS 则是 Facebook 内部的软硬件状态统计系统。这三个应用场景都有各自的特色,但简单地来说,面临的问题是同样的:单机或者拆分的关系型数据库无法满足需求。

基于应用场景的数据特征,Facebook 抽象出了几个对存储系统的需求。由于描述起来有些复杂,例如 Efficient and low-latency strong consistency semantics within a data center,这些需求就不一一列举了。相比需求,更让人感兴趣的是它的那些“非需求”,总共有三条:

  1. 容忍单数据中心内部的网络分化,Facebook 认为这个问题应该从网络硬件层面(做冗余设计)而不是软件层面去解决;
  2. 单个数据中心宕机不影响服务,Facebook 认为这种灾难很难发生,因而愿意接受这种风险;
  3. 跨数据中心的数据热备服务能力,Facebook 假设用户数据是分配到固定的数据中心的,可能带来的响应延迟问题应该通过缓存来解决。

从这些“非需求”上可以看出,Facebook 考虑的是更实际的情况,而不是一个理想中的分布式系统,在这点上有一定的借鉴意义。

根据以上的需求和非需求,Facebook 自然而然地给出选择 Apache Hadoop 这套系统的理由,其中有社区的成熟度、Hadoop 在一致性、扩展性、可用性、故障容忍、读写效率等等的各项优点,这些方面的优点也是有目共睹的。

2. 打造实时的 HDFS

HDFS 本身设计来支持离线 MapReduce 计算的分布式文件系统,虽然在扩展性和吞吐上有很好的表现,但在实时性方面表现并不好。如果想让基于 HDFS 的 HBase 有更好的性能,HDFS 层的优化是不可避免的。为了把 HDFS 打造成一个通用的低时延文件系统,Facebook 主要做了以下一些优化。

2.1 实现 NameNode 的高可用——AvatarNode

HDFS 的 NameNode 是系统单点,就意味着 NameNode 挂掉会导致系统的不可用。NameNode 重启时加载内存快照、应用log和收集 DataNode 的数据块信息报告大概需要 45 分钟。即便使用了 BackupNode,仍然需要收集数据块信息报告,切换的时间仍然可能大于 20 分钟。但有实时性需求的系统一般都会要求系统 24x7 的可用性,因而 Facebook 对单点的 NameNode 进行了改进,实现了 NameNode 的双节点热备,称为 AvatarNode,如下图所示:

AvatarNode
AvatarNode

简单地来说,备份 AvatarNode 通过 NFS 读取并回放主 AvatarNode 的事务日志来保持数据的同步,并同时接收 DataNode 的数据块信息报告,这保证了主备 AvatarNode 的数据差距尽可能地小,使得备份 AvatarNode 能够很快地切换为主节点的角色。主备 AvatarNode 的角色是注册到 ZooKeeper 中的,DataNode 可以根据 ZooKeeper 中信息判断需要服从哪个 AvatarNode 节点的指令。

为了实现热备 AvatarNode 的数据同步和易用性,Facebook 还改进了 NameNode 事务日志,并部署了 DAFS (Distributed Avatar File System) 屏蔽了 AvatarNode 的故障切换,使得这些改变对客户端透明。文中并没有提到 AvatarNode 的切换是手工还是自动进行的,但是考虑到 ZooKeeper 的 lease 机制,自动切换应该不难实现。

2.2 Hadoop RPC 兼容性和数据块可用性

在之前的系统需求中,有提到一点是 Fault Isolation,并且 Facebook 的 Hadoop 系统是在单机房部署的,因而同一个服务必然会使用多套 Hadoop 系统。为了系统升级独立方便,使客户端兼容不同版本的 Hadoop RPC 是自然而然的事情。

HDFS 在分配副本数据块位置时,虽然会考虑到机架位,但整体来说仍然是相当随机的。其实我以前也曾经与同事讨论过类似的问题,到底是选择随机分配副本位置,还是使用一定的组策略去分配。随机分配的好处是简单均衡,坏处是一旦发生多台宕机,由于副本随机分布,导致某块数据副本全部丢失概率很大;用一定的组策略去分配的好处是多台宕机如果不发生在同一组里,不会丢数据,但是一旦多台宕机发生在同一组,会丢很多数据。看来 Facebook 是选用了组策略分配的方法,认为多台宕机发生在同一组的概率不大。

但这样做是否正确,我是有疑问的。同一个机架或相邻机架上的服务器一般上架时间、硬件型号等都相同,那么同时发生故障的事件不是完全独立的,其概率是要大于理想故障分布情况下概率的。我想这也是为什么 Facebook 最终方案中一组机器是 (2, 5),2 个机架,5 台服务器。这两个机架的选择,如果很谨慎的话,能够尽量避免我说的这种情况。不过,凡事还得看执行力,如果不了解部署情况去选择机架的话,不一定能够达到预期效果。

2.3 实时负载的性能优化

除了上面的改动之外,Facebook 还对客户端的 RPC 过程进行了优化。为 RPC 添加超时机制,加快文件 lease 的撤销速度(由于对 HDFS 文件操作不了解,我没明白为什么要加快 lease 撤销)。

此外,还提到了最重要的一点:局部性!Facebook 增加了一个检查文件块是否在本机的功能,如果在本机就直接读取。不知道它具体实现方式是怎样的,但我觉得这个做法其实是“很黄很暴力”的,不知道会不会破坏数据一致性。

2.4 HDFS sync 优化和并发读

为了提高写性能,Facebook 允许不等待 sync 结束就继续写,这一点看起来也很暴力,不知道会不会影响数据正确性。

为了能够读到最新数据,Facebook 允许客户端读一个还未写完的数据文件。如果读到正在写入的最后一个块,就重新计算 checksum。

3. 打造实时生产坏境的 HBase

3.1 行级别原子性和一致性

虽然 HBase 已经保证了行级别的原子性,但节点宕机可能导致最后一条更新日志不完整。Facebook 不够满意,引入了 WALEdit,一个日志事务概念来保证每条更新日志的完整性。

一致性方面,看来 HBase 能够满足需求。不过对于 3 个副本同时校验失败导致数据块不可用的情况,Facebook 增加了事后分析的机制,而不是简单丢弃。

3.2 可用性

为了提高 HBase 的可用性,Facebook 对其进行了完善的测试,并解决了以下几个问题:

  1. 重写 HBase Master,将 ragion 分配信息存储到 ZooKeeper 中以保证宕机切换正确完成。
  2. 使得 compaction 可以中断以加速 RegionServer 的正常退出速度,并实现 rolling restarts(就是逐台升级),降低程序升级对服务的影响。
  3. 将宕机 RegionServer 的日志拆分功能从 Master 中拆离,由多个 RegionServer 进行拆分,以提高 RegionServer 故障恢复效率。

这几个问题的解决倒是有通用的用途,我想不久以后很有可能会合并到 Hadoop 的代码中。

3.3 性能优化

性能优化主要从两点进行,一个是 compaction 性能,另一个是读性能。

读过 BigTable 论文的应该对其 memtable 和 compaction 的特性比较熟悉。这里主要讨论了让 minor compaction 也删除数据的好处,以及如何做 major compaction 能够提高合并的性能。

在数据读性能方面,文章里主要讨论了减少 IO 操作的方法,其中包括 bloom filter 和特定类型 meta 信息(时间戳)的使用。还有很重要的一点,在部署上保持 RegionServer 和物理文件的局部性!

文章后面还给出了 Facebook 在部署和运维方面的一些经验,其中有一些有趣的点,我后续可能会写篇文章专门讨论,这里就不详细说明了。

4. 总结

以前我们也曾经讨论过如何在分布式文件系统的基础上搭建一套实时数据分析系统,当时认为如果有成熟的 GFS 可用的话,这个工作会比较简单。现在读到 Facebook 的这篇文章,才发现当初想法的幼稚。仅仅从这篇文章中的技术点体现出的工作量来看,文中说这个系统是多年持续工作的结晶是令人信服的。当然,这也意味着想复制一套这样的系统并不是件轻松容易的事。

从系统设计的成果来看,这个系统应该能达到文章开头制定的需求目标,并也能够满足大部分应用场景的需要。不过有一点,我存在疑问,即是为 Insights 提供的 Realtime Analytics 功能。Realtime 没问题,但使用 HBase, Analytics 究竟能支持多好呢?可能还需要再去了解 HBase 的功能才能有答案。

从这个系统的很多细节可以发现,有不少折中和 trick。我想这就是现实世界,凡事很难做到尽善尽美,工程也一样。在设计系统时追求完美没有错,但是需要考虑代价和可行性,不要忘记满足需求才是最重要的目标。除此之外,也不妨再列出一些“非需求”,排除这些限制可能会降低不少的系统复杂度。

家居二三事

以前曾经有过吐槽,租房住的一大缺憾就是没法按照自己的想法布置房子,即使看上了某样家具,也能负担得起,往往却因为租房住搬家不便而作罢。于是乎生活就在“凑合”的质量上一直延续下去。

前些天光远君的一篇微博

又拨动了我内心的一点儿小小想法。我租的房子是大开间,除了卫生间和厨房,屋子里没有隔断,导致空间的利用上有些别扭。再加之装修和家具都比较简陋,生活还好,读书写字还是不太舒服。因而这次就向光远学习,把北向积灰甚厚的阳台稍微打扫了一下,布置了几样简易家具,聊作小书房之用。

正好顺便,把家里的桌子和床位置重构了一下,追求是电视前的空间基本满足玩 Wii 的要求。这样偶尔有同事或者朋友过来玩,不至于太局促。

说完了住,下面说吃。前面有篇《纸上烤肉》的博客抱怨“大伙烤肉”的消费比较高,今天忽然想到上次烤肉送的 30 元午餐券要到期了。于是我俩奔到烤肉店,要了两碗面,一分钱未花解决了午餐,估计那家的店员也比较郁闷。

昨天晚饭是在麦当劳解决的,并且确认了两件有问号的事情:麦当劳的免费 Wifi 信息跟网上流传的一样,但信号质量很差,数据时断时续,还不如用 GPRS;优惠券不用打印,手机上出示就可以,因此推荐安卓市场里一个叫“麦当劳优惠券”的免费应用,打折很方便 :)

广安门的麦当劳改了装修风格,店里一些新的圆桌和椅子非常舒服,有点儿向咖啡厅的档次看齐。我想如果 Wifi 给力的话,周末在这里上网消遣或 coding 倒也挺自在的。

后面说玩。我用“古剑奇谭”的激活码跟光远换了“仙剑奇侠传五”的激活码,但非常遗憾的是两台笔记本电脑配置都不到仙五的要求,太郁闷了。让人感觉不舒服的还有:如果配置不够,安装的时候就应该提示,为什么要等安装完且在线激活成功后,才不能进入游戏?这样就需要来回的激活反激活,但仙五又限制反激活 7 天后才能再次反激活,这让用多台低配电脑尝试激活的仙剑迷们情何以堪啊!

吐槽完仙五,再吐槽一下古剑。古剑对配置的要求是相对低一些,但游戏的操控感太差了!古剑里的游戏人物进行走动或者战斗的控制感是我玩过 RPG 游戏里最差的,没有之一!首先,地图的方向很诡异,路都不是跟方向键一个方向的,并且同时按两个方向键无法实现斜向走动;其次,无法通过鼠标点击控制人物走动方向,也不能自动寻路,地图上点左键居然只是简单的向前走!!!;再次,人物战斗没有键盘快捷键,只能通过鼠标点选 1、2 快捷键;再次,镜头变化诡异,走路时候镜头老是前后乱zoom,很容易错过某个路口或者宝箱;最后,BUG 多,我几次被卡到地图某个地方,最崩溃的一次是通过升级程序解决的,那次我足足打了十几遍那个甘泉村山洞!玩着这样的游戏,真怀念可以用游戏手柄流畅操作的仙剑二。

古剑在我看来唯二的好处是:剧情多,背景深,比较有趣;对白可以升级为有声版,绘声绘色,比纯文字好。不过,这游戏刨了剧情来看,做得真不怎么样。

在百度的第一年

半夜精神有些亢奋,混乱的思绪在脑袋里滚来滚去,没来由地忽然想起在百度这一年。想起这一年可以总结为:前半年拼命给自己揽事儿,后半年尽量往外推事儿。

我是去年大约这个时候加入(←_←这词儿帅吧)百度的,职位是商务搜索部的分布式计算工程师。可惜那时候我对分布式系统的理解还仅限于一本老套的教科书。

我加入的项目组当时致力于设计一套高可用、会话一致、高性能、易用的海量分布式类SQL数据库,因而我们花了很长时间去调研各种分布式系统,也包括一些数据库技术,如索引、压缩算法等。那段时间最主要的事情就是读paper,写报告和讨论。因而我对各种分布式系统和相关技术的了解也是突飞猛进,可以说是挺快乐的一段学习时光。

但在这种快乐时光的背后,也隐藏着问题。在领导层没有下决心投入所有人力做一个新系统之前,这个团队还有着维护线上系统的使命,而且这是一个关键的实时广告报表查询服务。所有人面前都放着一个选择题:新系统调研,还是维护已有系统?但这不是经理出的选择题,而是自己给自己出的,因为对这个问题的回答决定了对不同工作的态度。

我的回答你可能猜到了,为什么不同时都做呢?于是在研究新技术的同时,我也慢慢开始琢磨线上系统那近十万行代码。得益于以前开发过调试器的经验,我定位问题的能力还不错,找出了几个关键的系统缺陷。修复了这几个缺陷后,系统的稳定性提高了一大截。

这只是一个例子。总的来说,积极地去发现问题,解决问题——哪怕这问题不在自己的作用域,就是我说的前半年拼命给自己揽事儿。这让我承担了越来越多的工作,也很快地成长起来。

进入了工作的后半年,变动频频。新项目被移交到新部门,设计未能得到首肯,进行了相当大且不优雅的改动。项目组同事都去了新部门,只有我一个人决定留在原项目组负责线上系统。本来还承担了一些跨部门的合作任务,后来因压力太大,从新项目中抽身而出。

没曾想到的是新年刚过,各种新需求层出不穷。我既要负责处理和修复运行在几百台服务器上程序的各种问题,又要响应来自七八个上下游的新需求。虽然有新同事加入团队,但人力总是捉襟见肘,无法完全满足需要。于是这半年来学习到的最重要一点是——谈判。

每个项目都很着急,每个需求都很重要,但凡事都得讲先来后到,轻重缓急,都有谈判和折中的余地。推开事情并提出更合适的解决方案,远胜过揽来超过自己能力的事情然后搞砸,这就是我前面所说的后半年尽量往外推事儿。这让我不至于在工作中迷失。

上面这两点是我工作这一年来的经验,也是感慨。我不敢说自己做到都对,但在各种客观不客观的条件下,我在探索和学习着正确的方法。

进入新的“工作周年”,有好多改变。下周有合作半年的同事离职,本周有三个新同事加入。但我仍然在做着同样又不一样的事情——同样是这个系统,希望尽我所能地把它变成更好的不一样。

epoll 事件之 EPOLLRDHUP

在对系统问题进行排查时,我发现了一个奇怪的现象:明明是对方断开请求,系统却报告一个查询失败的错误,但从用户角度来看请求的结果正常返回,没有任何问题。

对这个现象深入分析后发现,这是一个基于 epoll 的连接池实现上的问题,或者说是特性 :)

首先解释一下导致这个现象的原因。

在使用 epoll 时,对端正常断开连接(调用 close()),在服务器端会触发一个 epoll 事件。在低于 2.6.17 版本的内核中,这个 epoll 事件一般是 EPOLLIN,即 0x1,代表连接可读。

连接池检测到某个连接发生 EPOLLIN 事件且没有错误后,会认为有请求到来,将连接交给上层进行处理。这样一来,上层尝试在对端已经 close() 的连接上读取请求,只能读到 EOF,会认为发生异常,报告一个错误。

因此在使用 2.6.17 之前版本内核的系统中,我们无法依赖封装 epoll 的底层连接库来实现对对端关闭连接事件的检测,只能通过上层读取数据时进行区分处理。

不过,2.6.17 版本内核中增加了 EPOLLRDHUP 事件,代表对端断开连接,关于添加这个事件的理由可以参见 “[Patch][RFC] epoll and half closed TCP connections”。

在使用 2.6.17 之后版本内核的服务器系统中,对端连接断开触发的 epoll 事件会包含 EPOLLIN | EPOLLRDHUP,即 0x2001。有了这个事件,对端断开连接的异常就可以在底层进行处理了,不用再移交到上层。

重现这个现象的方法很简单,首先 telnet 到 server,然后什么都不做直接退出,查看在不同系统中触发的事件码。

注意,在使用 2.6.17 之前版本内核的系统中,sys/epoll.h 的 EPOLL_EVENTS 枚举类型中是没有 EPOLLRDHUP 事件的,所以带 EPOLLRDHUP 的程序无法编译通过。

Shell Tips: Unix 时间到字面

我的工作需要天天跟报表数据打交道,在交换的文件中,一般时间的字段内容都是 Unix 时间。为了检查数据的正确性,不可避免地需要转换 Unix 时间到人类可读的字面时间。

下面想分享的是一个在 Shell 下转换 Unix 时间到字面的小方法。与前面几篇一样,这个小 shell 函数仍然可以放在 ~/.bashrc 中方便快捷使用。

# 转换 Unix 时间到本地时间字符串
function ctime()
{   
    date -d "UTC 1970-01-01 $1 secs"
}

使用方法很简单:

$ ctime 1234567890
Sat Feb 14 07:31:30 CST 2009

对 date 命令熟悉的同学会说,date 不是已经有直接转 Unix 时间的参数了吗?

$ date -d @1234567890
Sat Feb 14 07:31:30     2009

但是不好意思的是,小弟有时候用的 date 程序好老,不支持 @ 符号。

$ date --version
date (coreutils) 5.2.1
Written by David MacKenzie.

Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

PS: 写完这篇博文,我又想到了一个有趣的事情,既然很多 Linux 64 位版本的 time_t 已经是 long long 格式了,那么 date 命令有没有 year 2038 问题呢?

下面是 date (coreutils) 5.2.1 在 64 位服务器上的尝试结果:

$ date +%s -d "Tue Jan 19 11:14:07 CST 2038"
2147483647
$ date +%s -d "Tue Jan 19 11:14:08 CST 2038"
2147483648
$ date +%s -d "Tue Jan 19 11:14:09 CST 2999"
32473710849
$ ctime 2147483647
Tue Jan 19 11:14:07 CST 2038
$ ctime 2147483648
Sat Dec 14 04:51:44 LMT 1901
$ ctime 32473710849
Mon Mar 28 07:33:53 LMT 1910

看来字面时间和 Unix 时间之间互转存在着问题啊!但是用 Ubuntu 11.04 的 date (GNU coreutils) 8.5 尝试就不存在这个问题了。

淘宝OceanBase架构笔记

OceanBase架构图
OceanBase架构图(引自 rdc.taobao.com)

OceanBase 是淘宝研发的一套分布式 NoSQL 数据库系统。具体它是什么、怎样实现的,可以参考李震老师(花名楚材)的《OceanBase介绍》和杨传辉老师(花名日照)的《Oceanbase – 千亿级海量数据库》。这里我只是谈一下自己的感想,如有谬误,敬请指正。

OceanBase 相较于其它分布式存储系统,有一个特性是支持跨行跨表事务。这个特性太明星了,让几乎所有其它系统黯然失色。但实现这个是有代价和局限的,OceanBase 只能使用单机接受更新,也就是说它的 UpdateServer 只能有一个(或者准确地说,一组)。由于 UpdateServer 失去了扩展性,OceanBase 的应用必须建立在单机能够满足增量更新和查询性能需求(查询可以通过从机部分缓解)的前提下(或者硬件性能的增长快于性能需求的发展)。为满足这一点,需要对软件和硬件都进行很好的优化,幸运的是从淘宝核心系统团队成员的文章来看淘宝应该不缺这样的专家,也不缺买设备的钱。值得一提的是,每个公司看来都有自己的基因,看到 OceanBase 我脑子里就浮现出淘宝数据库架构中单机 Oracle 挂一堆 MySQL 的景象,何其相似啊!

阳振坤老师(花名正祥)在《淘宝海量数据库之二:一致性选择》这篇文章中说 OceanBase 是支持强一致性的。如果 UpdateServer 没有从库的话, 能够很容易理解。但考虑到 UpdateServer 从库也提供读服务,且 UpdateServer 之间使用 binlog 进行同步,那么还能否保证强一致性这一点我比较怀疑。也许会有其它的辅助机制来保证这一点,例如在 MergeServer 上做一定的策略。

在高可用性方面,对 RootServer 的说明较少,不清楚 OceanBase 有没有实现 UpdateServer 宕机后的 Master 选举。由于使用 binlog 同步,可能宕机恢复方面还是有一些风险的。

将 MergeServer 和 ChunkServer 部署在一起是个很好的选择,这样查询时能够利用一定的局部性。但除非根据业务需求非常精妙地部署,否则不可避免需要请求其它 ChunkServer 上的数据。我不知道它的查询是 MergeServer->(UpdateServer, ChunkServer0, ChunkServer1...),还是 MergeServer->(UpdateServer, MergeServer0, MergeServer1)。不同的模式有同的优缺点,如果 ChunkServer 只做存储的话,查询的过滤、合并应该是在 MergeServer 上做的。如果选用第一种方式请求,ChunkServer 间传输的数据没有过滤和合并,数据量较大;如果选用第二种方式请求,UpdateServer 的压力可能会被放大,视 MergeServer 封装的功能而定。

OceanBase 的介绍中没有提到 Chunk 或者 ChunkServer 是否分主从,也没有提到整体更新机制。考虑到更新是以批量方式合并到 Chunk 中,也许为了简化,Chunk 或者 ChunkServer 只是互备,没有主从。为了保证 ChunkServer 合并 UpdateServer 上冻结/转储数据时查询的正确性,可能用两阶段提交,不过我想仍然是一个很复杂的过程。

早上起来就想到这里,以后有问题再补充吧。

PS: 之前将楚材错当成了阳振坤老师的花名,已更正之。

纸上烤肉

大伙烤肉

听了凯哥对有孩子之前美好生活的描述,我觉得有必要趁着年轻清闲多腐败几把。于是当我家那个吃货缠着要吃好吃的时候,就去了离家不远但觊觎很久的大伙烤肉(南横西街店)。

在纸上烤肉这一点,我以前从来没有体验过,觉得很新鲜。电烤盘上面是过了油的纸,肉是放在纸上烤的。电烤盘下面是平底勺,鸡翅放在勺子里。这样的话,鸡肉是从上面开始熟的。用电烤的话,油烟是没那么大,但油滴溅的厉害,尤其是刚换上新纸的时候,会溅到桌边上。

烤肉店消费可真是贵,人均六十的话,不点主食我估计只能吃半饱。我跟吃货同学也就点了两个菜:调味牛排和鸡翅。调味牛排味道的确很赞!还有它配的酸甜的小料,非常合我的胃口。吃第一口的时候我简直有惊艳的感觉,我跟吃货同学说以后还要来。鸡翅倒一般了,贵且味道没特色。延吉冷面倒不贵,十块那么大一碗,我都没吃完。冷面也完全是酸甜味儿的,很好吃。

但是,酸甜味儿的最大缺陷,就是吃多了容易腻。我的实力还是不够,快吃完的时候已经有些腻了,看来这样的味道还是得适可而止。

亲历 Philips 电吹风召回事件

Philips 服务中心

我从没想到在中国能经历一次商品召回事件,而且是电吹风这种小家电的召回。6 月 1 日,飞利浦召回170余万台电吹风,很巧的是我买的两台电吹风都在这个召回列表中。感谢卓越亚马逊在第一时间发了两封电子邮件通知了我:

尊敬的客户:

您好!

您的订单(订单号 ***)订购的飞利浦电吹风商品正在全球召回和更换。详情请登录指定网站 (www.philips.com.cn/replace) 或拨打热线电话(800-820-0930)以便鉴别及更换。

受影响产品的型号和批次为:

飞利浦轻巧便携系列电吹风 HP4930 生产日期在080117到110130之间(含)
飞利浦轻巧便携系列电吹风 HP4931 生产日期在080117到110130之间(含)
飞利浦轻巧便携系列电吹风 HP4940 生产日期在080612到100613之间(含)

我网上申请了下更换,第二天就收到 Philips 服务中心的电话,约了一周内去领。今天中午骑车去了崇文区幸福大街路口的一个服务点,顺利地把货换回来了。更换的流程极其简单,提供一下预约人和电话,就从一个包装盒里拿出来新机器,把旧机器收走装起来。服务小姐说更换后的机器从今天开始算起享受两年的保修期。

值得一提的是,这两个电吹风都是在 09 年上半年买的,刚过两年的保修期。这次召回我算是捡了个便宜。感慨嘛,自然是有。这次召回事件抵消了 Philips 手机给我带来的坏印象。以后买小家电,Philips 会是首选了。我写这篇博客,也是觉得关心消费者的行为(Philips的召回和卓越亚马逊的及时通知)应该受到褒扬。

PS: 在骑车去幸福大街的一段路上,我忽然发现两边的建筑居然都不超过四五层高,但都挺气派。如果不看牌子的话,恍惚到了哪个小城市的新城区。又骑了一段我才反映过来,这是天安门正南啊,怪不得没有高楼!