# 服務器的優化
作為一個技術人員,不能犯兩種錯誤:一個是安全問題、一個是高并發的問題。前面我們說了安全問題,這里我們再來說說高并發的問題。
產品的新增用戶越來越多,本來產品走勢很好。但可能會因為不能承受高并發,影響用戶正常使用功能,用戶就都紛紛去用競爭對手的產品了。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伍星老師也是本書作者之一,他曾是開心網創始團隊成員,親手部署過上千臺服務器,處理過上億的高并發,他們的全棧課是能真正學到如何處理高并發的,我也在全棧課里面為大家講幾節課。