Facebook对memcached的提升

如果你翻阅过一些关于大型网站扩展(Scaling)的资料,那么你可能听说过一个叫memcached的东西。memcached是一个高性能、分布式的内存对象缓存系统。我们Facebook可能是世界上最大的memcached用户了。我们利用memcached来减轻数据库的负担。memcached确实很快,但是我们还要让他更快、更高效。我们使用了超过800台服务器,提供超过28TB的内存来服务于用户。在过去的一年里,随着Facebook的用户量直线上升,我们遇到了一系列的扩展问题。日益增长的需求使得我们必须对操作系统和memcached进行一些修改,以获得足够的性能来为我们的用户提供最好的体验。

因为我们有好几千台机器,每个都运行了几百个Apache进程甚至更多,最终导致到memcached进程的TCP链接有几十万个。这些链接本身并不是什么大问题,但是memcached为每个TCP链接分配内存的方法却很成问题。memcached为每个链接使用单独的缓存进行数据的读写。当达到几十万链接的时候,这些累计起来达好几个G——这些内存其实可以更好地用于存储用户数据。为了收复这些内存,我们实现了一个针对TCP和UDP套接字的每线程共享的链接缓存池。这个改变使每个服务器可以收回几个G的内存。

虽然TCP上我们改进了内存的使用效率,但我们还是转向了UDP,目的是让get(获取)操作能降低网络流量、让multi-get(同时并行地获取几百个键值)能实现应用程序级别的流量控制。我们发现Linux上到了一定负载之后,UDP的性能下降地很厉害。这是由于,当从多个线程通过单个套接字传递数据时,在UDP套接字锁上产生的大量锁竞争导致的。要通过分离锁来修复内核恐怕不太容易。所以,我们使用了分离的UDP套接字来传递回复(每个线程用一个答复套接字)。这样改动之后,我们就可以部署UDP同时后端性能不打折。

另一个Linux中的问题是到了一定负载后,某个核心可能因进行网络软终端处理会饱和而限制了网络IO。在Linux中,网络中断只会总是传递给某个核心,因此所有的接受软终端的网络处理都发生在该内核上。另外,我们还发现某些网卡有过高的中断频率。我们通过引入网络接口的“投机”轮询解决了这两个问题。在该模型中,我们组合了中断驱动和轮询驱动的网络IO。一旦进入网络驱动(通常是传输一个数据包时)以及在进程调度器的空闲循环的时候,对网络接口进行轮询。另外,我们也用到了中断(来控制延迟),不过网络中断用到的数量大大减少(一般通过大幅度提升中断联结阈值interrupt coalescing thresholds)。由于我们在每个核心上进行网络传输,同时由于在调度器的空闲循环中对网络IO进行轮询,我们将网络处理均匀地分散到每个核心上。

最后,当开始部署8核机器的时候,我们在测试中发现了新的瓶颈。首先,memcached的stat工具集依赖于一个全局锁。这在4核上已经很令人讨厌了,在8核上,这个锁可以占用20-30%的CPU使用率。我们通过将stats工具集移入每个线程,并且需要的时候将结果聚合起来。其次,我们发现随着传递UDP数据包的线程数量的增加,性能却在降低。最后在保护每个网络设备的传送队列的锁上发现了严重的争用。数据包是由设备驱动进行入队传输和出队。该队列由Linux的“netdevice”层来管理,它位于IP和设备驱动之间。每次只能有一个数据包加入或移出队列,这造成了严重的争用。我们当中的一位工程师修改了出队算法,实现了传输的批量出队,去掉了队列锁,然后批量传送数据包。这个更正将请求锁的开销平摊到了多个数据包,显著地减少了锁争用,这样我们就能在8核系统上将memcached伸展至8线程。

做了这些修改之后,我们可以将memcached提升到每秒处理20万个UDP请求,平均延迟降低为173微秒。可以达到的总吞吐量为30万UDP请求/s,不过在这个请求速度上的延迟太高,因此在我们的系统中用处不大。对于普通版本的Linux和memcached上的50,000 UDP请求/s而言,这是个了不起的提升。

我们希望尽快将我们的修改集成到官方的memcached仓库中去,我们决定在这之前,先将我们对memcached的修改发布到github上。

硬盘损坏+高可用性有感

最近一直觉得笔记本的硬盘有些问题,因为有时候系统会莫名奇妙地停止响应,硬盘灯则长亮,只听见硬盘在有规律的间隔发出声音。果然,先是本周周一的时候,我的个人用户配置文件损坏,导致无法登录Windows,接下来就是前天windows\system32\config\system系统配置文件损坏,导致无法启动系统。

估计是硬盘出现了坏道。为什么会这样,我本身在使用上也有很大责任,估计是这些原因:

  • 一直只有一个分区:因为笔记本硬盘本来就小,如果分成若干区,虽然划分地清楚,但难免有某个区总是闲置,另一个区却被塞满了,只有用一个分区才能最大化利用硬盘容量
  • 硬盘一直很满:硬盘上总是塞了很多乱七八糟的文档,以及一些电影、游戏等。比如WoW就占了7G
  • 从不检查清理:因为NTFS号称可以不用整理磁盘、又是日志文件系统,又号称可以自动修复,所以我也从来没有整理过,也没有检查过磁盘,甚至包括各种临时文档,也很少去清理
  • 从不备份:因为备份很消耗时间和空间,虽然IBM提供了Thinkadvantage全套的工具,但是我很少用,因为本身硬盘空间就不足。另外也就是根本不知道Windows里面哪些东西是关键,比如system32\config\system文件,如果这次没有出现这个事情,我估计这辈子不会了解Windows还用了这个文件存放系统配置
  • 长时间连续运行:我经常拿笔记本当服务器,会连续几天不关,用来下东西或者是挂机

幸好只是出现了坏道,我找了个移动硬盘盒就把东西备份出来了。那么接下来是恢复单个文件还是全部恢复整个硬盘的,但这都不是我想搞的,因为仅仅恢复单个文件,我不知道其他文件还有没有问题,还有没有可能出现其他坏道,而且很可能硬盘需要低级格式化。恢复整个硬盘也是很麻烦的,因为我需要重装很多东西。

最后我的选择是换一块硬盘,装个Ubuntu,虽然还是放不下预装Windows XP,因为那毕竟是唯一一份正版,而且Linux毕竟在桌面系统的硬件支持上还是较为落后的,比如不支持指纹识别等等。不过所幸现在Ubuntu做得已经够好了,而且我更相信Linux的文件系统。

高可用性

公司正好也要搞高可用性,因为公司网站需要对客户提供24小时不间断的服务。高可用性就是让系统在出现问题(宕机、维护等)的时候不至于全部瘫痪,用户依然可以使用系统。高可用性的特点是事先进行可以控制的投入,来防止出现事后不可控制的损失。高可用性的核心是冗余,也就是什么东西都有备份,多出来一个以备不测。这就意味着必须要付出很多成本来维持高可用性。

比如我这次的事故,如果我没有能成功备份出硬盘上的数据,那么我记录的一些关键信息、银行证书、过去搜集的很多资料和在做的一些东西都付诸东流,对我个人的损失不可估计。

但我可以有很多方案达到备份的目的:

  • Windows自带的系统还原,从来没用这个功能
  • IBM ThinkVantage 的快速备份和恢复(RRU)软件,功能十分强大,可以放在隐藏分区中,也可以放在单独的移动硬盘中,问题是备份消耗很多空间以及时间,这都是针对个人用户而言。可以完整备份、增量备份,但消耗巨大空间,以前我有一台IBM R50,硬盘只有30G,隐藏空间5G,如果备份一下就只有20G了,出厂设置的Windows基本上需要10G,所以对于硬盘的消耗是惊人的。如果要选择性备份,则无法了解Windows需要备份什么——看来移动硬盘非买不可了
  • 使用Norton的Ghost,以及世面上流行的一键恢复加上单独备份数据——还是要移动硬盘吧?
  • 另外买台机器……,可以做到使用上连续,如果数据不重要的——你得有钱买另外一台机器

基本上可以发现,要想冗余,想备份,必须有实打实的投入,物质上的人力上的。如果仅仅是物质上的投入,还能令人接受,但目前功能还都十分繁琐,备份过程相当长,而且备份介质的安全性也不是十分可靠。服务器的高可用性是需要大量人力物力投入的、做集群、做双机热备份等等。

无论是个人电脑,还是公司服务器,越是偷懒,就越可能出现大问题,这印证了墨菲定律

“如果一件事情有可能向坏的方向发展,就一定会向最坏的方向发展”。

只有把工作做在前面,才能防止事后的损失,这其实是一个管理层面上的问题了。

总结

其实我总期待更好的备份方案,我希望等将来网络速度更快、存储器更廉价的时候,可以适当推出网络备份,那时候应该我们基本上每人有一台电脑,甚至多数白领会有多台机器(自己的、公司的)。这种网络备份其实就是类似于如今Google Browser Sync的功能,结合另一种版本控制软件类似于Subversion,无须消耗本地的存储器容量,本地崩溃了还有主机上的备份,那么我们重装系统或者迁移到另一台机器,或者几台机器之间同步,就会变得非常方便。

Linux 上配置 Nginx + Mongrel cluster

Nginx不仅是一个小巧且高效的HTTP服务器,也可以做一个高效的负载均衡反向代理,通过它接受用户的请求并分发到多个Mongrel进程可以极大提高Rails应用的并发能力。下面介绍一下如何在一台服务器上配置Nginx + Mongrel cluster。

获得Nginx方法可以参考前一篇配置Nginx+PHP5 FastCGI的文章,这里我们假设大家是通过自己编译,并配置了默认的编译的参数,此处使用的是Nginx 0.5.x版。

配置Mongrel cluster

我们还需要获得Mongrel和其Cluster插件(用来方便得启动多个Mongrel进程),如下通过gem进行安装:

然后建立mongrel_cluster的配置文件。进入Rails应用即你的程序的根部目录(以下假设/usr/rails),运行:

然后mongrel_cluster便会在config目录下生成一个mongrel_cluster.yml,内容如下:

我们可以通过修改其中的设置来更改mongrel_cluster的运行,这个范例配置省略了一些其他参数,具体的参数的含义如下:

  • address: 指定绑定的地址
  • port: 指定mongrel_cluster所运行的mongrel进程从哪个端口开始绑定
  • servers: 指定同时运行多少个mongrel进程,结合port参数,就是表示port到port+servers-1(含)的端口将被使用
  • environment: 指定Rails运行的配置环境
  • user: 指定mongrel进程以什么用户的身份运行
  • group: 指定mongrel进程以什么组的身份运行
  • cwd: 指定mongrel运行的根目录
  • log_file: 各个mongrel进程的输出日志的位置,相对于cwd的目录,会在文件的扩展名之前加上各进程对应的端口号
  • pid_file: 各个mongrel进程的pid文件的位置,相对于cwd的目录,会在文件的扩展名之前加上各进程对应的端口号

大家可以根据自己的具体情况进行修改。以下是一个完整的mongrel_cluster.yml配置文件:

接下来便可以启动mongrel_cluster了,以下是控制mongrel_cluster的命令:

配置Nginx负载均衡反向代理

利用nginx的upstream指令配置哪些服务器需要进行负载均衡。在这里也可以说直接说告诉nginx mongrel_cluster在哪些地址和端口上,按照上面的mongrel_cluster的配置,在nginx中应该这样写:

upstream指令后面的mongrel指定了这批上游服务器的的名称,大家可以使用别的名字。每个server指令指定了一个服务器,server指令还支持别的参数可以设置重试次数和超时时间以及不同服务器的权重。

接下来配置nginx在接受哪些http请求时转发到mongrel cluster,因为nginx处理静态文件的速度远远高于mongrel,所以一般当请求的路径不存在的时候才将请求转发到mongrel cluster:

整个Nginx的配置文件的范例请参考:http://wiki.codemongers.com/NginxConfiguration

然后重启Nginx,配置便成功了.

Linux 上配置 Nginx + PHP5 FastCGI

Nginx是俄罗斯人编写的十分轻量级的HTTP服务器,以事件驱动的方式编写,所以有非常好的性能,同时也是一个非常高效的反向代理、负载平衡。其拥有匹配Lighttpd的性能,同时还没有Lighttpd的内存泄漏问题,而且Lighttpd的mod_proxy也有一些问题并且很久没有更新。

因此我打算用其替代Apache应用于Linux服务器上。但是Nginx并不支持cgi方式运行,原因是可以减少因此带来的一些程序上的漏洞。那么我们必须使用FastCGI方式来执行PHP程序。

下面是我成功地配置Nginx + PHP5 FastCGI的过程

继续阅读“Linux 上配置 Nginx + PHP5 FastCGI”

VirtualBox + gOS

不知多久之前听说了gOS,当时隐约记得有人误传说他是Google推出的操作系统,而且也看到了这个操作系统界面上有相当多的Google应用,后来澄清了其实gOS代表的是green OS的意思,和Google毫无关系(看他的主页底部的声明),主要的目的是为了一个廉价PC而设计的操作系统,所以整合了很多Web应用的快捷方式。

其实这些我都不关心,我最关心gOS的地方,其实是他使用了Enlightenment DR17(简称e17)作为他的窗口管理器和桌面环境(也就是把Gnome给替换掉了,其实早在GNOME 1.0的时候,它也整合了Enlightenment作为其窗口管理器,只是后来改成了Metacity),而且是一个非常轻量级的桌面环境。早在3年前,e17便有了一些测试版本,我当时在Fedora Core 3上安装了测试版,完全被其华丽的界面给震撼了,看过了e17的界面,我觉得完全不输给MacOS的任何版本。时隔三年,终于e17顺利发布,而且能够有一个Linux发行版预设了e17,尤其gOS又是基于现在我最喜欢的一个Linux发行版Ubuntu,实在是太完美了!

gOS with e17gOS为e17做了一套自己的最简化的主题。如果有哪位朋友装了gOS,一定要尝试一下e17默认的主题 “bling”。

唯一的遗憾是,gOS和e17在默认情况下对国际化的支持并不是太好,默认的桌面字体非常小 ,虽然很适合西方字幕,但对于中文字就不行了,默认的终端窗口也显示中文字体都是方块,不过经过适当的调整,还是可以正常显示的。

Ubuntu硬盘ISO安装一法

我在公司的机器没有光驱,有Ubuntu的Desktop安装盘也没有用,捣鼓了很久的硬盘安装也没有成功,因为Ubuntu Dapper使用了Live CD的启动方式。而我尝试过唯一能从硬盘ISO安装成功的就是Fedora Core 5了。
在研究了数周毫无结果就要放弃的时候,我突然灵机一动,觉得既然Ubuntu Dapper的CD就是一个完整的Linux系统,那何不直接拿来用呢?在经过了一整夜的研究之后,我终于用这种丑陋的方法,将Ubuntu Dapper装在了没有光驱的机器上。
具体步骤如下:

  1. 进入原有的Linux系统,先对系统进行分区,将目标系统的root分区准备好,并挂载在某个目录比如/target下,然后将其他分区准备好,相应挂载在/target下(比如/usr, /var, /home等)。在此不赘述,可以参考Gentoo磁盘准备的教程
  2. 使用

    mount -o loop -t iso9660 [ISO文件路径] [挂载路径A]

    将镜像挂载到某个目录下A,这里我使用的镜像是Ubuntu Dapper的desktop安装镜像。

  3. 然后再用

    mount -o loop -t squashfs A/casper/filesystem.squashfs [挂载目录B]

    ,因为Ubuntu Dapper的Live CD的文件系统内容都是以squashfs形式压缩在光盘的casper/filesystem.squashfs文件中的。

  4. 在刚刚挂载好的Live CD的目录B中,复制所有文件到/target下,

    cp -p -r B/* -t /target

    其中-p表示保留所有的文件权限信息,-r表示递归复制。这时候基本的系统内容就有了

  5. 如果有chroot,也可以调用:

    chroot /target /bin/bash

    (以下假设运行了这条指令,根目录转到了/target中)

  6. 准备/etc/fstab,起初的内容都是空的,根据自己分区的情况酌情编写,以下是一个参考例子:

  7. 准备引导文件,如果已经装了Grub或者Lilo,只需要修改一下相应的配置文件,比如,在grub的menu.lst中加入:

    title Load Ubuntu Dapper
    root (hd0,5) #假设是这里的安装根目录
    kernel /vmlinuz root=/dev/hda6 quite splash –
    initrd /initrd.img

  8. 如果是自己用无所谓建立新用户的话,将来可以直接使用root(初始密码为空),但必须在/etc/X11/gdm中,将gdm.conf和factory-gdm.conf中的AllowRoot设置为true。如果需要更加安全,则请建立相应的用户,但必须在/etc/sudoers中添加相应的用户,使用visudo来进行编辑。
  9. 编辑/etc/apt/source.list,添加一些ubuntu的apt源,具体就不列出来了。
  10. 调用dpkg-reconfigure -a对所有安装了的Debian包进行重新配置,尤其重要的是xserver-xorg和语言等等
  11. 之后重启,便可选择进入我们克隆出来的Live CD的系统了。这时候应该对系统进行一些设置,比如在系统中选择自己的语言,并使用新立得来更新系统,同时也可以将Ubuntu Live CD相关的一些包删除。
  12. 这时候,终于可以在机器上使用Ubuntu Dapper了。

唉……瞎折腾啊。

Linux各种文件系统比较

根据一些参考资料:

  1. Comparison of file systems – Wikipedia
  2. Linux File System Benchmarks
  3. Benchmarking Filesystems Part II

对主流的Linux操作系统所使用的文件系统ext2/3、jfs、reiserfs(v3/v4)、xfs作了一些评估,这里得出的结论是,

  • ext2由于没有日志记录功能,所以性能很好——当然也不够强壮和安全。
  • ext3性能最差(期待ext4)
  • jfs的平均CPU占用最低,如果系统运行于一个对CPU占用率有限制的系统中,考虑使用jfs
  • reiserfs的硬盘IO带宽占用率最低,如果系统运行于一个对于硬盘带宽有限制的系统中,考虑使用reiserfs,尤其是reiserfs v4。
  • xfs则是介于jfs和reiserfs之间,各种情况下都是比较好的选择。

回忆我的Linux使用历程兼介绍Fedora Core 5 Test

我以前实验过很多Linux分版,也经历过很多次失败,比如安装Linux不当导致硬盘分区丢失等等.虽然我现在还不能说是高手,但已经可以熟练应用Linux了. 如果按照使用的时间排序,

  • 1999-2000 (完全不知如何应用)
    • BluePoint (现在在网上还能找到它的地址,但已经打不开了,http://bluepoint.com.cn/)
    • Lindows(国外有一个商业Linux发行版,也叫Lindows,做的相当不错,现在改名叫Linspire了)
    • RedFlag 3.0
    • Mandrake 8.0 是我买的一套正版,还送了一套字处理软件,可惜好像被我扔了,这是当时唯一用起来比较舒服的分版,可惜当时不能上网,很多软件都没有,所以最后还是放弃了
    • RedHat 7.0
  • 2002-2004上 (开始熟练应用Linux)
      RedHat 9.0 有友好的安装界面,已经相当易用,当时同学买了一套正版,很多人都使用

    • VMWare WorkStation,虽然不是Linux,但不得不提,因为这个软件,使得我们不用重启就可以在Windows和Linux之间切换,而且在虚拟机的环境中实验Linux更加安全
  • 2004下 至今 (完全适应使用Linux进行开发作业,而且可以完全抛弃商业软件使用开源软件)
    • Fedora Core 2,3,4 Fedora Core系列一直都是我十分喜欢使用的,
    • Ubuntu Linux (Linux for human beings)秉承了Debian的丰富的软件包,容易使用,易用扩展,缺点是速度慢(不知道为什么)
    • Gentoo Linux 直接从源码进行构建,提供极大的自定义空间,也能更好地为自给的机器进行优化,可惜不适合初学者,我当时学习Gentoo的时候,安装编译整个系统花了好几天时间
    • Mandrake 2005 Mandrake也提供了很好的界面,提供和KDE更好的集成,现在是和Connectiva合并为Mandriva
    • Arch Linux 是针对i686优化的分版,十分简洁,系统的一些机制也很简单(简陋?)可惜安装并不容易,缺乏文档支持,并不如Gentoo好

现在我很期待Fedora Core 5的发布,因为他有很多振奋人心的特性,内核是针对i686优化的,使用了Gnome 2.12(界面效果更好),一些控件加入了动画特效,如淡入淡出,窗体的显示也可以与Windows媲美

而且加入了最近的FreeType,已经可以进行仿粗体的渲染,以前使用Linux经常头疼如何配置中文字体粗体、斜体的问题,都已经得到了解决了,同时直接提供了SCIM作为输入法基础,这是迎和广大宽字符集用户需求的。其他的新我,比如使用GCC4.4,并集成了ECLIPSE 3.1的支持。

据说开发小组打算把所有的包都针对i686优化,如果那样,真是不得了。