主持人:冯大辉,现任丁香园 (http://www.dxy.cn)网站CTO。曾历任支付宝架构师、数据库团队负责人等职。

架构师2
邝宇恒:在架构设计上,你们是否已形成某种设计风格或设计惯性?如何评价这种风格或惯性的影响? 侯震宇:设计风格不敢妄谈,不过我们在设计上确实存在一定的惯性思维。比如哪些类型的服务应该使用数据库,哪些不应该使用。惯性思维是建立在成功经验积累之上的,这在很大程度上简化了我们的设计工作并降低了引入新技术(这里指与习惯不同的技术)可能带来的风险。 但惯性思维往往阻碍了我们的技术发展。比如长期以来我们认为数据库的性能是相对比较差的,不足以满足很多业务的大压力的读请求,于是我们在一开始系统设计时就对服务架构设计了拆库拆表的支持,增加了系统的复杂度。这时惯性思维会让我们认为这样设计合情合理,不会进一步考虑是否有更好、更简单的设计。但在SSD引入可以解决高并发的随机读问题后,原先的系统认识已经不存在了,设计可以大大简化了,这时我们的惯性思维是必须打破的了。 所以作为一个架构师,我们要非常清楚,所有的设计都是建立在一些假设条件和系统规模之上的。当系统规模变得超出早期设计的假设条件,或者一些外部新技术打破我们早期的一些认识时(如SSD的引入对存储系统的冲击),惯性思维就必须打破。 邝宇恒:对某个特殊需求,是采用修改定制已有开源软件,还是采用从头自主开发的方式实现,应如何权衡考虑? 侯震宇:这个还是要具体情况具体分析。要根据需求的重要性、紧迫性和特殊性以及可能适用的开源软件的成熟度、复杂度以及我们的开发人员的熟悉程度等方面综合考虑。特别是开源软件的成熟度和社区的活跃程度,如果这两项比较差的话,那我们对此开源软件仅仅会是关注。 这里我只谈谈我们对开源软件的态度和使用情况。一般来说,我们的工作思路是首先明确自己的需求是什么,然后再调研是否有合适的开源软件可以满足或部分满足我们的需求。如果完全满足,我们会再看这个开源软件的成熟度和社区的活跃度,都没有问题的话我们就会直接使用。如果只是部分满足需求,我们会根据实际情况选择引入修改代码或参考重新设计的思路,这要从此软件与需求的差距点具体来看。 举几个例子说明一下。比如数据交换协议,仔细分析一下我们的需求可能是什么样子。数据交换协议一般有三种类型的应用:第一,作为数据格式存储到硬盘;第二,通信密集型服务之间的通信;第三,普遍意义上的网络数据交换,可能跨大服务平台。这三类需求其实对数据交换协议的要求是不同的。第一个应用的需求是协议包越小越好而且尽量不要发生变化;第二个需求也是要求协议包比较小但还要考虑支持一些高并发;第三个要求足够标准化同时易读。开源Google的ProtoBuf被很多人使用,但在第三个需求上就不是很适用,因为它不支持协议的自解析,这样我们可能会针对第三类需求自己开发一套协议。再比如从编程框架来说,Boost很好很强大。但它支持的功能过于复杂,掌握起来不容易(这个掌握是要求出现任何问题都能很快定位并解决,同时有需求还可能进一步升级开发)。我们会考虑参照它的一些思路做一个简化版的框架出来。再比如Hadoop,这是个超级大规模的开源项目,我们在大量使用,但它的部分功能已经不满足我们的需求,这时我们必须对其进行部分改造,用C改写了大部分的Java代码以提升性能。 邝宇恒:在一个Web2.0应用中,希望能够支持对用户产生的所有内容的实时搜索,在基础架构上应如何支持? 侯震宇:如何支持Web2.0应用中的实时搜索,这是个大问题,很难简单说清楚。我们还是先把问题分解。从服务提供商来看分为Web2.0服务站内搜索和专业的搜索引擎,从技术角度来看又可以分为强调实效性的搜索技术和Web2.0内容组织技术。我们先分别谈谈这些问题,然后再综合考虑看看是否可以提供一个基础平台来简化这些服务的搭建。 Web2.0网站重在用户交互,其外在表现形式比传统的信息类网站要复杂得多,主要内容包括用户提交的数据和用户之间的关系。如果只分析用户提交的原始信息数据,就退化成一个传统搜索引擎的问题,但由于数据类型太多而需要操作的技巧也很多。对于搜索引擎提供商来说,需要解决信息源的发现问题(可以通过配一些最重要的种子站点简单解决)、页面解析问题(大部分Web2.0网站特效很炫,但对搜索引擎不友好)、页面质量判断问题(大部分Web2.0页面的价值相对于传统信息页面如新闻页面来说,价值都不高)、索引及排序问题(哪些内容进入索引、哪些指标影响排序,这与传统的文本检索有差别)、时效性问题(涉及到Spider的及时抓取和快速入库),这些都是经典的搜索引擎需要处理的问题。尽管Web2.0由于页面元素和页面组织形式与传统网页差别很大,带来非常多的技术难点,但仍可以通过经典的搜索技术解决。 以上是从搜索引擎角度来看的,如果是Web2.0网站自身提供搜索的话,则涉及的技术要简单很多,但这两者都涉及到Web2.0内容的组织问题。Web2.0网站除去用户提交信息外,最重要的数据是用户与用户之间的关系,如何组织这部分数据是Web2.0网站后端设计的一大难点。提供对用户关系以及用户关系的衍生物的搜索,这不是传统搜索引擎技术所能解决的问题。这其实不能称为搜索(Search),而是对内部数据组织的一种查询(Query)服务。比较经典的架构设计是将Web2.0网站的数据分成两大类:第一,用户提交的原始数据,这可能是帖子内容、一幅图片、一个评价等,这部分数据总量很大但数据变化不大;第二,数据之间、用户之间的关系数据,这部分数据变化相对频繁且数据总量不算大。对于第一种数据,我们可以设计一个简单的存储系统,支持相对弱化的Schema,存储在便宜的介质上(如硬盘);对于第二种,由于变化频繁而且查询量大,可以存储在访问高效的介质上(如内存或SSD),而这部分数据的操作相对复杂,可以直接使用数据库如MySQL。这两种存储系统能够满足大部分Web2.0服务的要求了,再提供一套经典的内容检索系统对原始数据进行索引并对外提供服务,带有实时搜索的Web2.0服务就基本成型了。 上面一直没有谈的一个问题是隐私问题,在网站内部设计就是用户访问权限问题。这不仅是个复杂的技术问题,同样也涉及法律问题。一个只对登录用户开放访问权限的用户,搜索引擎提供商是无法收录其内容从而提供搜索服务的。对于Web2.0网站内部,判断哪些内容可以对浏览者开放也是比较困难的,特别是有非常复杂的好友系统存在的情况下。不过只要对用户数据进行合理组织还是可以做到的,当然要对业务逻辑的复杂与服务性能做权衡。 邝宇恒:SSD的特性与传统硬盘差别很大,它的广泛应用,会对存储系统设计带来什么影响? 侯震宇:这个答案是肯定的。软件是跑在硬件上的,任何一次硬件的创新应用都会带来一次软件设计上的革命性升级。对存储系统来讲,性能上最重要的指标是IOPS和吞吐。而SSD在随机查询上的延时与硬盘相比是非常低的,这对于提供高并发的在线类服务是非常有价值的,也会使存储服务考虑的问题发生变化。举两个最简单的例子。比如上文提到的使用MySQL,很多应用因为响应效率的问题而进行的分库分表工作,在引入SSD之后又变回了一个简单的单机问题。比如在Cache方面,由于内存容量的问题不得不考虑多机,而SSD在很多场合下是可以为Cache服务提供存储的,这也回到了一个单机问题。单机比多机和分布式系统要简单很多。 存储系统需要考虑两个层面的问题:是单机或者单存储介质的问题,还是多机和分布式的问题。就存储问题本身来说,可以看作是个单机问题。硬盘读写时间由寻道时间和吞吐来决定,在小数据量的情况下寻道时间占读写时间的大部分。以往我们在硬盘上做存储总是要考虑硬盘顺序读写以发挥硬盘的特性,这是由硬盘的特性决定的。比如对写操作我们会考虑将多次随机写转变为批量的顺序写以提高系统的吞吐能力,对于读操作我们会尽可能地将一些数据按照其访问模式组织数据把可能的多次随机读转化成一次顺序读。对于SSD来说,我们同样要根据其硬件特性来设计我们的存储系统。SSD随机读能力超强,写也不错,但擦除性能很差。这就要求我们在写的时候要尽可能避免对块的擦除,将多次修改直接写盘然后Merge成对一个块的擦除,这样可以大大提升SSD的写性能。 SSD最大的价值是弥补了内存和硬盘两个介质之间在容量、价格、响应延时等方面巨大的鸿沟,把SSD作为一个大容量的内存使用或者作为IOPS很高的硬盘使用在某些场合下都是可以的。Jim Gray的名言“Tape is Dead, Disk is Tape, Flash is Disk, Ram Locality is King”是对不同存储介质的发展的一个精辟的论断。当然即便SSD大规模使用,硬盘也不会消亡。硬盘在顺序访问大规模数据上是有优势的,比如MapReduce类应用。上面举的例子是SSD全面替换硬盘,而在实际工作中内存、SSD、硬盘这几种物理存储介质一般都是混合应用的。这需要我们根据应用的实际需求,来选择使用不同的存储介质。通过对需求的抽象,可以按照数据的特性和访问的特性来分析存储系统对于不同硬件的需求。对于数据,需要分析数据的规模、单条数据的大小以及数据是否可变和变化的频率。对于访问模式,需要分析是随机查询、批量查询和顺序查询。另外一个考虑的因素是价格和功耗,SSD是SAS硬盘价格的2倍、SATA硬盘价格的20倍,但功耗要小很多。未来的存储系统必然是内存、SSD和硬盘(特别是SATA)混用,这需要我们在架构层面上给予足够的支持。 邝宇恒:你的团队会对所有代码进行代码评审吗?具体如何实施?如何评价代码评审的作用? 侯震宇:我们是做代码评审的,但不会对所有代码做评审。代码是可以分级的,一方面从代码本身的重要性来分,另一方面从代码编写者的熟练度来分。代码评审在我们内部叫Code Review,其有两个目的。第一也是最主要的目的是检查代码的质量,第二是学习一些优秀的代码,大家关心的可能是第一点。对入职时间不长的新人,需要监控其代码质量,纠正其在编写代码中的不良习惯,并使他们逐步融入我们统一的编程风格中。对于这种情况,我们一般有安排专人Review新人的代码。需要指出的是,不能指望Code Review发现所有代码层面的问题,它只能解决一些表面问题,而很难发现代码逻辑上的问题。代码逻辑上的问题需要通过后期的测试来解决。这里说的表面问题主要指缺乏注释、大量使用全局变量以及明显的一些可能引发Bug的问题(如在不知道字符串长度的情况下使用Copy等),这其中包括程序员编码的基本素质和编程风格两个因素。编码的素质可以通过训练和逐步积累提高,但编程风格不容易改变,只能进行约束。 所以说Code Review只是发现问题的一种手段,但不是解决问题的方法。提高代码质量我觉得分为三个层次:第一,通过框架约束;第二,工具检查;第三,Code Review检查错误。我们逐个层面来看: 编程框架的约束。减少程序员代码中的Bug的最好方法,就是少写代码。我们会有专人编写适合绝大多数业务的编程框架,将我们的程序员从编写没有营养、易出错的代码工作中解放出来。程序员只需要写一些配置或描述,就可以由框架生成可运行的代码框架。这既提高了程序员的工作效率,使程序员关注在业务逻辑实现上,也由于框架的约束使程序形成了统一的风格和代码结构。同时由于是自动生成的框架代码,这部分经过严格的测试,可以确保是高质量的代码,大大降低Bug数。 编程规范的工具检查。肉眼Review代码的效率是很低的。结合我们自己的编程规范编写的工具,可以像编译检查一样检查出大批不符合规范的代码缺陷。这些缺陷如代码缺乏注释、参数未被使用、函数没有返回值等。代码检查工具执行后会形成报告,指出缺陷的位置和一定的统计信息,这对于程序员提高自己的代码水平是很有帮助的。将工具检查结果与代码准入的流程相结合,可以大批过滤掉提交代码的低水平缺陷或Bug。 最后才是Code Review,再往后就到了测试阶段了。 总之,代码评审对于提高代码质量是很有帮助的,但只做代码评审是不够的或者说意义不大。如何最大限度地提高代码质量减少缺陷,是每个工程师和架构师都需要考虑的问题,涉及诸多方面,这都需要我们去关注。 (本文来自《程序员》杂志10年10期) 《程序员》11期精彩内容预告:互联网架构集结号 《程序员》订阅

Logo

20年前,《新程序员》创刊时,我们的心愿是全面关注程序员成长,中国将拥有新一代世界级的程序员。20年后的今天,我们有了新的使命:助力中国IT技术人成长,成就一亿技术人!

更多推荐