分布式實(shí)時(shí)搜索和分析引擎——Elasticsearch

一、概述
Elasticsearch是一個(gè)基于Lucene的搜索引擎。它提供了具有HTTP Web界面和無架構(gòu)JSON文檔的分布式,多租戶能力的全文搜索引擎。Elasticsearch是用Java開發(fā)的,根據(jù)Apache許可條款作為開源發(fā)布。
二、節(jié)點(diǎn)類型&作用
1)master節(jié)點(diǎn)(主節(jié)點(diǎn))
配置
node.master:truenode.data:false(這里也可以配置成node.data:true)
【注意】node.master和node.data默認(rèn)都是true, 但還是建議顯式配置
作用
索引的創(chuàng)建或刪除
跟蹤哪些節(jié)點(diǎn)是集群的一部分
決定哪些分片分配給相關(guān)的節(jié)點(diǎn)
2)data節(jié)點(diǎn)(數(shù)據(jù)節(jié)點(diǎn))
配置
node.master:false(這里也可以配置成node.master:true)node.data:true
作用
存儲(chǔ)索引數(shù)據(jù)
對(duì)文檔進(jìn)行增刪改查,聚合操作
3)Coordinating節(jié)點(diǎn)(協(xié)調(diào)節(jié)點(diǎn)/Client節(jié)點(diǎn))
每一個(gè)節(jié)點(diǎn)都隱式的是一個(gè)協(xié)調(diào)節(jié)點(diǎn),Coordinating節(jié)點(diǎn)是負(fù)責(zé)接收任何 Client 的請(qǐng)求,包括 REST Client 等。該節(jié)點(diǎn)將請(qǐng)求分發(fā)到合適的節(jié)點(diǎn),最終把結(jié)果匯集到一起。一般來說,每個(gè)節(jié)點(diǎn)默認(rèn)起到了 Coordinating節(jié)點(diǎn) 的職責(zé)。
配置
node.master:falsenode.data:false
作用
處理路由請(qǐng)求
處理搜索
分發(fā)索引
4)Ingest節(jié)點(diǎn)(預(yù)處理節(jié)點(diǎn))
在索引數(shù)據(jù)之前可以先對(duì)數(shù)據(jù)做預(yù)處理操作,所有節(jié)點(diǎn)其實(shí)默認(rèn)都是支持 Ingest 操作的,也可以專門將某個(gè)節(jié)點(diǎn)配置為 Ingest 節(jié)點(diǎn)。
配置
node.ingest:true
作用
Ingest節(jié)點(diǎn)和集群中的其他節(jié)點(diǎn)一樣,但是它能夠創(chuàng)建多個(gè)處理器管道,用以修改傳入文檔。類似 最常用的Logstash過濾器已被實(shí)現(xiàn)為處理器。
Ingest節(jié)點(diǎn) 可用于執(zhí)行常見的數(shù)據(jù)轉(zhuǎn)換和豐富。 處理器配置為形成管道。 在寫入時(shí),Ingest Node有20個(gè)內(nèi)置處理器,例如grok,date,gsub,小寫/大寫,刪除和重命名
在批量請(qǐng)求或索引操作之前,Ingest節(jié)點(diǎn)攔截請(qǐng)求,并對(duì)文檔進(jìn)行處理。
三、Elasticsearch 是如何實(shí)現(xiàn) master 選舉的
前提條件
只有候選主節(jié)點(diǎn)(master:true)的節(jié)點(diǎn)才能成為主節(jié)點(diǎn)。
最小主節(jié)點(diǎn)數(shù)(min_master_nodes)的目的是防止腦裂。
實(shí)現(xiàn)步驟
第一步:確認(rèn)候選主節(jié)點(diǎn)數(shù)達(dá)標(biāo),elasticsearch.yml 設(shè)置的值discovery.zen.minimum_master_nodes;
第二步:對(duì)所有候選主節(jié)點(diǎn)根據(jù)nodeId字典排序,每次選舉每個(gè)節(jié)點(diǎn)都把自己所知道節(jié)點(diǎn)排一次序,然后選出第一個(gè)(第0位)節(jié)點(diǎn),暫且認(rèn)為它是master節(jié)點(diǎn)。
第三步:如果對(duì)某個(gè)節(jié)點(diǎn)的投票數(shù)達(dá)到一定的值(候選主節(jié)點(diǎn)數(shù)n/2+1)并且該節(jié)點(diǎn)自己也選舉自己,那這個(gè)節(jié)點(diǎn)就是master。否則重新選舉一直到滿足上述條件。
【補(bǔ)充】master 節(jié)點(diǎn)的職責(zé)主要包括集群、節(jié)點(diǎn)和索引的管理,不負(fù)責(zé)文檔級(jí)別的管理;data 節(jié)點(diǎn)可以關(guān)閉 http 功能。
四、如何解決ES集群的腦裂問題
【原因】
所謂集群腦裂,是指 Elasticsearch 集群中的節(jié)點(diǎn)(比如共 20 個(gè)),其中的 10 個(gè)選了一個(gè) master,另外 10 個(gè)選了另一個(gè) master 的情況。
【解決】
當(dāng)集群 master 候選數(shù)量不小于 3 個(gè)時(shí),可以通過設(shè)置最少投票通過數(shù)量(discovery.zen.minimum_master_nodes)超過所有候選節(jié)點(diǎn)一半以上來解決腦裂問題;
五、調(diào)優(yōu)
1)部署時(shí),對(duì) Linux 的設(shè)置優(yōu)化方法
關(guān)閉緩存 swap;
堆內(nèi)存設(shè)置為:Min(節(jié).點(diǎn)內(nèi)存/2,理想是 32GB);
設(shè)置最大文件句柄數(shù);
線程池+隊(duì)列大小根據(jù)業(yè)務(wù)需要做調(diào)整;
磁盤存儲(chǔ) raid 方式——存儲(chǔ)有條件使用 RAID10,增加單節(jié)點(diǎn)性能以及避免單節(jié)點(diǎn)存儲(chǔ)故障。
2)寫入調(diào)優(yōu)
寫入前副本數(shù)設(shè)置為 0;
寫入前關(guān)閉 refresh_interval 設(shè)置為-1,禁用刷新機(jī)制;
寫入過程中:采取 bulk 批量寫入;
寫入后恢復(fù)副本數(shù)和刷新間隔;
盡量使用自動(dòng)生成的 id。
3)查詢調(diào)優(yōu)
禁用 wildcard(wildcard 檢索可以定義為:支持通配符的模糊檢索。類似于mysql的like);
禁用批量 terms(成百上千的場景);
充分利用倒排索引機(jī)制,能 keyword 類型盡量 keyword;
據(jù)量大時(shí)候,可以先基于時(shí)間敲定索引再檢索;
設(shè)置合理的路由機(jī)制。
六、Elasticsearch 索引文檔的過程
這里的索引文檔應(yīng)該理解為文檔寫入 ES,創(chuàng)建索引的過程。
文檔寫入包含:單文檔寫入和批量 bulk 寫入,這里只解釋一下:單文檔寫入流程。

第一步:客戶寫集群某節(jié)點(diǎn)寫入數(shù)據(jù),發(fā)送請(qǐng)求。(如果沒有指定路由/協(xié)調(diào)節(jié)點(diǎn),請(qǐng)求的節(jié)點(diǎn)扮演路由節(jié)點(diǎn)的角色。)
第二步:節(jié)點(diǎn) 1 接受到請(qǐng)求后,使用文檔_id 來確定文檔屬于分片 0。請(qǐng)求會(huì)被轉(zhuǎn)到另外的節(jié)點(diǎn),假定節(jié)點(diǎn) 3。因此分片 0 的主分片分配到節(jié)點(diǎn) 3 上。
第三步:節(jié)點(diǎn) 3 在主分片上執(zhí)行寫操作,如果成功,則將請(qǐng)求并行轉(zhuǎn)發(fā)到節(jié)點(diǎn) 1和節(jié)點(diǎn) 2 的副本分片上,等待結(jié)果返回。所有的副本分片都報(bào)告成功,節(jié)點(diǎn) 3 將向協(xié)調(diào)節(jié)點(diǎn)(節(jié)點(diǎn) 1)報(bào)告成功,節(jié)點(diǎn) 1 向請(qǐng)求客戶端報(bào)告寫入成功。
七、索引模板
索引模板,簡而言之,是一種復(fù)用機(jī)制,就像一些項(xiàng)目的開發(fā)框架如 Laravel 一樣,省去了大量的重復(fù),體力勞動(dòng)。當(dāng)新建一個(gè) Elasticsearch 索引時(shí),自動(dòng)匹配模板,完成索引的基礎(chǔ)部分搭建。
典型的如下所示:
{ ?"order": 0, ?"template": "sample_info*", ?"settings": { ? ?"index": { ? ? ?"number_of_shards": "64", ? ? ?"number_of_replicas": "1"
? ?}
?}, ?"mappings": { ? ?"info": { ? ? ?"dynamic_templates": [
? ? ? ?{ ? ? ? ? ?"string_fields": { ? ? ? ? ? ?"mapping": { ? ? ? ? ? ? ?"analyzer": "only_words_analyzer", ? ? ? ? ? ? ?"index": "analyzed", ? ? ? ? ? ? ?"type": "string", ? ? ? ? ? ? ?"fields": { ? ? ? ? ? ? ? ?"raw": { ? ? ? ? ? ? ? ? ?"ignore_above": 512, ? ? ? ? ? ? ? ? ?"index": "not_analyzed", ? ? ? ? ? ? ? ? ?"type": "string"
? ? ? ? ? ? ? ?}
? ? ? ? ? ? ?}
? ? ? ? ? ?}, ? ? ? ? ? ?"match_mapping_type": "string", ? ? ? ? ? ?"match": "*"
? ? ? ? ?}
? ? ? ?}
? ? ?], ? ?"properties": { ? ? ? ?"user_province": { ? ? ? ? ?"analyzer": "lowercase_analyzer", ? ? ? ? ?"index": "analyzed", ? ? ? ? ?"type": "string", ? ? ? ? ?"fields": { ? ? ? ? ? ?"raw": { ? ? ? ? ? ? ?"ignore_above": 512, ? ? ? ? ? ? ?"index": "not_analyzed", ? ? ? ? ? ? ?"type": "string"
? ? ? ? ? ?}
? ? ? ? ?}
? ? ? ?}
? ? ?}
? ?}
?}, ?"aliases": {}
}
上述模板定義,看似復(fù)雜,拆分來看,主要為如下幾個(gè)部分:
{ ?"order": 0, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 模板優(yōu)先級(jí)
?"template": "sample_info*", ? ? ? ? ? ? ? // 模板匹配的名稱方式
?"settings": {...}, ? ? ? ? ? ? ? ? ? ? ? ?// 索引設(shè)置
?"mappings": {...}, ? ? ? ? ? ? ? ? ? ? ? ?// 索引中各字段的映射定義
?"aliases": {...} ? ? ? ? ? ? ? ? ? ? ? ? ?// 索引的別名}
1)模板優(yōu)先級(jí)
一個(gè)模板可能絕大部分符合新建索引的需求,但是局部需要微調(diào),此時(shí),如果復(fù)制舊的模板,修改該模板后,成為一個(gè)新的索引模板即可達(dá)到我們的需求,但是這操作略顯重復(fù)。此時(shí),可以采用模板疊加與覆蓋來操作。模板的優(yōu)先級(jí)是通過模板中的 order 字段定義的,數(shù)字越大,優(yōu)先級(jí)越高。
如下為定義所有以 te 開頭的索引的模板:
{ ? ?"order": 0
? ?"template" : "te*", ? ?"settings" : { ? ? ? ?"number_of_shards" : 1
? ?}, ? ?"mappings" : { ? ? ? ?"type1" : { ? ? ? ? ? ?"_source" : { "enabled" : false }
? ? ? ?}
? ?}
}
索引模板是有序合并的。如何想單獨(dú)修改某一小類索引的一兩處單獨(dú)設(shè)置,可以在累加一層模板:
{ ? ?"order" : 1, ? ?"template" : "tete*", ? ?"settings" : { ? ? ? ?"number_of_shards" : 2
? ?}, ? ?"mappings" : { ? ? ? ?"type1" : { ? ? ? ? ? ?"_all" : { "enabled" : false }
? ? ? ?}
? ?}
}
上述第一個(gè)模板的 order 為0,第二個(gè)模板的 order 為1,優(yōu)先級(jí)高于第一個(gè)模板,其會(huì)覆蓋第一個(gè)模板中的相同項(xiàng)。所以對(duì)于所有以 tete 開頭的索引模板效果如下:
{ ? ?"settings" : { ? ? ? ?"number_of_shards" : 2
? ?}, ? ?"mappings" : { ? ? ? ?"type1" : { ? ? ? ? ? ?"_source" : { "enabled" : false }, ? ? ? ? ? ?"_all" : { "enabled" : false }
? ? ? ?}
? ?}
}
兩個(gè)模板疊加了,項(xiàng)目的字段,優(yōu)先級(jí)高的覆蓋了優(yōu)先級(jí)低的,如分片數(shù)。
2)索引模板的匹配
索引模板中的 "template" 字段定義的是該索引模板所應(yīng)用的索引情況。如?"template": "tete*" 所表示的含義是,當(dāng)新建索引時(shí),所有以 tete 開頭的索引都會(huì)自動(dòng)匹配到該索引模板。利用該模板進(jìn)行相應(yīng)的設(shè)置和字段添加等。
3)setting 部分
索引模板中的 setting 部分一般定義的是索引的主分片、拷貝分片、刷新時(shí)間、自定義分析器等。常見的 setting 部分結(jié)構(gòu)如下:
"settings": { ? ?"index": { ? ? ?"analysis": {...}, ? ? ? ? ? ? ? ?// 自定義的分析器
? ? ?"number_of_shards": "32", ? ? ? ? // 主分片的個(gè)數(shù)
? ? ?"number_of_replicas": "1", ? ? ? ?// 主分片的拷貝分片個(gè)數(shù)
? ? ?"refresh_interval": "5s" ? ? ? ? ?// 刷新時(shí)間
? ?}
?}
八、冷熱數(shù)據(jù)分離
ES集群的索引寫入及查詢速度主要依賴于磁盤的IO速度,冷熱數(shù)據(jù)分離的關(guān)鍵為使用SSD磁盤存儲(chǔ)數(shù)據(jù)。若全部使用SSD,成本過高,且存放冷數(shù)據(jù)較為浪費(fèi),因而使用普通SATA磁盤與SSD磁盤混搭,可做到資源充分利用,性能大幅提升的目標(biāo)。為了解決控制成本的前提下讀寫性能問題,Elasticsearch冷熱分離架構(gòu)應(yīng)運(yùn)而生。
冷數(shù)據(jù)索引:查詢頻率低,基本無寫入,一般為當(dāng)天或最近2天以前的數(shù)據(jù)索引
熱數(shù)據(jù)索引:查詢頻率高,寫入壓力大,一般為當(dāng)天數(shù)據(jù)索引

1)實(shí)現(xiàn)原理
Hot節(jié)點(diǎn)設(shè)置:索引節(jié)點(diǎn)(寫節(jié)點(diǎn)),同時(shí)保持近期頻繁使用的索引。 屬于IO和CPU密集型操作,建議使用SSD的磁盤類型,保持良好的寫性能;節(jié)點(diǎn)的數(shù)量設(shè)置一般是大于等于3個(gè)。將節(jié)點(diǎn)設(shè)置為hot類型:
node.attr.box_type: hot
Warm節(jié)點(diǎn)設(shè)置: 用于不經(jīng)常訪問的read-only索引。由于不經(jīng)常訪問,一般使用普通的磁盤即可。內(nèi)存、CPU的配置跟Hot節(jié)點(diǎn)保持一致即可;節(jié)點(diǎn)數(shù)量一般也是大于等于3個(gè)。將節(jié)點(diǎn)設(shè)置為warm類型:
node.attr.box_type: warm
es1:master節(jié)點(diǎn)
# elasticsearch.ymlnode.name: "master"cluster.name: "test-cluster"network.host: 0.0.0.0node.master: truenode.data: false
es2、es3、es4 熱數(shù)據(jù)節(jié)點(diǎn)
# elasticsearch.ymlnode.name: "hot-datanode-00x" # 提示:自行修改其他節(jié)點(diǎn)的名稱cluster.name: "test-cluster"network.host: 0.0.0.0node.master: falsenode.data: truediscovery.zen.ping.unicast.hosts: ["master"]node.attr.box_type: "hot" ?# 標(biāo)識(shí)為熱數(shù)據(jù)節(jié)點(diǎn)
es5、es6 冷/溫?cái)?shù)據(jù)節(jié)點(diǎn)
# elasticsearch.ymlnode.name: "cold-datanode-00x" # 提示:自行修改其他節(jié)點(diǎn)的名稱cluster.name: "docker-cluster" network.host: 0.0.0.0 node.master: false node.data: true discovery.zen.ping.unicast.hosts: ["master"] node.attr.box_type: "warm" # 標(biāo)識(shí)為溫?cái)?shù)據(jù)節(jié)點(diǎn)
九、分片不均衡原因&解決方案
原因
可能存在的部分原因有以下幾種:
Shard設(shè)置不合理。
說明:大多數(shù)負(fù)載不均問題是由于shard設(shè)置不合理導(dǎo)致,建議優(yōu)先排查。
Segment大小不均。
存在典型的冷熱數(shù)據(jù)需求場景。
說明:例如查詢中添加了routing或查詢頻率較高的熱點(diǎn)數(shù)據(jù),則必然導(dǎo)致數(shù)據(jù)出現(xiàn)負(fù)載不均。
沒有釋放長連接,導(dǎo)致流量不均。
說明:該問題時(shí)常暴露于采用負(fù)載均衡及多可用區(qū)架構(gòu)部署時(shí)。
解決方案
1)方案一:手動(dòng)移動(dòng)分片
例如移動(dòng)node-1的分片0到node-4
curl -XPOST 'http://localhost:9200/_cluster/reroute' -d '{
?"commands":[{ ?"move":{ ? ?"index":"indexName", ? ?"shard":0, ? ?"from_node":"node-1", ? ?"to_node":"node-4"}}]}'
優(yōu)點(diǎn):操作簡單,恢復(fù)時(shí)間短;不必修改master node的配置,master node長期負(fù)載后高
缺點(diǎn):索引大,移動(dòng)時(shí)有很高的IO,索引容易損壞,需要做備份,不能解決master node既是數(shù)據(jù)節(jié)點(diǎn)又是負(fù)載均衡轉(zhuǎn)發(fā)器的問題
【注意】分片和副本無法移動(dòng)到同一個(gè)節(jié)點(diǎn)
2)方案二:重建索引,從另外一個(gè)集群導(dǎo)入
刪除原來的索引,重新建立索引,;利用elasticsearch dump等工具從另一個(gè)集群中把數(shù)據(jù)導(dǎo)入到新的索引中
優(yōu)點(diǎn):可以重新配置master node和data node,主從負(fù)載均勻
缺點(diǎn):費(fèi)時(shí)間,容易數(shù)據(jù)丟失,需要驗(yàn)證數(shù)據(jù)的一致性
3)方案三:配置平衡參數(shù)
使用下面的命令恢復(fù)平衡
PUT_cluster/settings
{ "persistent": { "cluster.routing.rebalance.enable": "all"
}
}
十、解決Elasticsearch分片未分配的問題
原因整體概述:
出現(xiàn)這個(gè)問題的原因是原有分片未正常關(guān)閉和清理,所以當(dāng)分片要重新分配回出問題節(jié)點(diǎn)的時(shí)候沒有辦法獲得分片鎖。
這不會(huì)造成分片數(shù)據(jù)丟失,只需要重新觸發(fā)一下分配。
unassigned 分片問題可能的原因如下:
INDEX_CREATED: 由于創(chuàng)建索引的API導(dǎo)致未分配。
CLUSTER_RECOVERED: 由于完全集群恢復(fù)導(dǎo)致未分配。
INDEX_REOPENED: 由于打開open或關(guān)閉close一個(gè)索引導(dǎo)致未分配。
DANGLING_INDEX_IMPORTED: 由于導(dǎo)入dangling索引的結(jié)果導(dǎo)致未分配。
NEW_INDEX_RESTORED: 由于恢復(fù)到新索引導(dǎo)致未分配。
EXISTING_INDEX_RESTORED: 由于恢復(fù)到已關(guān)閉的索引導(dǎo)致未分配。
REPLICA_ADDED: 由于顯式添加副本分片導(dǎo)致未分配。
ALLOCATION_FAILED: 由于分片分配失敗導(dǎo)致未分配。
NODE_LEFT: 由于承載該分片的節(jié)點(diǎn)離開集群導(dǎo)致未分配。
REINITIALIZED: 由于當(dāng)分片從開始移動(dòng)到初始化時(shí)導(dǎo)致未分配(例如,使用影子shadow副本分片)。
REROUTE_CANCELLED: 作為顯式取消重新路由命令的結(jié)果取消分配。
REALLOCATED_REPLICA: 確定更好的副本位置被標(biāo)定使用,導(dǎo)致現(xiàn)有的副本分配被取消,出現(xiàn)未分配。
解決方案如下:
執(zhí)行修復(fù)命令
POST /_cluster/reroute?retry_failed
十一、ES讀寫數(shù)據(jù)過程
1)ES寫入數(shù)據(jù)的過程

客戶端發(fā)送任何一個(gè)請(qǐng)求到任意一個(gè)node,這個(gè)節(jié)點(diǎn)就成為協(xié)調(diào)節(jié)點(diǎn)(coordinate node)
協(xié)調(diào)節(jié)點(diǎn)對(duì)document(可以手動(dòng)設(shè)置doc id,也可以由系統(tǒng)分配)進(jìn)行hash路由,將請(qǐng)求轉(zhuǎn)發(fā)給對(duì)應(yīng)的node
node上的primary shard處理請(qǐng)求,然后將數(shù)據(jù)同步到replica node
協(xié)調(diào)節(jié)點(diǎn)如果發(fā)現(xiàn)primary shard所在的node和所有的replica shard所對(duì)應(yīng)的node都搞定之后,就會(huì)將請(qǐng)求返回給客戶端
2)ES讀數(shù)據(jù)過程

可以通過doc id來查詢,根據(jù)doc id進(jìn)行hash,判斷當(dāng)時(shí)寫這個(gè)document時(shí)是分配到哪個(gè)shard上去了,然后就去那個(gè)shard上查詢。
客戶端發(fā)送任何一個(gè)請(qǐng)求到任意一個(gè)node,這個(gè)節(jié)點(diǎn)就成為協(xié)調(diào)節(jié)點(diǎn)(coordinate node)
協(xié)調(diào)節(jié)點(diǎn)對(duì)doc id進(jìn)行hash路由,將請(qǐng)求轉(zhuǎn)發(fā)到對(duì)應(yīng)的node,此時(shí)會(huì)使用round-robin隨機(jī)輪詢算法,在primary shard以及所有的replica shard中隨機(jī)選擇一個(gè),讓讀請(qǐng)求負(fù)載均衡
接受請(qǐng)求的node,返回document給協(xié)調(diào)節(jié)點(diǎn)
協(xié)調(diào)節(jié)點(diǎn)再將數(shù)據(jù)返回給客戶端
十二、在海量數(shù)據(jù)中提高效率的幾個(gè)手段
filesystem cache:ES的搜索引擎嚴(yán)重依賴底層的filesystem cache,如果給filesystem cache更多的內(nèi)存,盡量讓內(nèi)存可以容納所有的index segment file索引數(shù)據(jù)文件
數(shù)據(jù)預(yù)熱:對(duì)于那些你覺得比較熱的數(shù)據(jù),即經(jīng)常會(huì)有人訪問的數(shù)據(jù),最好做一個(gè)專門的緩存預(yù)熱子系統(tǒng),對(duì)于熱數(shù)據(jù),每隔一段時(shí)間,系統(tǒng)本身就提前訪問一下,讓數(shù)據(jù)進(jìn)入filesystem cache里面去,這樣下次訪問的時(shí)候,性能會(huì)更好一些。
冷熱分離:冷數(shù)據(jù)索引:查詢頻率低,基本無寫入,一般為當(dāng)天或最近2天以前的數(shù)據(jù)索引,這種數(shù)據(jù)可以存儲(chǔ)在機(jī)械硬盤HDD中熱數(shù)據(jù)索引:查詢頻率高,寫入壓力大,一般為當(dāng)天的數(shù)據(jù)索引,這種數(shù)據(jù)可以存儲(chǔ)在SSD中
document的模型設(shè)計(jì):不要在搜索的時(shí)候去執(zhí)行各種復(fù)雜的操作,盡量在document模型設(shè)計(jì)和數(shù)據(jù)寫入的時(shí)候就將復(fù)雜操作處理掉
分頁性能優(yōu)化:翻頁的時(shí)候,翻得越深,每個(gè)shard返回的數(shù)據(jù)越多,而且協(xié)調(diào)節(jié)點(diǎn)處理的時(shí)間越長,此時(shí),要用scroll,scroll會(huì)一次性的生成所有數(shù)據(jù)的快照,然后每次翻頁都是通過移動(dòng)游標(biāo)來完成