高性能服務器之性能調(diào)優(yōu)

2023-06-11    分類: 網(wǎng)站建設

讓咱們先來說說怎么什么是體系功能。這個界說十分要害,假如咱們不清楚什么是體系功能,那么咱們將無法定位之。我見過許多朋友會覺得這很簡略,可是仔細一問,其實他們并沒有一個比較體系的辦法,所以,在這兒我想告訴咱們怎么體系地來定位功能。 總體來說,體系功能便是兩個事:

一般來說,一個體系的功能遭到這兩個條件的束縛,缺一不可。比方,我的體系能夠頂?shù)米∫话偃f的并發(fā),可是體系的推遲是2分鐘以上,那么,這個一百萬的負載毫無意義。體系推遲很短,可是吞吐量很低,相同沒有意義。所以,一個好的體系的功能測驗必定遭到這兩個條件的一起作用。 有經(jīng)驗的朋友必定知道,這兩個東西的一些聯(lián)系:

二、體系功能測驗

其次,開發(fā)功能測驗東西,一個東西用來制作高強度的Throughput,另一個東西用來丈量Latency。關于怎么丈量Latency,你能夠在代碼中丈量,可是這樣會影響程序的履行,而且只能測驗到程序內(nèi)部的Latency,真實的Latency是整個體系都算上,包括操作體系和網(wǎng)絡的延時,你能夠運用Wireshark來抓網(wǎng)絡包來丈量。這兩個東西詳細怎么做,這個還請咱們自己思考去了。

再多說一些,

關于功能測驗,咱們還需求界說一個時間段。比方:在某個吞吐量上繼續(xù)15分鐘。由于當負載到達的時分,體系會變得不安穩(wěn),當過了一兩分鐘后,體系才會安穩(wěn)。其他,也有或許是,你的體系在這個負載下前幾分鐘還表現(xiàn)正常,然后就不安穩(wěn)了,乃至垮了。所以,需求這么一段時間。這個值,咱們叫做峰值極限。

功能測驗有許多很復要的東西,比方:burst test等。 這兒不能一一詳述,這兒只說了一些和功能調(diào)優(yōu)相關的東西??倸w,功能測驗是一細活和累活。

有了上面的襯托,咱們就能夠測驗到到體系的功能了,再調(diào)優(yōu)之前,咱們先來說說怎么找到功能的瓶頸。我見過許多朋友會覺得這很簡略,可是仔細一問,其實他們并沒有一個比較體系的辦法。

首先,當咱們體系有問題的時分,咱們不要急于去查詢咱們代碼,這個毫無意義。咱們首要需求看的是操作體系的陳述??纯床僮黧w系的CPU運用率,看看內(nèi)存運用率,看看操作體系的IO,還有網(wǎng)絡的IO,網(wǎng)絡鏈接數(shù),等等。Windows下的perfmon是一個很不錯的東西,Linux下也有許多相關的指令和東西,比方:vmstat, sar, iostat, top, tcpdump等等 。經(jīng)過調(diào)查這些數(shù)據(jù),咱們就能夠知道咱們的軟件的功能根本上出在哪里。比方:

2)然后,咱們能夠看一下服務器IO大不大,IO和CPU一般是反著來的,CPU運用率高則IO不大,IO大則CPU就小。關于IO,咱們要看三個事,一個是磁盤文件IO,一個是驅(qū)動程序的IO(如:網(wǎng)卡),一個是內(nèi)存換頁率。這三個事都會影響體系功能。

4)假如服務器CPU不高,IO不高,內(nèi)存運用不高,網(wǎng)絡帶寬運用不高??墒求w系的功能上不去。這闡明你的程序有問題,比方,你的程序被堵塞了?;蛟S是由于等那個鎖,或許是由于等某個資源,或者是在切換上下文。

3.2)運用Profiler測驗

咱們要點調(diào)查運轉(zhuǎn)時間最多,調(diào)用次數(shù)最多的那些函數(shù)和指令。這兒留意一下,關于調(diào)用次數(shù)多可是時間很短的函數(shù),你或許只需求輕微優(yōu)化一下,你的功能就上去了(比方:某函數(shù)一秒種被調(diào)用100萬次,你想想假如你讓這個函數(shù)進步0.01毫秒的時間 ,這會給你帶來多大的功能)

1)在你的代碼中自己做核算,運用微秒級的計時器和函數(shù)調(diào)用核算器,每隔10秒把核算log到文件中。

終究再說一點,關于功能測驗,不同的Throughput會呈現(xiàn)不同的測驗成果,不同的測驗數(shù)據(jù)也會有不同的測驗成果。所以,用于功能測驗的數(shù)據(jù)十分重要,功能測驗中,咱們需求觀測驗不同Throughput的成果。

下面這些東西是我所經(jīng)歷過的一些問題,或許并不全,或許并不對,咱們能夠彌補糾正,我純屬拋磚引玉。關于體系架構(gòu)方面的功能調(diào)優(yōu),咱們可移步看一下,關于Web方面的一些功能調(diào)優(yōu)的東西,咱們能夠看看一文中的功能一章。我在這兒就不再說規(guī)劃和架構(gòu)上的東西了。

用空間換時間。各種cache如CPU L1/L2/RAM到硬盤,都是用空間來換時間的戰(zhàn)略。這樣戰(zhàn)略根本上是把核算的進程一步一步的保存或緩存下來,這樣就不必每次用的時分都要再核算一遍,比方數(shù)據(jù)緩沖,CDN,等。這樣的戰(zhàn)略還表現(xiàn)為冗余數(shù)據(jù),比方數(shù)據(jù)鏡象,負載均衡什么的。

簡化代碼。最高效的程序便是不履行任何代碼的程序,所以,代碼越少功能就越高。關于代碼級優(yōu)化的技能大學里的教科書有許多示例了。如:削減循環(huán)的層數(shù),削減遞歸,在循環(huán)中少聲明變量,少做分配和釋放內(nèi)存的操作,盡量把循環(huán)體內(nèi)的表達式抽到循環(huán)外,條件表達的中的多個條件判斷的次序,盡量在程序啟動時把一些東西準備好,留意函數(shù)調(diào)用的開支(棧上開支),留意面向目標言語中暫時目標的開支,當心運用異常(不要用異常來檢查一些可接受可疏忽并常常產(chǎn)生的過錯),…… 等等,等等,這連東西需求咱們十分了解編程言語和常用的庫。

總歸,

依據(jù)2:8準則來說,20%的代碼耗了你80%的功能,找到那20%的代碼,你就能夠優(yōu)化那80%的功能

。 下面的一些東西都是我的一些經(jīng)驗,我只例舉了一些最有價值的功能調(diào)優(yōu)的的辦法,供你參閱,也歡迎彌補。

一個是過濾算法,體系需求對收到的懇求做過濾,咱們把能夠被filter in/out的東西裝備在了一個文件中,原有的過濾算法是遍歷過濾裝備,后來,咱們找到了一種辦法能夠?qū)@個過濾裝備進行排序,這樣就能夠用二分折半的辦法來過濾,體系功能增加了50%。

分而治之和預處理。曾經(jīng)有一個程序為了生成月報表,每次都需求核算很長的時間,有時分需求花將近一整天的時間。所以咱們把咱們找到了一種辦法能夠把這個算法發(fā)成增量式的,也便是說我每天都把當天的數(shù)據(jù)核算好了后和前一天的報表兼并,這樣能夠大大的節(jié)省核算時間,每天的數(shù)據(jù)核算量只需求20分鐘,可是假如我要算整個月的,體系則需求10個小時以上(SQL句子在大數(shù)據(jù)量面前功能成級數(shù)性下降)。這種分而治之的思路在大數(shù)據(jù)面前對功能有很幫助,就像merge排序相同。SQL句子和數(shù)據(jù)庫的功能優(yōu)化也是這一戰(zhàn)略,如:運用嵌套式的Select而不是笛卡爾積的Select,運用視圖,等等。

字符串操作。這是最費體系功能的事了,無論是strcpy, strcat仍是strlen,最需求留意的是字符串子串匹配。所以,能用整型最好用整型。舉幾個比方,第一個比方是N年前做銀行的時分,我的搭檔喜愛把日期存成字符串(如:2012-05-29 08:30:02),我勒個去,一個select  where between句子適當耗時。另一個比方是,我曾經(jīng)有個搭檔把一些狀況碼用字符串來處理,他的理由是,這樣能夠在界面上直接顯示,后來功能調(diào)優(yōu)的時分,我把這些狀況碼全改成整型,然后用位操作查狀況,由于有一個每秒鐘被調(diào)用了150K次的函數(shù)里面有三處需求檢查狀況,經(jīng)過改進以后,整個體系的功能上升了30%左右。還有一個比方是,我曾經(jīng)從事的某個產(chǎn)品編程規(guī)范中有一條是要在每個函數(shù)中把函數(shù)名界說出來,如:const char fname[]=”functionName()”, 這是為了好打日志,可是為什么不聲明成 static類型的呢?

內(nèi)存分配。不要小看程序的內(nèi)存分配。malloc/realloc/calloc這樣的體系調(diào)十分耗時,尤其是當內(nèi)存呈現(xiàn)碎片的時分。我曾經(jīng)的公司出過這樣一個問題——在用戶的站點上,咱們的程序有一天不呼應了,用GDB跟進去一看,體系hang在了malloc操作上,20秒都沒有返回,重啟一些體系就好了。這便是內(nèi)存碎片的問題。這便是為什么許多人訴苦STL有嚴峻的內(nèi)存碎片的問題,由于太多的小內(nèi)存的分配釋放了。有許多人會以為用內(nèi)存池能夠處理這個問題,可是實際上他們僅僅從頭發(fā)明了Runtime-C或操作體系的內(nèi)存管理機制,徹底杯水車薪。當然處理內(nèi)存碎片的問題仍是經(jīng)過內(nèi)存池,詳細來說是一系列不同尺度的內(nèi)存池(這個留給咱們自己去思考)。當然,少進行動態(tài)內(nèi)存分配是最好的。提到內(nèi)存池就需求說一下池化技能。比方線程池,銜接池等。池化技能關于一些短作業(yè)來說(如http服務) 適當適當?shù)挠杏?。這項技能能夠削減鏈接樹立,線程創(chuàng)立的開支,然后進步功能。

言語和代碼庫。咱們要熟悉言語以及所運用的函數(shù)庫或類庫的功能。比方:STL中的許多容器分配了內(nèi)存后,那怕你刪除元素,內(nèi)存也不會收回,其會形成內(nèi)存走漏的假像,并或許形成內(nèi)存碎片問題。再如,STL某些容器的size()==0  和 empty()是不相同的,由于,size()是O(n)雜亂度,empty()是O(1)的雜亂度,這個要當心。Java中的JVM調(diào)優(yōu)需求運用的這些參數(shù):-Xms -Xmx -Xmn -XX:SurvivorRatio -XX:MaxTenuringThreshold,還需求留意JVM的GC,GC的霸氣咱們都知道,尤其是full GC(還收拾內(nèi)存碎片),他就像“恐龍?zhí)丶壙速愄枴毕嗤?,他運轉(zhuǎn)的時分,整個國際的時間都中止了。

關于網(wǎng)絡調(diào)優(yōu),尤其是TCP Tuning(你能夠以這兩個要害詞在網(wǎng)上找到許多文章),這兒面有許多許多東西能夠說。看看Linux下TCP/IP的那么多參數(shù)就知道了(趁便說一下,你或許不喜愛Linux,可是你不能否認Linux給咱們了許多能夠進行內(nèi)核調(diào)優(yōu)的權利)。強烈建議咱們看看這本書。我在這兒只講一些概念上的東西。

咱們知道TCP鏈接是有許多開支的,一個是會占用文件描述符,另一個會開緩存,一般來說一個體系能夠支撐的TCP鏈接數(shù)是有限的,咱們需求清楚地認識到TCP鏈接對體系的開支是很大的。正是由于TCP是耗資源的,所以,許多進犯都是讓你體系上呈現(xiàn)許多的TCP鏈接,把你的體系資源耗盡。比方著名的SYNC Flood進犯。


net.ipv4.tcp_keepalive_probes = 5

net.ipv4.tcp_keepalive_intvl = 20

net.ipv4.tcp_fin_timeout = 30


net.ipv4.tcp_tw_reuse=1

net.ipv4.tcp_tw_recycle=1

TCP還有一個重要的概念叫RWIN(TCP Receive Window Size),這個東西的意思是,我一個TCP鏈接在沒有向Sender發(fā)出ack時能夠接收到的大的數(shù)據(jù)包。為什么這個很重要?由于假如Sender沒有收到Receiver發(fā)過來ack,Sender就會中止發(fā)送數(shù)據(jù)并會等一段時間,假如超時,那么就會重傳。這便是為什么TCP鏈接是牢靠鏈接的原因。重傳還不是最嚴峻的,假如有丟包產(chǎn)生的話,TCP的帶寬運用率會馬上遭到影響(會盲目折半),再丟包,再折半,然后假如不丟包了,就逐步康復。相關參數(shù)如下:

一般來說,理論上的RWIN應該設置成:吞吐量  * 回路時間。Sender端的buffer應該和RWIN有相同的巨細,由于Sender端發(fā)送完數(shù)據(jù)后要等Receiver端承認,假如網(wǎng)絡延時很大,buffer過小了,承認的次數(shù)就會多,所以功能就不高,對網(wǎng)絡的運用率也就不高了。也便是說,關于推遲大的網(wǎng)絡,咱們需求大的buffer,這樣能夠少一點ack,多一些數(shù)據(jù),關于呼應快一點的網(wǎng)絡,能夠少一些buffer。由于,假如有丟包(沒有收到ack),buffer過大或許會有問題,由于這會讓TCP重傳一切的數(shù)據(jù),反而影響網(wǎng)絡功能。(當然,網(wǎng)絡差的狀況下,就別玩什么高功能了) 所以,高功能的網(wǎng)絡重要的是要讓網(wǎng)絡丟包率十分十分地?。ǜ旧鲜怯迷贚AN里),假如網(wǎng)絡根本是可信的,這樣用大一點的buffer會有更好的網(wǎng)絡傳輸功能(來來回回太多太影響功能了)。

B)UDP調(diào)優(yōu)

再多說一下,運用Socket編程的時分,你能夠運用setsockopt() 設置 SO_SNDBUF/SO_RCVBUF 的巨細,TTL和KeepAlive這些要害的設置,當然,還有許多,詳細你能夠檢查一下Socket的手冊。

C)網(wǎng)卡調(diào)優(yōu)

D)其它網(wǎng)絡功能

其他,關于一些和DNS Lookup的體系調(diào)用要當心,比方:gethostbyaddr/gethostbyname,這個函數(shù)或許會適當?shù)馁M時,由于其要到網(wǎng)絡上去找域名,由于DNS的遞歸查詢,會導致嚴峻超時,而又不能經(jīng)過設置什么參數(shù)來設置time out,對此你能夠經(jīng)過裝備hosts文件來加快速度,或是自己在內(nèi)存中管理對應表,在程序啟動時查好,而不要在運轉(zhuǎn)時每次都查。其他,在多線程下面,gethostbyname會一個更嚴峻的問題,便是假如有一個線程的gethostbyname產(chǎn)生堵塞,其它線程都會在gethostbyname處產(chǎn)生堵塞,這個比較反常,要當心。(你能夠試試GNU的gethostbyname_r(),這個的功能要好一些) 這種到網(wǎng)上找信息的東西許多,比方,假如你的Linux運用了NIS,或是NFS,某些用戶或文件相關的體系調(diào)用就很慢,所以要當心。

A)I/O模型

第一種,服務器同步堵塞式I/O,這個不說了。

第三種,關于select/poll/epoll這三個是I/O不堵塞,可是在事件上堵塞,算是:I/O異步,事件同步的調(diào)用。

第四種由于沒有任何的堵塞,無論是I/O上,仍是事件告訴上,所以,其能夠讓你充分地運用CPU,比起第二種同步無堵塞優(yōu)點便是,第二種要你一遍一遍地去輪詢。Nginx之所所以高效,是其運用了epoll和AIO的辦法來進行I/O的。

a)一個是WriteFile體系調(diào)用,這個體系調(diào)用能夠是同步堵塞的,也能夠是同步無堵塞的,關于看文件是不是以Overlapped翻開的。關于同步無堵塞,需求設置其終究一個參數(shù)Overlapped,微軟叫Overlapped I/O,你需求WaitForSingleObject才干知道有沒有寫完結(jié)。這個體系調(diào)用的功能可想而知。

c)然后是IOCP – IO Completion Port,IOCP會把I/O的成果放在一個行列中,可是,偵聽這個行列的不是主線程,而是專門來干這個事的一個或多個線程去干(老的渠道要你自己創(chuàng)立線程,新的渠道是你能夠創(chuàng)立一個線程池)。IOCP是一個線程池模型。這個和Linux下的AIO模型比較相似,可是完成辦法和運用辦法徹底不相同。

B)多核服務器CPU調(diào)優(yōu)

關于Windows來說,咱們能夠經(jīng)過“使命管理器”中的“進程”而中右鍵菜單中的“設置相關性……”(Set Affinity…)來設置并約束這個進程能被運轉(zhuǎn)在哪些核上。

多核CPU還有一個技能叫

技能(Non-Uniform Memory Access)。傳統(tǒng)的多核運算是運用SMP(Symmetric Multi-Processor )形式,多個處理器共享一個會集的存儲器和I/O總線。所以就會呈現(xiàn)一致存儲器拜訪的問題,一致性一般意味著功能問題。NUMA形式下,處理器被劃分成多個node, 每個node有自己的本地存儲器空間。關于NUMA的一些技能細節(jié),你能夠檢查一下這篇文章《

numactl 》,在Linux下,對NUMA調(diào)優(yōu)的指令是:

如下面的指令:(指定指令“myprogram arg1 arg2”運轉(zhuǎn)在node 0 上,其內(nèi)存分配在node 0 和 1上)

當然,上面這個指令并欠好,由于內(nèi)存跨越了兩個node,這十分欠好。最好的辦法是只讓程序拜訪和自己運轉(zhuǎn)相同的node,如:

C)文件體系調(diào)優(yōu)

接下來,咱們就能夠調(diào)優(yōu)文件體系裝備了,關于Linux的Ext3/4來說,幾乎在一切狀況下都有所幫助的一個參數(shù)是封閉文件體系拜訪時間,在/etc/fstab下看看你的文件體系 有沒有noatime參數(shù)(一般來說應該有),還有一個是dealloc,它能夠讓體系在終究時間決議寫入文件產(chǎn)生時運用哪個塊,可優(yōu)化這個寫入程序。還要注間一下三種日志形式:data=journal、data=ordered和data=writeback。默認設置data=ordered提供功能和防護之間的好平衡。

這兒介紹一個Linux下的檢查I/O的指令—— iotop,能夠讓你看到各進程的磁盤讀寫的負載狀況。

4.5)數(shù)據(jù)庫調(diào)優(yōu)

A)數(shù)據(jù)庫引擎調(diào)優(yōu)

數(shù)據(jù)庫的鎖的辦法。這個十分十分地重要。并發(fā)狀況下,鎖是十分十分影響功能的。各種阻隔等級,行鎖,表鎖,頁鎖,讀寫鎖,業(yè)務鎖,以及各種寫優(yōu)先仍是讀優(yōu)先機制。功能最高的是不要鎖,所以,分庫分表,冗余數(shù)據(jù),削減一致性業(yè)務處理,能夠有用地進步功能。NoSQL便是獻身了一致性和業(yè)務處理,并冗余數(shù)據(jù),然后達到了散布式和高功能。

數(shù)據(jù)庫的存儲機制。不但要搞清楚各種類型字段是怎么存儲的,更重要的是數(shù)據(jù)庫的數(shù)據(jù)存儲辦法,是怎么分區(qū)的,是怎么管理的,比方Oracle的數(shù)據(jù)文件,表空間,段,等等。了解清楚這個機制能夠減輕許多的I/O負載。比方:MySQL下運用show engines;能夠看到各種存儲引擎的支撐。不同的存儲引擎有不同的側(cè)要點,針對不同的業(yè)務或數(shù)據(jù)庫規(guī)劃會讓你有不同的功能。

數(shù)據(jù)庫的散布式戰(zhàn)略。最簡略的便是仿制或鏡像,需求了解散布式的一致性算法,或是主主同步,主從同步。經(jīng)過了解這種技能的機理能夠做到數(shù)據(jù)庫級其他水平擴展。

關于SQL句子的優(yōu)化,首先也是要運用東西,根本上來說,一切的RMDB都會有這樣的東西,來讓你檢查你的應用中的SQL的功能問題。 還能夠運用explain來看看SQL句子終究Execution Plan會是什么樣的。

下面我依據(jù)我有限的數(shù)據(jù)庫SQL的常識說幾個會有功能問題的SQL:

索引。關于索引字段,最好不要在字段上做核算、類型轉(zhuǎn)化、函數(shù)、空值判斷、字段銜接操作,這些操作都會損壞索引原本的功能。當然,索引一般都呈現(xiàn)在Where或是Order by字句中,所以對Where和Order by子句中的子段最好不要進行核算操作,或是加上什么NOT之類的,或是運用什么函數(shù)。

JOIN操作。有人說,Join表的次序會影響功能,只要Join的成果集是相同,功能和join的次序無關。由于后臺的數(shù)據(jù)庫引擎會幫咱們優(yōu)化的。Join有三種完成算法,嵌套循環(huán),排序歸并,和Hash式的Join。(MySQL只支撐第一種)

仍是那句話,詳細要看什么樣的數(shù)據(jù),什么樣的SQL句子,你才知道用哪種辦法是最好的。

字符串。正如我前面所說的,字符串操刁難功能上有十分大的惡夢,所以,能用數(shù)據(jù)的狀況就用數(shù)字,比方:時間,工號,等。

其它。

不要select *,而是明確指出各個字段,假如有多個表,必定要在字段名前加上表名,不要讓引擎去算。

不要用Having,由于其要遍歷一切的記載。功能差得不能再差。

盡或許地運用UNION ALL  取代  UNION。

索引過多,insert和delete就會越慢。而update假如update多數(shù)索引,也會慢,可是假如只update一個,則只會影響一個索引表。

等等。

新聞標題:高性能服務器之性能調(diào)優(yōu)
鏈接地址:http://www.bm7419.com/news37/264487.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供用戶體驗、建站公司、網(wǎng)站維護、Google、全網(wǎng)營銷推廣、網(wǎng)頁設計公司

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站建設網(wǎng)站維護公司