针对大流量高并发,不管是高富帅还是屌丝,首先考虑的都是如何用最少的资源处理最多的业务。一般来说,网站架构最初需要考虑三个方面:数据库瓶颈、代码执行效率和服务器端的配置。如果说这三块,网上千篇一律的资料还是蛮多的,例如Apache和Nginx的争论就能连篇累牍,所以我还是结合项目开发中经验总结一下,内容空洞枯燥无味,非码农请慎重。
1. 合理设计使用数据库
很多关于数据库的疑问其实都可以归纳到这两点:如何设计高性能的数据库?如何使用好数据库才能让数据库按照我们的意愿去工作?
首先,设计数据库之初就应该预估可能产生的数据量和所能承受的压力,该分表的分表,该分区的分区,该建立索引的就建立针对类型的索引,该读写分离的就趁早的做好数据库主从或者数据库集群。虽然大多数互联网公司都有专业的DBA,但是需求第一线的码农必须具有设计优秀数据库的能力,否则在下一步中就不得不消耗较多的资源。
其次,合理使用数据库,基础的概念已经老生常谈的内容:尽量少用或者不用JOIN、IN和GROUP BY等查询和临时表、数据库端编程,配置my.cnf记录慢查询,增加查询内存等。关于JOIN查询,每次JOIN都是两个表数据量的乘积(笛卡尔积),所以尽可能的在程序中处理。关于数据库段编程,我以前倒是经常在SQL Server中写存储过程和触发器,不过在MySQL中除非迫不得已一般都不会使用这些自定义函数,因为数据库的压力已经很大了。不过类似淘宝那种特征量对特征值的设计有时候是不可避免的使用多个JOIN查询的,这个就需要专业的DBA去从底层优化了。
下面这个SQL语句是一个很典型的反面教程,8000条数据卡死了,要是有人当着我的面写这样的查询我就地干掉他:
SELECT COUNT(*) FROM `dbkxdhk`.`shop_goods` AS g ,(SELECT goods_id, attr_value AS cut FROM `dbkxdhk`.`shop_goods_attr` WHERE attr_id=2) AS cut,(SELECT goods_id, attr_value AS color FROM `dbkxdhk`.`shop_goods_attr` WHERE attr_id=3) AS color,(SELECT goods_id, attr_value AS symmetry FROM `dbkxdhk`.`shop_goods_attr` WHERE attr_id=6) AS symmetry,(SELECT goods_id, attr_value AS clarity FROM `dbkxdhk`.`shop_goods_attr` WHERE attr_id=4) AS clarity,(SELECT goods_id, attr_value AS polish FROM `dbkxdhk`.`shop_goods_attr` WHERE attr_id=5) AS polish,(SELECT goods_id, attr_value AS cert FROM `dbkxdhk`.`shop_goods_attr` WHERE attr_id=8) AS cert,(SELECT goods_id, attr_value AS carat FROM `dbkxdhk`.`shop_goods_attr` WHERE attr_id=1) AS carat,(SELECT goods_id, attr_value AS location FROM `dbkxdhk`.`shop_goods_attr` WHERE attr_id=62) AS location,(SELECT goods_id, attr_value AS certificate FROM `dbkxdhk`.`shop_goods_attr` WHERE attr_id=8) AS certificate WHERE g.goods_id = cut.goods_id AND g.goods_id = color.goods_id AND g.goods_id = symmetry.goods_id AND g.goods_id = clarity.goods_id AND g.goods_id = polish.goods_id AND g.goods_id = cert.goods_id AND g.goods_id = carat.goods_id AND g.goods_id = location.goods_id AND g.goods_id = certificate.goods_id AND g.is_on_sale = 1 AND g.cat_id=1 AND g.is_alone_sale = 1 AND g.is_delete = 0 AND g.cat_id IN ("1","2","7","9","8")
最后,关于数据库缓存,现在一般都使用Memcache,PHP等语言也已经对Memcache提供了足够多的支持。此外也可以优化下数据库自身的缓存设置,例如增加查询内存等。而Key-Value的NoSQL的火爆能否完全解决关系型数据库的瓶颈这个我也没有实际的经验,我只能说在某些领域内或许NoSQL可以帮助我们更好的使用关系型数据库。
2. 代码效率 - 细节决定成败
一个最基础的例子,PHP中单引号和双引号的区别。我开始写PHP的时候基本上都用双引号,因为这样在字符串中我可以直接填入变量,例如:
$sql = "SELECT * FROM test WHERE id=$id";$arr["key"] = $value;
不过,zend在编译PHP代码的时候,如果是双引号的字符串,会首先针对整个字符串进行一次扫描以判断这个字符串是否有需要被替换的变量存在,而单引号的字符串则不会存在这个问题,所以说单引号字符串的执行效率是要高于双引号字符串的。
$sql = "SELECT * FROM test WHERE id=".$id;$arr["key"] = $value;
不要觉的这样的写法吹毛求疵,如果一个系统庞大的一定地步,这样的效率损失的绝对要避免的。其他代码问题网上有很多建议。
3. 选择最合适的服务
关于Apache和Nginx的争论从来没有少过,就像不同开发语言之间的争论一样。infoQ上有一篇不错的文章:和LNMP相比 LNMPA是否效率更高? 。同样,作为一个从Apache学起的码农,我是比较青睐Apache,在Ubuntu配置开发环境很方便,并且了解的Apache模块比较多,对Nginx了解的比较少。不过Nginx确实在处理静态文件上的效率要高出很多,所以目前大部分项目均采用Apache的前提下,已经开始实施把css、js、图片等静态文件部署到Nginx服务器上,无法实现静态话的程序比如一些API还是部署到Apache。在部署我自己开发的博客之前,我也曾经在服务器上编译了Nginx、MySQL和PHP-fpm,不过后来考虑到知之甚少,深入学习会影响到我最近的安排而作罢。
4. 使用缓存或者CDN减少HTTP请求
一个HTTP请求要经历连接、请求、应答和关闭4个主要过程,数据包经过多个层次的网络协议,所以减少HTTP请求是可以有效的降低服务器压力的。一般来说,网页中最消耗流量的图片甚至是js和css都可以尽可能的缓存到用户的浏览器,这样不仅仅是可以提高再次访问的速度。如果经济能力允许,最好的缓存到CDN。
5. 合并压缩静态文件
可以使用雅虎的Yslow去页面分析,将Css中用到的图片尽可能的合并为一张大图,同理,也不要使用过多的Css和Javascript文件,能合并的合并,能压缩的压缩。然后跳回上一步中执行缓存或者CDN即可。然后,页面内容可以采用Apache的gzip模块进行压缩,文字内容的压缩比例是非常高的。
6. 页面静态化
静态化与否和SEO没有关系,不管是动态还是静态的页面,爬虫抓取到的内容是一样的,不一样的只有抓取的速度问题。同样访问者也一样,静态页面可以在一定程度上提高加载速度,最重要的是可以强有力的缓解数据库的压力。以我这个博客为例,页面全部静态化,访问者唯一能够触发的就是页面访问次数的更改,其他时候只有在页面内容发生改变,比如新增加了评论或者新修改了文章内容后才会重新读取数据库生成页面。
理论上来说大部分网站都是可以静态化的,不过静态化存在服务器IO这个很严重的瓶颈,所以现在大互联网公司采用缓存的方式较多,缓存+squid或者varnish的方式除了数据库效率外和静态页面的加载速度确实基本上没差别的。当然还有很多应用是不能生成静态页面的,比如微博、SNS以及API等。
上面的是我浅显的总结了一些高流量高并发网站的基础工作,可能存在不少适用范围较小的地方。不足和缺点请不吝赐教。
文章来源:飞晏博客,转载请注明出处。