[WIP] Go練習 - PProf分析練習

Introduction

先說一下此次要作什麼, 咱們天天都須要同步一次本部門人員詳細信息. 可是隻有兩個接口可用:node

  • 獲取全部人員姓名
  • 根據指定姓名, 查詢詳細信息的接口

在得到詳細信息之後須要篩選出本部門的人員信息, 也就是org字段中包含雲部門的數據, 篩選出的數據算是最新人員名單, 對比數據庫中已有的數據, 作增量更新(也就是不存在的補上就夠了).golang

brain fuck 🖕

我剛拿到這個任務簡直無語, 真他媽無聊到批爆 ... fuck! 可是又隱約想起這彷佛是我之前遇到過的一個面試題, 簡單想一想好像沒什麼嘛, 可是在30萬的數量級, daily basis的狀況下. 任何微小的浪費都會被放大, 這樣一下就有意思了, 能作確定是能作, 可是怎麼作會比較"快", 比較"經濟"(不浪費內存), 針對這種狀況, 如今市面上有這些流言, 他們說:面試

  • 經過make作數組作預分配,能減輕Slice再分配內存所消耗的時間, 同時減小內存碎片化更好的利用內存
  • 經過sync.Pool能很大程度的提高短命內存的使用效率(這個我也說過)
  • 經過 for _,item := range array 中間會有一次值拷貝, 而range array[index]沒有
  • 合理設計協程,channel能加速你的爛程序, fuck !
  • 逐個執行SQL單條插入 vs SQL批量插入 那個比較好?

背景知識

咱們會從時間+內存+CPU佔用的狀況分析某個策略的好壞, 時間的話好辦, 那麼內存+CPU數據應該怎麼捕捉呢? 我知道有不少種方式, 目前比較常見的(網上教程比較多的)方法是經過pprof的方法來作. 我如今是經過最簡單最naive的方式實現了個人需求, 在我定位出了我面臨的問題之後, 我就能夠一個一個去驗證網上的這些"流言" (其實我想知道他們在說這些以前, 本身有沒有真的實踐過)數據庫

在開始用pprof以前, 先介紹一下這個工具, 這個概念:json

PProf是什麼

大部分市面上的教程呢, 都是教教你怎麼畫火焰圖, 調用圖而後就結束了. 我以爲pprof這個單詞有點鬼畜, 我就從定義開始說, 單獨搜了一下這個詞是什麼含義, 將它拆成兩段: pprof = p + prof = Package Profiling. Packge就是你的程序, Prof就是Profile的簡寫, Profile是指一組棧追蹤信息. 指, 當你但願完成一個事件的時候, 全部的棧調用信息.數組

那麼咱們就理解了, pprof是指咱們程序運行的時候, 來作棧分析, 進一步往下, 分析的內容能夠是CPU, 也能夠是內存分配分析等, 對應下來, 這個工具就提供了相應的指標供你觀察, 咱們從最小, 但卻最經常使用最簡單的指標開始學習: ( 想學習最全面的指標, 能夠點擊這裏 → 完整的指標 )函數

  • profile指標:
    • CPU佔用時長分析, 採樣時長默認30秒, 分析一下這30秒內那些函數, 分別消耗了多長時間
    • 上面提到了一個詞:"採樣", 咱們不能作到全實時監控這個程序, 所以咱們每10ms查看程序一次, 主要看看有哪些函數正在運行, 如今假設我執行了100次採樣(共1秒), 其中有50次函數f都在運行, 那麼咱們就估算出, 這個函數運行了50 * 10ms = 0.5秒
  • heap指標:
    • 內存分配分析, 函數分配的內存都是在Heap(堆)上, 所以分析函數內存佔用本質上就是對於堆內存的分析

佈置pprof採集點

爲了佈置pprof, 你一共有兩種方式, 兩句話歸納這兩種方式, 它們分別是:工具

  • runtime/pprof :
    • 使用這種方式, 你須要在程序裏經過調用API得到採集數據, 在程序運行結束的時候生成prof文件, 而後單獨分析這個文件.
    • 採集什麼指標是你決定的, 靈活性高
  • net/http/pprof: (咱們使用這種方式)
    • 這種方式更加簡單, 你只須要在程序裏單獨開一個接口, 而後程序運行的時候經過訪問這個接口就能訪問pprof數據了, 本質上也是經過第一種方式來實現的
    • 雖然沒有那麼靈活可是足夠簡單, 在你的程序裏添加下面的語句
import _ "net/http/pprof"

func main() {
  go func() {
    http.ListenAndServe("localhost:6060", nil)
  }
}
複製代碼

第一次採集

CPU數據採集

在pprof觀察點設置好了之後, 咱們讓程序運行起來, 而後在**命令行中輸入命令: go tool pprof http://localhost:6060/debug/pprof/profile來得到CPU分析結果, 輸入命令之後須要等待30秒鐘等待採樣完成. 隨後就進入命令行模式, 咱們經過top5命令**查詢CPU佔用前五高的函數post

這樣咱們就能得到, 在這30秒裏, 在CPU上都發生了什麼, 接下來, 咱們來詳細分析一下這裏面的一些指標都是什麼含義:學習

  • Duration: 30.15s, Total samples = 2.22s
    • 程序運行了30秒, 在這期間, 咱們總共花在採樣上的時間是2.22秒
  • Showing nodes accounting for 1040ms, 46.85% of 2220ms total
    • 咱們索取了前五高, 這前五高的時間加一塊兒一共佔據了1.04秒, 花在採樣上的時間總共是2.22秒, 所以, 就CPU佔用時間上來講, 這前五個函數, 佔掉了總時間的46%
    • 咱們看flat列中的時間, 這麼些時間加在一塊兒正好是1.04秒, 而後看看flat列中的百分比, 百分比加在一塊兒是46%. 也就是說:系統認爲, 五個函數消耗的時間是基於flat時間算出來的
  • flat/cum指標:
    • 區別是什麼? 爲何兩個值不同?
    • flat指標 = 採樣時正在運行這個函數
    • cum指標 = 採樣時,這個函數(A)出如今堆棧上, 多是正在運行, 也多是這個函數正在調用子函數(B), 這個函數A正在等待子函數B返回, 咱們要畫出來基本是這樣的

內存佔用採集

一樣的, 咱們使用這個命令來查看內存的使用狀況: go tool pprof http://localhost:6060/debug/pprof/heap

跟上面的分析相似, 咱們能發如今咱們的程序中, 內存消耗大戶是strings調用的函數, 以及json調用的函數. 若是你想知道更多, 咱們還能從網頁版裏面驗證這些說法:

太好拉, 經過堆棧調用信息, 一下就他媽破案了, 咱們內存佔用大戶主要來自這兩個函數的反覆調用:

  • strings.Spilit
  • json.Unmarshall

我不是說這兩個函數有問題, 你們之後不要用了, 不是這樣的, 只是說在個人程序中, 我反覆調用了這兩個函數, 不停的分配新內存. 如今真是太好了, 在"優化程序"這個巨大的話題中, 咱們至少已經定位出了咱們的問題, 圍繞咱們的問題, 咱們不用像無頭蒼蠅同樣了, 咱們能夠針對問題想辦法了 :)

Summary

總結一下我遇到的問題:

  • CPU佔用大戶: syscall
    • 多是由於我反覆發送請求索取數據, 反覆SQL插入致使的時間消耗
    • 即便請求數據我沒法優化, 那麼SQL插入或許能夠執行批量插入緩解?
  • 內存佔用大戶: strings.Split + json.Unmarshall
    • 在整理數據的時候大量的執行strings.Split並分配新內存
    • json.Unmarshall消耗大量內存. 這些問題經過sync.Pool能夠解決嗎?

流言都是真的嗎? 它們適用於個人狀況嗎? 又有多大程度的效果? 下次繼續更新

References

相關文章
相關標籤/搜索