百万级访问网站前期的技术准备

 

   (若是感受有帮助,请帮忙点推荐,添加关注,谢谢!你的支持是我不断更新文章的动力。本博客会逐步推出一系列的关于大型网站架构、分布式应用、设计模式、架构模式等方面的系列文章)css

1、服务器硬件  前端

  建议至少三台的标准配置,分别用做web处理数据库备份java

  web服务器至少要8G内存,双sata raid1若是经济稍微宽松,或静态文件或图片多,则15k sas raid1+0数据库至少16G内存,15k sas raid 1+0。备份服务器最好跟数据库服务器同等配置。硬件能够本身买品牌的底板,也就是机箱配主板和硬盘盒,CPU内存硬盘都本身配,也能够上整套品牌,也可 以兼容机。三台机器,市场行情67万也就配齐了。mysql

  web服务器能够既跑程序又当内存缓存,数据库服务器则只跑主数据库(假如是MySQL的话),备份服务器干的活就相对多一些,web配置、缓存配置、数据库配置都要跟前两台一致,这样WEB和数据库任意一台出问题,把备份服务器换个ip就切换上去了。备份策略,能够drbd,能够rsync,或者其余的不少不少的开源备份方案可选择。rsync最简单,放cron里本身跑就行。备份和切换,建议多作测试,选最安全最适合业务的,而且尽量异地备份。nginx

 

2、架构  web

  初期架构通常比较简单,web负载均衡+数据库主从+缓存+分布式存储+队列。sql

  只是您比其余人厉害之处就在于设计上考虑到缓存失效时的雪崩效应、主从同步的数据一致性和时间差、队列的稳定性和失败后的重试策略、文件存储的效率和备份方式等等意外状况。数据库

      缓存总有一天会失效,数据库复制总有一天会断掉,队列总有一天会写不进去,电源总有一天会烧坏。根据墨菲定律,若是不考虑这些,网站迟早会成为茶几。编程

 

3、服务器软件  json

  Linuxnginxjava、mysqlsvn几乎是标配,

  以上准备完毕,如今咱们有了运行环境,有了基本架构骨架,有了备份和切换方案,应该开始着手设计开发方面的事情了。

 

4、数据库

  几乎全部操做最后都要落到数据库身上,它又最难扩展(存储也挺难)。对于mysql,什么样的表用myisam,什么样的表用innodb,在开发以前要肯定。复制策略、分片策略,也要肯定。表引擎方面,通常,更新很少、不须要事务的表能够用myisam,须要行锁定、事务支持的,用innodb。 myisam的锁表不必定是性能低下的根源,innodb也不必定全是行锁,具体细节要多看相关的文档,熟悉了引擎特性才能用的更好。现代WEB应用越来 越复杂了,咱们设计表结构时经常设计不少冗余,虽然不符合传统范式,但为了速度考虑仍是值得的,要求高的状况下甚至要杜绝联合查询。编程时得多注意数据一致性。

  复制策略方面,多主多从结构也最好一开始就设计好,代码直接按照多主多历来编写,用一些小技巧来避免复制延时问题,而且还要解决多数据库数据是否一致,能够本身写或者找现成的运维工具。

  分片策略。总会有那么几个表数据量超大,这时分片必不可免。分片有不少策略,从简单的分区到根据热度自动调整,依照具体业务选择一个适合本身的。避免自增ID做为主键,不利于分片。

  用存储过程是比较难扩展的,这种情形多发生于传统C/S,特别是OA系统转换过来的开发人员。低成本网站不是一两台小型机跑一个数据库处理全部业务的模式,是机海做战。方便水平扩展比那点预分析时间和网络传输流量要重要的多的多。

  NoSQL只是一个概念。实际应用中,网站有着愈来愈多的密集写操做、上亿的简单关系数据读取、热备等,这都不是传统关系数据库所擅长的,因而就产生了不少非关系型数据库,好比Redis/TC&TT/MongoDB/Memcachedb等,在测试中,这些几乎都达到了每秒至少一万次的写操做,内存型的甚至5万以上。例如MongoDB,几句配置就能够组建一个复制+自动分片+failover的环境,文档化的存储也简化了传统设计库结构再开发的模式。不少业务是能够用这类数据库来替代mysql的。

 

5、缓存 

  数据库很脆弱,必定要有缓存在前面挡着,其实咱们优化速度,几乎就是优化缓存,能用缓存的地方,就不要再跑到后端数据库那折腾。缓存有持久化缓存、内存缓存,生成静态页面是最容易理解的持久化缓存了,还有不少好比varnish的分块缓存、前面提到的memcachedb等内存缓存,memcached首当其冲。缓存更新可用被动更新和主动更新。被动更新的好处是设计简单,缓存空了就自动去数据库取数据再把缓存填上,但容易引起雪 崩效应,一旦缓存大面积失效,数据库的压力直线上升极可能挂掉。主动缓存可避免这点可是可能引起程序取不到数据的问题。这二者之间如何配合,程序设计要多动脑筋。

 

6、队列  

  用户一个操做极可能引起一系列资源和功能的调动,这些调动若是同时发生,压力没法控制,用户体验也很差,能够把这样一些操做放入队列,由另几个模块去异步执行,例如发送邮件,发送手机短信。开源队列服务器不少,性能要求不高用数据库当作队列也能够,只要保证程序读写队列的接口不变,底层队列服务可随时更换就能够,相似Zend Framework里的Zend_Queue类,java.util.Queue接口等。

  

7、文件存储  

  除告终构化数据,咱们常常要存放其余的数据,像图片之类的。这类数据数量繁多、访问量大。典型的就是图片,从用户头像到用户上传的照片,还要生成不一样的缩略图尺寸。存储的分布几乎跟数据库扩展同样艰难。不使用专业存储的状况下,基本都是靠本身的NAS。这就涉及到结构。拿图片存储举例,图片是很是容易产生热点的,有些图片上传后就再也不有人看,有些可能天天被访问数十万次,并且大量小文件的异步备份也很耗费时间。

  为了未来图片走cdn作准备,一开始最好就将图片的域名分开,且不用主域名。不少网站都将cookie设置到了.domain.ltd,若是图片也在这个域名下,极可能由于cookie而形成缓存失效,而且占多余流量,还可能由于浏览器并发线程限制形成访问缓慢。

  若是用普通的文件系统存储图片,有一个简单的方法。计算文件的hash值,好比md5,以结果第一位做为第一级目录,这样第一级有16个目录。从0F,能够把这个字母做为域名,0.yourimg.comf.yourimg.com(客户端dns压力会增大),还能够扩展到最多16NAS集群 上。第二级可用年月例如,201011,第三级用日,第四级可选,根据上传量,好比am/pm,甚至小时。最终的目录结构可能会是 e/201008/25/am/e43ae391c839d82801920cf.jpgrsync备份时能够用脚本只同步某年某日某时的文件,避免计算大量文件带来的开销。

  固然最好是能用专门的分布式文件系统或更专业点的存储解决方案。

 

 

下面,咱们要谈谈代码了

  开始设计代码结构以前,先回顾一下以前准备过的事情:咱们有负载均衡的WEB服务器,有主从DB服务器并可能分片,有缓存,有可扩展的存储。在组织代码的各个方面,跟这些准备息息相关,我一二三的列出来分别说,而且每一条都以前面讲到这个经典句式开头,为了方便对照。

  别着急看经典句式,我思惟跳跃了,插一段。

  实际开发中,咱们总会在性能和代码优雅性上做折中。对于当今的计算机和语言解释器,多几层少几层对象调用、声明变量为Map仍是HashMap这种问题是最后才须要考虑的问题,永远要考虑系统最慢的部分,从最慢的部分解决。

  例如看看你用的ORM是否是作了不少你用不到的事情,是否是有重复的数据调用。咱们作的是web应用开发,不是底层框架API,代码易读易懂是保证质量很重要的一方面,你的程序是为了什么而设计,有不一样的方法……算了,这个话题另起一篇文章来讲,扯远了,咱继续……

  前面讲到,WEB服务器是要作负载均衡的,图片服务器是要分开的。对于这点,代码在处理客户端状态时,不要把状态放到单机上,举例,不要用文件session,嗯,常识。 

  若是有可能,最好在一开始就作好用户单点认证的统一接口,包括跨域如何判断状态、静态页面如何判断状态,须要登陆时的跳转和返回参数定义,底层给好接口,应用层直接就用(可参考GAE的 user服务)。登陆方面的设计要考虑移动设备的特性,好比电脑能够用浮动层窗口,但NOKIA自带的浏览器或UCWEB就没法处理这种表现形式,程序必定既能处理AJAX请求又能直接经过URL来处理请求。图片服务器分开,资源文件最好也布局到图片服务器,也就是WEB服务器只服务动态程序。虽然开发测试时稍微复杂(由于须要绝对URI才能访问),但未来页面前端优化上会轻松许多,而且你的WEB服务器IO优化也轻松许多。程序引用资源文件时,要有一个 统一的处理方法,在方法内部能够自动完成不少事情,例如将css/js根据组合,拼成一个文件,或者自动在生成的URI后面加上QUERYSTRING, 若是未来前端用了缓存服务,那生成QUERYSTRING是最简单的刷新服务端缓存和客户端缓存的办法。

  前面讲到,数据库会有复制,可能会多主多从,可能会分片。咱们程序在处理数据的过程当中,最好能抽象出来单独放作一层。拿如今流行的MVC模式来讲,就是在M层下方再放一个数据层,这个数据层不是一般所说的JDBC/PDO/ActiveRecord等,而是你本身的存取数据层,仅对外暴露方法,隐藏数据存取细节。这个数据层内部不要怕写的难看,但必定要提供全部的数据存储功能,其余任何层次不要看到跟数据库打交道的字眼。之因此这样作,是由于在单关系数据库的状况下,可能会SELECT…JOIN…或直接INSERT…INTO…,可你可能会将一些表放到key-value数据库里存储,或者分片,这么作以后原来的语句和方式要所有改变,若是过于分散,则移植时会耗费很大精力,或获得一个很大的Model

  在数据层面的设计上,尽可能避免JOIN查询,咱们能够多作冗余,多作缓存,每种数据尽可能只须要一次查询,而后在你的程序里面进行组合。对于比较复杂的数据组合,在实时性要求不高的状况下,可采用异步处理,用户访问时只取处理后的结果。在对于主键的处理上,避免使用自增ID,能够用必定规则生成的惟一值当作主键,这种主键是最简单的分片分布策略。即便用自增ID,也最好用一个自增ID发生器,不然从数据库不当心被写了一下,那主键很容易冲突。

 

  前面讲到,咱数据库前面还有某些缓存挡着。别把mysqlquery cache当缓存,应用稍复杂的时候QUERY CACHE反而会成为累赘。缓存跟数据库和业务结合的很紧密,正由于跟业务关系紧密,因此这点没有放之四海而皆准的方法。但咱们仍是有一些规则可参照。

  规则一:越接近前端,缓存的颗粒度越大。例如在WEB最前端缓存整个页面,再日后一层缓存部分页面区域,再日后缓存区域内的单条记录。由于越靠近后端,咱们的可操做性越灵活,而且变化最多的前端代码也比较方便编写。在实践中,由于产品需求变化速度很是快,迭代周期愈来愈短,有时很难将Controller和 Model分的那么清楚,Controller层面处理部分缓存必不可免,但要保证若是出现这种状况,Controller所操做的缓存必定不要影响其余 数据需求方,也就是要保证这个缓存数据只有这一个Controller在用。

  规则二:没有缓存时程序不能出错。在不考虑缓存失效引起的雪崩效应时,你的程序要有缓存跟没缓存一个样,不能像新浪微博同样,缓存一失效,粉丝微博全空,整个应用都乱套了。在缓存必不可少的状况下,给用户出错信息都比给一个让人误 解的信息强。

  规则三,缓存更新要保证原子性或称做线程安全,特别是采用被动缓存的方式时,极可能两个用户访问时致使同一个缓存被更新,一般状况这不是大问 题,可缓存失效后重建时极可能是引起连锁反应的缘由之一。

  规则四:缓存也是有成本的。不仅是技术成本,还有人工时间成本。若是一个功能使用缓存和不使用, 在可预见的访问量状况下区别微小,但使用缓存会使复杂度增长,那就不用,咱们能够加个TODO标注,在下次迭代的时候加上缓存处理。

 

  前面讲到,文件存储是独立的,那么全部的文件操做就都是远程调用。能够在文件服务器上提供一个很简单的RESTful接口,也能够提供xmlrpc json serveiceWEB服务器端所生成和处理的文件,所有经过接口通知文件服务器去处理,WEB服务器自己不要提供任何文件存储。你会发现不少大网站的上传图片跟保存文章是分两步完成的,就是基于这个缘由。

  以上几条前面讲到,其实无数人都讲过,我也只是结合前几篇文章用本身的话重复了一遍,真正分析起来精髓很简单——除了良好的功能逻辑分层,咱们还要为数据库存储、缓存、队列、文件服务等程序外层资源调用单独设计接口,你能够把你的程序想象成是运行在Amazon EC2上并用他的全部web service服务,你的数据库就是它的SimpleDB,你的队列就是他的SQS,你的存储就是他的S3,惟一不一样是amazon的接口是远程调用,你的是内部调用。

  将支撑服务接口化,意味着将MySQL更换到PostgreSQL不须要更改业务处理程序,移植团队甚至不须要跟业务开发团队过多沟通;意味着业务开发团队是对接口编程而不是对数据库编程;意味着不会由于某个业务开发人员的失误而拖垮性能。

 

  对程序扫盲不感兴趣的直接看这里——

  没有100%也有99.9%的网站安装了访问统计代码.

相关文章
相关标签/搜索