分佈式數據庫調優實踐

數據庫調優實踐案例linux

數據庫做爲基礎數據支撐層的核心部分,對於應用和平臺總體性能表現有着決定性的影響。所以,數據庫性能優化能夠說是最考驗DBA能力的工做了。本文咱們就由數據庫內核專家來,以 SequoiaDB 5.0 內核的部分性能優化爲例,帶領各位數據庫愛好者揭開數據庫性能優化的「神祕面紗」。數據庫

一般優化思路:緩存

提升數據庫性能的方式有不少,總結起來從易到難無外乎以下三種:性能優化

  1. 最簡單直觀的是經過使用數據庫提供的工具,找到SQL語句執行中消耗資源最大或耗時最長的部分,也即性能瓶頸。而後經過調整數據自己或數據庫配置解決這些性能瓶頸。好比說發現數據分佈不均勻,咱們能夠經過切分數據(split)達到數據均衡(rebalance);再好比咱們發現某些網絡時延較長,在肯定不是網絡自己的問題後,咱們能夠經過調整鏈接端口數和通信處理線程提升數據庫消息處理能力;再好比單點磁盤IO過多,須要調整緩存或調整部分數據的分佈。SequoiaDB提供了圖形化的性能診斷工具SequoiaPerf,能夠協助用戶完成上述的調優。
  2. 業界經驗證實,效果最明顯,成本最低的方法實際上是SQL語句的調優,一般是經過理解分析訪問計劃,對比實際語句執行時的開銷來判斷語句是否優化。好比對比索引讀和表讀的個數判斷否建立使用了合適的索引;對比訪問計劃的打分和時間執行開銷來判斷表/集合/索引的統計信息是否反映當前最新的狀態;觀察鎖等待時間來判斷系統中是否存在應用持鎖時間過長阻塞其餘應用而;對比join兩邊表的返回數據集以及使用的過濾條件判斷使用join的類型是否合理。SequoiaDB 提供了完善監控功能,經過結合圖形化的sequoiaPerf 與snapshot,用戶能夠相對容易的定位和實現SQL語句的調優。
  3. 前兩種方式一般是DBA或應用開發者就能完成的任務,第三種是數據庫內核的優化。這主要是數據庫廠商在不斷的實踐中,經過各類相對底層的性能診斷工具,定位和優化數據引擎的性能。

內核調優網絡

在數據庫內核的調優中,開發人員一般會跑必定的workload或benchmark,使用操做系統或三方提供的工具,持續監控系統各種資源的使用狀況,在高併發系統中,也會關注併發控制中使用的鎖和原子變量帶來的開銷。下面咱們經過TPCC場景下的逐步優化SequoiaDB內核的過程,來了解咱們是如何使用工具來定位優化數據引擎的。併發

1. CPU usage高併發

咱們常使用兩大神器觀察CPU使用狀況:top 和 perf。top能動態的顯示linux 系統中各進程/線程以及內存使用的彙總信息。工具

/性能

以上圖爲例,咱們知道這臺機器的CPU基本上被用滿了,其中系統CPU佔13%,用戶CPU佔81.7%。若是CPU出現過多的空閒,每每意味着系統要麼還能夠增長負載提升性能,要麼有瓶頸致使CPU上不去,好比說併發很差,太多等待,串行化太多。在這個例子中,咱們沒有看到等IO的狀況,idle的比例也很是小,這都是好的現象。在CPU用滿的狀況下,優化系統也意味着要儘可能減小開銷,讓系統能儘量的跑多點任務。須要注意的是,若是系統CPU太高,意味着CPU不是在執行跟程序邏輯相關的指令,也能夠理解爲是overhead。根據以往的經驗,這裏系統CPU佔比仍是偏高。使用線程模式,更進一步分析,咱們能夠看到潛在的問題多是在系統調用,context switch和併發控制的mutex上。優化

至於更精確的定位,就要perf出馬了。注意的是SequoiaDB 的代碼編譯時加入了debug symbol,這樣會帶來必定的性能損失,但可以極大的方便問題診斷和定位。

perf 是linux提供的一種基於event的性能蒐集分析工具,可以分析CPU/內存/鎖等資源的統計信息。perf自己已經提供了至關完整的文字的報表輸出功能。

好比這裏能看到system_call 也是跟sys_futex 相關的,一般是線程/進程同步共享資源互踩時形成的,還有部分是通信線程相關的。這樣咱們的方向就能夠從各類鎖衝突入手。Perf也能提供鎖衝突的信息。

爲了簡單直觀的分析結果,咱們還使用火焰圖(flame graph)來用圖形的方式展示結果,以利用更快的發現問題。下面兩張圖分別提供了CPU和鎖的使用統計:從中咱們發現的確有幾處熱的Latch/mutex。好比內存分配時使用共享內存池,這是會形成等鎖的現象,咱們能夠經過使用線程上獨享的內存池解決;還要部份內部表的物理鎖衝突嚴重,咱們經過增長鎖的控制粒度減小衝突;再有就是儘可能減小鎖內操做,好比內存分配,磁盤IO儘量的搬出熱鎖保護範圍。經過一系列優化,咱們實現了5%左右的性能提高。

CPU 火焰圖

鎖火焰圖

2. Memory allocation

內存是個好東西,如今計算機系統內存愈來愈大,軟件也儘可能經過使用內存來實現空間換時間以提升系統相應速度。可是動態內存分配經常成爲了高性能軟件的性能瓶頸。咱們經過perf 來抓取系統內存的使用狀況,並用火焰圖顯示出來:

這裏明顯看到的是不少動態內存分配發生在一個set的插入過程當中。Std::set內部使用的紅黑樹,每次結點的插入都要進行內存分配。爲了減小系統內存的動態分配與回收,SequoiaDB實現了一整套本身的內存管理機制。最開始儘可能在線程預分配好的內存池上分配空間,這點和tcmalloc的原理很接近,這時的開銷最小,內存事先已經從操做系統分配好了,並且本線程上分配是無鎖的。可是若是線程內存池用完了,咱們會到一個共享的預分配好的內存池上分配,這時會多一個鎖的開銷。但這兩處都用完了,咱們才向操做系統申請。從火焰圖上看,咱們基本上都走到向操做系統分配的分支中了。針對這種狀況,咱們優化了set的實現。當set中結點數量較小時,咱們用一個flat的較小的array存放數據,避免了動態內存分配。當結點數較多時,咱們再轉化成樹型結構以提升查找效率。可是咱們會提升線程上容許的緩衝池的大小,特別是小結構線程池大小。最終咱們避免了絕大多少的動態內存分配與回收,提高了系統性能。經過這塊的分析,咱們也反過來幫助肯定那些query會用到大量數據,並優化對應的query。

3. Cache line misses

你們知道現代CPU的主頻很是高,常有超過3GHz,執行指令速度很是快。可是咱們存儲訪問速度始終跟不上,高速的內存又很是貴,這就是現代CPU裏有幾級不一樣速度不一樣大小內存的緣由,常見的CPU內集成有L1,L2,L3級緩存。CPU執行時須要從緩存中獲取指令和數據。在咱們編譯程序的時候,編譯器會試圖優化程序,使得CPU能有效的重用或預提取數據和指令。當CPU在緩存中找不到合適的指令和數據時,就不等不從主存甚至磁盤上讀取他們,這樣的開銷很是大,咱們用CPU cache line miss來衡量這中狀況出現的頻繁程度。

咱們仍是經過perf命令來蒐集cache line miss的狀況,

詳細信息分解開來,最大一塊是由monitor引發的

而後咱們檢查monitor相關的代碼,發現代碼中有個switch語句公有14個分支,但最經常使用的一個分支放在了後面。咱們只須要將其挪到前面,咱們的miss就有顯著降低。

還有另一種狀況形成嚴重的cache line miss,就是使用原子變量,特別是頻繁使用的原子變量。由於一旦該變量被變動了,全部cache 裏的值都會變成無效,那麼CPU使用時必定會碰到cache line miss。咱們經過分析代碼邏輯,對於某些經常使用的確不須要時時精確的值,咱們能夠在程序邏輯開始存爲本地變量,避免過多的直接訪問。對於一些只須要單線程訪問的變量,咱們也避免使用原子變量。

小結:

上面咱們經過幾個例子,爲你們展示瞭如何經過系統工具進行數據庫內核性能優化,一樣的思路也能夠適用於其餘底層軟件的開發調試。在實際的實踐過程當中,除了使用合適的工具,更重要的是還要細心,有耐心和鑽研的精神,一步步的下手,從現象中抽絲剝繭,找到根本緣由。

相關文章
相關標籤/搜索