
Elasticsearch(看這一篇就夠了)
目錄:
-
• Elasticsearch -
• -
• 介紹 -
• 正排索引和倒排索引 -
• Elasticsearch安裝 -
• -
• 安裝ES服務 -
• 安裝服務 -
• 安裝kibana -
• 索引操作 -
• -
• 建立索引 -
• 查詢索引庫 -
• 修改索引庫 -
• 刪除索引庫 -
• Elasticsearch常用操作 -
• -
• 文件操作 -
• -
• 新增文件 -
• 查詢文件 -
• 刪除文件 -
• 根據id批次查詢文件 -
• 查詢所有文件 -
• 修改文件部分欄位 -
• 域的屬性 -
• 分詞器 -
• -
• 預設分詞器 -
• IK分詞器 -
• 拼音分詞器 -
• 自定義分詞器 -
• Elasticsearch搜尋文件 -
• -
• 搜尋方式 -
• SpringDaraES案例 -
• -
• 使用Repository繼承的方法查詢文件 -
• 使用DSL語句查詢文件 -
• 按照規則命名方法查詢文件 -
• 分頁查詢 -
• 結果排序 -
• template工具類 -
• -
• 操作索引 -
• 操作文件 -
• 查詢文件 -
• 複雜條件查詢 -
• 分頁查詢 -
• 結果排序 -
• Elasticsearch叢集 -
• -
• 搭建叢集 -
• 測試叢集狀態 -
• 故障應對和水平擴容 -
• 記憶體設定 -
• 磁碟選擇 -
• 分片策略 -
• Elasticsearch案例 -
• -
• 需求說明 -
• ES自動補全 -
• 建立索引 -
• mysql資料匯入es -
• 專案搭建 -
• 建立實體類 -
• 建立Repository介面 -
• 自動補全功能 -
• 搜尋關鍵字功能 -
• 建立Controller類 -
• 前端頁面

Elasticsearch
介紹

Elasticsearch是一個全文檢索伺服器
全文檢索是一種非結構化資料的搜尋方式
-
• 結構化資料:指具有固定格式固定長度的資料,如資料庫中的欄位。 -
• 非結構化資料:指格式和長度不固定的資料,如電商網站的商品詳情。
結構化資料一般存入資料庫,使用sql語句即可快速查詢。但由於非結構化資料的資料量大且格式不固定,我們需要採用全文檢索的方式進行搜尋。全文檢索透過建立倒排索引加快搜索效率。
正排索引和倒排索引
索引
將資料中的一部分資訊提取出來,重新組織成一定的資料結構,我們可以根據該結構進行快速搜尋,這樣的結構稱之為索引。
索引即目錄,例如字典會將字的拼音提取出來做成目錄,透過目錄即可快速找到字的位置。
索引分為正排索引和倒排索引。
正排索引(正向索引)將文件id建立為索引,透過id快速可以快速查詢資料。如資料庫中的主鍵就會建立正排索引。

倒排索引(反向索引)非結構化資料中我們往往會根據關鍵詞查詢資料。此時我們將資料中的關鍵詞建立為索引,指向文件資料,這樣的索引稱為倒排索引。

Elasticsearch安裝
安裝ES服務
準備工作1.準備一臺搭載有CentOS7系統的虛擬機器,使用XShell連線虛擬機器
2.關閉防火牆,方便訪問ES
#關閉防火牆:systemctl stop firewalld.service#禁止防火牆自啟動:systemctl disable firewalld.service
3.配置最大可建立檔案數大小
#開啟系統檔案:vim /etc/sysctl.conf#新增以下配置:vm.max_map_count=655360#配置生效:sysctl -p
-
4. 由於ES不能以root使用者執行,我們需要建立一個非root使用者,此處建立一個名為es的使用者:
#建立使用者:useradd es
安裝服務
-
1. 使用rz命令將linux版的ES上傳至虛擬機器 -
2. 解壓ES
#解壓:tar -zxvf elasticsearch-8.10.4-linux-x86_64.tar.gz#重新命名:mv elasticsearch-8.10.4 elasticsearch1#移動資料夾:mv elasticsearch1 /usr/local/#es使用者取得該資料夾許可權:chown -R es:es /usr/local/elasticsearch1
-
3. 啟動ES服務:
#切換為es使用者:su es#進入ES安裝資料夾:cd /usr/local/elasticsearch1/bin/#啟動ES服務:./elasticsearch
-
4. 當啟動成功,可以看到類似以下的日誌輸出。首次啟動Elasticsearch,預設會啟用安全配置功能,啟用身份認證和授權,內建超級使用者elastic,並生成預設密碼,此時要注意儲存,否則之後啟動不會再顯示。 在這裡插入圖片描述
# 重置預設密碼:cd /usr/local/elasticsearch1/bin/./elasticsearch-reset-password -u elastic# 自定義密碼:cd /usr/local/elasticsearch1/bin/./elasticsearch-reset-password --username elastic -i
-
5. 連線ES,查詢ES服務是否啟動成功
# 引數 --cacert指定了證書curl --cacert /usr/local/elasticsearch1/config/certs/http_ca.crt -u elastic https://localhost:9200
安裝kibana
Kibana是一款開源的資料分析和視覺化平臺,設計用於和Elasticsearch協作。我們可以使用Kibana對Elasticsearch索引中的資料進行搜尋、檢視、互動操作。
-
1. 使用rz工具將Kibana壓縮檔案上傳到Linux虛擬機器 -
2. 解壓
tar -zxvf kibana-8.10.4-linux-x86_64.tar.gz -C /usr/local/
-
3. 修改配置
# 進入Kibana解壓路徑cd /usr/local/kibana-8.10.4/config# 修改配置檔案vim kibana.yml# 加入以下內容# 主機IP,服務名server.host: "虛擬機器IP"server.name: "kibana"
-
4. 啟動:
kibana不能以root使用者執行,我們給es使用者設定kibana目錄的許可權,並使用es使用者執行kibana
# 給es使用者設定kibana目錄許可權chown -R es:es /usr/local/kibana-8.10.4/# 切換為es使用者su es# 啟動kibanacd /usr/local/kibana-8.10.4/bin/./kibana
-
5. 訪問kibana:http://虛擬機器IP:5601首次訪問Kibana管理臺會提示輸入ES生成的token秘鑰,可以在ES首次啟動日誌中找。

如果token已失效或不正確,你也可以重新生成token。
# 進入es安裝目錄cd /usr/local/elasticsearch1/bin# 重新生成kibana的token.elasticsearch-create-enrollment-token --scope kibana
緊接著輸入登入賬號 elastic,密碼也同樣是從ES首次啟動日誌中找。
-
6. 點選 Management
=>Index Management
可以檢視es索引資訊。
索引操作
建立索引
Elasticsearch是使用RESTful風格的http請求訪問操作的,請求引數和返回值都是Json格式的,我們可以使用kibana傳送http請求操作ES。
建立沒有結構的索引路徑:ip地址:埠號/索引名
注:在kibana中所有的請求都會省略ip地址:埠號,之後的路徑我們省略寫ip地址:埠號
格式:
PUT /索引庫名稱{"mappings": {"properties": {"欄位名":{"type": "text","analyzer": "ik_smart" },"欄位名2":{"type": "keyword","index": "false" },"欄位名3":{"properties": {"子欄位": {"type": "keyword" } } }, // ...略 } }}
基本語法:
-
• 請求方式:PUT -
• 請求路徑:/索引庫名,可以自定義 -
• 請求引數:mapping對映
格式:
為索引新增結構
POST /索引名/_mapping{ "properties":{ "域名1":{ "type":域的型別, "store":是否儲存, "index":是否建立索引, "analyzer":分詞器 }, "域名2":{ ... } }}
PUT /索引庫名稱{"mappings": {"properties": {"欄位名":{"type": "text","analyzer": "ik_smart" },"欄位名2":{"type": "keyword","index": "false" },"欄位名3":{"properties": {"子欄位": {"type": "keyword" } } }, // ...略 } }}
查詢索引庫
基本語法:
-
• 請求方式:GET -
• 請求路徑:/索引庫名 -
• 請求引數:無
格式:
GET /索引庫名
修改索引庫
倒排索引結構雖然不復雜,但是一旦資料結構改變(比如改變了分詞器),就需要重新建立倒排索引,這簡直是災難。因此索引庫一旦建立,無法修改
mapping
。雖然無法修改mapping中已有的欄位,但是卻允許新增新的欄位到mapping中,因為不會對倒排索引產生影響。
語法說明:
PUT /索引庫名/_mapping{"properties": {"新欄位名":{"type": "integer" } }}
刪除索引庫
語法:
-
• 請求方式:DELETE -
• 請求路徑:/索引庫名 -
• 請求引數:無
格式:
DELETE /索引庫名
Elasticsearch常用操作
文件操作
新增文件
POST /索引/_doc/[id值]{"field名":field值}POST /索引庫名/_doc/文件id{"欄位1": "值1","欄位2": "值2","欄位3": {"子屬性1": "值3","子屬性2": "值4" }, // ...}
示例:
POST /jjy/_doc/1{"info": "jjy最牛啦","email": "[email protected]","name": {"firstName": "雲","lastName": "趙" }}
注:id值不寫時自動生成文件id,id和已有id重複時修改文件
查詢文件
GET /索引/_doc/id值
示例:
GET /jjy/_doc/1
刪除文件
DELETE /索引/_doc/id值
示例:
DELETE /jjy/_doc/1
根據id批次查詢文件
GET /索引/_mget{"docs":[ {"_id":id值}, {"_id":id值} ] }
示例:
GET /jjy/_mget{"docs":[ {"_id":1}, {"_id":2} ] }
查詢所有文件
GET /索引/_search{"query": {"match_all": {} }}
示例:
GET /jjy/_search{"query": {"match_all": {} }}
修改文件部分欄位
POST /索引/_update/1/{ "doc":{ 域名:值 } }
示例:
POST /jjy/_update/id值/{ "doc":{ info:"jjy好厲害哦" } }
注:Elasticsearch執行刪除操作時,ES先標記文件為deleted
狀態,而不是直接物理刪除。當ES儲存空間不足或工作空閒時,才會執行物理刪除操作。Elasticsearch執行修改操作時,ES不會真的修改Document中的資料,而是標記ES中原有的文件為deleted狀態,再建立一個新的文件來儲存資料。
域的屬性
index
該域是否建立索引。只有值設定為true,才能根據該域的關鍵詞查詢文件。// 根據關鍵詞查詢文件GET /索引名/_search{"query":{"term":{ 搜尋欄位: 關鍵字 } }}
type
域的型別
核心型別 | 具體型別 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
store是否單獨儲存。如果設定為true,則該域能夠單獨查詢。
// 單獨查詢某個域:GET /索引名/_search{"stored_fields": ["域名"]}
分詞器
預設分詞器
ES文件的資料拆分成一個個有完整含義的關鍵詞,並將關鍵詞與文件對應,這樣就可以透過關鍵詞查詢文件。要想正確的分詞,需要選擇合適的分詞器。
standard analyzer
:Elasticsearch預設分詞器,根據空格和標點符號對英文進行分詞,會進行單詞的大小寫轉換。-
• 檢視分詞效果
GET /_analyze{"text":測試語句, "analyzer":分詞器}
IK分詞器
IKAnalyzer是一個開源的,基於java語言開發的輕量級的中文分詞工具包。提供了兩種分詞演算法:
-
• ik_smart:最少切分 -
• ik_max_word:最細粒度劃分
安裝IK分詞器
-
1. 關閉es服務 -
2. 使用rz命令將ik分詞器上傳至虛擬機器
注:ik分詞器的版本要和es版本保持一致。
-
3. 解壓ik分詞器到elasticsearch的plugins目錄下
unzip elasticsearch-analysis-ik-8.10.4.zip -d /usr/local/elasticsearch1/plugins/analysis-ik
-
4. 啟動ES服務
su es#進入ES安裝資料夾:cd /usr/local/elasticsearch1/bin/#啟動ES服務:./elasticsearch
測試分詞器效果
GET /_analyze{"text":"測試語句", "analyzer":"ik_smart/ik_max_word"}
IK分詞器詞典IK分詞器根據詞典進行分詞,詞典檔案在IK分詞器的config目錄中。
-
• main.dic:IK中內建的詞典。記錄了IK統計的所有中文單詞。 -
• IKAnalyzer.cfg.xml:用於配置自定義詞庫。
<properties> <comment>IK Analyzer 擴充套件配置</comment> <!--使用者可以在這裡配置自己的擴充套件字典 --> <entry key="ext_dict">ext_dict.dic</entry> <!--使用者可以在這裡配置自己的擴充套件停止詞字典--> <entry key="ext_stopwords">ext_stopwords.dic</entry> <!--使用者可以在這裡配置遠端擴充套件字典 --> <!-- <entry key="remote_ext_dict">words_location</entry> --> <!--使用者可以在這裡配置遠端擴充套件停止詞字典--> <!-- <entry key="remote_ext_stopwords">words_location</entry> --></properties>
拼音分詞器
拼音分詞器可以將中文分成對應的全拼,全拼首字母等。
安裝拼音分詞器
-
1. 關閉es服務 -
2. 使用rz命令將拼音分詞器上傳至虛擬機器
注:拼音分詞器的版本要和es版本保持一致。
解壓pinyin分詞器到elasticsearch的plugins目錄下
unzip elasticsearch-analysis-pinyin-8.10.4 -d /usr/local/elasticsearch1/plugins/analysis-pinyin啟動ES服務
su es#進入ES安裝資料夾:cd /usr/local/elasticsearch1/bin/#啟動ES服務:./elasticsearch
測試分詞效果
GET /_analyze{"text":測試語句, "analyzer":"pinyin"}
自定義分詞器
真實開發中我們往往需要對一段內容既進行文字分詞,又進行拼音分詞,此時我們需要自定義ik+pinyin分詞器。
建立自定義分詞器在建立索引時自定義分詞器
PUT /索引名{"settings" : {"analysis" : {"analyzer" : {"ik_pinyin" : { //自定義分詞器名"tokenizer":"ik_max_word", // 基本分詞器"filter":"pinyin_filter" // 配置分詞器過濾 } },"filter" : { // 分詞器過濾時配置另一個分詞器,相當於同時使用兩個分詞器"pinyin_filter" : { "type" : "pinyin", // 另一個分詞器 // 拼音分詞器的配置"keep_separate_first_letter" : false, // 是否分詞每個字的首字母"keep_full_pinyin" : true, // 是否分詞全拼"keep_original" : true, // 是否保留原始輸入"remove_duplicated_term" : true // 是否刪除重複項 } } } },"mappings":{"properties":{"域名1":{"type":域的型別,"store":是否單獨儲存,"index":是否建立索引,"analyzer":分詞器 },"域名2":{ ... } } }}
測試自定義分詞器
GET /索引/_analyze{"text": "你好程式設計師","analyzer": "ik_pinyin"}
Elasticsearch搜尋文件
新增一些文件資料
PUT /students{"mappings":{"properties":{"id": {"type": "integer","index": true },"name": {"type": "text","store": true,"index": true,"analyzer": "ik_smart" },"info": {"type": "text","store": true,"index": true,"analyzer": "ik_smart" } } }}POST /students/_doc/{"id":1,"name":"程式設計師","info":"I love baizhan"}POST /students/_doc/{"id":2,"name":"美羊羊","info":"美羊羊是羊村最漂亮的人"}POST /students/_doc/{"id":3,"name":"懶羊羊","info":"懶羊羊的成績不是很好"}POST /students/_doc/{"id":4,"name":"小灰灰","info":"小灰灰的年紀比較小"}POST /students/_doc/{"id":5,"name":"沸羊羊","info":"沸羊羊喜歡美羊羊"}POST /students/_doc/{"id":6,"name":"灰太狼","info":"灰太狼是小灰灰的父親,每次都會說我一定會回來的"}
搜尋方式
match_all:查詢所有文件
{"query":{"match_all":{} }}
match:全文檢索。將查詢條件分詞後再進行搜尋。
{"query":{"match":{ 搜尋欄位:搜尋條件 } }}
注:在搜尋時關鍵詞有可能會輸入錯誤,ES搜尋提供了自動糾錯功能,即ES的模糊查詢。使用match方式可以實現模糊查詢。模糊查詢對中文的支援效果一般,我們使用英文資料測試模糊查詢。
{"query": {"match": {"域名": {"query": 搜尋條件,"fuzziness": 最多錯誤字元數,不能超過2 } } }}
range:範圍搜尋。對數字型別的欄位進行範圍搜尋
{"query":{"range":{ 搜尋欄位:{ "gte":最小值,"lte":最大值 } } }}
gt/lt:大於/小於gte/lte:大於等於/小於等於match_phrase:短語檢索。搜尋條件不做任何分詞解析,在搜尋欄位對應的倒排索引中精確匹配。
{"query":{"match_phrase":{ 搜尋欄位:搜尋條件 } }}
term/terms:單詞/片語搜尋。搜尋條件不做任何分詞解析,在搜尋欄位對應的倒排索引中精確匹配
{"query":{"term":{ 搜尋欄位: 搜尋條件 } }}{"query":{"terms":{ 搜尋欄位: [搜尋條件1,搜尋條件2] } }}
複合搜尋
GET /索引/_search{ "query": { "bool": { // 必須滿足的條件 "must": [ 搜尋方式:搜尋引數, 搜尋方式:搜尋引數 ], // 多個條件有任意一個滿足即可"should": [ 搜尋方式:搜尋引數, 搜尋方式:搜尋引數 ], // 必須不滿足的條件"must_not":[ 搜尋方式:搜尋引數, 搜尋方式:搜尋引數 ] } } }
結果排序
ES中預設使用相關度分數實現排序,可以透過搜尋語法定製化排序。
GET /索引/_search{ "query": 搜尋條件,"sort": [ {"欄位1":{"order":"asc" } }, { "欄位2":{ "order":"desc" } } ] }
由於ES對text型別欄位資料會做分詞處理,使用哪一個單詞做排序都是不合理的,所以 ES中預設不允許對text型別的欄位做排序。如果需要使用字串做結果排序,可以使用 keyword型別的欄位作為排序依據,因為keyword欄位不做分詞處理。
高亮查詢
在進行關鍵字搜尋時,搜尋出的內容中的關鍵字會顯示不同的顏色,稱之為高亮。
我們可以在關鍵字左右加入標籤字串,資料傳入前端即可完成高亮顯示,ES可以對查詢出的內容中關鍵字部分進行標籤和樣式的設定。
GET /索引/_search{ "query":搜尋條件,"highlight":{"fields": { "高亮顯示的欄位名": { // 返回高亮資料的最大長度"fragment_size":100, // 返回結果最多可以包含幾段不連續的文字"number_of_fragments":5 } },"pre_tags":["字首"], "post_tags":["字尾"] } }
SQL查詢
在ES7之後,支援SQL語句查詢文件:
GET /_sql?format=txt{"query": SQL語句}
開源版本的ES並不支援透過Java操作SQL進行查詢,如果需要操作 SQL查詢,則需要氪金(購買白金版)
SpringDaraES案例
Java原生程式碼可以操作Elasticsearch,但操作比較繁瑣,類似於資料庫中的JDBC,我們還需要將ES文件手動封裝為Java物件。所以開發中我們一般使用框架操作Elasticsearch。
Spring Data ElasticSearch是JAVA操作Elasticsearch的框架。它透過對原生API的封裝,使得JAVA程式設計師可以簡單的對Elasticsearch進行操作。
使用Repository繼承的方法查詢文件
-
1. 建立SpringBoot專案,加入Spring Data Elasticsearch起步依賴:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope></dependency>
-
2. 編寫配置檔案,連線elasticsearch
spring:elasticsearch:uris:https://192.168.0.187:9200username:elasticpassword:elastic#日誌格式logging:pattern:console:'%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n'
-
3. 建立配置類,跳過SSL證書檢查。
從ES8開始,訪問ES的協議從http變成了https,訪問https請求需要SSL證書,在開發環境下我們不需要配置該證書,在專案中新增一個配置類,跳過SSL證書檢查即可。
@ComponentpublicclassRestClientBuilderCustomizerImplimplementsRestClientBuilderCustomizer {@Overridepublicvoidcustomize(RestClientBuilder builder) { }/** * 跳過SSL的證書檢查 */@Overridepublicvoidcustomize(HttpAsyncClientBuilder builder) {SSLContextBuildersscb= SSLContexts.custom();try { sscb.loadTrustMaterial((chain, authType) -> {returntrue; }); } catch (NoSuchAlgorithmException e) {thrownewRuntimeException(e); } catch (KeyStoreException e) {thrownewRuntimeException(e); }try { builder.setSSLContext(sscb.build()); } catch (KeyManagementException | NoSuchAlgorithmException e) { e.printStackTrace(); } }}
-
4. 建立實體類:
// 一個實體類的所有物件都會存入ES的一個索引中,所以我們在建立實體類時關聯ES索引@Document(indexName = "product",createIndex = true)publicclassProduct {@Id@Field(type = FieldType.Integer,store = true,index = true)private Integer id;@Field(type = FieldType.Text,store = true,index = true,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")private String productName;@Field(type = FieldType.Text,store = true,index = true,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")private String productDesc;}
@Document:標記在類上,標記實體類為文件物件,一般有如下屬性:indexName:對應索引的名稱createIndex:是否自動建立索引@Id:標記在成員變數上,標記一個欄位為主鍵,該欄位的值會同步到ES該文件的id值。@Field:標記在成員變數上,標記為文件中的域,一般有如下屬性:type:域的型別index:是否建立索引,預設是 truestore:是否單獨儲存,預設是 falseanalyzer:分詞器searchAnalyzer:搜尋時的分詞器
-
5. 建立Repository介面
// Repository介面繼承ElasticsearchRepository,該介面提供了文件的增刪改查方法@RepositorypublicinterfaceProductRepositoryextendsElasticsearchRepository<Product,Integer> {}
-
6. 測試Repository介面的方法
@SpringBootTestpublicclassProductRepositoryTest {@Autowiredprivate ProductRepository repository;@TestpublicvoidaddProduct(){Productproduct=newProduct(1, "iphone30", "iphone30是蘋果最新手機"); repository.save(product); }@TestpublicvoidupdateProduct(){Productproduct=newProduct(1, "iphone31", "iphone31是蘋果最新手機"); repository.save(product); }@TestpublicvoidfindAllProduct(){ Iterable<Product> all = repository.findAll();for (Product product : all) { System.out.println(product); } }@TestpublicvoidfindProductById(){ Optional<Product> product = repository.findById(1); System.out.println(product.get()); }@TestpublicvoiddeleteProduct(){ repository.deleteById(1); }}
接下來我們講解SpringDataES支援的查詢方式,首先準備一些文件資料:
// 新增一些資料repository.save(newProduct(2, "三體1", "三體1是優秀的科幻小說"));repository.save(newProduct(3, "三體2", "三體2是優秀的科幻小說"));repository.save(newProduct(4, "三體3", "三體3是優秀的科幻小說"));repository.save(newProduct(5, "elasticsearch", "elasticsearch是基於lucene開發的優秀的搜尋引擎"));
使用DSL語句查詢文件
ES透過json型別的請求體查詢文件,方法如下:
GET /索引/_search{"query":{ 搜尋方式:搜尋引數}}
query後的json物件稱為DSL語句,我們可以在介面方法上使用@Query註解自定義DSL語句查詢。
@Query("{" + " \"match\": {" + " \"productDesc\": \"?0\"" + " }" + " }")List<Product> findByProductDescMatch(String keyword);@Query("{" + " \"match\": {" + " \"productDesc\": {" + " \"query\": \"?0\"," + " \"fuzziness\": 1" + " }" + " }" + "}")List<Product> findByProductDescFuzzy(String keyword);
按照規則命名方法查詢文件
-
• 只需在Repository介面中按照一定的規則命名方法,該方法就能完成相應的查詢。 -
• 規則:查詢方法以findBy開頭,涉及查詢條件時,條件的屬性用條件關鍵字連線。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<Product> findByProductName(String productName);List<Product> findByProductNameOrProductDesc(String productName,String productDesc);List<Product> findByIdBetween(Integer startId,Integer endId);
分頁查詢
在使用繼承或自定義的方法時,在方法中新增Pageable型別的引數,返回值為Page型別即可進行分頁查詢。
// 測試繼承的方法:@TestpublicvoidtestFindPage(){// 引數1:頁數,引數2:每頁條數Pageablepageable= PageRequest.of(1, 3); Page<Product> page = repository.findAll(pageable); System.out.println("總條數"+page.getTotalElements()); System.out.println("總頁數"+page.getTotalPages()); System.out.println("資料"+page.getContent());}// 自定義方法Page<Product> findByProductDescMatch(String keyword, Pageable pageable);// 測試自定義方法@TestpublicvoidtestFindPage2(){Pageablepageable= PageRequest.of(1, 2); Page<Product> page = repository.findByProductDescMatch("我喜歡三體", pageable); System.out.println("總條數"+page.getTotalElements()); System.out.println("總頁數"+page.getTotalPages()); System.out.println("資料"+page.getContent());}
結果排序
使用繼承或自定義的方法時,在方法中新增Sort型別的引數即可進行結果排序。
// 結果排序@TestpublicvoidtestFindSort(){Sortsort= Sort.by(Sort.Direction.DESC, "id"); Iterable<Product> all = repository.findAll(sort);for (Product product : all) { System.out.println(product); }}// 測試分頁加排序@TestpublicvoidtestFindPage2(){Sortsort= Sort.by(Sort.Direction.DESC,"id");Pageablepageable= PageRequest.of(0, 2,sort); Page<Product> page = repository.findByProductDescMatch("我喜歡三體", pageable); System.out.println("總條數"+page.getTotalElements()); System.out.println("總頁數"+page.getTotalPages()); System.out.println("資料"+page.getContent());}
template工具類
SpringDataElasticsearch提供了一個工具類ElasticsearchTemplate,我們使用該類物件也可以對ES進行操作。
操作索引
@SpringBootTestpublicclassTemplateTest {@Autowiredprivate ElasticsearchTemplate template;// 新增索引@TestpublicvoidaddIndex() {// 獲得索引操作物件IndexOperationsindexOperations= template.indexOps(Product.class);// 建立索引,注:該方法無法設定索引結構,不推薦使用 indexOperations.create(); }// 刪除索引@TestpublicvoiddelIndex() {// 獲得索引操作物件IndexOperationsindexOperations= template.indexOps(Product.class);// 刪除索引 indexOperations.delete(); }}
操作文件
// 新增/修改文件@TestpublicvoidaddDocument() {Productproduct=newProduct(7, "es1", "es是一款優秀的搜尋引擎"); template.save(product);}// 刪除文件@TestpublicvoiddelDocument() { template.delete("7", Product.class);}// 根據id查詢@TestpublicvoidfindAllDocument() {Productproduct= template.get("1", Product.class); System.out.println(product);}
查詢文件
template的search方法可以查詢文件:
SearchHits<T> search(Query query, Class<T> clazz):查詢文件,query是查詢條件物件,clazz是結果型別。
用法如下:
// 查詢文件2@TestpublicvoidsearchDocument2() {StringproductName="三體";StringproductDesc="優秀";// 1.構建查詢條件NativeQueryBuildernativeQueryBuilder=newNativeQueryBuilder();// 如果沒有傳入引數,查詢所有if (productName == null && productDesc == null) { nativeQueryBuilder.withQuery(Queries.matchAllQueryAsQuery()); } else { BoolQuery.BuilderboolQuery= QueryBuilders.bool();if (productName != null) { boolQuery.must(Queries.matchQueryAsQuery("productName", productName, null, null)); }if (productDesc != null) { boolQuery.must(Queries.matchQueryAsQuery("productDesc", productDesc, null, null)); } nativeQueryBuilder.withQuery(boolQuery.build()._toQuery()); }NativeQueryquery= nativeQueryBuilder.build();// 2.查詢 SearchHits<Product> result = template.search(query, Product.class);// 3.處理查詢結果for (SearchHit<Product> productSearchHit : result) {Productproduct= productSearchHit.getContent(); System.out.println(product); }}
複雜條件查詢
// 查詢文件2@TestpublicvoidsearchDocument2(){StringproductName="三體";StringproductDesc="優秀";// 1.構造查詢條件NativeQueryBuildernativeQueryBuilder=newNativeQueryBuilder();// 如果沒有傳入引數,查詢所有if (productName == null && productDesc == null){ nativeQueryBuilder.withQuery(Queries.matchAllQueryAsQuery()); }else { BoolQuery.BuilderboolQuery= QueryBuilders.bool();if (productName != null){ boolQuery.must(Queries.matchQueryAsQuery("productName",productName,null,null)); }if (productDesc != null){ boolQuery.must(Queries.matchQueryAsQuery("productDesc",productDesc,null,null)); } nativeQueryBuilder.withQuery(boolQuery.build()._toQuery()); }NativeQueryquery= nativeQueryBuilder.build();// 2.查詢 SearchHits<Product> result = template.search(query, Product.class);// 3.處理查詢結果for (SearchHit<Product> productSearchHit : result) {Productproduct= productSearchHit.getContent(); System.out.println(product); }}
分頁查詢
// 分頁查詢文件@TestpublicvoidsearchDocumentPage() {// 1.構建查詢條件Pageablepageable= PageRequest.of(0, 3);NativeQueryquery=newNativeQueryBuilder() .withQuery(Queries.matchAllQueryAsQuery()) .withPageable(pageable) .build();// 2.查詢 SearchHits<Product> result = template.search(query, Product.class);// 3.處理查詢結果 List<Product> content = newArrayList();for (SearchHit<Product> productSearchHit : result) {Productproduct= productSearchHit.getContent(); content.add(product); }/** * 封裝Page物件,引數1:具體資料,引數2:分頁條件物件,引數3:總條數 */ Page<Product> page = newPageImpl(content, pageable, result.getTotalHits()); System.out.println(page.getTotalElements()); System.out.println(page.getTotalPages()); System.out.println(page.getContent());}
結果排序
// 結果排序@TestpublicvoidsearchDocumentSort() {// 1.構建查詢條件NativeQueryquery=newNativeQueryBuilder() .withQuery(Queries.matchAllQueryAsQuery()) .withSort(Sort.by(Sort.Direction.DESC, "id")) .build();// 2.查詢 SearchHits<Product> result = template.search(query, Product.class);// 3.處理查詢結果for (SearchHit<Product> productSearchHit : result) {Productproduct= productSearchHit.getContent(); System.out.println(product); }}
Elasticsearch叢集

在單臺ES伺服器上,隨著一個索引內資料的增多,會產生儲存、效率、安全等問題。
-
1. 假設專案中有一個500G大小的索引,但我們只有幾臺200G硬碟的伺服器,此時是不可能將索引放入其中某一臺伺服器中的。

-
2. 此時我們需要將索引拆分成多份,分別放入不同的伺服器中,此時這幾臺伺服器維護了同一個索引,我們稱這幾臺伺服器為一個叢集,其中的每一臺伺服器為一個節點,每一臺伺服器中的資料稱為一個分片。

-
3. 此時如果某個節點故障,則會造成叢集崩潰,所以每個節點的分片往往還會建立副本,存放在其他節點中,此時一個節點的崩潰就不會影響整個叢集的正常執行。 在這裡插入圖片描述 節點(node):一個節點是叢集中的一臺伺服器,是叢集的一部分。它儲存資料,參與叢集的索引和搜尋功能。叢集中有一個為主節點,主節點透過ES內部選舉產生。
叢集(cluster):一組節點組織在一起稱為一個叢集,它們共同持有整個的資料,並一起提供索引和搜尋功能。
分片(shards):ES可以把完整的索引分成多個分片,分別儲存在不同的節點上。
副本(replicas):ES可以為每個分片建立副本,提高查詢效率,保證在分片資料丟失後的恢復。

注:分片的數量只能在索引建立時指定,索引建立後不能再更改分片數量,但可以改變副本的數量。為保證節點發生故障後集群的正常執行,ES不會將某個分片和它的副本存在同一臺節點上。
搭建叢集

安裝第一個ES節點
-
1. 修改系統程序最大開啟檔案數
#修改系統檔案vim /etc/security/limits.conf#新增如下內容es soft nofile 65535es hard nofile 131072
-
2. 安裝
#解壓:tar -zxvf elasticsearch-8.10.4-linux-x86_64.tar.gz#重新命名:mv elasticsearch-8.10.4 myes1#移動資料夾:mv myes1 /usr/local/#安裝ik分詞器unzip elasticsearch-analysis-ik-8.10.4.zip -d /usr/local/myes1/plugins/analysis-ik#安裝拼音分詞器unzip elasticsearch-analysis-pinyin-8.10.4.zip -d /usr/local/myes1/plugins/analysis-pinyin#es使用者取得該資料夾許可權:chown -R es:es /usr/local/myes1
-
3. 修改配置檔案
#開啟節點一配置檔案:vim /usr/local/myes1/config/elasticsearch.yml
配置如下資訊:
#叢集名稱,保證唯一cluster.name: my_elasticsearch#節點名稱,必須不一樣node.name: node1#可以訪問該節點的ip地址network.host: 0.0.0.0#該節點服務埠號http.port: 9200#叢集間通訊埠號transport.port: 9300#候選主節點的裝置地址discovery.seed_hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]#候選主節點的節點名cluster.initial_master_nodes: ["node1","node2","node3"]#關閉安全認證xpack.security.enabled: false
-
4. 啟動
#切換為es使用者:su es#後臺啟動第一個節點:ES_JAVA_OPTS="-Xms512m -Xmx512m" /usr/local/myes1/bin/elasticsearch -d
安裝第二個ES節點
-
1. 安裝
#解壓:tar -zxvf elasticsearch-8.10.4-linux-x86_64.tar.gz#重新命名:mv elasticsearch-8.10.4 myes2#移動資料夾:mv myes2 /usr/local/#安裝ik分詞器unzip elasticsearch-analysis-ik-8.10.4.zip -d /usr/local/myes2/plugins/analysis-ik#安裝拼音分詞器unzip elasticsearch-analysis-pinyin-8.10.4.zip -d /usr/local/myes2/plugins/analysis-pinyin#es使用者取得該資料夾許可權:chown -R es:es /usr/local/myes2
-
2. 修改配置檔案
#開啟節點二配置檔案:vim /usr/local/myes2/config/elasticsearch.yml配置如下資訊:
#叢集名稱,保證唯一cluster.name: my_elasticsearch#節點名稱,必須不一樣node.name: node2#可以訪問該節點的ip地址network.host: 0.0.0.0#該節點服務埠號http.port: 9201#叢集間通訊埠號transport.port: 9301#候選主節點的裝置地址discovery.seed_hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]#候選主節點的節點名cluster.initial_master_nodes: ["node1","node2","node3"]#關閉安全認證xpack.security.enabled: false
-
3. 啟動
#切換為es使用者:su es#後臺啟動第二個節點:ES_JAVA_OPTS="-Xms512m -Xmx512m" /usr/local/myes2/bin/elasticsearch -d
安裝第三個ES節點
-
1. 安裝
#解壓:tar -zxvf elasticsearch-8.10.4-linux-x86_64.tar.gz#重新命名:mv elasticsearch-8.10.4 myes3#移動資料夾:mv myes3 /usr/local/#安裝ik分詞器unzip elasticsearch-analysis-ik-8.10.4.zip -d /usr/local/myes3/plugins/analysis-ik#安裝拼音分詞器unzip elasticsearch-analysis-pinyin-8.10.4.zip -d /usr/local/myes3/plugins/analysis-pinyin#es使用者取得該資料夾許可權:chown -R es:es /usr/local/myes3
-
2. 修改配置檔案
#開啟節點一配置檔案:vim /usr/local/myes3/config/elasticsearch.yml
配置如下資訊:
#叢集名稱,保證唯一cluster.name: my_elasticsearch#節點名稱,必須不一樣node.name: node3#可以訪問該節點的ip地址network.host: 0.0.0.0#該節點服務埠號http.port: 9202#叢集間通訊埠號transport.port: 9302#候選主節點的裝置地址discovery.seed_hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]#候選主節點的節點名cluster.initial_master_nodes: ["node1","node2","node3"]#關閉安全認證xpack.security.enabled: false
-
3. 啟動
#切換為es使用者:su es#後臺啟動第三個節點:ES_JAVA_OPTS="-Xms512m -Xmx512m" /usr/local/myes3/bin/elasticsearch -d
測試叢集訪問
http://虛擬機器IP:9200/_cat/nodes
檢視叢集是否搭建成功。kibana連線es叢集
-
1. 在kibana中訪問叢集
# 開啟kibana配置檔案vim /usr/local/kibana-8.10.4/config/kibana.yml
新增如下配置
# 該叢集的所有節點elasticsearch.hosts: ["http://127.0.0.1:9200","http://127.0.0.1:9201","http://127.0.0.1:9202"]
啟動kibana
#切換為es使用者:su es#啟動kibana:cd /usr/local/kibana-8.10.4/bin./kibana
-
3. 訪問kibana: http://虛擬機器IP:5601
測試叢集狀態
-
1. 在叢集中建立一個索引
PUT /product1{"settings": {"number_of_shards": 5, // 分片數"number_of_replicas": 1 // 每個分片的副本數 },"mappings": {"properties": {"id": {"type": "integer","store": true,"index": true },"productName": {"type": "text","store": true,"index": true },"productDesc": {"type": "text","store": true,"index": true } } }}
-
2. 檢視叢集狀態
# 檢視叢集健康狀態GET /_cat/health?v# 檢視索引狀態GET /_cat/indices?v# 檢視分片狀態GET /_cat/shards?v
故障應對和水平擴容
-
1. 關閉一個節點,可以發現ES叢集可以自動進行故障應對。 -
2. 重新開啟該節點,可以發現ES叢集可以自動進行水平擴容。 -
3. 分片數不能改變,但是可以改變每個分片的副本數、
PUT /索引/_settings{"number_of_replicas": 副本數}
記憶體設定
ES預設佔用記憶體是4GB,我們可以修改config/jvm.option設定ES的堆記憶體大小,Xms表示堆記憶體的初始大小,Xmx表示可分配的最大記憶體。
-
• Xmx和Xms的大小設定為相同的,可以減輕伸縮堆大小帶來的壓力。 -
• Xmx和Xms不要超過物理記憶體的50%,因為ES內部的Lucene也要佔據一部分物理記憶體。 -
• Xmx和Xms不要超過32GB,由於Java語言的特性,堆記憶體超過32G會浪費大量系統資源,所以在記憶體足夠的情況下,最終我們都會採用設定為31G:
-Xms 31g-Xmx 31g
例如:在一臺128GB記憶體的機器中,我們可以建立兩個節點,每個節點分配31GB記憶體。
磁碟選擇
ES的最佳化即透過調整引數使得讀寫效能更快
磁碟通常是伺服器的瓶頸。Elasticsearch重度使用磁碟,磁碟的效率越高,Elasticsearch的執行效率就越高。這裡有一些最佳化磁碟的技巧:
-
• 使用SSD(固態硬碟),它比機械磁碟優秀多了。 -
• 使用RAID0模式(將連續的資料分散到多個硬碟儲存,這樣可以並行進行IO操作),代價是一塊硬碟發生故障就會引發系統故障。 -
• 不要使用遠端掛載的儲存。
分片策略
分片和副本數並不是越多越好。每個分片的底層都是一個Lucene索引,會消耗一定的系統資源。且搜尋請求需要命中索引中的所有分片,分片數過多會降低搜尋效能。索引的分片數需要架構師和技術人員對業務的增長有預先的判斷,一般來說我們遵循以下原則:
-
• 每個分片佔用的硬碟容量不超過ES的最大JVM的堆空間設定(一般設定不超過32G)。比如:如果索引的總容量在500G左右,那分片數量在16個左右即可。 -
• 分片數一般不超過節點數的3倍。比如:如果叢集內有10個節點,則分片數不超過30個。 -
• 推遲分片分配:節點中斷後叢集會重新分配分片。但預設叢集會等待一分鐘來檢視節點是否重新加入。我們可以設定等待的時長,減少重新分配的次數:
PUT /索引/_settings{"settings":{"index.unassianed.node_left.delayed_timeout":"5m" }}
-
• 減少副本數量:進行寫入操作時,需要把寫入的資料都同步到副本,副本越多寫入的效率就越慢。我們進行大批次進行寫入操作時可以先設定副本數為0,寫入完成後再修改回正常的狀態。
Elasticsearch案例
需求說明
使用ES模仿百度搜索,即自動補全+搜尋引擎效果:
ES自動補全
GET /索引/_search{"suggest": {"prefix_suggestion": {// 自定義推薦名"prefix": "elastic",// 被補全的關鍵字"completion": {"field": "productName",// 查詢的域"skip_duplicates": true, // 忽略重複結果"size": 10 //最多查詢到的結果數 } } }}
注:自動補全對效能要求極高,ES不是透過倒排索引來實現的,所以需要將對應的查詢欄位型別設定為completion。
PUT /product2{"mappings":{"properties":{"id":{"type":"integer","store":true,"index":true },"productName":{ "type":"completion" },"productDesc":{ "type":"text","store":true,"index":true } } }}POST /product2/_doc{"id":1,"productName":"elasticsearch1","productDesc":"elasticsearch1 is a good search engine"}POST /product2/_doc{"id":2,"productName":"elasticsearch2","productDesc":"elasticsearch2 is a good search engine"}POST /product2/_doc{"id":3,"productName":"elasticsearch3","productDesc":"elasticsearch3 is a good search engine"}
建立索引
PUT /news{"settings": {"number_of_shards": 5,"number_of_replicas": 1,"analysis": {"analyzer": {"ik_pinyin": {"tokenizer": "ik_smart","filter": "pinyin_filter" },"tag_pinyin": {"tokenizer": "keyword","filter": "pinyin_filter" } },"filter": {"pinyin_filter": {"type": "pinyin","keep_joined_full_pinyin": true,"keep_original": true,"remove_duplicated_term": true } } } },"mappings": {"properties": {"id": {"type": "integer","index": true },"title": {"type": "text","index": true,"analyzer": "ik_pinyin","search_analyzer": "ik_smart" },"content": {"type": "text","index": true,"analyzer": "ik_pinyin","search_analyzer": "ik_smart" },"url": {"type": "keyword","index": true },"tags": {"type": "completion","analyzer": "tag_pinyin","search_analyzer": "tag_pinyin" } } }}
mysql資料匯入es
使用logstash工具可以將mysql資料同步到es中:
-
1. 解壓logstash-8.10.4-windows-x86_64
logstash要和elastisearch版本一致
-
2. 在logstash解壓路徑下的/config中建立mysql.conf檔案,檔案寫入以下指令碼內容:
input { jdbc { jdbc_driver_library => "F:\Elasticsearch8\mysql-connector-java-5.1.37-bin.jar" jdbc_driver_class => "com.mysql.jdbc.Driver" jdbc_connection_string => "jdbc:mysql:///news" jdbc_user => "root" jdbc_password => "root" schedule => "* * * * *" jdbc_default_timezone => "Asia/Shanghai" statement => "SELECT * FROM news;" }}filter { mutate { split => {"tags" => ","} }}output { elasticsearch { hosts => ["http://192.168.0.187:9200","http://192.168.0.187:9201","http://192.168.0.187:9202"] index => "news" document_id => "%{id}" }}
-
3. 在解壓路徑下開啟cmd黑視窗,執行命令:
bin\logstash -f config\mysql.conf
-
4. 測試自動補齊
GET /news/_search{"suggest": {"my_suggest": {"prefix": "li","completion": {"field": "tags","skip_duplicates": true,"size": 10 } } }}
專案搭建
建立Springboot專案,加入SpringDataElasticsearch和SpringMVC的起步依賴
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
寫配置檔案:
# 連線elasticsearchspring:elasticsearch:uris:192.168.0.187:9200,192.168.0.187:9201,192.168.0.187:9202# 日誌格式logging:pattern:console:'%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n'
建立實體類
@Document(indexName = "news")@DatapublicclassNews {@Id@Fieldprivate Integer id;@Fieldprivate String title;@Fieldprivate String content;@Fieldprivate String url;@CompletionField@Transientprivate Completion tags;}
建立Repository介面
@RepositorypublicinterfaceNewsRepositoryextendsElasticsearchRepository<News, Integer> {}
自動補全功能
@ServicepublicclassNewsService {@Autowiredprivate ElasticsearchClient client;// 自動補齊public List<String> autoSuggest(String keyword)throws IOException {// 1.自動補齊查詢條件Suggestersuggester= Suggester.of( s -> s.suggesters("prefix_suggestion", FieldSuggester.of( fs -> fs.completion( cs -> cs.skipDuplicates(true) .size(10) .field("tags") ) )).text(keyword) );// 2.自動補齊查詢 SearchResponse<Map> response = client.search(s -> s.index("news") .suggest(suggester), Map.class);// 3.處理查詢結果MapresultMap= response.suggest(); List<Suggestion> suggestionList = (List) resultMap.get("prefix_suggestion");Suggestionsuggestion= suggestionList.get(0); List<CompletionSuggestOption> resultList = suggestion.completion().options(); List<String> result = newArrayList<>();for (CompletionSuggestOption completionSuggestOption : resultList) {Stringtext= completionSuggestOption.text(); result.add(text); }return result; }}
搜尋關鍵字功能
在repository介面中新增高亮搜尋關鍵字方法
// 高亮搜尋關鍵字@Highlight(fields = {@HighlightField(name = "title"), @HighlightField(name = "content")})List<SearchHit<News>> findByTitleMatchesOrContentMatches(String title, String content);
service類中呼叫該方法
// 查詢關鍵字public List<News> highLightSearch(String keyword){ List<SearchHit<News>> result = repository.findByTitleMatchesOrContentMatches(keyword, keyword);// 處理結果,封裝為News型別的集合 List<News> newsList = newArrayList();for (SearchHit<News> newsSearchHit : result) {Newsnews= newsSearchHit.getContent();// 高亮欄位 Map<String, List<String>> highlightFields = newsSearchHit.getHighlightFields();if (highlightFields.get("title") != null){ news.setTitle(highlightFields.get("title").get(0)); }if (highlightFields.get("content") != null){ news.setContent(highlightFields.get("content").get(0)); } newsList.add(news); }return newsList;}
建立Controller類
@RestControllerpublicclassNewsController {@Autowiredprivate NewsService newsService;@GetMapping("/autoSuggest")public List<String> autoSuggest(String term){ // 前端使用jqueryUI,傳送的引數預設名為termreturn newsService.autoSuggest(term); }@GetMapping("/highLightSearch")public List<News> highLightSearch(String term){return newsService.highLightSearch(term); }}
前端頁面
我們使用jqueryUI中的autocomplete外掛完成專案的前端實現。
<script> $("#newsTag").autocomplete({source: "/autoSuggest", // 請求路徑delay: 100, //請求延遲minLength: 1//最少輸入多少字元像伺服器傳送請求 })functionsearch() {var term = $("#newsTag").val(); $.get("/highLightSearch", {term: term}, function (data) {var str = "";for (var i = 0; i < data.length; i++) {vardocument = data[i]; str += "<li>" +" <h4>" +" <a href='" + document.url + "' target='_blank'>" + document.title + "</a>" +" </h4> " +" <p>" + document.content + "</p>" +" </li>"; } $("#news").html(str); }) }</script>
如果我的內容對你有幫助,請==點贊,評論,收藏==。創作不易,大家的支援就是我堅持下去的動力
連結:https://blog.csdn.net/m0_74436895/article/details/142526240?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522f799522f5fddae157ff926066df2d9ef%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=f799522f5fddae157ff926066df2d9ef&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-6-142526240-null-null.142^v102^pc_search_result_base1&utm_term=Elasticsearch&spm=1018.2226.3001.4187
官方站點:www.linuxprobe.com
Linux命令大全:www.linuxcool.com

劉遄老師QQ:5604215
Linux技術交流群:2636170
(新群,火熱加群中……)
想要學習Linux系統的讀者可以點選"閱讀原文"按鈕來了解書籍《Linux就該這麼學》,同時也非常適合專業的運維人員閱讀,成為輔助您工作的高價值工具書!