高性能和高并發,聽著就有點類似,并且他們還經常一起提及,比如提高我們的并發性能,顯然,高性能可以提高我們的并發,但是細化來看,他們是有區別的,他們的考量點的維度不同。高性能需要我們從單機維度到整體維度去考慮,更多的是先從編碼角度、架構使用角度去讓我們的單機(單實例)有更好的性能,然后再從整個系統層面來擁有更好的性能;高并發則直接是全局角度來讓我們的系統在全鏈路下都能夠抗住更多的并發請求。
一、高性能架構和系統設計的幾個層面
高性能架構設計主要集中在單機優化、服務集群優化、編碼優化三方面。但架構層面的設計是高性能的基礎,如果架構層面的設計沒有做到高性能,僅依靠優化編碼,對整體系統的提升是有限的。我們從一個全局角度來看高性能的系統設計,需要整體考慮的包括如下幾個層面:
- 前端層面。后端優化的再好,如果前端(客戶端)的性能不 ok,那么對用戶而言,他們的體感還是很差的,因此前端層也是有必要考慮的,只是不在我們本文的設計范圍之內,在實際工作中是需要進行探討的。
- 編碼實現層面:代碼邏輯的分層、分模塊、協程、資源復用(對象池,線程池等)、異步、IO 多路復用(異步非阻塞)、并發、無鎖設計、設計模式等。
- 單機架構設計層面:IO 多路復用、Reactor 和 Proactor 架構模式
- 系統架構設計層面:架構分層、業務分模塊、集群(集中式、分布式)、緩存(多級緩存、本地緩存)、消息隊列(異步、削峰)
- 基礎建設層面:機房、機器、資源分配
- 運維部署層面:容器化部署、彈性伸縮
- 性能測試優化層面:性能壓測、性能分析、性能優化
二、前端層面
后端優化的再好,如果前端(客戶端)的性能不 ok,那么對用戶而言,他們的體感還是很差的,因此前端層也是有必要考慮的,只是不在我們本文的設計范圍之內,在實際工作中是需要進行探討的。
這里簡單說明下,從我個人工作的經歷來看,前端(客戶端)這里可以優化的點包括但不限于:數據預加載、數據本地緩存、業務邏輯前置處理、CDN 加速、請求壓縮、異步處理、合并請求、長連接、靜態資源等
三、編碼實現層面
編碼實現層面:代碼邏輯的分層、分模塊、協程、資源復用(對象池,線程池等)、異步、IO 多路復用(異步非阻塞)、并發、無鎖設計、設計模式等。
多線程、多協程
大多數情況下,多進程、多線程、多協程都可以大大提高我們的并發性能,尤其是是多協程。
在網絡框架層面,現在一般成熟的后端系統框架(服務化框架)都是支持多線程、多協程的,因此對于網絡框架這點,我們只要是引用相對成熟的服務化框架來實現我們的業務,基本上可以不用過多考慮和設計。
在業務層面,如果是 Go 語言,天然支持大量并發,并且創建 Go 的協程非常容易,一個 go 關鍵字就搞定,因此多協程那就非常容易了,Go 里面可以創建大量協程來提高我們的并發性能。如果是其他語言,我們盡可能的使用多協程、多線程去執行我們的業務邏輯。
無鎖設計(lock free)
在多線程、多協程的框架下,如果我們并發的線程(協程)之間訪問共享資源,那么需要特別注意,要么通過加鎖、要么通過無鎖化設計,否則沒有任何處理的訪問共享資源會產生意想不到的結果。而加鎖的設計,在并發較大的時候,如果鎖的力度不合適,或者頻繁的加鎖解鎖,又會使我們的性能嚴重下降。
為此,在追求高性能的時候,大家就比較推崇無鎖化的設計。目前很多后臺底層設計,為了避免共享資源的競爭,都采用了無鎖化設計,特別是在底層框架上。無鎖化主要有兩種實現,無鎖隊列和原子操作。
- 無鎖隊列。可以通過 鏈表或者 RingBuffer(循環數組)來實現無鎖隊列。
- 原子操作。利用硬件同步原語 CAS 來實現各種無鎖的數據結構。比如 Go 語言中的 atomic 包、C++11 語言中的 atomic 庫。
數據序列化
為什么要說 數據序列化協議?因為我們的系統,要么就是各個后端微服務之間通過 RPC 做交互,要么就是通過 HTTP/TCP 協議和前端(終端)做交互,因此不可避免的需要我們進行網絡數據傳輸。而數據,只有序列化后,才方便進行網絡傳輸。
序列化就是將數據結構或對象轉換成二進制串的過程,也就是編碼的過程,序列化后,會把數據轉換為二進制串,然后可以進行網絡傳輸;反序列化就是在序列化過程中所生成的二進制串轉換成數據結構或者對象的過程,將二進制轉換為對象后業務才好進行后續的邏輯處理。
常見的序列化協議如下
- Protocol Buffer(PB)
- JSON
- XML
- 內置類型(如 JAVA 語言就有 java.io.Serializable)
常見的序列化協議的對比在網上有各種性能的對比,這里就不在貼相關截圖了,只說結論:從性能上和使用廣泛度上來看,后端服務之間現在一般推薦使用 PB。如果和前端交互,由于 HTTP 協議只能支持 JSON,因此一般只能 JSON。
池化技術(資源復用)
池化技術是非常常見的一個提高性能的技術,池化的核心思想就是對資源進行復用,減少重復創建銷毀所帶來的開銷。復用就是創建一個池子,然后再在這個池子里面對各種資源進行統一分配和調度,不是創建后就釋放,而是統一放到池子里面來復用,這樣可以減少重復創建和銷毀,從而提高性能。而這個資源就包括我們編程中常見到如 線程資源、網絡連接資源、內存資源,具體到對應的池化技術層面就是 線程池(協程池)、連接池、內存池等。
- 線程池(協程池)。本質都是進程、線程、協程這些維度的一個池子,先創建合適數量的線程(協程)并且初始處于休眠狀態,然后當需要用到的時候,從池子里面喚醒一個,然后執行業務邏輯,處理完業務邏輯后,資源并不釋放,而是直接放回池子里面休眠,等待后續的請求被喚醒,這樣重復利用。
創建線程的開銷是很大的,因此如果來一個請求就頻創建一個線程、進程,那么請求的性能肯定不會太高。
- 連接池。這個是最常用的,一般我們都要操作 MySQL、redis 等存儲資源,同樣的,我們并不是每次請求 MySQL、Redis 等存儲的時候就新創建一個連接去訪問數據,而是初始化的時候就創建合適數量的連接放到池子里面,當需要連接去訪問數據的時候,從池子里面獲取一個空閑的連接去訪問數據,訪問完了之后不釋放連接,而是放回池子里面。
連接池需要保證連接的可用性,就是這個連接和 MySQL、Redis 等存儲是必須要定期發送數據來保證連接的,要不然會被斷開。同時我們要針對已經失效(斷開)的連接進行檢測和摘除。
- 內存池。常規的情況下,我們都是直接調用 new、malloc 等 linux 操作系統的 API 來申請分配內存,而每次申請的內存塊的大小不定,所以,當我們頻繁 分配內存、回收內存的時候,會造成大量的內存碎片,同時每次使用內存都要重新分配也會降低性能。內存池就是先預先分配足夠大的一塊內存,當做我們的內存池,然后每次用戶請求分配內存的時候,就會返回內存池中的一塊空閑的內存,并將這塊內存的標志置為已使用,當內存使用完畢釋放內存的時候,也不是真正地調用 free 或 delete 來釋放內存,而是把這塊內存直接放回內存池內并且同時把標志置為空閑。一般業內都有相關的套件來幫我們來做這個事情,比如在 C/C++ 語言里面,都有相關庫去封裝原生的 malloc,glibc 實現了一個 ptmalloc 庫,google 實現了一個 tcmalloc 庫。
- 對象池。其實前面幾種類型的池化技術,其實都可以作為對象池的各種應用,因為各種資源都可以當做一個對象。對象池就是避免大量創建同一個類型的對象,從而進行池化,保證對象的可復用性。
異步IO 和 異步流程
異步有兩個層面的意思:
- IO 層面的異步調用
- 業務邏輯層面的異步流程
異步是相對同步而言的,同步就是要等待前面一個事情執行完畢才能繼續執行,異步就是可以不用等待,可想而知,異步的性能要比同步好很多。
IO 層面的異步調用
針對 IO 層面的異步調用,就是我們常說的 I/O 模型,有 阻塞、非阻塞、同步、異步這幾種類型。在 Linux 操作系統內核中,內置了 5 種不同的 IO 交互模式,分別是阻塞 IO、非阻塞 IO、多路復用 IO、信號驅動 IO、異步 IO。
針對網絡 IO 模型而言,Linux 下,使用最多性能較好的是同步非阻塞模型,具體代表是 AIO,而 windows 下的代表作 IOCP 則實現了真正的異步非阻塞 I/O。
業務邏輯層面的異步流程
業務邏輯層面的異步流程,就是指讓我們的應用程序在業務邏輯上可以異步的執行。通常比較復雜的業務,都會有很多步驟流程,如果所有步驟都是同步的話,那么當這些步驟中有一步卡住,那么整個流程都會卡住,這樣的流程顯然性能不會很高。為此,在業內,我們如果想要提高性能,提高并發,那么基本上都會采用異步流程的方式。
舉個實際的應用案例,針對 IM 系統的發送消息的這個場景,比如微信發送消息,那么當客戶端發送的消息,服務端收到后,這個消息肯定要落地存儲,這個發送的流程才能算完畢,但是,如果每條消息,服務端都真正存儲到 DB 后再返回給客戶端說已經正確收到,那么這個性能顯然會很低,因為我們知道,寫 DB 的性能是很低的,尤其是像微信這種每天有大量消息的 App。那么這個流程就可以異步化,服務端收到消息后,先把消息寫入消息隊列,寫入隊列成功就返回給客戶端,然后異步流程去從消息隊列里面消費數據然后落地存儲到 DB 里面,這樣性能就非常高了,因為消息隊列的性能會很高。而比較低性能的操作都是異步處理。
并發流程
并發流程,同樣是針對我們上層的應用程序而言的,我們在處理業務邏輯的時候,尤其是相對負責的業務邏輯,一般下游都可能會有多個請求,或者說多個流程,如果依賴的下游多個請求之間沒有強依賴關系,那么我們可以將這些請求的流程并發處理,這個是后端系統設計里面非常常見的優化手段。
通過并發的處理流程,可以將串行的疊加處理耗時優化為單個處理耗時,這樣就大大的降低了整體耗時,舉個例子,一個商品活動頁面,渲染的數據包括 用戶基本信息、用戶活動積分、用戶推薦商品列表。那么當收到這個用戶的請求的時候,我們需要 查詢用戶的基本信息、用戶的活動積分,還有用戶的商品推薦,而這 3 個步驟完全是沒有相互依賴關系的,因此,我們可以并發去分別查詢,這樣可以極大的減少耗時,從而提高我們的性能。
四、單機架構設計層面
單機優化的關鍵點
單機優化層面就是要盡量提升單機的性能,將單機的性能發揮到極致的其中一個關鍵點就是我們服務器采取的并發模型,然后在這個模型下,去設計好我們的服務器對連接的管理、對請求的處理流程。而這些就涉及到我們的多協程、多線程的進程模型和異步非阻塞、同步非阻塞的 IO 模型。
在具體實現細節上,針對連接的管理,要想提高性能,那么就要采用 IO 多路復用技術,可以參考I/O Multiplexing查看,I/O 多路復用技術的兩個關鍵點在于:
- 當多條連接共用一個阻塞對象后,進程只需要在一個阻塞對象上等待,而無須再輪詢所有連接,常見的實現方式有 select、epoll、kqueue 等。
- 當某條連接有新的數據可以處理時,操作系統會通知進程,進程從阻塞狀態返回,開始進行業務處理。
IO 多路復用(epoll 模型)
基本上來說,異步 I/O 模型的發展技術是:select -> poll -> epoll -> aio -> libevent -> libuv。
而且現在大家比較熟悉和使用的最多的恐怕就是 epoll 和 aio ,尤其是 epoll 模型,基本是 Linux 后端系統下的大部分框架和軟件都是采用 epoll 模型。
但是,需要特別強調的是,僅僅依靠 epoll 不是萬能的,連接數太多的時候單進程的 epoll 也是不行的。
Reactor 和 Proactor 架構模式
epoll 只是一個 IO 多路復用的模型,在后端系統設計里面,要想實現單機的高性能,那在 IO 多路復用基礎之上,我們的整個網絡框架,還需要配合池化技術來提高我們的性能。因此,業界一般都是采用 I/O 多路復用 + 線程池(協程池、進程池)的方式來提高性能。與之對應的,在業界常用的兩個單機高性能的架構模式就是Reactor 和 Proactor 模式。Reactor 模式屬于非阻塞同步網絡模型,Proactor 模式屬于非阻塞異步網絡模型。
在業內開源軟件里面,Redis 采用的是 單 Reactor 單進程的方式,Memcache 采用的是 多 Reactor 多線程的方式,Nginx 采用的是多 Reactor 多進程的方式。關于 的詳細介紹,可以查看The Design and Implementation of the Reactor。
Redis 可以用單進程 Reactor 模式的是因為 Redis 的應用場景是內部訪問,并發數一般不會超過 1w,而 Nginx 必須用多進程 Reactor 模式是因為 Nginx 是外網訪問,并發數很容易超過 1w,因此我們的網絡架構模式,必須要通過 I/O 多路復用 + 線程池(協程池、進程池)來配合。
可以看到,單機優化層面其實和編碼層面上的多協程、異步 IO、 池化技術都是有強關聯的。這里也是一個知識相通的典型,我們所學的一些基礎層面的知識點,在架構層面、模型層面都是有用武之地的。
五、系統架構設計層面
架構設計層面:架構分層、業務分模塊、集群(集中式、分布式)、緩存(多級緩存、本地緩存)、消息隊列(異步、削峰)
架構和模塊劃分的設計
整個系統想要有一個高性能,那么首先就需要有個合理的架構設計,這里需要根據一些架構設計原則,比如高內聚低耦合,職責單一等來去構建我們的架構。最有效的方式包括架構分層設計、業務分模塊設計。
這么設計之后,在整體的系統性能優化上,后面就會有比較大的優化空間,從而不至于后面想要優化就根本無從下手,只能重構系統。
服務化框架的設計
目前的互聯網時代,我們基本上都是采用微服務來搭建我們的系統,而微服務化的必要條件就是要有一套服務化框架,這個服務化框架最核心的功能包括 RPC 請求和最基礎的服務治理策略(服務注冊和發現、負載均衡等)。
為此,這里服務化框架的性能就尤為重要,這里主要包括這個服務化框架里面實現:
- 數據處理。
數據序列化協議,一般有些采用 PB 協議,不管是從性能還是維護都是最優的。
數據壓縮,一般采用 gzip 壓縮,壓縮后可以減少網絡上的數據傳輸。
- 網絡模型。
同步還是異步流程,如果是 Go 語言,那么可以來一個請求 go 一個協程來處理。
是否有相關連接池的能力。
其他的一些優化。
負載均衡
負載均衡系統是水平擴展的關鍵技術,通過負載均衡,相當于可以把流量分散到不同的機器的不同的服務實例里面,這樣每個服務實例都可以承擔一部分請求,從而可以提高我們的整體系統的性能。
對于負載均衡的方式,大都是在客戶端發現模式(client-side) 來實現服務路和負載均衡,一般也都會支持常見的負載均衡策略,如隨機,輪訓,hash,權重,連接數【連接數越少,優先級越高】。
合理采用各種隊列
在后端系統設計里面,很多流程和請求并不要求實時處理,更不需要做到強一致,大部分情況下,我們只需要實現最終一致性就可以了。故而,我們通過隊列,就可以使我們的系統能夠實現異步處理邏輯、流程削峰、業務模塊解耦、柔性事務等多種效果,從而可以完成最終一致性,并且能夠極大的提高我們系統的性能。
我們常見的隊列包括
- 消息隊列:使用的最為廣泛的隊列之一,代表作有 RabbitMQ、RocketMQ、Kafka 等??梢杂脕韺崿F異步邏輯、削峰、解耦等多種效果。從而可以極大的提高我們的性能
- 延遲隊列:延時隊列相比于普通隊列最大的區別就體現在其延時的屬性上,普通隊列的元素是先進先出,按入隊順序進行處理,而延時隊列中的元素在入隊時會指定一個延遲時間,表示其希望能夠在經過該指定時間后處理。延遲隊列的目的是為了異步處理。延遲隊列的應用場景其實也非常的廣泛,比如說以下的場景:
到期后自動執行指定操作。
在指定時間之前自動執行某些動作
查詢某個任務是否完成,未完成等待一定時間再次查詢
回調通知,當回調失敗時,等待后重試
- 任務隊列:將任務提交到隊列中異步執行,最常見的就是線程池的任務隊列。
各級緩存的設計
分布式緩存
分布式緩存的代表作有 Redis、Memcache。通過分布式緩存,我們可以不直接讀數據庫,而是讀取緩存來獲取數據,可以極大的提高我們讀數據的性能。而一般的業務都是讀多寫少,因此,對我們的整體性能的提高是非常有效的手段,而且是必須的手段。
本地緩存
本地緩存可以從幾個維度來看:
- 客戶端的本地緩存:針對一些不常改變的數據,客戶端也可以緩存,這樣就可以避免請求后端,從而可以改善性能
- 后端服務的本地緩存:后端服務中,一般都會采用分布式緩存,但是,有些場景下,如果我們的數據量比較小,那么可以直接將這些數據緩存到進程里面,這樣直接通過內存讀取,而不用網絡耗時,性能會更高。但是本地緩存一般只會緩存少量數據。數據量太大就不合適。
多級緩存
多級緩存是一個更為高級的緩存架構設計,比如最簡單的模式可以是 本地緩存 + 分布式緩存這樣形成一個多級緩存架構。
我們把全量要緩存的數據都放到分布式緩存里面,然后把一些熱點的少量緩存放到本地緩存里面,這樣大部分熱點數據都能夠從本地直接讀取,而其他非熱點的數據還是通過分布式緩存讀取,這樣可以極大的提高我們的性能,提高并發能力。
舉個例子,電商系統里面,我們做一個活動頁,活動頁的前面 10 個商品是特賣商品,然后后面的其他商品就是常規商品,因為是活動頁面,那么這個頁面的訪問肯定就會非常大。而活動頁面的前 10 個商品,必然是用戶首先進來頁面就一定會看到的,而用戶想要繼續看其他商品,那么就需要在手機上手動上滑刷新一下。這個場景下,前面 10 個商品的訪問量無疑是最大的,而用戶手動上滑刷新后的請求就會少很多。為此,我們可以把全量商品都緩存在分布式緩存如 redis 里面,然后再在這個基礎之上,把前面 10 個商品的信息緩存到本地,這樣,當活動開始后,拉取的第一頁 10 個商品數據,都是從本地緩存拉取的,本地讀取性能會非常高,因為內存讀取就行,完全不需要網絡交互。
其他的模式,可以 本地緩存 + 二級分布式緩存 + 一級分布式緩存,也就是針對分布式緩存再做一層分級,這樣每一級的緩存都能抗一部分的量,因此整體來看,能夠對外提供的性能就足夠高。
緩存預熱
通過異步任務提前將接下來要大量訪問的數據預熱到我們緩存里面。這樣當有請求的突峰的時候,可以從容應對。
其他高性能的 NoSQL
除了 Redis、本地緩存這些,其他的一些 NoSQL 中,MongoDB、Elasticserach 也是常見的性能很高的組件,我們可以根據適用場景,合理選用。
比如我們在電商系統里面,我們針對商品的搜索、推薦都是采用 Elasticserach 來實現。
存儲的設計
數據分區
數據分區是把數據按一定的方式分成多個區(比如通過地理位置),不同的數據區來分擔不同區的流量,這需要一個數據路由的中間件,但會導致跨庫的 Join 和跨庫的事務非常復雜。
將數據分布到多個分區有兩種比較典型的方案:
- 根據鍵做哈希,根據哈希值選擇對應的數據節點。
- 根據范圍分區,某一段連續的鍵都保存在一個數據節點上。
分庫分表
一般來說,影響數據庫最大的性能問題有兩個,一個是對數據庫的操作,一個是數據庫中數據的大小。對于前者,我們需要從業務上來優化。一方面,簡化業務,不要在數據庫上做太多的關聯查詢,而對于一些更為復雜的用于做報表或是搜索的數據庫操作,應該把其移到更適合的地方。比如,用 ElasticSearch 來做查詢,用 Hadoop 或別的數據分析軟件來做報表分析。對于后者,一般就是拆分。分庫分表技術,有些地方也稱為 Sharding、分片,通過分庫分表可以提高我們的讀寫性能,
分庫分表有垂直切分和水平切分兩種:
- 垂直切分(分庫),一般按照業務功能模塊來劃分,分庫后分表部署到不同的庫上。分庫是為了提高并發能力,比如讀寫請求量大就需要分庫。
- 水平切分(分表),當一個表中的數據量過大時,我們可以把該表的數據通過各種 ID 的 hash 散列來劃分,比如 用戶 ID、訂單 ID 的 hash。分表更多的是應對性能問題,比如查詢慢的問題。單表一般情況下,千萬級別后各種性能就開始下降了,就要考慮開始分表了。
分表包括垂直切分和水平切分,而分區只能起到水平切分的作用。
讀寫分離
互聯網系統大多數都是讀多寫少,因此讀寫分離可以幫助主庫抗量,讀寫分離就是將讀的請求量改為從庫承擔,寫還是主庫來承擔。一般我們都是一主多從的架構,既可以抗量,又可以保證數據不丟。
冷熱分離
針對業務場景而言,如果數據有冷熱之分的話,可以將歷史冷數據與當前熱數據分開存儲,這樣可以減輕當前熱數據的存儲量,可以提高性能。
我們常見的存儲系統比如 MySQL、Elasticserach 等都可以支持。
分布式數據庫
分布式數據庫的基本思想是將原來集中式數據庫中的數據分散存儲到多個通過網絡連接的數據存儲節點上,以獲取更大的存儲容量和更高的并發訪問量,從而提高我們的性能。現在傳統的關系型數據庫已經開始從集中式模型向分布式架構發展了。一般云服務廠商,都會提供分布式數據庫的解決方案,比如騰訊云的 TDSQL MySQL 版,TDSQL for MySQL 是騰訊打造的一款分布式數據庫產品,具備強一致高可用、全球部署架構、分布式水平擴展、高性能、企業級安全等特性,同時提供智能 DBA、自動化運營、監控告警等配套設施,為客戶提供完整的分布式數據庫解決方案。
六、基礎建設層面
基礎建設層面,大體分為 3 大塊:
- 機房層面,主要關注機房的網絡出口帶寬、入口帶寬。一般這個對我們業務開發來說,都接觸不到,但是這里還是需要注意,如果機房帶寬不夠,那么我們的服務就支撐不了大的并發,從而也沒法讓我們的系統有一個好的性能。
- 機器配置層面,服務器本身的性能要足夠好,包括 CPU、內存、磁盤(SSD)等資源。同理,一般這個對我們業務開發來說,都接觸不到,但是如果機器配置較差,那么我們的服務部署在這樣的機器上面,也無法充分發揮,從而使得我們的業系統也無法擁有一個好的性能。
- 資源使用層面,我們要合理的分配 CPU 和內存等相關資源,一般 CPU 的使用率不要超過 70%-80%,超過這個閾值后,我們服務的性能就會開始下降,因此一般我們在 70% 的時候就要開始執行擴容。如果是 K8s 容器部署的話,我們可以設置 CPU 使用率超過指定閾值后就自動擴容。當然,如果是物理機部署,或者其他方式,可以同樣的進行監控和及時擴容。也就是說,要保證我們所需的各種資源(CPU、內存、磁盤、帶寬)都在一個合理的范圍。
七、運維部署層面
在運維部署層面做好相關建設,是有助于提高我們系統的整體性能的。比如,我們可以通過容器化部署做到彈性伸縮,通過彈性伸縮的能力,可以使得我們的服務,在資源分配使用上,一直保持合理的 CPU、內存等資源的使用率。
八、性能測試優化層面
我們從架構設計層面、編碼實現層面按照高性能的解決方案和思路實現了我們系統之后,理論上,我們的系統性能不會太差,但是,具體我們的系統性能如何?是否存在可優化點?代碼的實現是否有性能問題?我們的依賴服務是否存在性能問題?等等,這些對我們大部分人來說,如果沒有一個合理的性能壓測和分析,那么可能還是黑盒的。
因此,針對我們研發人員而言,在高性能架構設計方面的最后一個環節,就是進行性能測試優化,具體包括三個環節:
- 性能壓測。針對系統的各個環節先分別做壓測,然后有條件的情況下,最好能夠做全鏈路壓測。
- 性能分析。壓測后,最優的分析方式是結合火焰圖去分析,看看性能最差的是哪里,是否有可優化的點。一定是先找到性能最差的進行優化,這樣事半功倍。
- 性能優化。找到可優化點后,進行優化。然后反復這三個步驟,直到你認為性能已經完全符合預期。
本文轉載自微信公眾號「 后端系統和架構」,作者「 AllenWu」