Tag Archives: 架构

如何打造一个日均PV千万级别的大型系统?

作者介绍

周金桥,具有丰富的系统规划、设计、开发、运维及团队组织管理工作经验,熟悉.Net、J2EE技术架构及应用。微软2008-2012五届最有价值专家(MVP),2009年单独著有《ASP.NET夜话》一书,2010年与人合著《程序员的成长之路》。至今活跃在多个技术社区。

本文我选定的方向是如何开发一个大型系统,在这里我对大型系统的定义为日均PV在千万级以上,而京东和淘宝这类则属于巨型系统了。因此在本篇中讲述的都是基于一些开源免费的技术实现,至于通过F5硬件加速、DNS来实现负载均衡、CDN加速等需要花钱购买的技术或者服务则不再本篇介绍范围之类。

一、从两个系统说起

1、某移动互联网公司服务器端架构图

架构

上图是某移动互联网公司的服务器端架构图,它支撑了国内外数百万客户端的访问请求,有如下特点:

  1. 多层级集群,从Web服务器层、NoSQL层级数据库层都实现了集群,这样使得每一层的响应时间大大缩短,从而能够在单位时间内响应更多请求;
  2. NoSQL应用(Memcached),在NoSQL领域Memcached和Redis都有大量的用户群,在这个架构里使用的是Memcached。
  3. 数据库读写分离,当前大多数数据库服务器支持主从机制或订阅发布机制,这样一来就为读写分离创造了条件,减少了数据库竞争死锁出发条件,使响应时间大为缩短(非数据库集群情况下还可以考虑分库机制)。
  4. 负载均衡,Nginx实现Web服务器的负载均衡,Memcached自带负载均衡实现。

2、某公司生产管理系统架构图

系统架构

上图是为某公司的一个分散型系统做的架构设计,这家公司拥有多个跨市、跨省的生产片区,在各片区都有自己的生态车间,各片区与总公司之间通过数据链路连接。这个系统的特点是所有的流水线上的产品都贴有唯一的条码,在生产线的某个操作位操作之前都会扫描贴在产品上的条码,系统会根据条码做一些检查工作,如:产品条码是否应被使用过(比如之前应发货给客户过)、产品是否完成了本道工序之前的全部必须完成工序,如果满足条件则记录当前操作工序名称、操作人、操作时间和操作结果等。

一件产品从上线到完成有数十道工序,而每月下线的产品有少则数十万、多则数百万,一个月下来的数据量也是不小的。特别是在跨厂区网络不稳定的情况下如何保证对生产的影响最小。

本系统架构特点:

  • 所有业务逻辑集中在服务器端,并以Service形式提供,这样便于业务逻辑调整客户端能及时得到最新更新;
  • 部署Service的服务器采用集群部署,Nginx实现调度;
  • NoSQL采用了Redis,与Memcached相比,Redis支持的数据类型更多,同时Redis带有持久化功能,可以将每个条码对应的产品的最终信息存储在Redis当中,这样一般的查询工作(如条码是否被使用、产品当前状态)都可以在Redis中查询而不是数据库查询,这样大大减轻了数据库压力;
  • 数据库采用了主从机制,实现了读写分离,也是为了提高响应速度;
  • 使用了消息队列MQ和ETL,将一些可以异步处理的动作存放在MQ中,然后由ETL来执行(比如订单完成后以邮件形式通知相关人员);
  • 实现了系统监控,通过Zabbix来对服务器、应用及网络关键设备实行7×24小时监控,重大异常及时邮件通知IT支持人员。

由于总部其它地方生产规模较小,所以生产分布未采用复杂架构,不过因为从客户处退回的不良产品都会在总部生产车间进行返修处理,因此总部生产系统需要保存分部生产车间数据,因此分部生产车间数据会同时写进分部生产数据库和分部MQ服务器,然后由总部ETL服务器读取写入到总部系统中。在分部与总部网络中断的情况下分部系统仍可独立工作,直到网络恢复。

二、系统质量保证

1、单元测试

单元测试是指对软件中的最小可测试单元进行检查和验证。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为,常见的开发语言都有对应的单元测试框架,常见的单元测试工具:Junit/Nunit/xUnit.Net/Microsoft.VisualStudio.TestTool

关于单元测试的重要性和如何编写单元测试用例,在本篇就不详述了,网上有大量相关的文章。总之,越大型的系统、越重要的系统,单元测试的重要性越大。

针对一些需要外部依赖的单元测试,比如需要Web容器等,可以使用mock测试,Java测试人员可以使用EasyMock这个测试框架,其网址是http://easymock.org/。

2、代码质量管理平台


对于多人参与的团队项目,虽然大多数情况下会有编码规范拉指导大家如何编写团队风格一致的编码,但不能保证团队中每个成员、尤其是后期加入的团队成员仍能按照编码规范来编写代码,因此需要有一个平台来保证,在这里推荐SonarQube。

SonarQube是一个开源平台,用于管理源代码的质量。Sonar不只是一个质量数据报告工具,更是代码质量管理平台。支持的语言包括:Java、PHP、C#、C、Cobol、PL/SQL、Flex 等。

主要特点:

  • 代码覆盖:通过单元测试,将会显示哪行代码被选中
  • 改善编码规则
  • 搜寻编码规则:按照名字,插件,激活级别和类别进行查询
  • 项目搜寻:按照项目的名字进行查询
  • 对比数据:比较同一张表中的任何测量的趋势

代码

当然除了代码质量管理平台外,还有借助源代码管理系统,并且在每次提交代码前进行代码审核,这样每次代码的异动都可以追溯出来。我管理和经历过的一些重要系统中采用过这样的做法:除了管理所有程序代码之外,还将系统中数据库中的表、视图、函数及存储过程的创建都使用源代码版本管理工具管控起来,而且粒度很小,每个对象的创建都是一个SQL文件。这种方式虽然操作起来有些琐碎,但对于代码的变迁追溯非常方便。

三、系统性能保证

1、缓存


所谓缓存就是将一些频繁使用、但改动相对不平凡的数据保存在内存中,每次更新这些数据的时候同时持久化到数据库或文件系统,并同步更新到缓存中,查询的时候尽可能利用缓存。

缓存的实现方法:自定义实现或利用NoSQL。

  • 自定义实现

自定义实现可利用SDK中提供的类,如Dictionary等。

优点:可以局部提高查询效率;
缺点:不能跨应用、跨服务器,仅限于单个应用;没有较好缓存生命周期管理策略。

  • NoSQL

Memcached

优点:可以跨应用、跨服务器,有灵活的生命周期管理策略;支持高并发;支持分布式。
缺点:不支持持久化,仅在内存存储,重启后数据丢失,需要“热加载”;仅支持Key/Value。

Redis

优点:可以跨应用、跨服务器,有灵活的生命周期管理策略;支持高并发;支持集群;支持持久化;支持Key/Value、List、Set、Hash数据结构;

以上几种方法都存在一个特点:需要通过Key去寻找对应的Value、List、Set或Hash。

除了Memcached和Redis之外,还出现了一些NoSQL数据库和支持NoSQL的数据库,前者如MongoDB,后者如PostgreSQL(>V9.4),下面是一个MongoDB与PostgreSQL的NoSQL特性的对比:

Redis

文档型NoSQL数据库的特点:

  • 不定义表结构

即使不定义表结构,也可以像定义了表结构一样使用,还省去了变更表结构的麻烦。

  • 可以使用复杂的查询条件 


跟键值存储不同的是,面向文档的数据库可以通过复杂的查询条件来获取数据,虽然不具备事务处理和Join这些关系型数据库所具有的处理能力,但初次以外的其他处理基本上都能实现。

NoSQL主要是提高效率,关系数据库可以保证数据安全;各有使用场景,一般的企业管理系统,没多少并发量没必要使用NoSQL,互联网项目或要求并发的NoSQL使用比较多,但是最终重要的数据还是要保存到关系数据库。这也是为什么很多公司会同时使用NoSQL和关系型数据库的原因。

2、异步


所谓异步就是调用一个方法后并不等该方法执行完毕后再继续执行后续的操作,而是调用完毕后马上等待用户的其它指令。打印机管理程序就是一个异步的例子,某个人可能有几个数百页的文档需要打印,可以在打开一个文档之后点击打印,然后继续打开另一个文档继续点打印。尽管打印数百页文档需要较长时间,但后续的打印请求会在打印管理程序中排队,等第一个文档打印完成后再继续第二个文档的打印。

异步有两个层面:编程语言层面的异步和通过消息队列等机制实现的异步。

语法层面异步:像Java/C#等大多数语言都支持异步处理。

  • 消息队列实现异步

用消息队列实现异步只是消息队列的一个基本功能之一,消息队列还具有如下功能:

  • 解耦
  • 灵活性 & 峰值处理能力
  • 可恢复性
  • 送达保证
  • 排序保证
  • 缓冲
  • 理解数据流
  • 异步通信

注:消息队列成为在进程或应用之间进行通信的最好形式。消息队列队列是创建强大的分布式应用的关键。

常用消息队列有如下,可根据系统特点和运维支持团队的掌握程度选择:

  • MSMQ
  • ActiveMQ
  • RabbitMQ
  • ZeroMQ
  • Kafka
  • MetaMQ
  • RocketMQ

3、负载均衡


负载均衡是根据某种负载策略把请求分发到集群中的每一台服务器上,让整个服务器群来处理网站的请求。

常见负载均衡方案

Windows负载均衡:NLB
Linux负载均衡:LVS
Web负载均衡:Nginx
硬件级负载均衡:F5

前面几种都是免费的解决方案,F5作为一种硬件及解决方案在一般企业很少用到。我目前知道的仅有一家世界级饮料公司使用了F5作为负载均衡解决方案,因为这个方案据说相当昂贵。

4、读写分离


读写分离为了确保数据库产品的稳定性,很多数据库拥有双机热备功能。

也就是,第一台数据库服务器,是对外提供增删改业务的生产服务器;第二台数据库服务器,主要进行读的操作。

原理:让主数据库(master)处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库(slave)处理SELECT查询操作。

一般情况下我们是在代码中进行处理,但目前也有不少商业中间件形式的读写分离中间件,能自动将读写数据库操作调度到不同数据库上。

数据库

在大型系统中,有时候主、从数据库都是一个集群,这样可以保证响应速度更快,同时集群中单台服务器故障也不影响整个系统对外的响应。

四、系统安全性保证

1、XSS攻击

  • 防范XSS攻击

XSS攻击类似于SQL注入攻击,攻击之前,我们先找到一个存在XSS漏洞的网站,XSS漏洞分为两种,一种是DOM Based XSS漏洞,另一种是Stored XSS漏洞。理论上,所有可输入的地方没有对输入数据进行处理的话,都会存在XSS漏洞,漏洞的危害取决于攻击代码的威力,攻击代码也不局限于script。

  • DOM Based XSS

DOM Based XSS是一种基于网页DOM结构的攻击,该攻击特点是中招的人是少数人。

  • Stored XSS

Stored XSS是存储式XSS漏洞,由于其攻击代码已经存储到服务器上或者数据库中,所以受害者是很多人。假如有两个页面,一个负责提交内容,一个负责将提交的内容(论坛发帖、读帖就是这种形式的典型):

提交内容:
页面内容:<%=request.getParameter(“content”)%>

这样用户在a站提交的东西,在显示的时候如果不加以处理就会打开b站页面将相关敏感内容显示出来。

针对XSS攻击的防范办法:

Html encode
特殊字符过滤:<,>

2、SQL注入

  • SQL Injection

所谓SQL注入式攻击,就是攻击者把SQL命令插入到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令。在某些表单中,用户输入的内容直接用来构造(或者影响)动态SQL命令,或作为存储过程的输入参数,这类表单特别容易受到SQL注入式攻击。

例如我们在登录一个系统时,在软件底层按照如下方式查询数据:

登录SQL语句:

SELECT COUNT(*) FROM Login WHERE UserName=’admin’ AND Password=’123456‘
SELECT COUNT(*) FROM Login
WHERE UserName=’admin’–
Password=’123′

SQL

针对SQL注入防范办法:

  • 数据输入验证
  • 特殊字符过滤:特殊字符过滤
  • 参数化SQL语句(包括存储过程)
  • 不使用sa级别账户作为连接账户或限制连接IP

3、CSRF攻击


CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。

其核心策略是利用了浏览器Cookie或者服务器Session策略,盗取用户身份。
针对CSRF攻击防范办法:

  • 表单Token
  • 验证码
  • Referer检查
  • 关键操作身份确认

4、其它攻击


Error Code:即错误代码回显,许多Web服务器为调试方便默认显示详尽错误信息,如错误发生的上下文、服务器及应用信息等,容易被恶意利用。

系统或者框架漏洞:如IIS6.0以下版本存在“JPG漏洞”;Apache Struts2服务在开启动态方法调用任意方法漏洞(CVE-2016-3081);OpenSSL的heartbeat漏洞(CVE-2014-0160);Apache解析漏洞;Nginx(

  • 上传文件时对MIME进行检查,必要情况下对上传文件更名
  • 及时关注安全网站及产品官方网站,发现漏洞及时打补丁
  • 对Web Server运用的用户角色权限进行限制
  • 使用漏洞扫描工具模拟攻击
  • 下面是一些我见过的被攻击后的系统截图,如下图是CCTV音乐频道被攻击的截图:

    还有本人2008年前后搭建PHPWind运行的画面:

    PHPWind

    上图中是本人2006年前后搭建的一个论坛,有人利用系统漏洞注册了很多用户名为空的用户(其实是身份遗失),,然后又利用这些账户在论坛中大量发布广告、色情等违法违纪的帖子,因为使用了一些不可见字符进行注册的,在后台无法管理,最后只好在数据库中操作管理了。

    五、开发相关的经验教训

    1、应用日志记录

    以前团队运维着一个老系统,系统中没有日志功能,而系统的操作人员的计算机水平又较低,每次打电话都是说系统不能用或者是一些根本无法快速定位原因的描述,每次接到求助后需要花费大量时间来分析定位原因,后来将系统中增加了日志功能,并且在网络状态连通情况下可自动将错误日志以邮件形式发送到负责同事组成的用户组,自此以后处理这类问题的响应时间大大缩短了,双方都很满意。

    现在已经有很多开源日志库,比如.NET的Log4Net,Java的Log4j,可以很轻松地配置启用日志功能。利用日志组件可以将信息记录到文件或数据库,便于发现问题时根据上下文环境发现问题,这一点在调试多线程时尤其重要。

    日志级别:FATAL(致命错误)、ERROR(一般错误)、WARN(警告)、INFO(一般信息)、DEBUG(调试信息)。

    注意:在调试环境中时日志级别尽量低(warn/info),在生产环境中日志级别尽量高(error),且对日志文件大小一定要进行控制。不然也会产生问题。

    案例:某国内有名的管业集团公司的一个系统的重要模块发生问题,启用了日志功能以便通过日志组件快速将问题定位并修复。在发布到生产环境时,运行一段时间之后发现程序运行效率相当低下,多位开发人员对模块代码进行性能分析未发现问题,大家发现同样的数据量和操作在生产环境和开发环境效率差巨大,无意中发现生产服务器上日志文件已超过5G!事后发现是由于疏忽未调高日志级别且未对日志进行控制,调整日志模式为按日记录,问题解除。

    参考:《log4net使用详解》 http://blog.csdn.net/zhoufoxcn/article/details/222053

    2、历史记录追踪

    • 代码管控

    尽可能使用代码管控工具对源代码进行管控,如SVN/TFS/Git,如果有可能不但管控程序代码,还要管控数据库相关的SQL文件(包括初始化脚本及存储过程和使用ORM框架中的Mapping文件),做到系统的一切变动皆有记录。

    • 代码审核

    任何人提交代码都必须本人本地编译、调试无误后,再有人review后方可提交,且针对bug修复的提交需注明所修复的bug信息。

    • Bug记录

    通过Bug记录系统记录整个bug的生命周期,包括发现、修复、关闭。TFS本身支持bug记录,开源系统中禅道也是一个不错的Bug记录工具。

    六、总结

    本篇主要是就系统从开发到最终部署运维过程中常用的技术、框架和方法做了一个总结,当然以上经验总结来源于本人从业以来所经历的项目中的经验和教训,可能还有更好更完美的方案,在此权当抛砖引玉

    from:http://www.yunweipai.com/archives/22697.html

    Pinterest谈实战经验:如何在两年内实现零到数百亿的月访问

    Pinterest一直保持着指数增长,每一个半月都会翻一翻。在两年内,他们实现了从0到数百亿的月PV;从开始的两个创始人加一个工程师增长到现在超过40个工程师,从一个小型的MySQL服务器增长到180个Web Enigne、240个API Enigne、88个MySQL DB(cc2.8xlarge,每个DB都会配置一个从属节点)、110个Redis Instance以及200个Mmecache Instance。

    在一个名为 《Scaling Pinterest》 的主题演讲上,Pinterest的Yashwanth NelapatiMarty Weiner为我们讲述了这个戏剧性的过程。当然扩展到当下规模,Pinterest在众多选择中不可避免的走了许多的弯路,而Todd Hoff认为其中最宝贵的经验该归结于以下两点:

    1. 如果你的架构应对增长所带来的问题时,只需要简单的投入更多的主机,那么你的架构含金量十足。
    2. 当你把事物用至极限时,这些技术都会以各自不同的方式发生故障,这导致他们对工具的选择有着特殊的偏好:成熟、简单、优秀、知名、被更多的用户喜爱、更好的支持、稳定且杰出的表现、通常情况下无故障以及免费。使用这些标准,他们选择了MySQL、Solr、Memcache、Redis、Cassandra,同时还抛弃了MongoDB。

    同样这两个点是有关联的,符合第二个原则的工具就可以通过投入更多的主机进行扩展。即使负载的增加,项目也不会出现很多故障。即使真的出现难以解决的问题,至少有一个社区去寻找问题解决的方案。一旦你选择过于复杂和挑剔的工具,在扩展的道路上将充满荆棘。

    需要注意的是所有他们选择的工具都依靠增加分片来进行扩展,而非通过集群。讲话中还阐述了为什么分片优于集群以及如何进行分片,这些想法可能是之前你闻所未闻的。

    下面就看一下Pinterest扩展的阶段性时间轴:

    项目背景

    • Pins是由其它零零碎碎信息集合成的图片,显示了对客户重要的信息,并且链接到它所在的位置。
    • Pinterest是一个社交网络,你可以follow(关注)其他人以及board。
    • 数据库:Pinterest的用户拥有board,而每个board都包含pin;follow及repin人际关系、验证信息。

    1. 2010年3月发布——寻找真我的时代

    在那时候,你甚至不知道需要建立一个什么样的产品。你有想法,所以你快速的迭代以及演变。而最终你将得到一些很小的MySQL查询,而这些查询在现实生活中你从未进行过。

    Pinterest初期阶段的一些数字:

    • 2个创始人
    • 1个工程师
    • Rackspace
    • 1个小的网络引擎
    • 1个小的MySQL数据库
    • 2011年11月

    仍然是小规模,产品通过用户反馈进行演变后的数字是:

    • Amazon EC2 + S3 + CloudFront
    • 1 NGinX, 4 Web Engines (用于冗余,不全是负载)
    • 1 MySQL DB + 1 Read Slave (用于主节点故障情况)
    • 1 Task Queue + 2 Task Processors
    • 1 MongoDB (用于计数)
    • 2 Engineers

    2. 贯穿2011年——实验的时代

    迈上疯狂增长的脚步,基本上每1个半月翻一翻。

    • 当你增长的如此之快,每一天每一星期你可能都需要打破或者抛弃一些东西。
    • 在这个时候,他们阅读大量的论文,这些论文都阐述着只需要添加一台主机问题就会得以解决。他们着手添加许多技术,随后又不得不放弃。
    • 于是出现了一些很奇怪的结果
    • Amazon EC2 + S3 + CloudFront
    • 2NGinX, 16 Web Engines + 2 API Engines
    • 5 Functionally Sharged MySQL DB + 9 read slaves
    • 4 Cassandra Nodes
    • 15 Membase Nodes (3 separate clusters)
    • 8 Memcache Nodes
    • 10 Redis Nodes
    • 3 Task Routers + 4 Task Processors
    • 4 Elastic Search Nodes
    • 3 Mongo Clusters
    • 3个工程师
    • 5个主数据库技术,只为了独立其中的数据。
    • 增长太快以至于MySQL疲于奔命,所有其它的技术也达到了极限。
    • 当你把事物用至极限时,这些技术都会以各自不同的方式出错。
    • 开始抛弃一些技术,并且自我反省究竟需要些什么,基本上重做了所有的架构。

    3. 2012年2月——成熟的时代

    • 在重做了所有的架构后,系统呈现了如下状态
    • Amazon EC2 + S3 + Akamai, ELB
    • 90 Web Engines + 50 API Engines
    • 66 MySQL DBs (m1.xlarge) +,每个数据库都配备了从属节点
    • 59 Redis Instances
    • 51 Memcache Instances
    • 1 Redis Task Manager + 25 Task Processors
    • Sharded Solr
    • 6个工程师
    • 现在采用的技术是被分片的MySQL、Redis、Memcache和Solr,有点在于这些技术都很简单很成熟。
    • 网络传输增长仍然保持着以往的速度,而iPhone传输开始走高。

    4. 2012年10月12日 —— 收获的季节

    大约是1月份的4倍

    • 现在的数据是:
    • Amazon EC2 + S3 + Edge Cast,Akamai, Level 3
    • 180 Web Engines + 240 API Engines
    • 88 MySQL DBs (cc2.8xlarge) ,同样每个数据库都有一个从属节点
    • 110 Redis Instances
    • 200 Memcache Instances
    • 4 Redis Task Manager + 80 Task Processors
    • Sharded Solr
    • 40个工程师(仍在增长)
    • 需要注意的是,如今的架构已趋近完美,应对增长只需要投入更多的主机。
    • 当下已开始转移至SSD

    下面一览该演讲中的干货,决策的制定:

    为什么会选择EC2和S3

    1. 相当好的可靠性,即使数据中心发生故障。多租户会增加风险,但是也不是太坏。
    2. 良好的报告和支持。它们(EC2和S3)有着良好的架构,并且知道问题所在。
    3. 完善的周边设施,特别是在你需要快速增长时。你可以从APP Engine处获得maged cache、负载均衡、MapReduce、数据库管理以及其它你不想自己动手编写的组件,这可以加速你应用程序的部署,而在你工程师空闲时,你可以着手编写你需要的一切。
    4. 新的实例可以在几秒内就绪,这就是云的力量;特别是在只有两个工程师的初期,不需要去担心容量规划,更不需要花两个星期去建立自己的Memcache,你可以在数分钟内添加10个Memcached。
    5. 缺点:有限的选择。直到最近,才可以选择使用SSD,同时无法获得太大的内存配置。
    6. 优点:你不需要给大量的主机进行不同的配置。

    为什么会选择MySQL

    1. 非常成熟。
    2. 非常稳定。不会宕机,并且不会丢失数据。
    3. 在招聘上具有优势,市场上有大把的人才。
    4. 在请求呈直线上升时,仍能将相应时间控制在一定的范围内,有些数据库技术在面对请求的飙升时表现并不是很好。
    5. 非常好的周边软件支持——XtraBackup、Innotop、Maatkit。
    6. 可以从类似Percona这样的公司得到优秀的技术支持。
    7. 开源(免费)——这一点非常重要,特别是在资金缺乏的初期

    为什么使用Memcache

    • 非常成熟。
    • 非常简单。可以当成是一个socket哈希表
    • 杰出稳定的表现
    • 知名并为大量用户喜爱
    • 永不崩溃
    • 开源

    为什么选择Redis

    • 虽然还不够成熟,但是非常简单及优秀
    • 提供了大量的数据结构类型
    • 提供多种的选择进行持久化和备份:你可以备份而非持久化,选择备份的话你还可以选择多久备份一次;同样你还可以选择使用什么方式进行持久化,比如MySQL等。
    • Home feed被储存在Redis上,每3个小时保存一次;然而并不是3个小时持久化一次,只是简单的每3个小时备份一次。
    • 如果你存储数据的主机发生故障,丢失的也只是备份周期内的数据。虽然不是完全可靠,但是非常简单。避免了复杂的持久化及复制,这样的架构简单且便宜。
    • 知名并为大量用户喜爱
    • 稳定且杰出的表现
    • 很少出故障。有一些专有的故障模型,你需要学会解决。这也是成熟的优势,只需要学习就可以解决。
    • 开源

    Solr

    1. 只需要几分钟的安装时间,就可以投入使用
    2. 不能扩展到多于一台的机器上(最新版本并非如此)
    3. 尝试弹性搜索,但是以Pinterest的规模来说,可能会因为零碎文件和查询太多而产生问题。
    4. 选择使用Websolr,但是Pinterest拥有搜索团队,将来可能会开发自己的版本。

    集群vs.分片

    • 在迅速扩展的过程中,Pinterest认识到每次负载的增加,都需要均匀的传播他们的数据。
    • 针对问题先确定解决方案的范围,他们选择的范围是集群和分片之间的一系列解决方案。

    集群——所有的操作都是通过自动化

    • 比如:Cassandra、MemBase、HBase
    • 结论:没有安全感,将来可能会比较成熟,但是当下这个解决方案中还存在太多的复杂性和故障点。
    • 特性:
    • 数据自动分布
    • 节点间转移数据
    • 需要平衡分配
    • 节点间的相互通信,需要做很多措施用于防止干扰、无效传递及协商。
    • 优点:
    • 自动扩展你的数据存储,最起码论文中是这么说的。
    • 便于安装
    • 数据上的空间分布及机房共置。你可以在不同区域建立数据中心,数据库会帮你打理好一切。
    • 高有效性
    • 负载平衡
    • 不存在单点故障
    • 缺点:
    • 仍然不成熟。
    • 本质上说还很复杂。一大堆的节点必须对称协议,这一点非常难以解决。
    • 缺少社区支持。社区的讨论因为产品方向的不同而不能统一,而在每个正营中也缺乏强有力的支持。
    • 缺乏领域内资深工程师,可能大多数的工程师都还未使用过Cassandra。
    • 困难、没有安全感的机制更新。这可能是因为这些技术都使用API并且只在自己的领域内通行,这导致了复杂的升级路径。
    • 集群管理算法本身就用于处理SPOF(单点故障),如果存在漏洞的话可能就会影响到每个节点。
    • 集群管理器代码非常复杂,并且需要在所有节点上重复,这就可能存在以下的故障模式:
    • 数据平衡失控。当给集群中添加新的主机时,可能因为数据的拷贝而导致集群性能下降。那么你该做什么?这里不存在去发现问题所在的工具。没有社区可以用来求助,同样你也被困住了,这也是Pinterest回到MySQL的原因。
    • 跨节点的数据损坏。如果这里存在一个漏洞,这个漏洞可能会影响节点间的日志系统和压缩等其它组件?你的读延时增加,所有的数据都会陷入麻烦以及丢失。
    • 错误负载平衡很难被修复,这个现象十分普遍。如果你有10个节点,并且你注意到所有的负载都被堆积到一个节点上。虽然可以手动处理,但是之后系统还会将负载都加之一个节点之上。
    • 数据所有权问题,主次节点转换时的数据丢失。集群方案是非常智能的,它们会在特定的情况下完成节点权利的转换,而主次节点切换的过程中可能会导致数据的部分丢失,而丢失部分数据可能比丢失全部还糟糕,因为你不可能知道你究竟丢失了哪一部分。

    分片——所有事情都是手动的

    • 结论:它是获胜者。Todd Hoff还认为他们的分片架构可能与Flickr架构类似。
    • 特性:
    • 分片可以让你摆脱集群方案中所有不想要的特性。
    • 数据需要手动的分配。
    • 数据不会移动。Pinterest永远都不会在节点间移动,尽管有些人这么做,这让他们在一定范围内站的更高。
    • 通过分割数据的方式分配负载。
    • 节点并没有互相通信,使用一些主节点控制程序的运行。
    • 优点:
    • 可以分割你的数据库以提高性能。
    • 空间分布及放置数据
    • 高有效性
    • 负载平衡
    • 放置数据的算法非常简单。主要原因是,用于处理单点故障的代码只有区区的半页,而不是一个复杂的集群管理器。并且经过短暂的测试就知道它是否能够正常工作。
    • ID生成非常简单
    • 缺点:
    • 不可以执行大多数的join。
    • 失去所有事务的能力。在一个数据库上的插入可能会成功,而在另一个上会失败。
    • 许多约束必须放到应用程序层。
    • 模式的转变需要从长计议。
    • 报告需要在所有分片上执行查询,然后需要手动的进行聚合。
    • Join在应用程序层执行。
    • 应用程序必须容忍以上所有问题。

    什么时候进行分片

    1. 如果你的项目拥有PB级的数据,那么你需要立刻对其进行分片。
    2. Pin表格拥有百万行索引,索引大小已经溢出内存并被存入了磁盘。
    3. Pinterest使用了最大的表格,并将它们(这些索引)放入自己的数据库。
    4. 然后果断的超过了单数据库容量。
    5. 接着Pinterest必须进行分片。

    分片的过渡

    • 过渡从一个特性的冻结开始。
    • 确认分片该达到什么样的效果——希望尽少的执行查询以及最少数量的数据库去呈现一个页面。
    • 剔除所有的MySQL join,将要做join的表格加载到一个单独的分片去做查询。
    • 添加大量的缓存,基本上每个查询都需要被缓存。
    • 这个步骤看起来像:
    • 1 DB + Foreign Keys + Joins
    • 1 DB + Denormalized + Cache
    • 1 DB + Read Slaves + Cache
    • Several functionally sharded DBs+Read Slaves+Cache
    • ID sharded DBs + Backup slaves + cache
    • 早期的只读从属节点一直都存在问题,因为存在slave lag。读任务分配给了从属节点,然而主节点并没有做任何的备份记录,这样就像一条记录丢失。之后Pinterest使用缓存解决了这个问题。
    • Pinterest拥有后台脚本,数据库使用它来做备份。检查完整性约束、引用。
    • 用户表并不进行分片。Pinterest只是使用了一个大型的数据库,并在电子邮件和用户名上做了相关的一致性约束。如果插入重复用户,会返回失败。然后他们对分片的数据库做大量的写操作。

    如何进行分片

    • 可以参考Cassandra的ring模型、Membase以及Twitter的Gizzard。
    • 坚信:节点间数据传输的越少,你的架构越稳定。
    • Cassandra存在数据平衡和所有权问题,因为节点们不知道哪个节点保存了另一部分数据。Pinterest认为应用程序需要决定数据该分配到哪个节点,那么将永远不会存在问题。
    • 预计5年内的增长,并且对其进行预分片思考。
    • 初期可以建立一些虚拟分片。8个物理服务器,每个512DB。所有的数据库都装满表格。
    • 为了高有效性,他们一直都运行着多主节点冗余模式。每个主节点都会分配给一个不同的可用性区域。在故障时,该主节点上的任务会分配给其它的主节点,并且重新部署一个主节点用以代替。
    • 当数据库上的负载加重时:
    • 先着眼节点的任务交付速度,可以清楚是否有问题发生,比如:新特性,缓存等带来的问题。
    • 如果属于单纯的负载增加,Pinterest会分割数据库,并告诉应用程序该在何处寻找新的节点。
    • 在分割数据库之前,Pinterest会给这些主节点加入一些从属节点。然后置换应用程序代码以匹配新的数据库,在过渡的几分钟之内,数据会同时写入到新旧节点,过渡结束后将切断节点之间的通道。

    ID结构

    • 一共64位
    • 分片ID:16位
    • Type:10位—— Board、User或者其它对象类型
    • 本地ID——余下的位数用于表中ID,使用MySQL自动递增。
    • Twitter使用一个映射表来为物理主机映射ID,这将需要备份;鉴于Pinterest使用AWS和MySQL查询,这个过程大约需要3毫秒。Pinterest并没有让这个额外的中间层参与工作,而是将位置信息构建在ID里。
    • 用户被随机分配在分片中间。
    • 每个用户的所有数据(pin、board等)都存放在同一个分片中,这将带来巨大的好处,避免了跨分片的查询可以显著的增加查询速度。
    • 每个board都与用户并列,这样board可以通过一个数据库处理。
    • 分片ID足够65536个分片使用,但是开始Pinterest只使用了4096个,这允许他们轻易的进行横向扩展。一旦用户数据库被填满,他们只需要增加额外的分片,然后让新用户写入新的分片就可以了。

    查找

    • 如果存在50个查找,举个例子,他们将ID分割且并行的运行查询,那么延时将达到最高。
    • 每个应用程序都有一个配置文件,它将给物理主机映射一个分片范围。
    • “sharddb001a”: : (1, 512)
    • “sharddb001b”: : (513, 1024)——主要备份主节点
    • 如果你想查找一个ID坐落在sharddb003a上的用户:
    • 将ID进行分解
    • 在分片映射中执行查找
    • 连接分片,在数据库中搜寻类型。并使用本地ID去寻找这个用户,然后返回序列化数据。

    对象和映射

    • 所有数据都是对象(pin、board、user、comment)或者映射(用户由baord,pin有like)。
    • 针对对象,每个本地ID都映射成MySQL Blob。开始时Blob使用的是JSON格式,之后会给转换成序列化的Thrift。
    • 对于映射来说,这里有一个映射表。你可以为用户读取board,ID包含了是时间戳,这样就可以体现事件的顺序。
    • 同样还存在反向映射,多表对多表,用于查询有哪些用户喜欢某个pin这样的操作。
    • 模式的命名方案是:noun_verb_noun: user_likes_pins, pins_like_user。
    • 只能使用主键或者是索引查找(没有join)。
    • 数据不会向集群中那样跨数据的移动,举个例子:如果某个用户坐落在20分片上,所有他数据都会并列存储,永远不会移动。64位ID包含了分片ID,所以它不可能被移动。你可以移动物理数据到另一个数据库,但是它仍然与相同分片关联。
    • 所有的表都存放在分片上,没有特殊的分片,当然用于检测用户名冲突的巨型表除外。
    • 不需要改变模式,一个新的索引需要一个新的表。
    • 因为键对应的值是blob,所以你不需要破坏模式就可以添加字段。因为blob有不同的版本,所以应用程序将检测它的版本号并且将新记录转换成相应的格式,然后写入。所有的数据不需要立刻的做格式改变,可以在读的时候进行更新。
    • 巨大的胜利,因为改变表格需要在上面加几个小时甚至是几天的锁。如果你需要一个新的索引,你只需要建立一张新的表格,并填入内容;在不需要的时候,丢弃就好。

    呈现一个用户文件界面

    1. 从URL中取得用户名,然后到单独的巨型数据库中查询用户的ID。
    2. 获取用户ID,并进行拆分
    3. 选择分片,并进入
    4. SELECT body from users WHERE id =
    5. SELECT board_id FROM user_has_boards WHERE user_id=
    6. SELECT body FROM boards WHERE id IN ()
    7. SELECT pin_id FROM board_has_pins WHERE board_id=
    8. SELECT body FROM pins WHERE id IN (pin_ids)
    9. 所有调用都在缓存中进行(Memcache或者Redis),所以在实践中并没有太多连接数据库的后端操作。

    脚本相关

    1. 当你过渡到一个分片架构,你拥有两个不同的基础设施——没有进行分片的旧系统和进行分片的新系统。脚本成为了新旧系统之间数据传输的桥梁。
    2. 移动5亿的pin、16亿的follower行等。
    3. 不要轻视项目中的这一部分,Pinterest原认为只需要2个月就可以完成数据的安置,然而他们足足花了4至5个月时间,别忘了期间他们还冻结了一项特性。
    4. 应用程序必须同时对两个系统插入数据。
    5. 一旦确认所有的数据都在新系统中就位,就可以适当的增加负载来测试新后端。
    6. 建立一个脚本农场,雇佣更多的工程师去加速任务的完成。让他们做这些表格的转移工作。
    7. 设计一个Pyres副本,一个到GitHub Resque队列的Python的接口,这个队列建立在Redis之上。支持优先级和重试,使用Pyres取代Celery和RabbitMQ更是让他们受益良多。
    8. 处理中会产生大量的错误,用户可能会发现类似丢失board的错误;必须重复的运行任务,以保证在数据的处理过程中不会出现暂时性的错误。

    开发相关

    • 开始尝试只给开发者开放系统的一部分——他们每个人都拥有自己的MySQL服务器等,但是事情改变的太快,以至于这个模式根本无法实行。
    • 转变成Facebook模式,每个人都可以访问所有东西,所以不得不非常小心。

    未来的方向

    • 基于服务的架构
    • 当他们发现大量的数据库负载,他们开始布置大量的应用程序服务器和一些其它的服务器,所有这些服务器都连接至MySQL和Memcache。这意味着在Memcache上将存在3万的连接,这些连接将占用几个G的内存,同时还会产生大量的Memcache守护进程。
    • 为了解决这个问题,将这些工作转移到了一个服务架构。比如:使用一个follower服务,这个服务将专注处理follower查询。这将接下30台左右的主机去连接数据库和缓存,从而减少了连接的数量。
    • 对功能进行隔离,各司其职。让一个服务的开发者不能访问其它的服务,从而杜绝安全隐患。

    学到的知识

    1. 为了应对未来的问题,让其保持简单。
    2. 让其变的有趣。只要应用程序还在使用,就会有很多的工程师加入,过于复杂的系统将会让工作失去乐趣。让架构保持简单就是大的胜利,新的工程师从入职的第一周起就可以对项目有所贡献。
    3. 当你把事物用至极限时,这些技术都会以各自不同的方式发生故障。
    4. 如果你的架构应对增长所带来的问题时,只需要简单的投入更多的主机,那么你的架构含金量十足。
    5. 集群管理算法本身就用于处理SPOF,如果存在漏洞的话可能就会影响到每个节点。
    6. 为了快速的增长,你需要为每次负载增加的数据进行均匀分配。
    7. 在节点间传输的数据越少,你的架构越稳定。这也是他们弃集群而选择分片的原因。
    8. 一个面向服务的架构规则。拆分功能,可以帮助减少连接、组织团队、组织支持以及提升安全性。
    9. 搞明白自己究竟需要什么。为了匹配愿景,不要怕丢弃某些技术,甚至是整个系统的重构。
    10. 不要害怕丢失一点数据。将用户数据放入内存,定期的进行持久化。失去的只是几个小时的数据,但是换来的却是更简单、更强健的系统!

    原文链接: Scaling Pinterest – From 0 To 10s Of Billions Of Page Views A Month In Two Years (编译/仲浩 审校/王旭东)

    from:http://www.csdn.net/article/2013-04-16/2814902-how-pinterest-scaling-0-to-billions-pv

    Highscalability All Time Favorites

     

    Posts Teaching the Basics

    from:http://highscalability.com/all-time-favorites/ 2017-7-27

    Facebook Architecture

    1. Facebook Science & Social Graph (Video)
    2. Scale at Facebook
    3. Facebook Chat Architecture
    4. Facebook Blog
    5. Facebook Cassandra Architecture and Design
    6. Facebook Engineering Notes
    7. Quora – Facebook Architecture
    8. Facebook for 600M users
    9. Hadoop & its usage at Facebook
    10. Erlang at Facebook: Chat Architecture
    11. Facebook Performance Caching
    12. Facebook Connect Architecture

    refer:http://stackoverflow.com/questions/3533948/facebook-architecture

    互联网后端基础设施

    对于一个互联网企业,后端服务是必不可少的一个组成部分。抛开业务应用来说,往下的基础服务设施做到哪些才能够保证业务的稳定可靠、易维护、高可用呢?纵观整个互联网技术体系再结合公司的目前状况,个人认为必不可少或者非常关键的后端基础技术/设施如下图所示:

    这里的后端基础设施主要指的是应用在线上稳定运行需要依赖的关键组件/服务等。开发或者搭建好以上的后端基础设施,一般情况下是能够支撑很长一段时间内的业务的。此外,对于一个完整的架构来说,还有很多应用感知不到的系统基础服务,如负载均衡、自动化部署、系统安全等,并没有包含在本文的描述范围内。