一种抵御 DDoS 攻击的 IP 追踪技术

在拾掇家务时,发现一页我在 2008 年 10 月做的备忘录,记录了一个可用于抵御 DDoS 攻击的 IP 追踪技术。当时因为觉得 idea 太小,不值当写成文章投出去。纸张放那里总是占地方,在博客里电子化一下,然后就能销毁了。

Differential Deterministic Packet Marking

这个 idea 是在 IP 协议的基础上做一些扩展,可以帮助用户在 DDoS 攻击时识别攻击数据包和定位攻击者。在理解这个 idea 之前,可能需要先看几篇参考文献:

[1] Z. Gao and N. Ansari, "Tracing cyber attacks from the practical perspective, " IEEE Communications Magazine, vol. 43, no. 5, pp. 123-131, 2005.

[2] A. Belenky and N. Ansari, "On deterministic packet marking, " Computer Networks, vol. 51, no. 10, pp. 2677–2700, 2007.

[3] Y. Xiang, W, Zhou, and M. Guo, "Flexible deterministic packet marking: an iP traceback system to find the real source of attacks, " IEEE Transactions on Parallel and Distributed Systems, vol. 20, no. 4, pp. 567-580, 2009.

这个 idea 主要是在 [3] 基础上做的改进,其 motivation 是仅仅使用标记段 [3] 内容太保守,用标记段再结合 IP 头中已有的信息,可以做得更好。简单来说就是运营商的接入路由器在 IP 头中增加一些标记,服务器在遭遇到 DDoS 攻击时,可以根据接入路由器增加的标记再结合 IP 头中已有的信息,识别攻击流量,以及确认攻击源。

下文的内容基于三个假设:

  1. Source IP 和 ingress router 的接入 interface 的 IP 经常在同一个网段中;
  2. 大部分网络流是正常的网络流而非 DDoS 的网络流。
  3. ingress router 在可控域中,未被入侵。

图1: 典型网络拓扑示意图,来源于[2]
由图中可见,如果主机使用真实IP 的话,Host 1 发出数据包的 source IP 和 router interface 1 的 IP 仅在最后八位不同,Host 2 发出数据包的 IP 和 router interface 2 的IP 也是仅仅在最后八位不同。

由假设有大部分数据包的 source IP 与 router interface IP 在同一个网段中,这样 ingress router 只需在标记段中标记与 source IP 不同的位即可进行追踪。

图2: IP 头和标记段,标记段与[3]相同
在我的 idea 里,Mark 使用的是 IP 头中的 IDENTIFICATION 域,共 16 位(我们也可以用上 TOS,这样就有 19 或者 24 位),其中各个位的作用如图 3 所示:

图3: 标记段内容

入口路由器上执行的标记算法是:

Algorithm:( 16-bit Mark case, RI for Router Interface)

if (SourceAddr RIAddr ) ⊕ & 0xffff8000 = 0 // Case 1
    Mark := SourceAddr ⊕ RIAddr // 1 packet
else if (SourceAddr ⊕ RIAddr ) & 0xff000000 = 0 // Case 2
    Digest := hash(RIAddr)
    for i=0 to 2 // 3 packets
        Mark[i].M := 1
        Mark[i].A := 0
        Mark[i].seg := i
        Mark[i].digest := Digest
        Mark[i].address_diff := ((SourceAddr ⊕ RIAddr) >> (i*8)) & 0xff
else // Case 3
    Digest := hash(RIAddr)
    for i=0 to 3 // 4 packets
        Mark[i].M := 1
        Mark[i].A := 1
        Mark[i].seg := i
        Mark[i].digest := Digest
        Mark[i].address_diff := ( RIAddr >> (i*8)) & 0xff

我提出的新 idea 对 [3] 的改进能够带来以下几个好处:

  1. 大部分数据包仅靠 1 个包就可以 traceback。根据上面的假设,正常的数据包应该仅仅在 IP 地址的低位与路由器接口 IP 不同,这样仅仅需要在 mark 中的低 15 位与源 IP 异或就能得到路由器接口 IP(case 1)。
  2. 加快路由器对正常数据包的处理速度。由于正常的数据包仅需要 1 个包就能 traceback,不需要计算地址的 hash 值,大大加快了路由器对正常数据包,也是大部分数据包的处理速度。
  3. 不影响正常 IP 数据包的 fragment 策略。由于大部分正常的数据包仅仅需要一个包就能 traceback,那么修改 IDENTIFICATION 域对这些数据包的 fragment 策略毫无影响。对于非正常的数据包,本算法需要多个包才能 traceback,所以对其 fragment 策略会有影响,但由于它是非正常的数据包,不用考虑后果。

之所以觉得这个 idea 小,有两个原因:

  • 第一是在别人方案基础上做的改进,创新不够;
  • 第二是需要部署到全网(至少某个运营商内部)所有接入路由器上,(尤其在 IPv6 已增强安全性的前提下)不太可能实现。

这也符合我对很多研究的看法,虽然有意义,但在工程上基本价值不大,基本上就自己 YY 一下。

消息队列

整理浏览器书签时候发现以前对 Message Queue 产生过一些兴趣,以后说不定还会仔细做一下调研。在这里先简单记录一下,也好精简书签数量。

1. MemcacheQ

使用 memcache 协议的消息队列,存储看是使用的 Berkeley DB。受限于 Berkeley DB,对消息大小有一些限制——不超过 64K,但简单易用,缺点还包括缺乏复杂的消息队列特性。

2. ØMQ (ZeroMQ)

专门的消息队列实现,支持多种语言,支持较多的高级特性,也可用于进程内通信。类似于 TCP,不假设消息格式,需要用户自己处理消息封包、解包。文档很清楚,看起来很酷的样子,未调研是否支持分布式扩展。

体验海淘-Google Nexus 7

一直想买个安卓 Pad 玩玩,正好 Google 家出了 Nexus 7,很眼馋。但遍览淘宝代购,发现 8G 版最便宜的还要 1670,排队者甚众。仔细算了一下,觉得代购有点儿黑。本来价格比算上关税、运费都要贵,还不是现货,需要预付款排队,不定得等多少天。于是就抱着体验的想法,直接海淘了一把。

海淘肯定得看攻略,Nexus 7 的攻略虽然不多,但也足够了。其实难选的无非是两点:一是在哪里买,除了 Google Play,还有其它网上商店,各有优劣;二是用哪个转运公司,这个也是各有优劣,都得看前人的经验。

我比较懒,转运公司选择了用的比较多的 CUL 中美速递。注册以后会给两个转运地址:一个 CA 加州的,一个 DE 特拉华州的。地址中有个人识别码,在网站下单后送到该地址的商品会被记录到个人的名下,并转运回国内。

我想买 8G 版,只能选择 Google Play 商店并且交 13.99$ 的运费。由于 Google Play 限制销售地域,我就挂了个美国的 SOCKS 代理,在 Google Wallet 顺利用一张 Visa 信用卡配合 CUL DE 的地址下单,没有被砍单。下面就简单用时间记录一下海淘过程,都转换为北京时间:

2012-07-25 21:19:Google Play 下单成功,Gmail 信箱收到订单收据;
2012-07-26 13:23:Gmail 信箱收到发货通知,拿到 UPS 追踪号码,于是去 CUL 页面填写货物预报。
2012-07-26 14:25:填写货物预报时发现收货地址写错(街道地址填成了加州的 914 Ajax Ave),在 Google Play 提交请求修改 UPS 包裹收货地址街道部分;
2012-07-27 02:17:UPS 显示包裹离开 Louisville, KY;
2012-07-27 05:56:收到客服响应,同意修改 UPS 包裹收货地址,但要求提交完整的地址,并说明只能尽力帮忙修改;
2012-07-27 08:03:提交完整地址给 Google 客服;
2012-07-27 08:14:Google 客服帮忙完成了 UPS 包裹收货地址的修改,UPS 追踪显示地址已修改;
2012-07-27 23:05:UPS 显示包裹已完成配送;
2012-07-28 09:00:登陆 CUL 发现包裹已入库,重2磅,于是提交订单,给了5元小费,支付宝付款,然后订单状态就一直显示为处理中;
2012-07-31 09:00:登陆 CUL 发现总算发货了,订单中能找到 EMS 追踪号。但我傻了吧唧地去 EMS 网站上查了两天,都没有查到追踪信息;
2012-08-02 21:00:总算发现早期那个追踪号应该在 UCS 网站上查,查了一下,是7月30日 10:26 发货的,看来包裹在 CUL 仓库里呆了两天。
2012-08-06 11:09:接到快递电话,Nexus 7 到手。

由于中间回了趟家,也没有预计到转运到国内速度会那么快,于是2日到6日之间一直没有关注包裹的追踪情况。到手之后查了一下 EMS 网站,才发现转运的速度还不算慢(EMS的时间貌似都是本地时间):

2012-07-29 22:26:00 UC 纽约 到达处理中心
2012-07-30 21:00:00 UC 纽约 离开处理中心,发往中国 上海
2012-08-04 07:32:00 上海邮政速递物流大宗邮件收寄处 收寄
2012-08-04 10:51:09 上海邮政速递物流大宗邮件收寄处 到达处理中心,来自上海邮政速递物流大宗邮件收寄处
2012-08-04 11:30:00 上海邮政速递物流大宗邮件收寄处 离开处理中心,发往上海市邮政公司邮政速递局
2012-08-04 14:04:11 上海邮政速递物流大宗邮件收寄处 离开处理中心,发往国内出口
2012-08-04 16:34:00 上海市 到达处理中心,来自上海邮政速递物流大宗邮件收寄处
2012-08-04 16:58:00 上海市 离开处理中心,发往北京市
2012-08-05 09:27:41 北京市 到达处理中心,来自上海市
2012-08-05 12:58:42 北京市 离开处理中心,发往北京邮政速递上地分公司上地营运部
2012-08-05 15:10:50 北京邮政速递上地分公司上地营运部 到达处理中心,来自北京市
2012-08-05 15:54:48 北京邮政速递上地分公司上地营运部 安排投递
2012-08-05 16:14:00 北京邮政速递上地分公司上地营运部 未妥投
2012-08-06 07:26:37 北京邮政速递上地分公司上地营运部 安排投递
2012-08-06 11:37:00 北京邮政速递上地分公司上地营运部 投递并签收

最后总结一下几点经验:

  • 全部时间:11天14小时
  • 全部费用:199$(8G版)+13.99$(美国国内运费)+ 75¥(CUL转运费) + 5¥(CUL小费),整体算下来比淘宝代购便宜 200+
  • 信用卡:工商银行信用卡网银支持开通/关闭境外无卡支付功能,支付前开通,扣款完关闭,海淘比较安全
  • CUL 服务:比较差,包裹入库、发出没有邮件通知,只能人肉登陆去看;QQ 客服常年离线;微博官方账号无响应。但从整个转运结果来看,也还算靠谱。更新:最近很多人抱怨CUL不靠谱,所以我这里也不建议大家用了。
  • CUL 羊毛:邀请用户注册,下单付款后,邀请人和被邀请人都能获得 20 积分,以后下单能抵 20 元运费。我的 CUL ID 是 solrex,也可以直接点击这个邀请链接注册,你懂的!

第一笔博客收入

博客上放百度网盟广告2318个月,终于迎来了第一笔联盟分成103.37元。我还以为是补充医疗保险的理赔款,因为款额不对纳闷了半天,刚刚登邮箱看到上周广告报表才突然想起来。我的博客总算赚钱了!

另外,感谢部分同事的关心:我注册百度联盟是早在进百度之前的事情,而且由于存在利益相关,我一年多前就将个人博客投放百度联盟广告报公司备案了,公司并没有禁止这种行为。

上图,都是一分一毛地攒出来的啊!

第一笔博客收入

妄谈时间序列表格型大数据系统设计

一直在特定领域的分布式系统一线摸爬滚打,曾取得一些微不足道的成绩,也犯过一些相当低级的错误。回头一看,每一个成绩和错误都是醉人的一课,让我在兴奋和懊恼的沉迷中成长。自己是个幸运儿,作为一个 freshman 就能够有机会承担许多 old guy 才能够有的职责。战战兢兢、如履薄冰的同时,在一线的实作和思考也让我获得了一些珍贵的经验,却直至今日才够胆量写出来一晒。这篇文章标题前面是“妄谈”两字,所持观点未必被所有人认可,我姑妄言之,有心之人姑听之。若有些友好的讨论,亦我所愿也。

我做的虽然也是分布式系统,却不够胆去讨论通用分布式系统的设计原则。因而这篇文章的主题限定到一个特定领域的分布式系统设计,这样即使别人有疑惑,我也可以把 TA 拖到我擅长的领域打败 TA :)

既然要限定,我们需要给这个系统下个定义,就有必要解释一下标题。

大数据(Big Data),这是由于分布式系统和云计算的风靡而变得很火的一个词。那么多大的规模才算大数据呢?目前没有定义,但要讨论这个问题,就必须给个确定的范围。在本文中,这个范围暂时定义为 10TB~1PB 的数据量。为什么是这个范围?我的理由是,小于 10TB 的数据规模有比较多的可选方案;大于 1PB 的数据规模,讨论的意义不大,下面会谈到。

表格型数据,是指数据是有结构的,类似于关系型数据库中的表,但不是关系型,至少不是完整的关系型。在大数据的范围内,不能说完全没有关系型的需求,但这个需求实际上是很小的。因为关系操作的复杂性,使得其在大数据上的性能非常差,此类的需求往往使用数据冗余等其它方式来实现。是性能原因,而不仅是实现难度导致它不被需求。

时间序列数据,是指数据是按照时间产生的,跟随时间而变化的分析型数据。其实分析型数据一般都是时间序列的。与操作型数据不同,在分析型数据中单单一条记录的信息是很小的,只有与其它数据进行对比、组合、分解,这条记录才会体现出其价值。

在这些限定词下,这个系统的用途就比较清楚了。它可以被用到很多地方:比如网站访问统计(Google Analytics 和百度统计)、APP 的数据统计、集群服务器状态收集、在线广告的展现和点击量等等。它是一个数据仓库,但庞大于一般的数据仓库,功能需求却少于一般的数据仓库,而且很强调性能。在这个级别上,我还没看到成熟的开放系统解决这个问题(也许我是孤陋寡闻),基本上每家都是自己实现,所以它也更值得讨论。

由于不知该如何系统地探讨,我下面只能把自己发散的思维整理为一条条简单的原则,可能会有很大的跳跃性。但是,谁在乎它连不连贯呢?

latency 对你很重要时,不要采用分层设计,优化做得越底层越好

事实上,对于有兴趣做这样一套系统的公司,latency 都很重要。因为 latency 不重要时它们完全可以使用 HBase。而且,当你有超过 1PB 数据时,你会发现其中很大一部分的 latency 不重要,那剥离出来这部分,用 HBase 吧。

在这个数据量上,必须采用分布式的实现方案。但不要为了系统逻辑的清晰而做存储层与应用层分离的实现,像 BigTable 那样。因为 locality 可以显著地降低 latency,做了存储层和应用层的分离,那你就放弃了很多可以优化的地方。否则你必须破坏分层的封装性,像 Facebook 对 HBase 做的那样。

MySQL 不是一个选项,分布式 MySQL 也不是,分布式 KV 也不是,做自己的系统吧

总会有人问这些问题:为什么数据库(分布式数据库、分布式 KV 存储)不能用于这样的场景?我只能说,原因关键是上面三个形容词:时间序列数据、表格型数据、大数据。此外可能还要加上性能、成本等其它因素。

问出上面这个问题的人,其实都可以去用数据库或者 KV 系统,大部分情况下他们的需求会被满足。因为实践过且不满足需求的人,不会问上面这个问题,所以自己找出为什么吧,更容易些。

索引很重要,但要注意控制粒度

上面说过,对于分析型数据而言,单条记录没那么重要,所以快速地获取一条记录不会成为此类系统的目标,而且索引会降低数据更新的性能。但是能不要索引吗?开玩笑,那你怎么查询!索引必须要有,但要考虑到业务场景,做到合适的粒度。所谓合适的粒度,就是能快速获得目标数据而又不至于影响数据更新的性能。

内存很重要,能省则省,能用就用完

内存的重要性大家都明白,但很少人能真正理解。能省则省——说的是不要用浪费空间的数据结构;能用就用完——说的是在保证服务器能正常工作的前提下,使用最多的内存。

IO 很重要,做任何能减少 IO 次数和数据量的事,如果要折衷,选择优化次数

对于分析型数据而言,CPU 向来不是瓶颈,IO 才是。做任何能减少 IO 次数和数据量的事,比如各种缓存(块缓存、索引缓存、请求结果缓存),比如数据压缩。如果在减少 IO 次数和减少数据量上做折衷,选择减少 IO 次数,除非这会导致数据量爆炸。

即使没分层,也不要随机写

即使能直接访问到本地文件系统,也不要使用随机写,不要向一个文件中插入内容,而是将更新与基准合并写入另一个文件。这样性能更高,真的。

支持 CRUD?不,只支持 CRA,A for aggregate

其实很多数据都可以表示成时间序列型数据,例如 MySQL 的数据表内容完全可以用时间序列的操作日志来表示,这也是 Twitter 首席工程师 Nathan Marz 提倡的,他说有 CR 就够了。虽然我没有那么极端,但是朋友,我们处理的就是时间序列数据啊,所以我们完全不需要 UD。增加 A 的原因是,聚合会减少数据量,聚合会提升查询性能。

一定要压缩数据,选择一个合适的压缩算法

原因很简单,这能够减少 IO 数据量。但不要傻乎乎地压缩整个文件,跟 BigTable 学,分块压缩。考虑到对数据更新和读取的性能偏重不同,选择对自己合适的压缩算法。因为列存储的压缩比一般而言更高,所以

如果能做列存储,就做吧

尽量分离更新和读取的压力

如果数据需要做清洗,可以聚合,那么在导入系统前做这件事,而不是让承担查询压力的系统做这件事。

实时性没那么重要,批量更新会让你更轻松

如果能接受一天的延迟,就每天一批;能接受一个小时的延迟,就不做分钟级更新。更新次数越少,预聚合效果越好,数据量越小;更新次数越少,一致性越容易保证;更新次数越少,事故处理越从容。实时更新的话,很多事情会变得非常复杂,尤其是故障处理。

用数据冗余实现关系型需求或者高性能需求

如果有关系型运算需求,一定要逼 PM 改掉。实在改不掉,在导入系统前(或者过一段时间后)计算得到结果,直接导入到系统中。高性能需求也是这样,提前在系统外聚合好再导入,让系统做最少的事情它才能更快。

分布式架构?不重要,重要的是可靠性

至于采取什么样的分布式架构,其实不重要。只要它能实现 IO 的(大致)负载均衡,并且可靠就够了。另外,值得一提的是,如果想实现中心机,选举,分片自动分裂、合并、迁移等 fancy 分布式技术,首先想想自己公司是不是行业领导者。Perfect is the enemy of good. 对于很多人来说,Zookeeper 足够了。

好的运维工具,比完美的设计更靠谱[20110222]

在完成一个大规模系统时,往往很难做到完美,尤其是当这个完美设计很复杂时。事实上 fancy 的功能也不是不能折中,例如可伸缩性通过运维工具而不是内建于系统中实现,其复杂度会大大下降,稳定性会大大提高。所以如果没有足够的能力或者时间去实现一个完美的系统,不如好好地去做一些简洁方便的运维工具。

借鉴别人经验

这个不用我解释了吧。找一切可利用的信息,和一些人讨论,自己做决定。 :)

(暂时写到这里,但我可能会更新这篇文章,当我想到更多时。)

史蒂夫·乔布斯传

我一直羞于去崇拜某个人,死去的或者正在死去的。我总害怕他做了或者将要做一件我并不认同的事情,这就好比冬至吃饺子时吃到一块脏兮兮的硬币,不是每个人都能享受这种运气。所幸的是《史蒂夫·乔布斯传》的作者所持角度还算客观。读完这本书之后你不免去崇拜乔布斯,但又会有所保留。你会想:我真希望能达到他那样的成就,却不想成为他那样的人。

每个人做事都有自己的方法,但乔布斯采用的那种,恐怕要被归到异类中去。只有他那种天生的特质,结合独特的人生经历,还有过早成功的光环,才能造就那样强大的现实扭曲力场。跋扈如他那样,做一个打工仔的话,恐怕不出半月就要卷铺盖走人了。

在他的传奇一生中,有一股认真劲儿让人不能不佩服。包括他对事件策划、产品外观、字体、广告、包装,甚至于名片美学上的执念,无不让你觉得这家伙真倔,真难办。但当你转了一圈儿,看到那么多平庸的玩意儿时,你就会想:哦,这家伙还是有一定的道理的。

除了认真之外,他还有一点让我非常羡慕的,就是不屈于现实。软驱、吸入式光驱、玻璃屏幕、拉丝铝外框,到处都透着不妥协。但奇迹的是,那些硅谷工程师们居然办到了!这一点也被我归结为我们信息技术落后于硅谷的原因,奇思妙想得不到尊重,创新的阻力远大于推动力,有批判性,无建设性。

如果在这里赞扬他,能够列出的点还有很多。看完这本传记后,我甚至有去买苹果产品的冲动。但就像前面所说,我,一个平庸的家伙,对乔布斯的革命性的设计和产品,一直没有足够到转化为购买力的欣赏。创新是一样高投入的工作,必须有高回报的支持,而这依赖于掏腰包的人的欣赏和价值认可。对于某个产品来说是这样,对于一个公司来说,同样如此。可惜的是,很多公司的老板更像我,而不是乔布斯。

关于自动分裂的思考

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

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

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

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

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

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

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

技术人员的眼界

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

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

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

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

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

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

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

在百度的第一年

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

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

我加入的项目组当时致力于设计一套高可用、会话一致、高性能、易用的海量分布式类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 的程序无法编译通过。

淘宝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: 之前将楚材错当成了阳振坤老师的花名,已更正之。

入手国产安卓机华为U8800感受

我总算理解了为什么别人说安卓用户三大爱好是:刷机、重启、换电池!

作为一个打拼在互联网界的民工,我不喜欢花很多钱在电子设备上,但又不想太落后于时代。在使用黑莓 8700 的这一年半中,对它的感觉经历了 “哦,这是一个很便宜的智能机” 到 “唉,这还能算是智能手机吗?” 的变化。用到目前,黑莓 8700 让我满意的地方就唯一剩下它可以独立为来电和短信铃声分配不同的提醒模式(振动、响铃、音量或LED闪),设置成为自定义的提醒profile。这对一天要24小时开机接收服务器告警短信的西二旗孩纸很有用!

于是乎,我就开始找性价比高的 Android 手机。其实之前拜托叶蒙买过一个内购的华为 U8220(坛子上一般称大内),女友一直在用;兼之电信送了一个华为 C5700,我也一直在用。不得不说使用后我对华为家终端的信任度大幅上升,因此第一考虑的国产品牌就是华为家了。

U8800 是从新蛋上买的,裸机价 1699(找代下单便宜20),移仓+快递用了 6 天——新蛋物流伤不起啊。

我在 U8800 坛子里逛了不少次,给我的第一印象是如果能忍受自带应用的话,不用刷机。回想起 U8220,貌似带的应用也不多,一直也没刷机提权啥的。谁知拿到 U8800 才发现,我靠,带了二三十个垃圾应用

于是我想到了在坛子里的第二印象,提权之后删掉自带的应用就干净了。于是我就 recovery,提权啥的,总算把垃圾应用删完了,这下该能用了吧。然后我发现,咦,Gtalk 呢?电子市场呢?同步 Gmail 联系人的地方呢?都去哪儿了?然后就开始搜啊搜,有人说得装 Google 服务包,结果下了N个装上都不管用。最后我搞明白了一个道理:行货手机自带系统是不能用 Google 服务的,除非有人专门弄出来一个针对它的 Google 服务包。坑爹啊!你们坛子里讨论这个 ROM、那个 ROM,肿么没人说联通定制 ROM 不能用 Google 服务呢?如果这样我还费劲提个屁权啊!

于是只好刷机了。其实华为家有一点比较好的地方,就是如果某款手机也在国外或者香港销售的话,找一个标准干净的官方 ROM 比较容易(垃圾的东西只敢用来欺负自己人),而且使用的是官方的比较安全的固件升级方法。比如 U8800 就有海外版 ROM,从华为的官网上能下到,下来之后直接看帮助文件就可以,不用从论坛上找什么安装方法。

于是折腾了将近 5 个小时,这款手机总算可用了。其实时间大部分都花在前面“不想刷机”的状态上了,到最后决定刷机倒反而很快了,除了下载固件比较慢。

我真的无法理解这些国内厂商,用别人开源的手机操作系统,却要把别人的服务全抹了去,这是怎样一种忘恩负义!还有一个关键是,你又无法提供更好的服务!拿我来说,为什么一定需要 Google 服务?因为我的联系人全在 Gmail 里,我的日程安排是通过 Google Calendar 双向同步到电脑和手机。光这两点,我就必需连接到 Google 服务。我敢把联系人全部存到开心网吗?我敢用手机直连我公司的 Exchange 服务器吗?

这货还是Kindle吗?

自从大家了解到 Kindle 的系统是 Linux 后,无数仁人志士前赴后继地尝试以各种方式 hack 这个系统,对于我等用户来说真是一大幸事!

好久没有跟进电纸书论坛,居然不知道神器 FBReader Kindle 版 —— fbKindle 的发布。mobileread 网站的原帖中,已经给出了安装和配置修改方法。不过我是遵从这篇帖子中的步骤,无它,摘要读起来更快。

没用过的朋友可能不太了解 FBReader,它是一个开源的跨平台电子书阅读器,支持很多种格式,包括我们常用的 GBK 编码 txt 文档以及 epub 格式。由于 epub 格式电子资源很多,以前我还纠结于为了这些资源要不要装多看系统。现在有了原生的 fbKindle,从 epub 支持这点来看,多看是没有什么必要装了。

此外,还有国人开发的Kindle 原生系统拼音输入法,可以支持在书中或者浏览器中输入中文。虽然我没有安装成功,但我对作者的努力是相当的佩服!

Kindle 原生系统拼音输入法
图片截取于输入法作者相册

在最近的使用中,我也发现了一个 Kindle3 离线读网上文章的小技巧。用 pdfcreator 将网页打印成 A6 幅面的 pdf 文件,并且在页面设置中设置四周页边距为 0(Chrome不支持,Firefox/IE可以),上下不打印网址、页数等信息。这样的 pdf 文件在 Kindle3 上会有比较好的展示效果(字体很小时顶多需要横屏),并且也有比较好的打印效果。由于 pdfcreator 还可以设置自动保存位置和自动文件命名规则,用起来还是挺方便的。

当然,如果你是可注册 K3-wifi 版用户,SendToKindle 浏览器插件可能更有用。

在越来越多有趣的事情发生在 Kindle 上之后,真期待这个生态圈将会变成什么样子。

有没有这样一种手机应用?

Google 的 Jeff Dean 在演讲中提过:对一套系统来说,每年典型的事故率会是这样的:1. 1-5% 的硬盘会坏掉;2. 全系统宕机至少两次。以前没有深切体会,现在我会说,i cannot agree more!

当你负责的系统线上服务器达到百台以上规模时,你就会发现这个系统频繁会出各种各样的问题:死机,是最频繁的,会有各种各样的原因导致死机,内存占满、CPU耗尽、硬盘故障,还有你永远不知道的原因;硬盘挂掉,如果对没有做 RAID 的系统来说,这是一个灾难,对于做 RAID 的系统来说,这是一个事故,不过也有可能是一个灾难;文件损坏,在脆弱的硬盘上面也是有可能的;存储空间耗尽,未必是所有空间都耗尽,但可能程序问题导致某个分区被写满;数据流延误,作为一个系统总有上下游吧,只要有一个地方卡住了,下面的数据流也就停了。

所以我想任何一个互联网企业都会支持异常监控报警机制,最典型的做法应该就是邮件和短信。虽然我不是运维人员,但是自己写的系统出了问题,也脱不了干系。于是报警短信就成了生活旅行必备之调剂品。

但是,报警多了也郁闷啊!为了尽早地发现系统问题,往往一台机器上会布多种监控,CPU、内存、硬盘、进程、数据流、文件,乱七八糟的一个不能少。这样以来误报率就很高,比如CPU IDLE可能一下子压到0但是迅速回升了,这没啥问题,但是报警短信是照发不误。更别说服务器多了,真出事儿的可能性也大,所以每天接个几条到几百条短信,都是很正常的事情。

让人郁闷的不仅仅是报警短信多,还有和平时短信分不清。时间长了,有些报警会出现在什么时候、会以什么样的频率出现,自己心里都有底了。有的短信不看,光凭规律就知道是啥问题,应该以什么优先级处理它。但是掺杂进平时短信就不一样了,这规律就被打乱了,只好每条必看。在无奈之下,现在我只好用一个手机专门收报警短信。

说了那么多,我就是想知道:现在智能手机那么多,有没有哪款智能手机或者APP,像邮件客户端一样,支持根据短信特征将短信分发到不同的文件夹中,并且可以给不同文件夹分配不同铃声或者干脆没铃声?

Infobright 数据仓库

最近有部分工作涉及到了 Infobright 数据仓库,就浏览了一些相关的资料,感觉很受启发。下面写一些感想,如有谬误,还请指正。

简单的来讲,Infobright 主要有下面的一些优点:

1. TB 级的数据存储和高效查询。大数据量存储主要依赖自己提供的高速数据加载工具(百G/小时)和高数据压缩比(>10:1),高效查询主要依赖特殊设计的存储结构对查询的优化,但这里优化的效果还取决于数据库结构和查询语句的设计。

2. 高数据压缩比,号称一般能够达到 10:1 以上的数据压缩率。高数据压缩比主要依赖列式存储和 patent-pending 的灵活压缩算法。

3. 与主要 BI 分析工具的兼容性。兼容性这点主要依赖与 MySQL 的集成,作为 MySQL 的存储引擎自然地能够保证与 BI 分析工具的兼容。

除了上面的优点外,它也有一些限制:

1. 不支持数据更新。这使对数据的修改变得很困难,这样就限制了它作为实时数据服务的数据仓库来使用。用户要么忍受数据的非实时或非精确,这样对最(较)新数据的分析准确性就降低了许多;要么将它作为历史库来使用,带来的问题是实时库用什么?很多用户选择数据仓库系统,不是因为存储空间不够,而是数据加载性能和查询性能无法满足要求。

2. 不支持高并发。虽然单库 10 多个并发对一般的应用来说也足够了,但较低的机器利用率对投资者来说总是一件不爽的事情,特别是在并发小请求较多的情况下。

3. 没有提供主从备份和横向扩展的功能。如果没有主从备份,想做备份的话,也可以主从同时加载数据,但只能校验最终的数据一致性,这会使得从机在数据加载时停服务的时间较长;横向扩展方面,倒不是 Infobright 的错,它本身就不是分布式的存储系统,但如果把它搞成一个分布式的系统,应该是一件比较好玩的事情。

在架构方面,Infobright 给我展示了不少新想法,算是受益颇多吧。首先是按列存储,然后把列数据切成小块(Data Pack),进行压缩和统计(DPN, Data Pack Node),然后再对多块数据之间进行知识关联(Knowledge Node),最后对整个表形成知识网格(Knowledge Grid)。虽然说 Infobright 没有提供索引结构,但它 Knowledge Grid 中的 Numerical Histogram、Character Map 和 Pack-to-Pack 结构,怎么看都和 bitmap 索引脱不了关系。只是它的组织形式不像传统数据库中的索引罢了。

其实我们在设计类似的分布式表格系统时,也可以实现类似于 Knowledge Grid 的结构。这个结构未必跟 Infobright 的一样,但是如果在压缩的基础上,基于系统查询模式(分布式系统的查询模式一般相对简单,复杂的也做不来),存储一些辅助的块统计信息以及块之间的关联信息,对于减少查询的资源消耗,提高查询效率会非常有帮助,这也正好是针对分布式表格系统很难建立索引这一缺点的弥补。

参考链接:

这篇文章对 Infobright 及其安装方法进行了基本介绍,最后的一个查询速度对比有些夸张(105:1),我觉得这可能跟查询条件正好能匹配上 Knowledge Grid 中的信息所致;这个博客很有趣,从 2010 年 3 月 8 日到 5 月 8 日之间的文章全是 Infobright 相关的,写的还是挺详细的;Brighthouse: An Analytic DataWarehouse for Ad-hoc Queries 是一篇相关的 08 年 VLDB paper;此外官网上的白皮书不能直接下载,但在搜索引擎中能搜到一些。

入手 Kindle

入手 Kindle 有一个多星期了,下面写点儿经验,希望对别人有用。

关于购买:

买 Kindle 3 (wifi) 的想法上个月就有了,犹豫了很多种购买方式,后来决定在淘宝上下手。9 月 21 号在五人行数码下了个单,但据说要很长很长时间才能拿到货(排队有 400+),就和店家协商退钱了事。就这样一直等到十一国庆假期,也没有找到合适的商家。国庆期间在豆瓣上看到一家说有现货的,国庆95折,1299 块,就买了一个。

第二天店家打电话说她的 Kindle 无法注册,问我还要不?我没有犹豫很久,就说还是要吧。第三天货到了,又联系店家,店家说愿意退款 100 作为无法注册的补偿。这样一想,还不错哦,就付款了。相当于 1199 买了个无法注册的 Kindle 3 wifi 版。

结果第四天就发现论坛上关于这批无法注册 Kindle 的话题已经热起来了,各种争论不休。大部分商家的做法是低价销售无法注册版的 Kindle 3,普遍降一百到两百不等。最便宜的是我在 Twitter 上看到的一个店家,卖到 1050,和我的入手价差 150 呢。唉,怎么说呢,心中还是有点儿小郁闷,要是再多观望那么两三天,就好了——不过这也是无法预料的事,不提了。

关于皮套:

我买的不是 Kindle 3 专用的皮套,是一般 7 寸电子书的皮套,图便宜。用起来感觉还行吧,反正主要是起一个保护屏幕的作用。

关于阅读:

各种评测啥的在hi-pda、多看等论坛上能找到很多,我就不赘述了。从个人的体验来讲,感觉还是挺值的,看书很舒服。而且我女朋友特别喜欢,已经抱着 Kindle 看完了阿西莫夫的《基地》系列。我主要是坐地铁时候用,也看了两本书:《民主的细节》和《1Q84》。

关于多看:

我尝试了一下多看系统,后来恢复出厂设置给删了,就没有再装。从个人来讲,我对多看系统主要有两点意见:1、对 mobi 格式支持的不好(封面、分节等显示不太好);2、没有 web 浏览器。虽然多看对中文 txt 支持较好,但是我用 mobipocket reader 转一下 txt 到 prc 格式也不复杂(想让 mobipocket reader 支持中文 txt 分段一般需要加空行,一句命令 sed 's/$/\n/g' file 就可以解决)。剩下对我来说,貌似没有什么用多看的理由——哦,中文输入法可能算是一个,以后再说吧。

关于 hack:

使用原生系统的话,还需要做些 hack 工作,主要有两点:1、修改 locale 为 zh-CN(可以加 .utf-8 或者 .gbk,如果想看 txt 的话),方法很多论坛上有流传;2、无法注册版本需要越狱一下,才可以支持显示用户名、创建 collection、修改时间等操作,俗称“假注册”,越狱方法见这里

关于图书管理:

图书管理的话,我用的是非常原始的方法:同步目录。在电脑上建一个 kindle 目录,使用 synctoy 将这个目录与 kindle 根目录同步。这样我下完图书就扔到电脑上的 kindle 目录里,连 kindle 的时候打开 synctoy 同步一下即可。

此外有一些比较好的图书管理软件,比如 calibre,但我觉得太重量级了。不过 calibre 还是有用处的,比如抓取新闻和 web 服务器功能。比如想看《南方周末》时,用 calibre 抓取下来做成 mobi 电子书,然后在 kindle 上用 wifi 访问 calibre 启动的 web 服务器,将做好的电子书直接下载到 kindle 里,少了 usb 连接的麻烦。

哦,越狱还有一个好处,可以获得 kindle 系统的 root 权限,当然也可以 scp 拷贝文件了,而且还有 vi、gdb、ssh 可用哦!里面看起来就是一整个简化过的 Linux 操作系统。

Linux kindle 2.6.26-rt-lab126 #5 Wed Sep 15 19:25:13 PDT 2010 armv6l unknown

关于图书资源:

我测量了一下,Kindle 3 wifi 的屏幕尺寸大约是 90mmx122mm,如果自己做 6 寸的 pdf 电子书的话,最好将页面大小调整为90mmx110mm,这样下面留12mm来显示阅读进度,pdf就不会变形很多了。 hi-pda 论坛的 e-ink 版面有很多资源(电子书、配置方法等),在入手前和入手后都可以常去逛逛。

关于升级 3.0.2:

我刚才升级了一下,感觉还是有点变化的。比如以前 pdf 翻页时候,连续按下一页或者上一页,响应会比较慢,升级之后速度会快一些。剩下的改进还有待观察。

悲剧的 MSN Space

MSN Space 总算倒了,所有用户都要求被迁往 wordpress.com,或者下载备份文件。话说我第一个用得顺手的 blog 还是 MSN Space,也用了很长的时间,不免觉得有些悲凉。

今天有同事问我,MSN Space 为什么混到这个地步?我说,本来 MSN Space 还凑合可用,但是每一次改版、每一次改名,都让你觉得更加难用。产品能做到这个份上,也真是不容易,不过无独有偶,MSN 也算得上跟它的绝配了!不知道什么时候能看到 MSN 整体搬迁用户到 AIM 或者改版到 XMPP?

哦,我又忘了,国内还有个飞信呢!不愧是外包给 MSN 做的产品,看看现在的飞信 4.x,我好怀念飞信 3.x 啊!

这件事情让我感兴趣的一点是,我总算看到一个可能,可以将以前在 MSN Space 上的评论,合并到现在的博客中了。或许需要自己写个小工具,假期可以尝试做一下。

一些论文相关 LaTeX 技巧

最近在写毕业论文,记录一下使用 LaTeX 排版时的一些笔记:

1. 正文英文使用 Times * 字体:

\usepackage{times}

2. 自定义列表样式

\usepackage{enumitem}
% 例:缩略语列表,缩略语大写,全称左侧缩进对齐
\begin{description}[font=\textbf, labelindent=2em, leftmargin=6em, style=sameline]

\item[CA] Central Authority.

\end{description}

% 例:列表标签使用 *) + 元素中段落首行缩进
\begin{enumerate}[label=\alph*)]
\setlength{\parindent}{2em}

\item 测试标签

测试段落

\end{enumerate}

3. 想使用列表但不希望列表中段落整体有缩进

% 自定义 minisection 命令,小标题,无编号
\newcommand\minisection[1]{\vspace{2ex}{\heiti #1}\vspace{1ex}}
% 例
\minisection{1)测试小标题}

4. vim 中使用 gqap 命令对中文文本格式化

下载 vim 插件:http://info.sayya.org/~edt1023/vim/format.vim

5. 按行方向合并单元格

\usepackage{multirow}

6. 拼凑中文生僻字

\hbox{\scalebox{0.4}[1]{王}\scalebox{0.6}[1]{莹}}

7. 为插图加框

\fbox{\includegraphics[width=0.9\textwidth]{figname}}

8. 设置 pdf 属性,设置参考文献链接和图片链接颜色为黑色

\hypersetup{linkcolor=black, %
            citecolor=black, %
             pdftitle={Title}, %
            pdfauthor={Name}, %
           pdfsubject={Subject}, %
          pdfkeywords={Key words}}

9. 对某页分栏排版

\usepackage{multicol}
% 分两栏
\begin{multicols}{2}

不可用于浮动环境。

\end{multicols}

10. 中文 LaTeX 编译 Makefile(注意缩进应替换为制表符)

ARTICLE=filename
ARTICLE_SRC=$(ARTICLE).tex $(ARTICLE).bib CASthesis.cls CASthesis.cfg
IMAGES=

all: article

article: $(ARTICLE).pdf

$(ARTICLE).pdf: $(ARTICLE_SRC) $(IMAGES)
    latex $(ARTICLE).tex
    bibtex $(ARTICLE)
    latex $(ARTICLE).tex
    gbk2uni $(ARTICLE)
    latex $(ARTICLE).tex
    dvipdfmx $(ARTICLE).dvi

clean:
    rm -f *.aux *.toc *.lon *.lor *.lof *.ilg *.idx *.ind *.out *.log *.exa
    rm -f *.nav *.snm *.bbl *.blg *.spl *.lot *.bak *~

distclean: clean
    rm -f *.pdf *.dvi *.ps

11. IEEE 投稿 LaTeX 编译 Makefile,主要处理嵌入字体和 pdf 版本问题(注意缩进应替换为制表符)

ARTICLE=filename
ARTICLE_SRC=$(ARTICLE).tex

#DVIFLAGS=-t [letter/a4]
DVIFLAGS=
# To avoid fonts and pdfinfo problems with the pdf file.
#PS2PDFFLAGS=-sPAPERSIZE=letter -dCompatibilityLevel=1.4 \
-dPDFSETTINGS=/prepress -dMaxSubsetPct=100 -dSubsetFonts=true \
-dEmbedAllFonts=true
PS2PDFFLAGS=-dCompatibilityLevel=1.4 -dPDFSETTINGS=/prepress \
-dMaxSubsetPct=100 -dSubsetFonts=true -dEmbedAllFonts=true

all: article

article: $(ARTICLE).pdf

$(ARTICLE).pdf: $(ARTICLE_SRC) $(IMAGES)
    latex $(ARTICLE).tex
    latex $(ARTICLE).tex
    dvips $(DVIFLAGS) $(ARTICLE).dvi
    ps2pdf $(PS2PDFFLAGS) $(ARTICLE).ps

test: $(ARTICLE).pdf
    @echo "****************************************************************************"
    @echo "** IMPORTANT: PDF version should be 1.4!!!                                **"
    @echo "****************************************************************************"
    @pdfinfo $<
    @echo "****************************************************************************"
    @echo "** IMPORTANT: All Type 1 and Type 1C fonts should be embeded!!!           **"
    @echo "****************************************************************************"
    @pdffonts $<

clean:
    rm -f *.aux *.toc *.lon *.lor *.lof *.ilg *.idx *.ind *.out *.log *.exa
    rm -f *.nav *.snm *.bbl *.blg

distclean: clean
    rm -f *.pdf *.dvi *~ *.ps

我的京东换货经历

京东上也买过不少东西,这两天第一次体验了京东的售后客服,如实记录一下经历:

2010 年 4 月 2 日 22:09,在京东下了买电熨斗的单;

2010 年 4 月 3 日 08:39,收到订单已到达自提点的手机短信通知;

2010 年 4 月 3 日 10:55,在石景山自提点付款提货(必须先付款才能检查物品),拿到货物检查发现有问题,电熨斗有明显使用过的痕迹。当场提出换货,被告知自提点只负责提货,不负责售后,需要自己回去与客服联系;

2010 年 4 月 3 日 11:44,由于网上订单仍然显示未完成,无法提交返修申请,于是拨打京东客服 400 电话询问了一下情况,客服小姐的回答仍是等订单显示完成后在网上提交返修单;

2010 年 4 月 3 日 15:56,网上订单显示已完成;

2010 年 4 月 3 日 16:26,提交返修单,返修类型:换货,问题描述:电熨斗被使用过: 1. 电熨斗水箱内有残留水珠; 2. 电熨斗尾部下方支撑脚有磨损和污迹; 3. 电熨斗中部塑料转盘有破损; 4. 电熨斗插头周围有污迹;

2010 年 4 月 3 日 16:54,收到返修已生成换货新单的手机短信通知;

2010 年 4 月 4 日 09:46,接到快递电话,更换了新品。

说实话,事情顺利地挺出乎我意料的。因为从论坛上还有其它网站看到很多对京东售后的抱怨,本来有做好长期抗战的心理准备,没想到那么容易就把问题解决了,这件事情的处理我还是很满意的。当然,这只是个案,我只如实记录个人遭遇,不参与对京东客服整体质量的讨论。