當前位置:首頁 » 編程軟體 » ES監控腳本

ES監控腳本

發布時間: 2023-07-16 06:26:15

Ⅰ ElasticSearch性能優化實踐(JVM調優+ES調優)

近一年內對公司的 ELK 日誌系統做過性能優化,也對 SkyWalking 使用的 ES 存儲進行過性能優化,在此做一些總結。本篇主要是講 ES 在 ELK 架構中作為日誌存儲時的性能優化方案。

隨著接入ELK的應用越來越多, 每日新增索引約 230 個,新增 document 約 3000萬到 5000萬

每日上午和下午是日誌上傳高峰期,在 Kibana 上查看日誌,發現問題:
(1) 日誌會有 5-40 分鍾的延遲
(2) 有很多日誌丟失,無法查到

數據先是存放在 ES 的內存 buffer,然後執行 refresh 操作寫入到操作系統的內存緩存 os cache,此後數據就可以被搜索到。

所以,日誌延遲可能是我們的數據積壓在 buffer 中沒有進入 os cache 。

查看日誌發現很多 write 拒絕執行的情況

從日誌中可以看出 ES 的 write 線程池已經滿負荷,執行任務的線程已經達到最大16個線程,而200容量的隊列也已經放不下新的task。

查看線程池的情況也可以看出悔侍 write 線程池有很多寫入的任務

所以我們需要優化 ES 的 write 的性能。

ES 的優化分為很多方面,我們要根據使用場景考慮對 ES 的要求。

根據個人實踐經驗,列舉三種不同場景碧沖吵下的特點

這三類場景的特點如下:

關於實時性

可以從三方面進行優化:JVM性能調優、ES性能調優、控制數據來源

可以從三方面進行優化:JVM 性能調優、ES 性能調優、控制數據來源

第一步是 JVM 調優。
因為 ES 是依賴於 JVM 運行,沒有合理的設置 JVM 參數,將浪費資源,甚至導致 ES 很容易 OOM 而崩潰。

(1) 查看 GC 日誌

(2) 使用 jstat 看下每秒的 GC 情況

用下面幾種方式都可查看新、老年代內存大小
(1) 使用 jstat -gc pid 查看 Eden 區、老年代空間大小
(2) 使用 jmap -heap pid 查看 Eden 區、老年代空間大小
(3) 查看 GC 日誌中的 GC 明細

上面的幾種方式都判羨查詢出,新生代總內存約1081M,即1G左右;老年代總內存為19864000K,約19G。新、老比例約1:19,出乎意料。

這真是一個容易踩坑的地方。
如果沒有顯示設置新生代大小,JVM 在使用 CMS 收集器時會自動調參,新生代的大小在沒有設置的情況下是通過計算得出的,其大小可能與 NewRatio 的默認配置沒什麼關系而與 ParallelGCThreads 的配置有一定的關系。

所以: 新生代大小有不確定性,最好配置 JVM 參數 -XX:NewSize、-XX:MaxNewSize 或者 -xmn ,免得遇到一些奇怪的 GC,讓人措手不及。

新生代過小,老年代過大的影響

32G 的內存,分配 20G 給堆內存是不妥當的,所以調整為總內存的50%,即16G。
修改 elasticsearch 的 jvm.options 文件

設置要求:

因為指定新生代空間大小,導致 JVM 自動調參只分配了 1G 內存給新生代。

修改 elasticsearch 的 jvm.options 文件,加上

老年代則自動分配 16G-8G=8G 內存,新生代老年代的比例為 1:1。修改後每次 Young GC 頻率更低,且每次 GC 後只有少數數據會進入老年代。

ES默認使用的垃圾回收器是:老年代(CMS)+ 新生代(ParNew)。如果是JDK1.9,ES 默認使用G1垃圾回收器。

因為使用的是 JDK1.8,所以並未切換垃圾回收器。後續如果再有性能問題再切換G1垃圾回收器,測試是否有更好的性能。

優化前

每秒列印一次 GC 數據。可以看出,年輕代增長速度很快,幾秒鍾年輕代就滿了,導致 Young GC 觸發很頻繁,幾秒鍾就會觸發一次。而每次 Young GC 很大可能有存活對象進入老年代,而且,存活對象多的時候(看上圖中第一個紅框中的old gc數據),有(51.44-51.08)/100 * 19000M = 約68M。每次進入老年代的對象較多,加上頻繁的 Young GC,會導致新老年代的分代模式失去了作用,相當於老年代取代了新生代來存放近期內生成的對象。當老年代滿了,觸發 Full GC,存活的對象也會很多,因為這些對象很可能還是近期加入的,還存活著,所以一次 Full GC 回收對象不多。而這會惡性循環,老年代很快又滿了,又 Full GC,又殘留一大部分存活的,又很容易滿了,所以導致一直頻繁 Full GC。

優化後

每秒列印一次 GC 數據。可以看出,新生代增長速度慢了許多,至少要60秒才會滿,如上圖紅框中數據,進入老年代的對象約(15.68-15.60)/100 * 10000 = 8M,非常的少。所以要很久才會觸發一次 Full GC 。而且等到 Full GC 時,老年代裡很多對象都是存活了很久的,一般都是不會被引用,所以很大一部分會被回收掉,留一個比較干凈的老年代空間,可以繼續放很多對象。

ES 啟動後,運行14個小時

優化前

Young GC 每次的時間是不長的,從上面監控數據中可以看出每次GC時長 1467.995/27276 約等於 0.05秒。那一秒鍾有多少時間實在處理Young GC ? 計算公式:1467秒/ (60秒×60分 14小時)= 約0.028秒,也就是100秒中就有2.8秒在Young GC,也就是有2.8S的停頓,這對性能還是有很大消耗的。同時也可以算出多久一次Young GC, 方程是: 60秒×60分*14小時/ 27276次 = 1次/X秒,計算得出X = 0.54,也就是0.54秒就會有一次Young GC,可見 Young GC 頻率非常頻繁。

優化後

Young GC 次數只有修改前的十分之一,Young GC 時間也是約八分之一。Full GC 的次數也是只有原來的八分之一,GC 時間大約是四分之一。

GC 對系統的影響大大降低,性能已經得到很大的提升。

上面已經分析過ES作為日誌存儲時的特性是:高並發寫、讀少、接受30秒內的延時、可容忍部分日誌數據丟失。
下面我們針對這些特性對ES進行調優。

本人整理了一下數據寫入的底層原理

refresh
ES 接收數據請求時先存入 ES 的內存中,默認每隔一秒會從內存 buffer 中將數據寫入操作系統緩存 os cache,這個過程叫做 refresh;

到了 os cache 數據就能被搜索到(所以我們才說 ES 是近實時的,因為1s 的延遲後執行 refresh 便可讓數據被搜索到)

fsync
translog 會每隔5秒或者在一個變更請求完成之後執行一次 fsync 操作,將 translog 從緩存刷入磁碟,這個操作比較耗時,如果對數據一致性要求不是跟高時建議將索引改為非同步,如果節點宕機時會有5秒數據丟失;

flush
ES 默認每隔30分鍾會將 os cache 中的數據刷入磁碟同時清空 translog 日誌文件,這個過程叫做 flush。

merge

ES 的一個 index 由多個 shard 組成,而一個 shard 其實就是一個 Lucene 的 index ,它又由多個 segment 組成,且 Lucene 會不斷地把一些小的 segment 合並成一個大的 segment ,這個過程被稱為 段merge 。執行索引操作時, ES會先生成小的segment ,ES 有離線的邏輯對小的 segment 進行合並,優化查詢性能。但是合並過程中會消耗較多磁碟 IO,會影響查詢性能。

為了保證不丟失數據,就要保護 translog 文件的安全:

該方式提高數據安全性的同時, 降低了一點性能.

==> 頻繁地執行 fsync 操作, 可能會產生阻塞導致部分操作耗時較久. 如果允許部分數據丟失, 可設置非同步刷新 translog 來提高效率,還有降低 flush 的閥值,優化如下:

寫入 Lucene 的數據,並不是實時可搜索的,ES 必須通過 refresh 的過程把內存中的數據轉換成 Lucene 的完整 segment 後,才可以被搜索。

默認1秒後,寫入的數據可以很快被查詢到,但勢必會產生大量的 segment,檢索性能會受到影響。所以,加大時長可以降低系統開銷。對於日誌搜索來說,實時性要求不是那麼高,設置為5秒或者10s;對於SkyWalking,實時性要求更低一些,我們可以設置為30s。

設置如下:

index.merge.scheler.max_thread_count 控制並發的 merge 線程數,如果存儲是並發性能較好的 SSD,可以用系統默認的 max(1, min(4, availableProcessors / 2)),當節點配置的 cpu 核數較高時,merge 佔用的資源可能會偏高,影響集群的性能,普通磁碟的話設為1,發生磁碟 IO 堵塞。設置max_thread_count 後,會有 max_thread_count + 2 個線程同時進行磁碟操作,也就是設置為 1 允許3個線程。

設置如下:

該方式可對已經生成的索引做修改,但是對於後續新建的索引不生效,所以我們可以製作 ES 模板,新建的索引按模板創建索引。

因為我們的業務日誌是按天維度創建索引,索引名稱示例:user-service-prod-2020.12.12,所以用通配符 202 ..*匹配對應要創建的業務日誌索引。

前文已經提到過,write 線程池滿負荷,導致拒絕任務,而有的數據無法寫入。

而經過上面的優化後,拒絕的情況少了很多,但是還是有拒絕任務的情況。

所以我們還需要優化write線程池。

從 prometheus 監控中可以看到線程池的情況:

為了更直觀看到ES線程池的運行情況,我們安裝了 elasticsearch_exporter 收集 ES 的指標數據到 prometheus,再通過 grafana 進行查看。

經過上面的各種優化,拒絕的數據量少了很多,但是還是存在拒絕的情況,如下圖:

write 線程池如何設置:

參考: ElasticSearch線程池

write 線程池採用 fixed 類型的線程池,也就是核心線程數與最大線程數值相同。線程數默認等於 cpu 核數,可設置的最大值只能是 cpu 核數加1,也就是16核CPU,能設置的線程數最大值為17。

優化的方案:

config/elasticsearch.yml文件增加配置

優化後效果

Swap 交換分區 :

參考: ElasticSearch官方解釋為什麼要禁用交換內存

有三種方式可以實現 ES 不使用Swap分區

執行命令

可以臨時禁用 Swap 內存,但是操作系統重啟後失效

執行下列命令

正常情況下不會使用 Swap,除非緊急情況下才會 Swap。

config/elasticsearch.yml 文件增加配置

分片

索引的大小取決於分片與段的大小,分片過小,可能導致段過小,進而導致開銷增加;分片過大可能導致分片頻繁 Merge,產生大量 IO 操作,影響寫入性能。

因為我們每個索引的大小在15G以下,而默認是5個分片,沒有必要這么多,所以調整為3個。

分片的設置我們也可以配置在索引模板。

副本數

減少集群副本分片數,過多副本會導致 ES 內部寫擴大。副本數默認為1,如果某索引所在的1個節點宕機,擁有副本的另一台機器擁有索引備份數據,可以讓索引數據正常使用。但是數據寫入副本會影響寫入性能。對於日誌數據,有1個副本即可。對於大數據量的索引,可以設置副本數為0,減少對性能的影響。

分片的設置我們也可以配置在索引模板。

有的應用1天生成10G日誌,而一般的應用只有幾百到1G。一天生成10G日誌一般是因為部分應用日誌使用不當,很多大數量的日誌可以不打,比如大數據量的列表查詢介面、報表數據、debug 級別日誌等數據是不用上傳到日誌伺服器,這些 即影響日誌存儲的性能,更影響應用自身性能

優化後的兩周內ELK性能良好,沒有使用上的問題:

參考

Ⅱ 07 ES部分更新,包括_update和script腳本兩種方式

主要講解的內容: 部分更新,包括_update和script腳本兩種方式

每次就傳遞少數幾個發生修改的field即可,不需要將全量的document數據發送過去

更新示例:

ES內部對partial update的實際執行,跟傳統的全量替換方式,是幾乎一樣的
1、內部先獲取document
2、將傳過來的field更新到document的json中
3、將老的document標記為deleted
4、將修改後的新的document創建出來

partial update相較於全量替換的優點
1、所有的查詢、修改和寫回操作,都發生在es中的一個shard內部,避免了網路數據傳輸的開銷(減少2次網路請求) , 大大提升了性能。
2.減少了查詢和修改中的時間間隔,可以大大減少並發沖突的情況。

通過腳本更新制定欄位,其中ctx是腳本語言中的一個執行對象,先獲取_source,再修改price欄位

在 ES安裝目錄/config/scripts目錄下創建groovy文件 ( 該方法 已過時 了)

使用 _script在ES中創建腳本 ecommerce_add_tags

_scripts腳本的簡單管理

使用id引用外部腳本更新document

與修改欄位類似

刪除欄位

刪除文檔

如果指定的document不存在,就執行upsert中的初始化操作;

如果指定的document存在,就執行doc或者script指定的partial update操作

相對於之前的使用upsert中的內容添加到不存在的文檔,使用doc_as_upsert可以在文檔不存在的時候,把doc中的內容插入到文檔中。

注意

參考文章:

ES 27 - Elasticsearch的腳本使用實踐 - 瘦風 - 博客園 https://www.cnblogs.com/shoufeng/p/11360177.html

Elasticsearch增刪改查 之 —— Update更新 - xingoo - 博客園 https://www.cnblogs.com/xing901022/p/5330778.html

How to use scripts | Elasticsearch Reference [7.x] | Elastic https://www.elastic.co/guide/en/elasticsearch/reference/7.x/moles-scripting-using.html

Painless API Reference | Painless Scripting Language [7.x] | Elastic https://www.elastic.co/guide/en/elasticsearch/painless/7.x/painless-api-reference.html#painless-api-reference

Ⅲ ES中通過腳本語言進行加減乘除和邏輯判斷進行過濾

GET task_sit/_search

  {

    "_source": ["taskActualEndTime","taskPlannedEndTime","taskSpecId","taskName","acceptOpinion","acceptTime","taskPlannedStartTime"],

  "query": {

    "bool" : {

    "must" : [

      {

        "script" : {

          "script" : {

            "source" : "if(doc['followUp'].size()==0 && doc['acceptTime'].value<=doc['taskPlannedStartTime'].value.toInstant().toEpochMilli()) {return true;}  else if(doc['followUp'].size()>0 && doc['acceptTime'].value<=(doc['taskPlannedStartTime'].value.toInstant().toEpochMilli()+doc['followUp'].value)) {return true;}  else{return false;}",

            "lang" : "painless",

            "params" : {

              "param" : 0

            }

          },

          "boost" : 1.0

        }

      },{

        "exists": {

          "field": "acceptTime"

        }

      }

    ],

    "adjust_pure_negative" : true,

    "boost" : 1.0

  }

  }

}

GET task_sit/_search

  {

    "_source": ["taskActualEndTime","taskPlannedEndTime","taskSpecId","taskName","acceptOpinion","acceptTime","taskPlannedStartTime"],

  "query": {

    "bool" : {

    "must" : [

      {

        "script" : {

          "script" : {

            "source" : "(doc['followUp'].size()==0 && doc['acceptTime'].value<=doc['taskPlannedStartTime'].value.toInstant().toEpochMilli()) || (doc['followUp'].size()>0 && doc['acceptTime'].value<=(doc['taskPlannedStartTime'].value.toInstant().toEpochMilli()+doc['followUp'].value))",

            "lang" : "painless",

            "params" : {

              "param" : 0

            }

          },

          "boost" : 1.0

        }

      },{

        "exists": {

          "field": "acceptTime"

        }

      }

    ],

    "adjust_pure_negative" : true,

    "boost" : 1.0

  }

  }

}

Ⅳ 如何獲取es文件管理器的pid

es啟動腳本是bin目錄下的elasticsearch。其中需要載入一些環境變數,諸如ES_CLASSPATH
JAVA_OPTS
ES_JAVA_OPTS等。推薦設置ES_HEAP_SIZE的值,來這只es進程需要的內存(經驗值為系統內存的一半以上),同時設置-p參數來指定pid文件的生成位置,在es關閉的時候會用到。

熱點內容
androidbug 發布:2025-02-06 23:31:56 瀏覽:49
php數字判斷 發布:2025-02-06 23:17:40 瀏覽:40
優路教育伺服器連接不上怎麼回事 發布:2025-02-06 23:03:49 瀏覽:141
資料庫加速 發布:2025-02-06 23:02:14 瀏覽:565
蘋果ipodpro如何連接安卓手機 發布:2025-02-06 23:00:56 瀏覽:529
android格式化sd卡 發布:2025-02-06 23:00:50 瀏覽:982
郝斌資料庫 發布:2025-02-06 22:44:57 瀏覽:182
全息存儲器 發布:2025-02-06 22:43:51 瀏覽:117
游戲源碼如何使用 發布:2025-02-06 22:43:40 瀏覽:716
表與資料庫 發布:2025-02-06 22:42:47 瀏覽:440