一、ES原理
1、索引結構ES是面向檔案的
各種文本內容以檔案的形式存盤到ES中,檔案可以是一封郵件、一條日志,或者一個網頁的內容,一般使用 JSON 作為檔案的序列化格式,檔案可以有很多欄位,在創建索引的時候,我們需要描述檔案中每個欄位的資料型別,并且可能需要指定不同的分析器,就像在關系型資料中“CREATE TABLE”一樣,在存盤結構上,由_index、_type和_id唯一標識一個檔案,
_index指向一個或多個物理分片的邏輯命名空間,_id檔案標記符由系統自動生成或使用者提供,洗掉過期老化的資料時,最好以索引為單位,而不是_id,由于_type在實際應用中容易引起概念混淆,在ES 6.x版本中,一個索引只允許存在一個_type,7.x版本將完全洗掉_type的概念,
2、分片(shard)
在分布式系統中,單機無法存盤規模巨大的資料,要依靠大規模集群處理和存盤這些資料,一般通過增加機器數量來提高系統水平擴展能力,因此,需要將資料分成若干小塊分配到各個機器上,然后通過某種路由策略找到某個資料塊所在的位置,除了將資料分片以提高水平擴展能力,分布式存盤中還會把資料復制成多個副本,放置到不同的機器中,這樣一來可以增加系統可用性,同時資料副本還可以使讀操作并發執行,分擔集群壓力,但是多資料副本也帶來了一致性的問題:部分副本寫成功,部分副本寫失敗,為了應對并發更新問題,ES將資料副本分為主從兩部分,即主分片(primary shard)和副分片(replica shard),主資料作為權威資料,寫程序中先寫主分片,成功后再寫副分片,恢復階段以主分片為準,資料分片和資料副本的關系如下圖所示,
分片(shard)是底層的基本讀寫單元,分片的目的是分割巨大索引,讓讀寫可以并行操作,由多臺機器共同完成,讀寫請求最終落到某個分片上,分片可以獨立執行讀寫作業,ES利用分片將資料分發到集群內各處,分片是資料的容器,檔案保存在分片內,不會跨分片存盤,分片又被分配到集群內的各個節點里,當集群規模擴大或縮小時,ES 會自動在各節點中遷移分片,使資料仍然均勻分布在集群里,
一個ES索引包含很多分片,一個分片是一個Lucene的索引,它本身就是一個完整的搜索引擎,可以獨立執行建立索引和搜索任務,Lucene索引又由很多分段組成,每個分段都是一個倒排索引,ES每次“refresh”都會生成一個新的分段,其中包含若干檔案的資料,在每個分段內部,檔案的不同欄位被單獨建立索引,每個欄位的值由若干詞(Term)組成,Term是原文本內容經過分詞器處理和語言處理后的最終結果(例如,去除標點符號和轉換為詞根),
索引建立的時候就需要確定好主分片數,在較老的版本中(5.x 版本之前),主分片數量不可以修改,副分片數可以隨時修改,現在(5.x~6.x 版本之后),ES 已經支持在一定條件的限制下,對某個索引的主分片進行拆分(Split)或縮小(Shrink),但是,我們仍然需要在一開始就盡量規劃好主分片數量:先依據硬體情況定好單個分片容量,然后依據業務場景預估資料量和增長量,再除以單個分片容量,分片數不夠時,可以考慮新建索引,搜索1個有著50個分片的索引與搜索50個每個都有1個分片的索引完全等價,或者使用_split API來拆分索引(6.1版本開始支持),
在實際應用中,我們不應該向單個索引持續寫資料,直到它的分片巨大無比,巨大的索引會在資料老化后難以洗掉,以_id 為單位洗掉檔案不會立刻釋放空間,洗掉的 doc 只在 Lucene分段合并時才會真正從磁盤中洗掉,即使手工觸發分段合并,仍然會引起較高的 I/O 壓力,并且可能因為分段巨大導致在合并程序中磁盤空間不足(分段大小大于磁盤可用空間的一半),因此,我們建議周期性地創建新索引,例如,每天創建一個,假如有一個索引website,可以將它命名為website_20180319,然后創建一個名為website的索引別名來關聯這些索引,這樣,對于業務方來說,讀取時使用的名稱不變,當需要洗掉資料的時候,可以直接洗掉整個索引,索引別名就像一個快捷方式或軟鏈接,不同的是它可以指向一個或多個索引,可以用于實作索引分組,或者索引間的無縫切換,現在我們已經確定好了主分片數量,并且保證單個索引的資料量不會太大,周期性創建新索引帶來的一個新問題是集群整體分片數量較多,集群管理的總分片數越多壓力就越大,在每天生成一個新索引的場景中,可能某天產生的資料量很小,實際上不需要這么多分片,甚至一個就夠,這時,可以使用_shrink API來縮減主分片數量,降低集群負載,
3、動態更新索引
為檔案建立索引,使其每個欄位都可以被搜索,通過關鍵詞檢索檔案內容,會使用倒排索引的資料結構,倒排索引一旦被寫入檔案后就具有不變性,不變性具有許多好處:對檔案的訪問不需要加鎖,讀取索引時可以被檔案系統快取等,那么索引如何更新,讓新添加的檔案可以被搜索到?答案是使用更多的索引,新增內容并寫到一個新的倒排索引中,查詢時,每個倒排索引都被輪流查詢,查詢完再對結果進行合并,每次記憶體緩沖的資料被寫入檔案時,會產生一個新的Lucene段,每個段都是一個倒排索引,在一個記錄元資訊的檔案中描述了當前Lucene索引都含有哪些分段,由于分段的不變性,更新、洗掉等操作實際上是將資料標記為洗掉,記錄到單獨的位置,這種方式稱為標記洗掉,因此洗掉部分資料不會釋放磁盤空間,
4、 近實時搜索
在寫操作中,一般會先在記憶體中緩沖一段資料,再將這些資料寫入硬碟,每次寫入硬碟的這批資料稱為一個分段,如同任何寫操作一樣,一般情況下(direct方式除外),通過作業系統write介面寫到磁盤的資料先到達系統快取(記憶體),write函式回傳成功時,資料未必被刷到磁盤,通過手工呼叫flush,或者作業系統通過一定策略將系統快取刷到磁盤,這種策略大幅提升了寫入效率,從write函式回傳成功開始,無論資料有沒有被刷到磁盤,該資料已經對讀取可見,ES正是利用這種特性實作了近實時搜索,每秒產生一個新分段,新段先寫入檔案系統快取,但稍后再執行flush刷盤操作,寫操作很快會執行完,一旦寫成功,就可以像其他檔案一樣被打開和讀取了,由于系統先緩沖一段資料才寫,且新段不會立即刷入磁盤,這兩個程序中如果出現某些意外情況(如主機斷電),則會存在丟失資料的風險,通用的做法是記錄事務日志,每次對ES進行操作時均記錄事務日志,當ES啟動的時候,重放translog中所有在最后一次提交后發生的變更操作,比如HBase等都有自己的事務日志,
5、段合并
在ES中,每秒清空一次寫緩沖,將這些資料寫入檔案,這個程序稱為refresh,每次refresh會創建一個新的Lucene 段,但是分段數量太多會帶來較大的麻煩,每個段都會消耗檔案句柄、記憶體,每個搜索請求都需要輪流檢查每個段,查詢完再對結果進行合并;所以段越多,搜索也就越慢,因此需要通過一定的策略將這些較小的段合并為大的段,常用的方案是選擇大小相似的分段進行合并,在合并程序中,標記為洗掉的資料不會寫入新分段,當合并程序結束,舊的分段資料被洗掉,標記洗掉的資料才從磁盤洗掉,HBase、Cassandra等系統都有類似的分段機制,寫程序中先在記憶體緩沖一批資料,不時地將這些資料寫入檔案作為一個分段,分段具有不變性,再通過一些策略合并分段,分段合并程序中,新段的產生需要一定的磁盤空間,我們要保證系統有足夠的剩余可用空間,Cassandra系統在段合并程序中的一個問題就是,當持續地向一個表中寫入資料,如果段檔案大小沒有上限,當巨大的段達到磁盤空間的一半時,剩余空間不足以進行新的段合并程序,如果段檔案設定一定上限不再合并,則對表中部分資料無法實作真正的物理洗掉,ES存在同樣的問題,
6、節點
在Elasticsearch中,每個節點可以有多個角色,節點既可以是候選主節點,也可以是資料節點,其中,資料節點負責資料的存盤相關的操作,如對資料進行增、刪、改、查和聚合等,正因為如此,資料節點往往對服務器的配置要求比較高,特別是對CPU、記憶體和I/O的需求很大,此外,資料節點梳理通常隨著集群的擴大而彈性增加,以便保持Elasticsearch服務的高性能和高可用,候選主節點是被選舉為主節點的節點,在集群中,只有候選主節點才有選舉權和被選舉權,其他節點不參與選舉作業,一旦候選主節點被選舉為主節點,則主節點就要負責創建索引、洗掉索引、追蹤集群中節點的狀態,以及跟蹤哪些節點是群集的一部分,并決定將哪些分片分配給相關的節點等,
二、集群
2.4)集群(cluster)
(1)集群由一個或多個節點組成,對外提供服務,對外提供索引和搜索功能,在所有節點,一個集群有一個唯一的名稱默認為“Elasticsearch”,此名稱是很重要的,因為每個節點只能是集群的一部分,當該節點被設定為相同的集群名稱時,就會自動加入集群(開啟廣播模式),當需要有多個集群的時候,要確保每個集群的名稱不能重復,否則,節點可能會加入錯誤的集群,
注意:
- 一個節點只能加入一個集群,此外,還可以擁有多個獨立的集群,每個集群都有其不同的集群名稱,例如,在開發程序中,可以建立開發集群庫和測驗集群庫,
- 當擴容集群、添加節點時,分片會均衡地分配到集群的各個節點,從而對索引和搜索程序進行負載均衡,這些都是系統自動完成的,
- 分布式系統中難免出現故障,當節點例外時,ES會自動處理節點例外,當主節點例外時,集群會重新選舉主節點,當某個主分片例外時,會將副分片提升為主分片,
eg: 擁有三個節點的集群——為了分散負載而對分片進行重新分配

Node 1 和 Node 2 上各有一個分片被遷移到了新的 Node 3 節點,現在每個節點上都擁有2個分片,而不是之前的3個, 這表示每個節點的硬體資源(CPU, RAM, I/O)將被更少的分片所共享,每個分片的性能將會得到提升,
分片是一個功能完整的搜索引擎,它擁有使用一個節點上的所有資源的能力, 以上這個擁有6個分片(3個主分片和3個副本分片)的索引可以最大擴容到6個節點,每個節點上存在一個分片,并且每個分片擁有所在節點的全部資源,
(2)分片策略
分片分配程序是分片到節點的一個處理程序,它可能發生在初始恢復程序中,副本分配中,再平衡程序中,或當節點被添加或洗掉時,
(2.1)分片分配設定
下面的動態設定可以用來控制分片的分配和回收,
□cluster.routing.allocation.enable:禁用或啟用哪種型別的分片,可選的引數有:
·all——允許所有的分片被重新分配,
·primaries——只允許主結點分片被重新分配,
·new_primaries——只允許新的主結點索引的分片被重新分配,
·none——不對任何分片進行重新分配,
□cluster.routing.allocation.node_concurrent_recoveries:允許在一個節點上同時并發多少個分片分配,默認為2,
□cluster.routing.allocation.node_initial_primaries_recoveries:當副本分片加入集群的時候,在一個節點上并行發生分片分配的數量,默認是4個, □cluster.routing.allocation.same_shard.host:在一個主機上的當有多個相同的集群名稱的分片分配時,是否進行檢查,檢查主機名和主機ip地址,默認為false,此設定僅適用于在同一臺機器上啟動多個節點時配置,
- indices.recovery.concurrent_streams:從一個節點恢復的時候,同時打開的網路流量的數量,默認為3,
- indices.recovery.concurrent_small_file_streams:從同伴的分片恢復時打開每個節點的小檔案(小于5M)流的數目,默認為2,
(2.2)分片平衡設定
下面的動態設定可以用來控制整個集群的碎片再平衡,配置有:
□cluster.routing.rebalance.enable:啟用或禁用特定種類的分片重新平衡,可選的引數有:
·all——允許所有的分片進行分片平衡,默認配置,
·primaries——只允許主分片進行平衡,
·replicas——只允許從分片進行平衡,
·none——不允許任何分片進行平衡,
□cluster.routing.allocation.allow_rebalance:當分片再平衡時允許的操作,可選的引數有:
·always——總是允許再平衡,
·indices_primaries_active——只有主節點索引允許再平衡,
·indices_all_active——所有的分片允許再平衡,默認引數,
·cluster.routing.allocation.cluster_concurrent_rebalance:重新平衡時允許多少個并發的分片同時操作,默認為2,
(2.3)啟發式分片平衡
以下設定用于確定在何處放置每個碎片的資料:
□cluster.routing.allocation.balance.shard:在節點上分配每個分片的權重,默認是0.45,
□cluster.routing.allocation.balance.index:在特定節點上,每個索引分配的分片的數量,默認0.55,
□cluster.routing.allocation.balance.threshold:操作的最小最優化的值,默認為1,
2.5).節點(node)
(1)一個節點是一個邏輯上獨立的服務,它是集群的一部分,可以存盤資料,并參與集群的索引和搜索功能,節點也有唯一的名字,在啟動的時候分配,該名稱是在啟動時分配給節點的隨機通用唯一識別符號(UUID),支持自定義名稱,這個名字在管理中很重要,在網路中Elasticsearch集群通過節點名稱進行管理和通信,一個節點可以被配置加入一個特定的集群,默認情況下,每個節點會加入名為Elasticsearch的集群中,這意味著如果你在網路上啟動多個節點,如果網路暢通,他們能彼此發現并自動加入一個名為Elasticsearch的集群中,當網路沒有集群運行的時候,只要啟動任何一個節點,這個節點會默認生成一個新的集群,這個集群會有一個節點,
(2)節點型別
主(master)節點:在一個節點上當node.master設定為true(默認)的時候,它有資格被選作為主節點,控制整個集群,它將負責管理:集群范圍內的所有變更,例如增加、洗掉索引,或者增加、洗掉節點等,
資料(data)節點:在一個節點上node.data設定為true(默認)的時候,該節點保存資料和執行資料相關的操作,如增刪改查、搜索和聚合,默認情況下,節點同時是主節點和資料節點,這是非常方便的小集群,但隨著集群的發展,分離主節點和資料節點將變得非常重要,
客戶端節點:當一個節點的node.master和node.data都設定為false的時候,它既不能保持資料也不能成為主節點,該節點可以作為客戶端節點,可以回應用戶的請求,并把相關操作發送到其他節點,
部落節點:當一個節點配置tribe.*的時候,它是一個特殊的客戶端,它可以連接多個集群,在所有連接的集群上執行搜索和其他操作,
客戶端節點在搜索請求或批量增加索引請求等可能涉及在不同資料節點上的操作,這些請求會分成兩個階段,一是接收客戶端的請求,二是協調節點執行相關操作,當資料分散在不同的節點上時,協調節點將請求轉發到資料節點,每個資料節點在本地執行請求并把結果傳輸給協調節點,然后協調節點收集各個資料節點的結果轉換成單個請求結果回傳,所以需要客戶端有足夠的記憶體和CPU來處理各個節點的回傳結果,
2.6)路由(routing)
當存盤一個檔案的時候,它會存盤在唯一的主分片中,具體哪個分片是通過散列值進行選擇,默認情況下,這個值是由檔案的ID生成,如果檔案有一個指定的父檔案,則從父檔案ID中生成,該值可以在存盤檔案的時候進行修改,
2.7)分片(shard)
分片是單個Lucene實體,這是Elasticsearch管理的比較底層的功能,索引是指向主分片和副本分片的邏輯空間,對于使用,只需要指定分片的數量,其他不需要做過多的事情,在開發使用的程序中,我們對應的物件都是索引,Elasticsearch會自動管理集群中所有的分片,當發生故障的時候,Elasticsearch會把分片移動到不同的節點或者添加新的節點,一個索引可以存盤很大的資料,這些空間可以超過一個節點的物理存盤的限制,例如,十億個檔案占用磁盤空間為1TB,僅從單個節點搜索可能會很慢,還有一臺物理機器也不一定能存盤這么多的資料,為了解決這一問題,Elasticsearch將索引分解成多個分片,當你創建一個索引,你可以簡單地定義你想要的分片數量,每個分片本身是一個全功能的、獨立的單元,可以托管在集群中的任何節點,
2.8)主分片(primary shard)
每個檔案都存盤在一個分片中,當你存盤一個檔案的時候,系統會首先存盤在主分片中,然后會復制到不同的副本中,默認情況下,一個索引有5個主分片,你可以事先制定分片的數量,當分片一旦建立,則分片的數量不能修改,
2.9)副本分片(replica shard)
每一個分片有零個或多個副本,副本主要是主分片的復制,其中有兩個目的:
增加高可用性:當主分片失敗的時候,可以從副本分片中選擇一個作為主分片,
提高性能:當查詢的時候可以到主分片或者副本分片中進行查詢,
默認情況下,一個主分片配有一個副本,但副本的數量可以在后面動態地配置增加,副本分片必須部署在不同的節點上,不能部署在和主分片相同的節點上,分片主要有兩個很重要的原因是:
允許水平分割擴展資料,
允許分配和并行操作(可能在多個節點上)從而提高性能和吞吐量,
這些很強大的功能對用戶來說是透明的,你不需要做什么操作,系統會自動處理,
2.10)復制(replica)
復制是一個非常有用的功能,不然會有單點問題,當網路中的某個節點出現問題的時候,復制可以對故障進行轉移,保證系統的高可用,因此,Elasticsearch允許你創建一個或多個拷貝,你的索引分片就形成了所謂的副本或副本分片,復制是重要的,主要的原因有:□它提供了高可用性,當節點失敗的時候不受影響,需要注意的是,一個復制的分片不會存盤在同一個節點中,□它允許你擴展搜索量,提高并發量,因為搜索可以在所有副本上并行執行,每個索引可以拆分成多個分片,索引可以復制零個或者多個分片,一旦復制,每個索引就有了主分片和副本分片,分片的數量和副本的數量可以在創建索引時定義,當創建索引后,你可以隨時改變副本的數量,但你不能改變分片的數量,默認情況下,每個索引分配5個分片和一個副本,這意味著你的集群節點至少要有兩個節點,你將擁有5個主要的分片和5個副本分片共計10個分片,[插圖]注意 每個Elasticsearch分片是一個Lucene的索引,有檔案存盤數量限制,你可以在一個單一的Lucene索引中存盤的最大值為lucene-5843,極限是2147483519(=integer.max_value-128)個檔案,你可以使用_cat/shards API監控分片的大小,
三、es結構
1、es模塊結構圖

Gateway: 代表ES的持久化存盤方式,包含索引資訊,ClusterState(集群資訊),mapping,索引碎片資訊,以及transaction log等;
- 對于分布式集群來說,當一個或多個節點down掉了,能夠保證我們的資料不能丟,最通用的解決方案就是對失敗節點的資料進行復制,通過控制復制的份數可以保證集群有很高的可用性,復制這個方案的精髓主要是保證操作的時候沒有單點,對一個節點的操作會同步到其他的復制節點上去,
- ES一個索引會拆分成多個碎片,每個碎片可以擁有一個或多個副本(創建索引的時候可以配置),如下:每個索引分成3個碎片,每個碎片有2個副本
$ curl -XPUT http://localhost:9200/twitter/ -d ' index : number_of_shards : 3 number_of_replicas : 2 -
每個操作會自動路由主碎片所在的節點,在上面執行操作,并且同步到其他復制節點,通過使用“non blocking IO”模式所有復制的操作都是并行執行的,也就是說如果你的節點的副本越多,你網路上的流量消耗也會越大,復制節點同樣接受來自外面的讀操作,意義就是你的復制節點越多,你的索引的可用性就越強,對搜索的可伸縮行就更好,能夠承載更多的操作,
- 第一次啟動的時候,它會去持久化設備讀取集群的狀態資訊(創建的索引,配置等)然后執行應用它們(創建索引,創建mapping映射等),每一次shard節點第一次實體化加入復制組,它都會從長持久化存盤里面恢復它的狀態資訊,
Discovery
Discovery模塊負責發現集群中的節點,以及選擇主節點,ES支持多種不同Discovery型別選擇,內置的實作稱為Zen Discovery,其他的包括公有云平臺亞馬遜的EC2、谷歌的GCE等,
-
節點啟動后先ping(這里的ping是 Elasticsearch 的一個RPC命令,如果 discovery.zen.ping.unicast.hosts 有設定,則ping設定中的host,否則嘗試ping localhost 的幾個埠, Elasticsearch 支持同一個主機啟動多個節點);
-
Ping的response會包含該節點的基本資訊以及該節點認為的master節點;
-
選舉開始,先從各節點認為的master中選,規則很簡單,按照id的字典序排序,取第一個;
-
如果各節點都沒有認為的master,則從所有節點中選擇,規則同上,這里有個限制條件就是 discovery.zen.minimum_master_nodes,如果節點數達不到最小值的限制,則回圈上述程序,直到節點數足夠可以開始選舉;
-
最后選舉結果是肯定能選舉出一個master,如果只有一個local節點那就選出的是自己;
-
如果當前節點是master,則開始等待節點數達到 minimum_master_nodes,然后提供服務, 如果當前節點不是master,則嘗試加入master;
-
ES支持任意數目的集群(1-N),通過一個規則,只要所有的節點都遵循同樣的規則,得到的資訊都是對等的,選出來的主節點肯定是一致的,但分布式系統的問題就出在資訊不對等的情況,這時候很容易出現腦裂(Split-Brain)的問題,大多數解決方案就是設定一個quorum值,要求可用節點必須大于quorum(一般是超過半數節點,(master_eligible_nodes / 2) + 1,例如,如果有3個具備Master資格的節點,則這個值至少應該設定為(3/2)+ 1 = 2),才能對外提供服務,而 Elasticsearch 中,這個quorum的配置就是 discovery.zen.minimum_master_nodes ;
memcached
-
通過memecached協議來訪問ES的介面,支持二進制和文本兩種協議.通過一個名為transport-memcached插件提供
-
Memcached命令會被映射到REST介面,并且會被同樣的REST層處理,memcached命令串列包括:get/set/delete/quit
2、es存盤結構

四、es優化方式
1、寫入速度優化
在 ES 的默認設定下,是綜合考慮資料可靠性、搜索實時性、寫入速度等因素的,有時候,業務上對資料可靠性和搜索實時性要求并不高,反而對寫入速度要求很高,此時可以調整一些策略,可以犧牲可靠性和搜索實時性為代價,最大化寫入速度,
從以下幾方面入手:
- 加大translog flush間隔,目的是降低iops、writeblock,
- 加大index refresh間隔,除了降低I/O,更重要的是降低了segment merge頻率,
- 調整bulk請求,
- 優化磁盤間的任務均勻情況,將shard盡量均勻分布到物理主機的各個磁盤,
- 優化節點間的任務分布,將任務盡量均勻地發到各節點,
- 優化Lucene層建立索引的程序,目的是降低CPU占用率及I/O,例如,禁用_all欄位,
2、搜索速度的優化
1)有些欄位的內容是數值,但并不意味著其總是應該被映射為數值型別,例如,一些識別符號,將它們映射為keyword可能會比integer或long更好,
2)預熱檔案系統cache
如果ES主機重啟,則檔案系統快取將為空,此時搜索會比較慢,可以使用index.store.preload設定,通過指定檔案擴展名,顯式地告訴作業系統應該將哪些檔案加載到記憶體中,在索引創建時設定:
PUT /my_index{"settings": {"index.store.preload": ["nvd", "dvd"]}}
注意:如果檔案系統快取不夠大,則無法保存所有資料,那么為太多檔案預加載資料到檔案系統快取中會使搜索速度變慢,應謹慎使用,
3)一個搜索請求涉及的分片數量越多,協調節點的CPU和記憶體壓力就越大,默認情況下,ES會拒絕超過1000個分片的搜索請求,因此應該更好地組織資料,讓搜索請求的分片數更少,如果想調節這個值,則可以通過action.search.shard_count配置項進行修改,雖然限制搜索的分片數并不能直接提升單個搜索請求的速度,但協調節點的壓力會間接影響搜索速度,例如,占用更多記憶體會產生更多的GC壓力,可能導致更多的stop-the-world時間等,因此間接影響了協調節點的性能,
官放檔案地址:https://www.elastic.co/guide/en/elasticsearch/reference/7.4/elasticsearch-intro.html
感謝閱讀,借鑒了不少大佬資料,整合了一個相對系統、簡潔、針對專案研發較為實用的版本,如需轉載,請注明出處,謝謝!https://www.cnblogs.com/huyangshu-fs/p/12181057.html
-i
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/399536.html
標籤:大數據
