1 Data Replication 簡介
作為一個分布式數據系統,Elasticsearch/OpenSearch 也有主副本( Primary Shard )和副本( Replica Shard )的概念。
Replica 是 Primary 的 Clone ( 復制體 ),這樣就可以實現系統的高可用,具備容災能力。
Replica 從 Primary 復制數據的過程被成為數據復制( Data Replication ),Data Replication 的核心考量指標是 Replica 和 Primary 的延遲( Lag )大小,如果 Lag 一直為0,那么就是實時復制,可靠性最高。
Data Replication 的方案有很多,接下來主要介紹基于文檔的復制方案( Document Replication ) 和基于文件的復制方案 ( Segment Replication )。
1.1 Document Replication
Elasticsearch/OpenSearch 目前采用的是基于文檔的復制方案,整個過程如下圖所示:
- Client 發送寫請求到 Primary Shard Node
- Primary Shard Node 將相關文檔先寫入本地的 translog,按需進行 refresh
- 上述步驟執行成功后,Primary Shard Node 轉發寫請求到 Replica Shard Nodes,此處轉發的內容是實際的文檔
- Replica Shard Node 接收到寫請求后,先寫入本地的 translog,按需進行 refresh,返回 Primary Shard Node 執行成功
- Primary Shard Node 返回 Client 寫成功。
- 后續 Primary Shard Node 和 Replica Shard Node 會按照各自的配置獨立進行 refresh 行為,生成各自的 segment 文件。
這里要注意的一點是:Primary Shard 和 Replica Shard 的 refresh 是獨立的任務,執行時機和時間會有所差異,這也會導致兩邊實際生成和使用的 segment 文件有差異。
以上便是 Document Replication 的簡易流程,對完整流程感興趣的,可以通過下面的連接查看更詳細的介紹。
1.2 Segment Replication
elasticsearch 數據寫入最耗時的部分是生成 segment 文件的過程,因為這里涉及到分詞、字典生成等等步驟,需要很多 CPU 和 Memory 資源。
而 Document Replication 方案需要在 Primary Node 和 Replica Nodes 上都執行 segment 文件的生成步驟,但是在 Replica Nodes 上的執行實際是一次浪費,如果可以避免這次運算,將節省不少 CPU 和 Memory 資源。
解決的方法也很簡單,等 Primary Node 運行完畢后,直接將生成的 segment 文件復制到 Replica Nodes 就好了。這種方案就是 Segment Replication。
Segment Replication 的大致流程如下圖所示:
- Client 發送寫請求到 Primary Shard Node
- Primary Shard Node 將相關文檔先寫入本地的 translog,按需和相關配置進行 refresh,此處不是一定觸發 refresh
- 上述步驟執行成功后,Primary Shard Node 轉發寫請求到 Replica Shard Nodes,此處轉發的內容是實際的文檔
- Replica Shard Node 接收到寫請求后,寫入本地的 translog,然后返回 Primary Shard Node 執行成功
- Primary Shard Node 返回 Client 寫成功。
- Primary Shard Node 在觸發 refresh 后,會通知 Replica Shard Nodes 同步新的 segment 文件。
- Replica Shard Nodes 會對比本地和 Primary Shard Node 上的 segment 文件列表差異,然后請求同步本地缺失和發生變更的 segment 文件。
- Primary Shard Node 根據 Replica Shard Nodes 的相關請求完成 segment 文件的發送
- Replica Shard Nodes 在完整接收 segment 文件后,刷新 Lucene 的 DirectoryReader 載入最新的文件,使新文檔可以被查詢
這里和 Document Replication 最大的不同是 Replica Shard Nodes 不會在獨立生成 segment 文件,而是直接從 Primary Shard Node 同步,本地的 translog 只是為了實現數據的可靠性,在 segment 文件同步過來后,就可以刪除。
以上便是 Segment Replication 的簡易流程,對完整流程感興趣的,可以通過下面的連接查看更詳細的介紹。
2 Segment Replication 初體驗
OpenSearch 在 2.3 版本中發布了實驗版本的 Segment Replication 功能,接下來就讓我們一起體驗一下吧~
2.1 準備 docker 環境和相關文件
本次體驗基于 docker-compose 來執行,如下為相關內容(docker-compose.yml
):
version: '3'
services:
opensearch-node1:
image: opensearchproject/opensearch:2.3.0
container_name: os23-node1
environment:
- cluster.name=opensearch-cluster
- node.name=opensearch-node1
- discovery.seed_hosts=opensearch-node1,opensearch-node2
- cluster.initial_cluster_manager_nodes=opensearch-node1,opensearch-node2
- bootstrap.memory_lock=true # along with the memlock settings below, disables swapping
- plugins.security.disabled=true
- "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m -Dopensearch.experimental.feature.replication_type.enabled=true" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536 # maximum number of open files for the OpenSearch user, set to at least 65536 on modern systems
hard: 65536
volumes:
- ./os23data1:/usr/share/opensearch/data
ports:
- 9200:9200
- 9600:9600 # required for Performance Analyzer
networks:
- opensearch-net
opensearch-node2:
image: opensearchproject/opensearch:2.3.0
container_name: os23-node2
environment:
- cluster.name=opensearch-cluster
- node.name=opensearch-node2
- discovery.seed_hosts=opensearch-node1,opensearch-node2
- cluster.initial_cluster_manager_nodes=opensearch-node1,opensearch-node2
- bootstrap.memory_lock=true
- plugins.security.disabled=true
- "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m -Dopensearch.experimental.feature.replication_type.enabled=true"
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
volumes:
- ./os23data2:/usr/share/opensearch/data
networks:
- opensearch-net
opensearch-dashboards:
image: opensearchproject/opensearch-dashboards:2.3.0
container_name: os23-dashboards
ports:
- 5601:5601
expose:
- "5601"
environment:
OPENSEARCH_HOSTS: '["http://opensearch-node1:9200","http://opensearch-node2:9200"]'
DISABLE_SECURITY_DASHBOARDS_PLUGIN: "true"
networks:
- opensearch-net
networks:
opensearch-net:
簡單說明如下:
- 為了演示方便,關閉了安全特性
- 要在
OPENSEARCH_JAVA_OPTS
中添加-Dopensearch.experimental.feature.replication_type.enabled=true
才能開啟segment replication 功能
2.2 運行 OpenSearch 集群
執行如下命令運行 OpenSearch Cluster:
docker-compose -f docker-compose.yml up
運行成功后,可以訪問 http://127.0.0.1:5601 打開 Dashboards 界面,進入 Dev Tools 中執行后續的操作
2.3 測試 Segment Replication
測試思路如下:
- 創建兩個 index,一個默認配置,一個啟用 segment replication,主分片數為1,副本數為1
- 向兩個 index 中插入若干條數據
- 比較兩個 index 中 segment file 的數量和大小
相關命令如下:
PUT /test-rep-by-doc
{
"settings": {
"index": {
"number_of_shards": 1,
"number_of_replicas": 1
}
}
}
GET test-rep-by-doc/_settings
POST test-rep-by-doc/_doc
{
"name": "rep by doc"
}
GET _cat/shards/test-rep-by-doc?v
GET _cat/segments/test-rep-by-doc?v&h=index,shard,prirep,segment,generation,docs.count,docs.deleted,size&s=index,segment,prirep
PUT /test-rep-by-seg
{
"settings": {
"index": {
"replication.type": "SEGMENT",
"number_of_shards": 1,
"number_of_replicas": 1
}
}
}
GET test-rep-by-seg/_settings
POST test-rep-by-seg/_doc
{
"name": "rep by seg"
}
GET _cat/shards/test-rep-by-seg
GET _cat/segments/test-rep-by-seg?v&h=index,shard,prirep,segment,generation,docs.count,docs.deleted,size&s=index,segment,prirep
插入文檔后,通過 _cat/segments
可以得到 segment file 列表,然后通過 size 一列可以對比 segment 文件大小。
如下是默認基于文檔復制的結果:
index shard prirep segment generation docs.count docs.deleted size
test-rep-by-doc 0 p _0 0 2 0 3.7kb
test-rep-by-doc 0 r _0 0 1 0 3.6kb
test-rep-by-doc 0 p _1 1 2 0 3.7kb
test-rep-by-doc 0 r _1 1 3 0 3.8kb
test-rep-by-doc 0 p _2 2 1 0 3.6kb
test-rep-by-doc 0 r _2 2 3 0 3.8kb
test-rep-by-doc 0 p _3 3 6 0 3.9kb
test-rep-by-doc 0 r _3 3 6 0 3.9kb
test-rep-by-doc 0 p _4 4 5 0 3.9kb
test-rep-by-doc 0 r _4 4 6 0 3.9kb
test-rep-by-doc 0 p _5 5 6 0 3.9kb
test-rep-by-doc 0 r _5 5 6 0 3.9kb
test-rep-by-doc 0 p _6 6 4 0 3.8kb
test-rep-by-doc 0 r _6 6 1 0 3.6kb
從中可以看到,雖然 Primary Shard 和 Replica Shard 的 segment 數相同,但是 size 大小是不同的,這也說明其底層的 segment 文件是獨立管理的。
如下是基于 Segment 復制的結果:
index shard prirep segment generation docs.count docs.deleted size
test-rep-by-seg 0 p _0 0 2 0 3.7kb
test-rep-by-seg 0 r _0 0 2 0 3.7kb
test-rep-by-seg 0 p _1 1 7 0 4kb
test-rep-by-seg 0 r _1 1 7 0 4kb
test-rep-by-seg 0 p _2 2 5 0 3.9kb
test-rep-by-seg 0 r _2 2 5 0 3.9kb
從中可以看到 Primary Shard 和 Replica Shard 的 segment 完全一致。
除此之外也可以從磁盤文件中對比,同樣可以得出相同的結論:Segment Replication 是基于文件的數據復制方案,Primary 和 Replica 的 segment 文件列表完全相同。
3 總結
根據 OpenSearch 社區的初步測試,Segment Replication 相較于 Document Replication 的性能結果如下:
- 在 Replica Nodes 上,CPU 和 Memory 資源減少 40%~50%
- 寫入性能方面,整體吞吐量提升約 50%,P99 延遲下降了 20% 左右
這個測試結果還是很誘人的,但 Segment Replication 也有其自身的局限,下面簡單列幾點( 不一定準確 ):
- Segment Replication 對于網絡帶寬資源要求更高,目前測試中發現有近1倍的增長,需要更合理的分配 Primary Shard 到不同的 Node 上,以分散網絡帶寬壓力
- Segment Replication 可能會由于文件傳輸的延遲而導致 Replica Shard 上可搜索的文檔短時間內與 Primary Shard 不一致
- Replica Shard 升級為 Primary Shard 的時間可能會因為重放 translog 文件而變長,導致 Cluster 不穩定
友情提示下,由于該特性目前還是實驗階段,還不具備上生產環境的能力,大家可以持續關注~