# 服务器的优化   作为一个技术人员,不能犯两种错误:一个是安全问题、一个是高并发的问题。前面我们说了安全问题,这里我们再来说说高并发的问题。   产品的新增用户越来越多,本来产品走势很好。但可能会因为不能承受高并发,影响用户正常使用功能,用户就都纷纷去用竞争对手的产品了。2015 年初足记 APP 突然走红,但因为足记承受不了高并发,足足宕机一周时间。猪八戒网以前竞争对手很多,他们的人告诉我之所以他们能幸存下来,是因为几年前新闻媒体大量报道威客模式时,流量突然猛增,他们意识到了流行的趋势,在那期间大量增加服务器,而他们的竞争对手不以为然导致最后承受不了高并发,然后用户都来猪八戒网了。 要让程序能承受高并发,我总结了 6 个要点,大家可以参考。 * 1、首先第一个是服务器层面的优化,服务器层面主要涉及到网络参数、操作系统配置的优化。以打开文件描述符的数目限制优化为例,缺省情况下是 1024 ,而对于高并发项目,很容易就超出了这个数目。所以可以使用 ulimit -HSn 65536 等来让修改生效。 另外,对于系统网络参数也需要做一些调整。 比如: net.ipv4.ip_forward net.ipv4.tcp_tw_reuse 等一系列的参数,都是有助于在不同场景下,网络配置的改善。 * 2、做好数据库优化 后端程序最可能出现瓶颈地方就是数据库,数据库做好了优化能让程序速度快很多。数据库优化分成以下几点来讲: * A、配置优化  首先第一个是设置好 MySQL 的配置, query_cache_* 相关的配置项可配置查询缓存,当然查询缓存,只有在读多写少的数据库中才能起到较好的作用,否则就不如直接关闭, key_buffer 可配置索引缓存, thread_cache_size 可配置线程缓存, tmp_table_size 配置临时表的大小。 另外还有各引擎一些自己的特殊的优化配置,比如 innodb_buffer_pool_size 这个值注意一般设置到内存的 2/3 甚至更多一些,innodb_per_file_table 有助于我们在清理无用数据表时释放空间。   * B、引擎优化   首先我们要选择好 MySQL 引擎,InnoDB 的优点是事务处理和行锁定,如果是一般的读写量可以使用 MyISAM,但是在高并发的情况下,建议使用 InnoDB,因为行锁定能支持更大的并发,而 MyISAM 是表锁定,当写量到一定的程度,会导致频繁锁表。 现在 InnoDB 也是 MySQL 默认的存储引擎。   * C、索引优化  要做好 MySQL 索引优化,如果 MySQL 没有索引,每次查询都是全表扫描,数据量大后性能就差,可以用 explain 来分析 MySQL 语句的性能,索引也分为普通索引、唯一索引、主键、复合索引等类型,在不同的情况下,使用不同类型的索引,效率也不甚相同。 * D、表结构与语句优化   做好表结构的优化,首先是需要进行合理的数据表设计 ,比如经常更新的字段和不怎么更新的字段分拆开来,比如大内容字段和一般描述性字段分拆开来,适当用一些冗余字段从而减少程序 SQL 语句出现 join 查询的情况, join 查询的性能比较低, group by 查询的性能更低,不要在访问量大的页面使用 group by 查询。 在高并发的分库分表项目中,我们不建议使用 JOIN 之类的查询,因为表与表之间不能确保在同一个数据库实例上,那解决办法就是用多个简单查询来达到一个组合查询同样的效果,虽然看似多了查询,但是效率方面,多个简单查询的速度远比一个复杂查询快,消耗的资源更少。 * 3、使用缓存 在高并发系统中,使用缓存可谓重中之重。并且缓存牵涉的种类很多,在各个层面都有。 从CPU的一级缓存、二级缓存,到宏观的CDN,都是缓存的类型,下面就几种重点缓存分别进行说明 : * A、在浏览器的层面,可以根据 HTTP 协议将缓存设置在用户的浏览器。 * B、访问路径的缓存,即 CDN 等的缓存,可以使用 Varnish、Squid 等实现缓存,降低对源站的压力,同时也加快浏览器端用户的访问速度,提高用户体验。 * C、在 Web 服务的层面, PHP 可以开启 Zend Opcache、APC 等扩展,可以对 Opcache 进行缓存,而在 Apache,Nginx,Varnish 等运行环境都有缓存模块,数据不经常更新的页面可以开启运行环境的缓存。 * D、另外,在服务器上,我们还可以使用APC、 XCache 等,对远程查询的条目,在Web服务器进行缓存,不过这些缓存仅限于本机使用。 * E、集中式的缓存,一般有 Memcached,可以对数据库查询结构进行缓存,从而降低数据库的压力。 * 4、使用队列   对于一些耗时的程序可以使用队列处理, NSQ,Gearman,Redis 都可以做队列。   使用队列可以减轻服务器的负载。 举一个应用场景,比如有一个招聘产品用户上传简历后要对简历进行分析,要提取简历的文字,要生成简历截图,这是一个很耗时的工作,每次分析简历都可能需要1分钟。在不用队列的情况下,如果有 1000 人同时在上传简历,那么就有 1000 个进程同时处理简历,每个进程都要花 1 分钟,此时服务器负载会十分高,内存和 CPU 也不够用,服务器会宕机。 而如果我们使用队列,让简历分析排队来处理,一次同时只处理 1、2份简历,服务器负载不会那么高。用户上传简历然后往队列里面加一个任务,再给用户显示一个 “简历分析中” 的页面,让用户等待一会儿, 这个页面每隔几秒钟会调用接口查询简历是否分析完成,如果分析完成页面就显示分析结果。 用了队列后 当访问量大的时候 服务器并不回宕机, 可能只是用户等待分析结果的时间会长一些而已。   12306网站上面购买火车票时下订单的时候也使用了队列。平时都能快速买到票,但在春节高峰期的时候下单等待时间能让用户等半个小时,但他们如果不用队列是支持不了这么大量的高并发的。 * 5、搭建分布式环境   当访问量大到一定程度时,一台服务器不能支持访问,需要多台服务器,这时候需要搭建分布式环境, 可以用 Nginx ,LVS 等做负载均衡来搭建分布式环境。 可以用 Docker 封装我们的应用,这样每次要扩容时启动 Docker 十分的快。   我们的数据库也可以做分布式的分库分表和主从读写分离,既提高了数据的安全性,又降低了单位节点的响应压力,当然,如果面对分布式数据库,就需要有相应的连接池工具来协调,否则将切换逻辑写死在程序中是不现实的。 * 6、压缩文件   Apache,Nginx 运行环境有压缩模块, PHP可以设置配置项 zlib.output_compression 进行压缩,前端 JS、CSS 文件可以用工具压缩减少体积 。 图片可以用 css sprite 的方法切图,将多张图片合并在一张图片上提高加载图片的速度。   对文件做上述压缩后,用户访问使用流量减少,用户访问速度更快。 * 7、使用云计算   支持高并发有一个最简单的方法就是使用云计算。分布式环境,分布式数据库这些环境云计算的服务商已经搭建好了,我们只管用就可以。而且云计算是弹性计费的, 用多少付多少,不像购买服务器,一年没有多少流量也要不少的服务器租赁费,使用云计算,我们不用把重心放在服务器的搭建和维护上,而可以专心开发自己的产品。阿里云,青云等都是 IaaS 类的云,他们提供的只是基础服务,使用它们我们还是需要花精力来维护操作系统。大家可以试一试新浪云 SAE,它是PaaS类的云,提供的是程序运行平台,我们连操作系统都不用维护。   我们要对自己的系统能承载的并发量进行测试,要知道自己系统能承受多大并发, 并对系统流量进行监控, 当发现流量变大,快到达最大能承受并发量时应即使扩容。 测试并发量可以用压力测试工具(如 ab,wrk,webbench等)。 可以用 OneAPM 对程序性能监控,实时知道应用的流量。   关于服务器优化的知识很多,本书由于字数限制只能做简要的介绍,大家还需收集其他资料来学习或者学习优才学院的web全栈课程,优才学院的 CEO伍星老师也是本书作者之一,他曾是开心网创始团队成员,亲手部署过上千台服务器,处理过上亿的高并发,他们的全栈课是能真正学到如何处理高并发的,我也在全栈课里面为大家讲几节课。