摘要:業(yè)務和架構不分家,架構是建立在對業(yè)務的理解之上的。主鍵最好保持順序遞增,隨機主鍵會導致聚簇索引樹頻繁分裂,隨機增多,數(shù)據(jù)離散,性能下降。沒有索引的更新,可能會導致全表數(shù)據(jù)都被鎖住。
性能優(yōu)化的原則本博客并非全部原創(chuàng),其實是一個知識的歸納和匯總,里面我引用了很多網(wǎng)上、書上的內(nèi)容。也給出了相關的鏈接。
本文涉及的知識點比較多,大家可以根據(jù)關鍵字去搜索相關的內(nèi)容和購買相應的書籍進行系統(tǒng)的學習。不對的地方大家予以批評指正。
有人給我留言說,億級 PV 就別寫文章了,隨便用幾個開源軟件就能搞定了,只要不犯什么大錯。我不以為然,如果你利用了相同的思想,使用了更高性能的基礎服務,也許就能支持更多的流量并發(fā),節(jié)約更多的服務器,優(yōu)化的思路才是重點。
本內(nèi)容的視頻分享見我的直播
PHP 進階之路 - 億級 pv 網(wǎng)站架構的技術細節(jié)與套路
PHP 進階之路 - 億級pv網(wǎng)站架構實戰(zhàn)之性能壓榨
性能優(yōu)化是建立在對業(yè)務的理解之上的
性能優(yōu)化與架構、業(yè)務相輔相成、密不可分的
性能優(yōu)化的引入我們先看一張簡單的 web 架構圖
從上到下從用戶的瀏覽器到最后的數(shù)據(jù)庫,那么我們說先前端的優(yōu)化。
前端優(yōu)化雅虎軍規(guī):http://www.cnblogs.com/paul-3...
圖片、css、script等等這些都會增加http請求數(shù),減少這些元素的數(shù)量就能減少響應時間。
把多個JS、CSS在可能的情況下寫進一個文件,頁面里直接寫入圖片也是不好的做法,應該寫進CSS里,小圖拼合后利用 background 來定位。
現(xiàn)在很多 icon 都是直接做成字體,矢量高清,也減少網(wǎng)絡請求數(shù)
現(xiàn)在的前端框架都會通過組件的方式開發(fā),最后打包生成一個 js 或者 兩個 js 文件 + 一個 css 或者兩個 css 文件。
利用瀏覽器緩存expires,cache-control,last-modified,etag
http://blog.csdn.net/eroswang...
防止緩存,比如資源更新了,原來的做法是?v=xxxx 現(xiàn)在前端的打包工作可以能會生成 /v1.2.0/xxx.js
接地氣利用 cdn 存儲前端資源
多域名訪問資源原因一:瀏覽器對同一域名的并行請求數(shù)有上限,多個域名則支持更多并行請求
原因二:使用同一域名的時候無用的 cookie 簡直是噩夢
數(shù)據(jù)壓縮開啟gzip
前端資源本身的壓縮,js/css 打包編譯(去掉空格,語意簡化)圖片資源的壓縮等。
優(yōu)化首屏展示速度資源的按需加載,延時加載 https://mengkang.net/229.html
圖片的懶加載,淘寶的商品介紹太多圖,用戶點擊進來又有多少人一直往下看圖的呢?
nginx 優(yōu)化
分為下面三個部分來
worker_processes auto 設置多少子進程
worker_cpu_affinity 親緣性綁定
worker_rlimit_nofile 65535 worker 進程打開的文件描述符的最大數(shù)
worker_connections 65535 子進程最多處理的連接數(shù)
epoll 多路復用
sendfile on 是對文件I/O的系統(tǒng)調(diào)用的一個優(yōu)化,系統(tǒng)api
如果是反向代理web服務器,需要配置fastcgi相關的參數(shù)
數(shù)據(jù)返回開啟gzip壓縮
靜態(tài)資源使用 http 緩存協(xié)議
開啟長連接 keepalive_timeout
fastcgi_connect_timeout 300; fastcgi_send_timeout 300; fastcgi_read_timeout 300; fastcgi_buffer_size 64k; fastcgi_buffers 4 64k; fastcgi_busy_buffers_size 128k; fastcgi_temp_file_write_size 256k;
gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.0; gzip_comp_level 2; gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/json; gzip_vary on; gzip_proxied expired no-cache no-store private auth; gzip_disable "MSIE [1-6].";
location ~ .*.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; }
http://mailman.nginx.org/pipe...
tcp/ip 網(wǎng)絡協(xié)議配置的優(yōu)化/proc/sys/net/ipv4/tcp_tw_recycle 1 開啟TCP連接中TIME-WAIT sockets的快速回收,保證tcp_timestamps = 1
/proc/sys/net/ipv4/tcp_tw_reuse 1 允許將TIME-WAIT sockets重新用于新的TCP連接 https://mengkang.net/564.html
/proc/sys/net/ipv4/tcp_syncookies 0 是否需要關閉洪水抵御 看自己業(yè)務,比如秒殺,肯定需要關閉了
/proc/sys/net/ipv4/tcp_max_tw_buckets 180000 否則經(jīng)常出現(xiàn) time wait bucket table overflow
tcp_nodelay on 小文件快速返回,我之前通過網(wǎng)絡掛載磁盤出現(xiàn)找不到的情況
tcp_nopush on
tcp_tw_recycle 快速回收可能導致丟包的問題 https://mengkang.net/1144.htmllinux 系統(tǒng)的優(yōu)化
除了上面的網(wǎng)絡協(xié)議配置也是在系統(tǒng)基礎之外,為了配合nginx自己里面的設定需要做如下修改
/proc/sys/net/core/somaxconn 65535
ulimit -a 65535
更多詳細的優(yōu)化配置說明:http://os.51cto.com/art/20140...
php 優(yōu)化 升級到 php7注意有很多函數(shù)和擴展被廢棄,比如mysql相關的,有風險,做好測試再切換。
opcode 緩存
php 5.5 之后好像就內(nèi)置了吧,需要在php.ini里添加如下配置
opcache.revalidate_freq=60 opcache.validate_timestamps=1 opcache.max_accelerated_files=1000 opcache.memory_consumption=512 opcache.interned_strings_buffer=16 opcache.fast_shutdown=1
opcache.revalidate_freq
這個選項用于設置緩存的過期時間(單位是秒),當這個時間達到后,opcache會檢查你的代碼是否改變,如果改變了PHP會重新編譯它,生成新的opcode,并且更新緩存。
opcache.validate_timestamps
當這個選項被啟用(設置為1),PHP會在opcache.revalidate_freq設置的時間到達后檢測文件的時間戳(timestamp)。
opcache.max_accelerated_files
這個選項用于控制內(nèi)存中最多可以緩存多少個PHP文件。
opcache.memory_consumption
你可以通過調(diào)用opcachegetstatus()來獲取opcache使用的內(nèi)存的總量
opcache.interned_strings_buffer
字符串opcache的復用,單位為MB
opcache.fast_shutdown=1
開啟快速停止續(xù)發(fā)事件,依賴于Zend引擎的內(nèi)存管理模塊
php7 hugepage 的使用Hugepage 的作用:間接提高虛擬地址和內(nèi)存地址轉換過程中查表的TLB緩存命中率
opcache.huge_code_pages=1
鳥哥博客詳細介紹:http://www.laruence.com/2015/...
代碼偽編譯以thinkphp為例,它會把框架基礎組件(必須用到的組件)合并壓縮到一個文件中,不僅減少了文件目錄查找,文件打開的系統(tǒng)調(diào)用。
通過stracephp-fpm子進程,可以清楚系統(tǒng)調(diào)用的過程,在我上面例子中有打開一個文件有12次系統(tǒng)調(diào)用(只是舉例,我這里相對路徑設置的原因導致多了兩次文件查找)。如果有10個文件,那就是120次,優(yōu)化的效果可能不是那么明顯,但是這是一種思路。
順便說下 set_include_path能不用就不要用,上面的demo的截圖里面找不到目錄就是證明。
模板把它們自定義的語法,最后轉換成php語法,這樣方便解析。而不是每次都解析一遍。
我的截圖一直上傳不成功,正好社區(qū)有這樣的博客,推薦下 https://segmentfault.com/a/11...
業(yè)務優(yōu)化 非侵入式擴展開發(fā)比如原來有一個model,叫問答,現(xiàn)在需要開發(fā)一個有獎問答,需要支持話題打賞,里面多了很多功能。這個時候應該利用面向對象的繼承的特性。而不是做下面的開發(fā)
這樣邏輯多了,子類型多了,邏輯判斷就非常重復,程序運行起來低效可能是一方面,更多的是不可維護性。
業(yè)務和架構不分家,架構是建立在對業(yè)務的理解之上的。再放下上次直播的PPT (sf故障無法傳圖,等會補吧)
異步思想舉例:
處理郵件發(fā)送。
gearman 圖片裁剪。
頁面上 ajax 加載動態(tài)數(shù)據(jù)。
圖片的懶加載,雙擊圖片看大圖。
sf 上通過websocket 通知你有新的消息,但是并沒有告訴你有什消息,點擊消息圖標才會去異步請求具體的消息。
這些都是異步的思想。能分步走就分步走,能不能請求的就不請求。
靜態(tài)化專題頁面,比如秒殺頁面,為了應對更大的流量、并發(fā)。而且更新起來也比較方便。
業(yè)務解耦比如剛剛上面說的專題頁面,還有必要走整個框架的一套流程嗎?進來引用一大堆的文件,初始化一大堆的東西?是不是特別低效呢?所以需要業(yè)務解耦,專題頁面如果真要框架(可以首次訪問之后生成靜態(tài)頁面)也應該是足夠輕量級的。不能與傳統(tǒng)業(yè)務混為一談。
分布式以及 soa說業(yè)務優(yōu)化,真的不得不提架構方面的東西,業(yè)務解耦之后,就有了分布式和soa,因為這在上次分享中已經(jīng)都說過了,就不多說了。
Mysql 優(yōu)化
只說下 soa 自定義 socket 傳輸協(xié)議。
最重要的就是在自定義頭里面強調(diào)body_len,注意設置為緊湊型,才能保證跨平臺性 具體說明:https://mengkang.net/586.html數(shù)據(jù)索引相關的文章網(wǎng)上很多了,不足的地方大家補充。
表設計 - 擁抱 innodb現(xiàn)在大多數(shù)情況都會使用innodb類型了。具體原因是 mysql 專家給的意見。
表設計 - 主鍵索引
我自己對 mysql 的優(yōu)化不了解,每一個細分領域都是一片汪洋,每個人的時間精力是有限的,所以大家也不用什么都非要深入去研究,往往是一些計算機基礎更為重要。
參考這份ppt https://static.mengkang.net/u...innodb 需要一個主鍵,主鍵不要有業(yè)務用途,不要修改主鍵。
主鍵最好保持順序遞增,隨機主鍵會導致聚簇索引樹頻繁分裂,隨機I/O增多,數(shù)據(jù)離散,性能下降。
舉例:
表設計 - 字段選擇
之前項目里有些索引是article_id + tag_id 聯(lián)合做的主鍵,那么這種情況下,就是業(yè)務了屬性了。主鍵也不是順序遞增,每插入新的數(shù)據(jù)都有可能導致很大的索引變動(了解下數(shù)據(jù)庫b+索引的原理)能選短整型,不選長整型。比如一篇文章的狀態(tài)值,不可能有超過100種吧,不過怎么擴展,沒必要用int了。
能選 char 就避免 varchar,比如圖片資源都有一個hashcode,固定長度20位,那么就可以選char了。
當使用 varchar 的時候,長度夠用就行,不要濫用。
大文本多帶帶分離,比如文章的詳情,多帶帶出一張表。其他基本信息放在一張表里,然后關聯(lián)起來。
冗余字段的使用,比如文章的詳情字段,增加一個文章markdown解析之后的字段。
索引優(yōu)化大多數(shù)情況下,索引掃描要比全表掃描更快,性能更好。但也不是絕對的,比如需要查找的數(shù)據(jù)占了整個數(shù)據(jù)表的很大比例,反而使用索引更慢了。
沒有索引的更新,可能會導致全表數(shù)據(jù)都被鎖住。所以更新的時候要根據(jù)索引來做。
聯(lián)合索引的使用
explain 的使用
聯(lián)合索引“最左前綴”,查詢優(yōu)化器還會幫你調(diào)整條件表達式的順序,以匹配組合索引的要求。
CREATE TABLE `test` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `a` int(10) unsigned NOT NULL, `b` int(10) unsigned NOT NULL, `c` int(10) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `index_abc` (`a`,`b`,`c`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;能使用到索引
explain select * from test where a=1; explain select * from test where a=1 and b=2; explain select * from test where a=1 and b=2 and c=3; explain select * from test where a=1 and b in (2,3) and c=3; explain select * from test where a=1 and b=2 order by c desc;不能使用索引
explain select * from test where a=1 and b in (2,3) order by c desc; explain select * from test where b=2;索引更詳細講解 https://mengkang.net/1302.htmlexplain 搜到一篇不錯的: http://blog.csdn.net/woshiqjs...
type 最常見的
很重要的參數(shù)type,key,extrasystem > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
值 | 說明 |
---|---|
const | 通過索引直接找到一個匹配行,一般主鍵索引的時候 |
ref | 沒有主鍵索引或者唯一索引的條件索引,查詢結果多行,在聯(lián)合查詢中很常見 |
index | 利用到了索引,有可能有其它排序,where 或者 group by 等 |
all | 全表掃描,沒有使用到索引 |
如果有Using filesort或者Using temporary的話,就必須要優(yōu)化了
收集慢查詢my.ini 配置里增加
long_query_time=2 log-slow-queries=/data/var/mysql_slow.log使用 nosql
redis 豐富的數(shù)據(jù)類型,非常適合配合mysql 做一些關系型的查詢。比如一個非常復雜的查詢列表可以將其插入zset 做排序列表,然后具體的信息,通過zset里面的紙去mysql 里面去查詢。
緩存優(yōu)化 多級緩存請求內(nèi)緩存
static 變量存儲,比如朋友圈信息流,在一次性獲取20條信息的時候,有可能,點贊的人里面20條里面有30個人是重復的,他們點贊你的a圖片也點贊了你的b圖片,所以這時,如果能使用static數(shù)組來存放這些用戶的基本信息就高效了些。
本地緩存
請求結束了,下拉更新朋友圈,里面又出現(xiàn)了上面的同樣的好友,還得重新請求一次。所以本地常駐內(nèi)存的緩存就更高效了。
分布式緩存
在A服務器上已經(jīng)查詢過了,在下拉更新的時候被分配到B服務器上了,難道同樣的數(shù)據(jù)再查一次再存到B服務器的本地緩存里面嗎,弄一個分布式緩存吧,這樣防止了重復查詢。但是多了網(wǎng)絡請求這一步。
很多時候是三者共存的。
避免緩存的濫用案例分析
用戶積分更新
比如用戶的基本信息和積分混在一起,當用戶登錄的時候贈送積分。則需要更新用戶的積分,這個時候更新整個用戶的基本信息緩存么?
所以這里也可以運用下面 hashes 分片的原則去更新
禮物和主題綁定緩存
為了取數(shù)據(jù)方便把多個數(shù)據(jù)源混合緩存了,這種情況,相比大家可能都見過,這是災難性的設計。
{ id:x, title:x, gift:{ id:x, name:x, img:x, } }
如果需要更新禮物的圖片,那么所有用到過這個禮物的話題的緩存都要更新。
redis 使用場景舉例由于比較基礎基礎好的老司機就可以忽略了,新人同學可以看下 https://mengkang.net/356.html
redis 優(yōu)化多實例化,更高效地利用服務器 cpu
內(nèi)存優(yōu)化,官方意見 https://redis.io/topics/memor... 有點老
盡可能的使用 hashes ,時間復雜度低,查詢效率高。同時還節(jié)約內(nèi)存。Instagram 最開始用string來存圖片id=>uid的關系數(shù)據(jù),用了21g,后來改為水平分割,圖片id 1000 取模,然后將分片的數(shù)據(jù)存在一個hashse 里面,這樣最后的內(nèi)容減少了5g,四分之一基本上。
每一段使用一個Hash結構存儲,由于Hash結構會在單個Hash元素在不足一定數(shù)量時進行壓縮存儲,所以可以大量節(jié)約內(nèi)存。這一點在String結構里是不存在的。而這個一定數(shù)量是由配置文件中的hash-zipmap-max-entries參數(shù)來控制的。服務器認知的提升
下面的內(nèi)容,只能是讓大家有一個大概的認識,了解一個優(yōu)化的方向,具體的內(nèi)容需要系統(tǒng)學習很多很多的知識。
多進程的優(yōu)勢多進程有利于 CPU 計算和 I/O 操作的重疊利用。一個進程消耗的絕大部分時間都是在磁盤I/O和網(wǎng)絡I/O中。
如果是單進程時cpu大量的時間都在等待I/O,所以我們需要使用多進程。
為了讓所有的進程輪流使用系統(tǒng)資源,進程調(diào)度器在必要的時候掛起正在運行的進程,同時恢復以前掛起的某個進程。這個就是我們常說的“上下文切換”。
關于上下文我之前寫一個簡單筆記 https://mengkang.net/729.html
無限制增加進程數(shù),則會增多 cpu 在各個進程間切換的次數(shù)。
如果我們希望服務器支持較大的并發(fā)數(shù),那么久要盡量減少上下文切換的次數(shù),比如在nginx服務上nginx的子進程數(shù)不要超過cpu的核數(shù)。
我們可以在壓測的時候通過vmstat,nmon來監(jiān)控系統(tǒng)上下文切換的次數(shù)。
# top top - 09:40:40 up 565 days, 5:47, 2 users, load average: 0.03, 0.03, 0.00 Tasks: 121 total, 2 running, 119 sleeping, 0 stopped, 0 zombie Cpu(s): 8.6%us, 0.3%sy, 0.0%ni, 90.7%id, 0.2%wa, 0.0%hi, 0.2%si, 0.0%st
一般情況下IOwait代表I/O操作的時間占(I/O操作的時間 + I/O和CPU時間)的比例。
但是也時候也不準,比如nginx來作為web服務器,當我們開啟很多nginx子進程,IOwait會很高,當再減少進程數(shù)到cpu核數(shù)附近時,IOwait會減少,監(jiān)控網(wǎng)絡流量會發(fā)現(xiàn)也增加。
只要是提供socket服務,就可以利用多路復用 I/O 模型。
需要補充的知識 https://mengkang.net/726.html
strace 非常方便統(tǒng)計系統(tǒng)調(diào)用
# strace -c -p 23374 Process 23374 attached - interrupt to quit ^CProcess 23374 detached % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 30.68 0.000166 0 648 poll 12.01 0.000065 0 228 munmap 11.65 0.000063 0 228 mmap 10.54 0.000057 0 660 recvfrom 10.35 0.000056 0 708 fstat 7.76 0.000042 0 252 open 6.10 0.000033 1 36 write 5.73 0.000031 0 72 24 access 5.18 0.000028 0 72 read 0.00 0.000000 0 276 close 0.00 0.000000 0 13 13 stat 0.00 0.000000 0 269 240 lstat 0.00 0.000000 0 12 rt_sigaction 0.00 0.000000 0 12 rt_sigprocmask 0.00 0.000000 0 12 pwrite 0.00 0.000000 0 48 setitimer 0.00 0.000000 0 12 socket 0.00 0.000000 0 12 connect 0.00 0.000000 0 12 accept 0.00 0.000000 0 168 sendto 0.00 0.000000 0 12 shutdown 0.00 0.000000 0 48 fcntl 0.00 0.000000 0 12 flock 0.00 0.000000 0 156 getcwd 0.00 0.000000 0 24 chdir 0.00 0.000000 0 24 times 0.00 0.000000 0 12 getuid ------ ----------- ----------- --------- --------- ---------------- 100.00 0.000541 4038 277 total
通過strace查看“系統(tǒng)調(diào)用時間”和“調(diào)用次數(shù)”來定位問題 https://huoding.com/2013/10/0...
自己構建web服務器要想理解web服務器優(yōu)化的原理,最好的辦法是了解它的來龍去脈,實踐就是最好的方式,我分為以下幾個步驟:
用 PHP 來實現(xiàn)一個動態(tài) Web 服務器 https://mengkang.net/491.html
簡單靜態(tài) web 服務器(循環(huán)服務器)的實現(xiàn) https://mengkang.net/563.html
多進程并發(fā)的面向連接 Web 服務器的實踐 https://mengkang.net/571.html
簡單靜態(tài) Select Web 服務器的實現(xiàn) https://mengkang.net/568.html
I/O 多路復用 https://mengkang.net/726.html
上面是我的學習筆記,圖片資源丟失了,大家可以根據(jù)相關關鍵詞去搜搜相關的文章和書籍,更推薦大家去看書。
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://www.ezyhdfw.cn/yun/25566.html
摘要:下面一起學習下鳥哥的框架。揭開神秘面紗采用客戶端服務器模式。在服務器端,進程保持睡眠狀態(tài)直到調(diào)用信息的到達為止。這和我們外網(wǎng)的原理不都一個樣么那么我們一起看看高大上的是怎么在玩。整個傳輸以二進制流的形式傳送。 各位老鐵在點贊、收藏的時候敢不敢報名小弟的直播分享,絕對有干貨,絕對有驚喜!一次早餐錢的投入,可能是薪資的翻倍,可能是視野的拓展! PHP 進階之路 - 億級 pv 網(wǎng)站架構...
摘要:去年年底因為使用了云存儲和其他方面的原因,計劃的將服務器縮減一個機柜出來。云服務的回源服務器的配置中間漏了一臺,后期給補上了。監(jiān)控遷移完畢之后,除了常規(guī)的業(yè)務代碼,還需要注意圖片資源的回源是否正常服務器壓力是否正常檢查日志是否出現(xiàn)錯誤。 去年年底因為使用了云存儲和其他方面的原因,計劃的將服務器縮減一個機柜出來。這樣今年每月機房的費用可以減少1萬左右。前前后后抽空在弄這個任務,現(xiàn)做個筆記...
摘要:如果現(xiàn)有子進程中的線程總數(shù)不能滿足負載,控制進程將派生新的子進程。為解決線程的并發(fā)問題,引入了線程安全資源管理器。的全拼,用來存放各個線程的鏈表。 PHP 進階之路 - 零基礎構建自己的服務治理框架(上) PHP 進階之路 - 零基礎構建自己的服務治理框架(下) PHP 進階之路 - 億級 pv 網(wǎng)站架構的技術細節(jié)與套路 PHP 進階之路 - 億級 pv 網(wǎng)站架構實戰(zhàn)之性能壓榨 注...
摘要:唯一的知識點就是的基礎使用。可以簡單的理解下面的代碼就構建了一個服務器。握手完成之后的消息傳遞則在中處理。實際情況下,不可能那么多人同時說話廣播,而是說話的人少,接受廣播的人多。 硬廣一波 SF 官方首頁推薦《PHP進階之路》(你又多久沒有投資自己了?先看后買) 我們下面則將一些實際場景都添加進去,比如用戶身份的驗證,游客只能瀏覽不能發(fā)言,多房間(頻道)的聊天。該博客非常適合 Java...
摘要:大家好,我是冰河有句話叫做投資啥都不如投資自己的回報率高。馬上就十一國慶假期了,給小伙伴們分享下,從小白程序員到大廠高級技術專家我看過哪些技術類書籍。 大家好,我是...
閱讀 3212·2021-11-11 16:54
閱讀 2376·2021-09-04 16:48
閱讀 3299·2019-08-29 16:08
閱讀 705·2019-08-29 15:13
閱讀 1429·2019-08-29 15:09
閱讀 2728·2019-08-29 12:45
閱讀 1989·2019-08-29 12:12
閱讀 508·2019-08-26 18:27