從代碼級別優化Zabbix的數據展現性能

MylesMartin 8年前發布 | 20K 次閱讀 數據庫 ZABBIX

一、問題背景

對于特大場景應用環境中,Zabbix本身對某些方面的性能并未考慮周全,本文就從代碼級別二次開發優化入手來達到我們的需求。

從選擇更好的硬件配置,如大容量內存、更快速的SSD固態磁盤、改raid配置等硬件角度優化,效果會立竿見影,不過大部分這些操作都是在規劃階段完成,后期往往通過軟件優化為主。

Zabbix 本身是一個高性能的分布式服務器集群監控系統,其基本結構是在多臺被監控機上安裝agent客戶端,由agent端通過trapper不斷捕獲數據發送到server端,server端對捕獲上來的數據進行解析再分別入庫到后臺數據庫表中(Zabbix可以支持MySQL、Oracle、PostgreSQL等數據庫),然后在前端管理界面上來訪問相應的頁面,并展現各臺被監控機上的各項性能指標的情況,它的各種指標性能的展現所需數據的主表基本上是一個歷史數據表,如下圖樣例:

上面圖中所展示數據就來自于歷史表之一history,此表基本結構如下:

注: Zabbix原始表里沒有為其添加任何主鍵和索引。)

其它的各種性能指標的展現所依賴的數據表結構基本如上表,只是value列類型不一樣,而且表名不一樣而已。

為何Zabbix的原設計者要如此設計這些表呢?我想是基于這樣的原因:就是 希望它能快速地進行數據入庫和數據展現 。因為,如果為表加主鍵或索引,則入庫速度肯定會變慢一些,在管理成百上千臺機器時,且管理指標項目又很多時(一個host下可以有多個不同類型的被監控項item),單位時間產生的數據量是相當可觀的,如果入庫一條記錄稍微慢那么一點點,那在特大場景應用環境中就會幾何倍數放大,收到的數據無法及時入庫,并且可能造成Zabbix server內存積壓,而連帶使Zabbix server本身收集數據的能力變慢(甚至Zabbix server因內存不足而崩潰),最終會導致采到的性能數據丟失,Zabbix的作用就大打折扣,所以入庫速度是它第一要考慮的問題。

另外,設計者為何要對不同類型的數據實行分開存放呢?我想他 同樣是為了增加入庫速度的目的,也是為了提升查詢展示的速度 ,因為不同數據類型分開存放后,一個表就“專用”化了,方便治理,也有利于將大表拆分成小表,減低單表壓力,對入庫速度和查詢速度都是有利的(特別是有利于降低對表鎖的爭搶)。

好了,我們已經明白,為了管理大量的機器,我們必須要求Zabbix入庫速度要盡可能地快,且查詢對入庫的影響要盡可能地小,為了這個目標它也有了比較多的考慮,但是經過我們的測試并梳理后,發現在展現最新數據時,仍舊是十分緩慢,我們觀察了一下最新數據的展現機制,發現它是要從歷史表中拿出最新入庫的數據來展現,展現示例界面如下:

其查詢語句大致如下:

select h.itemid,i.name,h.value,… from history hi, items It, hosts ht where hi.itemid=it.itemid and it.hostid=ho.hostid and ho.host='…' and it.name='…' group by it.itemid order by hi.clock desc limit 1

注: 以上語句是一個根據顯示數據的情況寫出的示意語句,不是原始的語句,且是基于MySQL,但實際用到的語句與此相似)

在數據量很大的情況下,基于現有的數據庫設計,上面語句很明顯會很慢。

1 )它需要關聯查詢items表和hosts表,而如果沒有主鍵,關聯查詢的性能肯定是相當慢(特別是數據量又相當大的情況下),而一般情況下,歷史表中的數據量肯定是相當巨大;

2 )需要用到group by查詢和order by查詢,在沒有索引或主鍵且獲取的數據量很大的情況下,其速度也肯定是很慢的;

3 )因為前述問題,后臺又要不斷往歷史表中插入數據,會爭搶表或行鎖,于是查詢和入庫之間會互相影響,導致大家都不好過。

4 )要是多人同時打開界面查看與歷史表相關的其它數據呢?那不是互相之間又產生更多影響。

于是,要讓最新性能數據的展現顯示得快,又對其它查詢和入庫不產生影響,非常有必要對最新數據的展現這一塊進行優化,要設計一種方案來優化。

二、我們的優化方案和目標

根據前面對問題的分析,我們需要實現以下幾點:

  1. 對最新數據的查詢要盡可能地快;

  2. 必須不能影響對歷史表的入庫;

  3. 保持對現有數據的結構。

基于以上目標,我們容易想到的方案是:在歷史數據入庫時,找出最新的數據并另存一份到一個獨立的結構相同的最新數據表中,最新數據的展示查詢時,只到這個獨立的表中去查詢,這樣就可以解決上述三個問題。

三、設計及實現

1 、首先必須設計幾個與歷史表對應的用于存放最新數據的表。

last 表(對應history表):

last_unit 表(對應history_unit表):

last_str 表(對應history_str表):

其它還有兩個字串型表就不羅列了。

以上各個表的結構基本一樣,與原對應的歷史表數據結構也是一樣的。

2 、為上述表添加主鍵索引

根據前面問題背景分析里所述的查詢語句,最新數據查詢時會關聯到items表和hosts表,為了加快數據查詢展示速度,需要為上面新加的這些表的itemid列添加主鍵索引,即將itemid列設為主鍵列,在入庫時每個itemid只有一行數據,就是最新的那條性能數據,同時要為相關的items.itemid列、hosts.hostid列添加主鍵索引(如果它們沒有被加上的話)。

3 、修改Zabbix server源碼,添加對最新數據的入庫,并做一些歷史數據的入庫優化。

找到Zabbix server程序的源碼中同步歷史數據的代碼處(即:將內存數據刷入數據庫的過程),根據其邏輯添加對最新數據的入庫邏輯,以下是修改后的對history表數據入庫的代碼邏輯過程:


其它表的數據入庫過程與此類似,參考代碼略。

簡單解釋:

在上面代碼里:ADD_HISTORY_PART_MID1(dbl, value.dbl)這一行展開的過程里會有以下代碼:

上面的這一行dc_add_last_dbl(phis->itemid, &(phis->ts), phis->vf, 0);就會向存最新數據的數據表中插入最新的性能數據行。并且,上面過程看出,對同個itemid的數據行,只會執行一次記錄插入(根據分析,在調用入庫邏輯過程前,同一批歷史數據是按itemid排好序了的,所以這樣做是可以的),另外,dc_add_last_dbl(phis->itemid, &(phis->ts), phis->vf, 0)過程(代碼略)在插入數據時,在插入語句里加了 "on duplicate key update value=values(value), clock=values(clock);" 這么一個子句,通過這個子句來實現插入時對已經存在的相同itemid數據進行修改,而不是插入一條新數據,這實現了數據的減量,但又不會增加插入性能消耗,因為表上只加了itemid主鍵。

改好后,重編譯安裝Zabbix server,再啟動即可。

4 、更改前端界面的查詢

前面修改好后,還需要把前面展現最新數據的界面那里的原來的數據查詢語句修改一下,將原來的從歷史表中查詢的方式改為從最新數據表中查詢(就是前面新加的表),修改時相當簡單,直接把前面問題背景分析里所述的查詢語句里的表名改掉即可,如:history改成last,history_uint改成last_uint即可,以此類推,因為新加的表的結構與原來對應的歷史表結構一模一樣。

四、效果與總結

經過上述修改后,我們發現,最新性能數據展現那里已經可以十分快速地打開界面了,這說明上面的做法起到了相當明顯的優化效果,同時,從這個效果里我們也看到解決了背后的一個問題:歷史數據的入庫也不會受到因對歷史表的group by/order by/關聯查詢的影響而變得快速了,不會因為查一下歷史表里的最新數據,就讓入庫都受到影響而變慢。

總結起來就是: 要盡量讓客戶端捕獲上來的數據盡快收集起來并能快速插入到數據庫中,要盡量避免在歷史表中使用可以避免的影響速度的查詢,否則可能會影響到入庫(同時查詢本身也會慢)導致采集到的數據丟失。

 

 

 

來自:http://mp.weixin.qq.com/s?__biz=MzA3MzYwNjQ3NA==&mid=2651297146&idx=1&sn=30e2eee558a55df52cd47b14c1edd1f7&scene=0#rd

 

 本文由用戶 MylesMartin 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!