大型网站技术架构:核心原理与案例分析

摘录

  应用程序、数据库、文件等所有的资源都在一台服务器上。通常服务器操作系统使用 Linux,应用程序使用 PHP 开发,然后部署在 Apache 上,数据库使用 MySQL,汇集各种免费开源软件及一台廉价服务器就可以开始网站的发展之路了。


  网站使用的缓存可以分为两种:缓存在应用服务器上的本地缓存和缓存在专门的分布式缓存服务器上的远程缓存。本地缓存的访问速度更快一些,但是受应用服务器内存限制,其缓存数据量有限,而且会出现和应用程序争用内存的情况。远程分布式缓存可以使用集群的方式,部署大内存的服务器作为专门的缓存服务器,可以在理论上做到不受内存容量限制的缓存服务


  通过负载均衡调度服务器,可将来自用户浏览器的访问请求分发到应用服务器集群中的任何一台服务器上,如果有更多的用户,就在集群中加入更多的应用服务器,使应用服务器的负载压力不再成为整个网站的瓶颈。


  分布式数据库是网站数据库拆分的最后手段,只有在单表数据规模非常庞大的时候才使用。不到不得已时,网站更常用的数据库拆分手段是业务分库,将不同业务的数据库部署在不同的物理服务器上。


  技术是用来解决业务问题的,而业务的问题,也可以通过业务的手段去解决。


  分布式数据和存储: 大型网站需要处理以 P 为单位的海量数据,单台计算机无法提供如此大的存储空间,这些数据需要分布式存储。除了对传统的关系数据库进行分布式部署外,为网站应用而生的各种 NoSQL 产品几乎都是分布式的。


  目前网站普遍使用 Hadoop 及其 MapReduce 分布式计算框架进行此类批处理计算,其特点是移动计算而不是移动数据,将计算程序分发到数据所在的位置以加速计算和分布式计算。


  使用缓存有两个前提条件,一是数据访问热点不均衡,某些数据会被更频繁的访问,这些数据应该放在缓存中;二是数据在某个时间段内有效,不会很快过期,否则缓存的数据就会因已经失效而产生脏读,影响结果的正确性。网站应用中,缓存除了可以加快数据访问速度,还可以减轻后端应用和数据存储的负载压力,这一点对网站数据库架构至关重要,网站数据库几乎都是按照有缓存的前提进行负载能力设计的。


  使用异步方式处理业务可能会对用户体验、业务流程造成影响,需要网站产品设计方面的支持。


  为了抵御地震、海啸等不可抗力导致的网站完全瘫痪,某些大型网站会对整个数据中心进行备份,全球范围内部署灾备数据中心 。网站程序和数据实时同步到多个灾备数据中心。


  一般说来,除了当前的系统功能需求外,软件架构还需要关注性能、可用性、伸缩性、扩展性和安全性这 5 个架构要素,架构设计过程中需要平衡这 5 个要素之间的关系以实现需求和架构目标,也可以通过考察这些架构要素来衡量一个软件架构设计的优劣,判断其是否满足期望。


  因此网站高可用架构设计的前提是必然会出现服务器宕机,而高可用设计的目标就是当服务器宕机的时候,服务或者应用依然可用。


  对于应用服务器而言,多台应用服务器通过负载均衡设备组成一个集群共同对外提供服务,任何一台服务器宕机,只需把请求切换到其他服务器就可实现应用的高可用,但是一个前提条件是应用服务器上不能保存请求的会话信息。否则服务器宕机,会话丢失,即使将用户请求转发到其他服务器上也无法完成业务处理。


  对于缓存服务器集群,加入新的服务器可能会导致缓存路由失效,进而导致集群中大部分缓存数据都无法访问。虽然缓存的数据可以通过数据库重新加载,但是如果应用已经严重依赖缓存,可能会导致整个网站崩溃。需要改进缓存路由算法保证缓存数据的可访问性。


  关系数据库虽然支持数据复制,主从热备等机制,但是很难做到大规模集群的可伸缩性,因此关系数据库的集群伸缩性方案必须在数据库之外实现,通过路由分区等手段将部署有多个数据库的服务器组成一个集群。

  至于大部分 NoSQL 数据库产品,由于其先天就是为海量数据而生,因此其对伸缩性的支持通常都非常好,可以做到在较少运维参与的情况下实现集群规模的线性伸缩


  测试程序通过多线程模拟并发用户的办法来测试系统的并发处理能力,为了真实模拟用户行为,测试程序并不是启动多线程然后不停地发送请求,而是在两次请求之间加入一个随机等待时间,这个时间被称作思考时间。


  在某些时候,静态资源文件变化需要及时应用到客户端浏览器,这种情况,可通过改变文件名实现,即更新 JavaScript 文件并不是更新 JavaScript 文件内容,而是生成一个新的 JS 文件并更新 HTML 文件中的引用。

  使用浏览器缓存策略的网站在更新静态资源时,应采用批量更新的方法,比如需要更新 10 个图标文件,不宜把 10 个文件一次全部更新,而是应一个文件一个文件逐步更新,并有一定的间隔时间,以免用户浏览器突然大量缓存失效,集中更新缓存,造成服务器负载骤增、网络堵塞的情况。


  4.CSS 放在页面最上面、JavaScript 放在页面最下面

  浏览器会在下载完全部 CSS 之后才对整个页面进行渲染,因此最好的做法是将 CSS 放在页面最上面,让浏览器尽快下载 CSS。JavaScript 则相反,浏览器在加载 JavaScript 后立即执行,有可能会阻塞整个页面,造成页面显示缓慢,因此 JavaScript 最好放在页面最下面。但如果页面解析时就需要用到 JavaScript,这时放在底部就不合适了。


  新启动的缓存系统如果没有任何数据,在重建缓存数据的过程中,系统的性能和数据库负载都不太好,那么最好在缓存系统启动时就把热点数据加载好,这个缓存预加载手段叫作缓存预热(warm up)。对于一些元数据如城市地名列表、类目信息,可以在启动时加载数据库中全部数据到缓存进行预热。


  分布式缓存指缓存部署在多个服务器组成的集群中,以集群方式提供缓存服务,其架构方式有两种,一种是以 JBoss Cache 为代表的需要更新同步的分布式缓存,一种是以 Memcached 为代表的不互相通信的分布式缓存。


  一台服务器上启动多少线程合适呢?假设服务器上执行的都是相同类型任务,针对该类任务启动的线程数有个简化的估算公式可供参考:

  启动线程数=[任务执行时间/(任务执行时间(IO 等待时间)]]CPU 内核数


  编程上,解决线程安全的主要手段有如下几点。

  将对象设计为无状态对象

  使用局部对象

  并发访问资源时使用锁


  从编程角度,资源复用主要有两种模式:单例(Singleton)和对象池(Object Pool)。


  常使用 Twitter 的用户或多或少遇到过那个著名的服务不可用的鲸鱼页面,事实上,Twitter 网站的可用性不足 2 个 9。


  为了保证核心应用和功能的正常运行,需要对服务进行降级。降级有两种手段:拒绝服务及关闭服务。


  必须在服务层保证服务重复调用和调用一次产生的结果相同,即服务具有幂等性。


  即使是经过严格的测试,软件部署到线上服务器之后还是经常会出现各种问题,甚至根本无法启动服务器。主要原因是测试环境和线上环境并不相同,特别是应用需要依赖的其他服务,如数据库,缓存、公用业务服务等,以及一些第三方服务,如电信短信网关、银行网银接口等。


  因此在网站发布时,并不是把测试通过的代码包直接发布到线上服务器,而是先发布到预发布机器上,开发工程师和测试工程师在预发布服务器上进行预发布验证,执行一些典型的业务流程,确认系统没有问题后才正式发布。


  一个真实的案例是某网站需要验证海外第三方支付功能,每件商品的售价本来是数千美金,工程师不可能花数千美金去验证自己开发的功能,于是将金额改成一美元,验证成功后,幸福地发布上线了,第二天上班后,发现大量商品以一美元的价格成交。


  对于有固定发布日期的网站(很多网站选择周四作为发布日,这样一周前面有三天时间可以准备发布,后面还有一天时间可以挽回错误。如果选择周五发布,发现问题就必须要周末加班了。)


  由于火车发布模型是基于规则驱动的流程,所以这个流程可以自动化。采用火车发布模型的网站会开发一个自动化发布的工具实现发布过程的自动化。根据响应驱动流程,自动构造代码分支,进行代码合并,执行发布脚本等。正常流程下,可以做到发布过程无人值守,无需 SCM(网站配置管理员)参与,每个项目相关人员基于流程执行相应的操作,即可完成应用自动发布。人的干预越少,自动化程度越高,引入故障的可能性就越小,火车准点到达,大家按时下班的可能性就越大。


  大型网站会使用灰度发布模式,将集群服务器分成若干部分,每天只发布一部分服务器,观察运行稳定没有故障,第二天继续发布一部分服务器,持续几天才把整个集群全部发布完毕,期间如果发现问题,只需要回滚已发布的一部分服务器即可。


  灰度发布也常用于用户测试,即在部分服务器上发布新版本,其余服务器保持老版本(或者发布另一个版本),然后监控用户操作行为,收集用户体验报告,比较用户对两个版本的满意度,以确定最终的发布版本。这种手段也被称作 AB 测试。


  此外,大型网站的用户日志数据量惊人,数据存储与计算压力很大,目前许多网站逐步开发基于实时计算框架 Storm 的日志统计与分析工具。


  监控管理系统可以配置报警阈值和值守人员的联系方式,报警方式除了邮件,即时通信工具,还可以配置手机短信,语音报警,系统发生报警时,工程师即使在千里之外、夜里睡觉也能被及时通知,迅速响应。


  一般说来,网站的伸缩性设计可分成两类,一类是根据功能进行物理分离实现伸缩,一类是单一功能通过集群实现伸缩。前者是不同的服务器部署不同的服务,提供不同的功能;后者是集群内的多台服务器部署相同的服务,提供相同的功能。


  利用 HTTP 重定向协议实现负载均衡。


  这种负载均衡方案的优点是比较简单。缺点是浏览器需要两次请求服务器才能完成一次访问,性能较差;重定向服务器自身的处理能力有可能成为瓶颈,整个集群的伸缩性规模有限;使用 HTTP302 响应码重定向,有可能使搜索引擎判断为 SEO 作弊,降低搜索排名。因此实践中使用这种方案进行负载均衡的案例并不多见。


  DNS 域名解析负载均衡的优点是将负载均衡的工作转交给 DNS,省掉了网站管理维护负载均衡服务器的麻烦,同时许多 DNS 还支持基于地理位置的域名解析,即会将域名解析成距离用户地理最近的一个服务器地址,这样可加快用户访问速度,改善性能。但是 DNS 域名解析负载均衡也有缺点,就是目前的 DNS 是多级解析,每一级 DNS 都可能缓存 A 记录,当下线某台服务器后,即使修改了 DNS 的 A 记录,要使其生效也需要较长时间,这段时间,DNS 依然会将域名解析到已经下线的服务器,导致用户访问失败;而且 DNS 负载均衡的控制权在域名服务商那里,网站无法对其做更多改善和更强大的管理。

  事实上,大型网站总是部分使用 DNS 域名解析,利用域名解析作为第一级负载均衡手段,即域名解析得到的一组服务器并不是实际提供 Web 服务的物理服务器,而是同样提供负载均衡服务的内部服务器,这组内部负载均衡服务器再进行负载均衡,将请求分发到真实的 Web 服务器上。


  由于反向代理服务器转发请求在 HTTP 协议层面,因此也叫应用层负载均衡。其优点是和反向代理服务器功能集成在一起,部署简单。缺点是反向代理服务器是所有请求和响应的中转站,其性能可能会成为瓶颈。


  使用三角传输模式的链路层负载均衡是目前大型网站使用最广的一种负载均衡手段。在 Linux 平台上最好的链路层负载均衡开源产品是 LVS(Linux Virtual Server)。


  最少连接(Least Connections)

  记录每个应用服务器正在处理的连接数(请求数),将新到的请求分发到最少连接的服务器上,应该说,这是最符合负载均衡定义的算法。同样,最少连接算法也可以实现加权最少连接。

  源地址散列(Source Hashing)

  根据请求来源的 IP 地址进行 Hash 计算,得到应用服务器,这样来自同一个 IP 地址的请求总在同一个服务器上处理,该请求的上下文信息可以存储在这台服务器上,在一个会话周期内重复使用,从而实现会话黏滞。


  分布式缓存服务器集群中不同服务器中缓存的数据各不相同,缓存访问请求不可以在缓存服务器集群中的任意一台处理,必须先找到缓存有需要数据的服务器,然后才能访问。这个特点会严重制约分布式缓存集群的伸缩性设计,因为新上线的缓存服务器没有缓存任何数据,而已下线的缓存服务器还缓存着网站的许多热点数据。


  必须让新上线的缓存服务器对整个分布式缓存集群影响最小,也就是说新加入缓存服务器后应使整个缓存服务器集群中已经缓存的数据尽可能还被访问到,这是分布式缓存集群伸缩性设计的最主要目标。


  很容易就可以计算出,3 台服务器扩容至 4 台服务器,大约有 75%(3/4)被缓存了的数据不能正确命中,随着服务器集群规模的增大,这个比例线性上升。当 100 台服务器的集群中加入一台新服务器,不能命中的概率是 99%(N /(N +1))。


  具体算法过程为:先构造一个长度为 0232 的整数环(这个环被称作一致性 Hash 环),根据节点名称的 Hash 值(其分布范围同样为 0232 )将缓存服务器节点放置在这个 Hash 环上。然后根据需要缓存的数据的 KEY 值计算得到其 Hash 值(其分布范围也同样为 0~232),然后在 Hash 环上顺时针查找距离这个 KEY 的 Hash 值最近的缓存服务器节点,完成 KEY 到服务器的 Hash 映射查找。


  具体应用中,这个长度为 232 的一致性 Hash 环通常使用二叉查找树实现,Hash 查找过程实际上是在二叉查找树中查找不小于查找数的最小数值。当然这个二叉树的最右边叶子节点和最左边的叶子节点相连接,构成环。


  计算机领域有句话:计算机的任何问题都可以通过增加一个虚拟层来解决 。


  显然每个物理节点对应的虚拟节点越多,各个物理节点之间的负载越均衡,新加入物理服务器对原有的物理服务器的影响越保持一致(这就是一致性 Hash 这个名称的由来)。那么在实践中,一台物理服务器虚拟为多少个虚拟服务器节点合适呢?太多会影响性能,太少又会导致负载不均衡,一般说来,经验值是 150,当然根据集群规模和负载均衡的精度需求,这个值应该根据具体情况具体对待。


  除了数据库主从读写分离,前面提到的业务分割模式也可以用在数据库,不同业务数据表部署在不同的数据库集群上,即俗称的数据分库。这种方式的制约条件是跨库的表不能进行 Join 操作。

  在大型网站的实际应用中,即使进行了分库和主从复制,对一些单表数据仍然很大的表,比如 Facebook 的用户数据库,淘宝的商品数据库,还需要进行分片,将一张表拆开分别存储在多个数据库中。


  高手定律:这个世界只有遇不到的问题,没有解决不了的问题,高手之所以成为高手,是因为他们遇到了常人很难遇到的问题,并解决了。所以百度有很多广告搜索的高手,淘宝有很多海量数据的高手,QQ 有很多高并发业务的高手,原因大抵如此。一个 100 万用户的网站,不会遇到 1 亿用户同时在线的问题;一个拥有 100 万件商品网站的工程师,可能无法理解一个拥有 10 亿件商品网站的架构。

  救世主定律:遇到问题,分析问题,最后总能解决问题。如果遇到问题就急匆匆地从外面挖一个高手,然后指望高手如探囊取物般轻松搞定,最后怕是只有彼此抱怨和伤害。许多问题只是看起来一样,具体问题总是要具体对待的,没有银弹,没有救世主。所以这个定律准确地说应该是“没有救世主定律”。


  经常听到各种场合中对扩展性和伸缩性的误用,包括许多资深网站架构师也常常混淆两者,用扩展性表示伸缩性。在此,我们澄清下这两个概念。

  扩展性(Extensibility)

  指对现有系统影响最小的情况下,系统功能可持续扩展或提升的能力。表现在系统基础设施稳定不需要经常变更,应用之间较少依赖和耦合,对需求变更可以敏捷响应。它是系统架构设计层面的开闭原则 (对扩展开放,对修改关闭),架构设计考虑未来功能扩展,当系统增加新功能时,不需要对现有系统的结构和代码进行修改。

  伸缩性(Scalability)

  指系统能够通过增加(减少)自身资源规模的方式增强(减少)自己计算处理事务的能力。如果这种增减是成比例的,就被称作线性伸缩性。在网站架构中,通常指利用集群的方式增加服务器数量、提高系统的整体事务吞吐能力。


  事件驱动架构(Event Driven Architecture):通过在低耦合的模块之间传输事件消息,以保持模块的松散耦合,并借助事件消息的通信完成模块间合作,典型的 EDA 架构就是操作系统中常见的生产者消费者模式。


  编译、部署困难:对于网站开发工程师而言,打包构建一个巨型应用是一件痛苦的事情,也许只是修改了一行代码,输入 build 命令后,抽完一支烟,回来一看,还在 building;又去喝了一杯水,回来一看,还在 building;又去了一次厕所,回来一看,还在 building;好不容易 build 结束,一看编译失败,还得重来……


  那么有没有办法能够做到可扩展的数据结构设计呢?无需修改表结构就可以新增字段呢?许多 NoSQL 数据库使用的 ColumnFamily(列族)设计就是一个解决方案。ColumnFamily 最早在 Google 的 Bigtable 中使用,这是一种面向列族的稀疏矩阵存储格式,如表 1 所示。


  使用支持 ColumnFamily 结构的 NoSQL 数据库,创建表的时候,只需要指定 ColumnFamily 的名字,无需指定字段(Column),可以在数据写入时再指定,通过这种方式,数据表可以包含数百万的字段,使得应用程序的数据结构可以随意扩展。而在查询时,可以通过指定任意字段名称和值进行查询。


  网站通过不断试错,在残酷的市场中寻找自己的竞争优势,持续地推出新功能,发现达不到预期,就立马下线。所以我们看到网站总是不停地推出新功能,发布新产品。打开 Google 首页的“更多”链接,Google 产品分门别类一大堆,这还只是 Google 重点推广的产品中的一小部分。这些走马灯般出现的产品背后则是网站工程师辛勤的工作和汗水。


  最早由微软提出,即浏览器禁止页面 JavaScript 访问带有 HttpOnly 属性的 Cookie。HttpOnly 并不是直接对抗 XSS 攻击的,而是防止 XSS 攻击者窃取 Cookie。对于存放敏感信息的 Cookie,如用户认证信息等,可通过对该 Cookie 添加 HttpOnly 属性,避免被攻击脚本窃取。


  贝叶斯算法认为特征值之间是独立的,所以也被称作是朴素贝叶斯算法(Native Bayes),这个假设很多时候是不成立的,特征值之间具有关联性,通过对朴素贝叶斯算法增加特征值的关联依赖处理,得到 TAN 算法。更进一步,通过对关联规则的聚类挖掘,得到更强大的算法,如 ARCS 算法(Association Rule Clustering System)等。但是由于贝叶斯分类算法简单,处理速度快,仍是许多实时在线系统反垃圾的首选。


  当交易的某些指标满足一定条件时,就会被认为具有高风险的欺诈可能性。比如用户来自欺诈高发地区;交易金额超过某个数值;和上次登录的地址距离差距很大;用户登录地与收货地不符;用户第一次交易等等。


  经过充分训练后的统计模型,准确率不低于规则引擎。分类算法的实时计算性能更好一些,由于统计模型使用模糊识别,并不精确匹配欺诈类型规则,因此对新出现的交易欺诈还具有一定预测性。


  2003 年,花 3000 美金买来的淘宝网站是用 PHP 开发的,淘宝的工程师做了简单的汉化处理,并对数据库做了读写分离,最早的淘宝网架构如图 5 所示。


  当时淘宝还开发了另一个重要产品 Antx,这个针对 Java 平台的、扩展自 Ant 的项目构建工具对于网站项目开发、测试、发布至关重要,一个非常重要的功能就是管理配置项。对于一个 Java 开发的大型 Web 系统,内部通常会包含数百个 jar 文件,每个 jar 文件都是一个独立的模块,这些模块由不同团队开发,实现不同功能,最后组成一个完整的系统。这些模块通常也都有自己的配置文件,比如数据库连接模块需要配置数据库 URL、连接池大小等,这些配置参数在开发环境、测试环境、生产环境各不相同。Antx 提供了一个灵活管理这些分散配置项的解决方案。


  这也再一次验证了辩证法关于事物发展的否定之否定及螺旋式上升的普遍规律,仿佛回到原点,但一切已经完全不同了。


  目前 Wikipedia 网站建立在 LAMP(LinuxLApacheAMySQLMPHP)之上,其他基础技术组件也全部采用免费的开源软件。因为 Wikipedia 是非盈利的,所以尽可能使用免费的软件和廉价的服务器,这种技术倾向使得技术团队不得不量体裁衣、看米下锅,榨尽系统所有资源的利用价值,用最少的资源成就最不可思议的奇迹,最终也让技术团队获得了真正的成长。


  Wikipedia 前端架构的核心是反向代理服务器 Squid 集群,大约部署有数十台服务器,请求通过 LVS 负载均衡地分发到每台 Squid 服务器,热点词条被缓存在这里,大量请求可直接返回响应,请求无需发送到 Apache 服务器,减轻应用负载压力。Squid 缓存不能命中的请求再通过 LVS 发送到 Apache 应用服务器集群,如果有词条信息更新,应用服务器使用 Invalidation Notification 服务通知 Squid 缓存失效,重新访问应用服务器更新词条。


  Wikipedia CDN 缓存的几条准则为:

  • 内容页面不包含动态信息,以免页面内容缓存很快失效或者包含过时信息。

  • 每个内容页面有唯一的 REST 风格的 URL,以便 CDN 快速查找并避免重复缓存。

  • 在 HTML 响应头写入缓存控制信息,通过应用控制内容是否缓存及缓存有效期等。


  后端优化最主要的手段是使用缓存,将热点数据缓存在分布式缓存系统的内存中,加速应用服务器的数据读操作速度,减轻存储和数据库服务器的负载。Wikipedia 的缓存使用策略如下:

  热点特别集中的数据直接缓存到应用服务器的本地内存中,因为要占用应用服务器的内存且每台服务器都需要重复缓存这些数据,因此这些数据量很小,但是读取频率极高。   缓存数据的内容尽量是应用服务器可以直接使用的格式,比如 HTML 格式,以减少应用服务器从缓存中获取数据后解析构造数据的代价。   使用缓存服务器存储 session 对象。   相比数据库,Memcached 的持久化连接非常廉价,如有需要就创建一个 Memcached 连接。


  解决办法是使用 JavaScript 脚本控制,在秒杀商品静态页面中加入一个 JavaScript 文件引用,该 JavaScript 文件中加入秒杀是否开始的标志和下单页面 URL 的随机数参数,当秒杀开始的时候生成一个新的 JavaScript 文件并被用户浏览器加载,控制秒杀商品页面的展示。这个 JavaScript 文件使用随机版本号,并且不被浏览器、CDN 和反向代理服务器缓存。


  先讲一个小故事。有一次,笔者和几个网站架构师讨论问题,期间,一位架构师被他们部门总监叫去面试一位应聘者。结果过了十几分钟,这位架构师就回来了,我们都很奇怪:怎么这么快?他笑道:“这位老兄,工作十几年,什么都不会,没什么好问的”。我们问他都问了什么问题,结果都是一些如果没有经历过,就永远不会想到的问题,而这些问题只要在大型网站技术一线呆上两三年,就一定会碰到。


  应用程序自己的日志输出配置和第三方组件日志输出要分别配置。   检查 log 配置文件,日志输出级别至少为 Warn,并且检查 log 输出代码调用,调用级别要符合其真实日志级别。   有些开源的第三方组件也会不恰当地输出太多的 Error 日志,需要关闭这些第三方库的日志输出,至于哪些第三方库有问题,只有在遇到问题时才知道。


  首页不应该访问数据库,首页需要的数据可以从缓存服务器或者搜索引擎服务器获取。

  首页最好是静态的。


  故障现象: 没有新应用发布,但是数据库服务器突然 Load 飙升,并很快失去响应。DBA 将数据库访问切换到备机,Load 也很快飙升,并失去响应。最终引发网站全部瘫痪。


  后台服务准备好,前台应用才能启动,否则就会导致故障。这种情况被内部人戏称作“姑娘们还没穿好衣服,老鸨就开门迎客了”。


  原因分析: 检查发现,该时段内网卡流量也下降,但是没有找到原因。过了一阵子才知道,原来有工程师在线上生产环境进行性能压力测试,占用了大部分交换机带宽。


  网站数据库有专门的 DBA 维护,如果发现数据库存在错误记录,需要进行数据订正,必须走数据订正流程,申请 DBA 协助。于是就有工程师为避免麻烦,直接写一段数据库更新操作的代码,悄悄放到生产环境应用服务器上执行,神不知鬼不觉地订正了数据。但是如果不小心写错了 SQL,后果可想而知。


  架构师是软件开发组织中一个比较特殊的角色,除了架构设计,软件开发等技术类工作,通常还需要承担一些管理职能:规划产品路线、估算人力资源和时间资源、安排人员职责分工,确定计划里程碑点、指导工程师工作、过程风险评估与控制等。这些管理事务需要对产品技术架构、功能模块划分、技术风险都熟悉的架构师参与或直接负责。


  架构师作为项目组最资深的专业技术人员,是项目组开发测试工程师的前辈。从架构师的身上,工程师可以看到自己的未来,因此架构师在做人做事方面需要严格要求自己,做好表率。


  没有懒惰的员工,只有没被激发出来的激情。所有强迫员工加班的管理者都应该为自己的无能而羞愧。


  不要企图在项目中证明自己是正确的,一定要记住,你是来做软件的,不是来当老大的。所以不要企图去证明自己了不起,永远也别干这种浪费时间、伤害感情的事。


  而当大家不再讨论架构的时候,表明架构已经融入到项目、系统和开发者中了,架构师越早被项目组遗忘,越表示架构非常成功;项目组越离不开架构师,越表示架构还有很多缺陷。


  架构师作为团队的技术领导者,在项目过程中不要去试图控制什么,带着一个弹性的计划和蓝图推进,团队会管好他们自己。你越是强加禁令,队伍就越是没有纪律;你越是强制,团队就越是不能独立自主;你越是从外面寻找帮助,大家就越是没有信心。


  所谓问题,就是体验—期望,当体验不能满足期望,就会觉得出了问题。消除问题有两种手段:改善体验或者降低期望。降低期望只是回避了问题,而如果直面期望和体验之间的差距,就会发现问题所在,找到突破点。


  2.给上司提封闭式问题,给下属提开放式问题

  不要问上司“你觉得该怎么办?”这种没有建设性的开放式问题,给上司提问题是希望能够得到他的支持,而不是带着一头雾水等他去指点迷津。公司付你薪水不是让你睁着迷茫的眼睛卖萌。给上司提问应该是“你觉得 A 和 B 两个方案哪个更好?”这种封闭式问题。

  给下属提问则相反,用开放式的问题启发他去思考,寻找创新的解决方案。

  所以,只有“元方,你怎么看?”,而没有“大人,你怎么看?”。


  3.指出问题而不是批评人

  如果在合作中出现问题,告诉他问题的存在和紧迫性,而不是责问他为什么出现问题。

  人在听到批评信息的时候,本能地想要去针对批评进行反驳或者辩解,于是谈话就变成关于批评是否合理的争论,离解决问题越来越远。


  先解决别人的问题有几个好处:

  你帮别人解决了问题,礼尚往来,别人也会帮你解决问题。   在帮别人解决问题的过程中,熟悉了情况,后面解决自己的问题也就得心应手了。   解决别人的问题时使用的是你的解决方案,这个方案在你的控制之中,将来往这个方案里再塞一些东西解决自己的问题,手到擒来。


  设计型架构师

  也就是一般意义上的架构师,负责系统架构设计,同时也要负责架构的实施落地、演化发展、推广重构。

  救火型架构师

  充当救火队员的角色,系统出现故障或者“灵异现象”,会请他们出马解决,有时重要而紧急的项目也会由此类架构师主持。他们通常是公司的元老,对系统有全局性的认识,知道“水有多深”。

  布道型架构师

  对某一领域有较深刻的认识,有时候甚至是坚定的技术信仰,乐于同他人分享自己的知识,希望能够推广自己的技术主张,此类架构师通常有较好的个人影响力。但有时,由于自身的局限或者不能跟上技术潮流的发展,会成为忽悠型的“大师”、偶像派的专家。

  Geek 型架构师

  架构师中的 Geek,对某些技术问题的研究达到疯狂偏执的境地,精益求精追求完美。通常由于知识技能不够全面,不符合许多企业对架构师“高大全”的要求,此类架构师常有怀才不遇之惑。


  最好的架构师

  和团队相处日久,通常情况下团队成员感觉不出他的存在,貌似没有他工作也可以完成得很好,但是如果他真的离开了,大家就会觉得心里空荡荡,没了主心骨。


  最差的架构师

  通过制造压力驱使团队成员努力去完成一些无价值的工作,让每个人都忙碌不堪以使大家都没有注意到他自己其实并不能胜任工作。这种架构师对组织整体和团队成员的伤害无以复加,却常常因为敬业和努力的形象而得到老板的肯定。