從零玩轉Elasticsearch:日誌分析,看這一篇就夠了

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
  1. 4. 由於ES不能以root使用者執行,我們需要建立一個非root使用者,此處建立一個名為es的使用者:
#建立使用者:useradd es

安裝服務

  1. 1. 使用rz命令將linux版的ES上傳至虛擬機器
  2. 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
  1. 3. 啟動ES服務:
#切換為es使用者:su es#進入ES安裝資料夾:cd /usr/local/elasticsearch1/bin/#啟動ES服務:./elasticsearch 
  1. 4. 當啟動成功,可以看到類似以下的日誌輸出。首次啟動Elasticsearch,預設會啟用安全配置功能,啟用身份認證和授權,內建超級使用者elastic,並生成預設密碼,此時要注意儲存,否則之後啟動不會再顯示。

    在這裡插入圖片描述
# 重置預設密碼:cd /usr/local/elasticsearch1/bin/./elasticsearch-reset-password -u elastic# 自定義密碼:cd /usr/local/elasticsearch1/bin/./elasticsearch-reset-password --username elastic -i
  1. 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. 1. 使用rz工具將Kibana壓縮檔案上傳到Linux虛擬機器
  2. 2. 解壓
tar -zxvf kibana-8.10.4-linux-x86_64.tar.gz -C /usr/local/
  1. 3. 修改配置
# 進入Kibana解壓路徑cd /usr/local/kibana-8.10.4/config# 修改配置檔案vim kibana.yml# 加入以下內容# 主機IP,服務名server.host: "虛擬機器IP"server.name: "kibana"
  1. 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
  1. 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首次啟動日誌中找。
  1. 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
域的型別
核心型別 具體型別
字串型別
text
整數型別
long, integer, short, byte
浮點型別
double, float
日期型別
date
布林型別
boolean
陣列型別
array
物件型別
object
不分詞的字串
keyword
store是否單獨儲存。如果設定為true,則該域能夠單獨查詢。
// 單獨查詢某個域:GET /索引名/_search{"stored_fields": ["域名"]}

分詞器

預設分詞器

ES文件的資料拆分成一個個有完整含義的關鍵詞,並將關鍵詞與文件對應,這樣就可以透過關鍵詞查詢文件。要想正確的分詞,需要選擇合適的分詞器。
standard analyzer:Elasticsearch預設分詞器,根據空格和標點符號對英文進行分詞,會進行單詞的大小寫轉換。
  • • 檢視分詞效果
GET /_analyze{"text":測試語句, "analyzer":分詞器}

IK分詞器

IKAnalyzer是一個開源的,基於java語言開發的輕量級的中文分詞工具包。提供了兩種分詞演算法:
  • • ik_smart:最少切分
  • • ik_max_word:最細粒度劃分
安裝IK分詞器
  1. 1. 關閉es服務
  2. 2. 使用rz命令將ik分詞器上傳至虛擬機器
注:ik分詞器的版本要和es版本保持一致。
  1. 3. 解壓ik分詞器到elasticsearch的plugins目錄下
unzip elasticsearch-analysis-ik-8.10.4.zip -d /usr/local/elasticsearch1/plugins/analysis-ik
  1. 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. 1. 關閉es服務
  2. 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. 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>
  1. 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'
  1. 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();     }   }}
  1. 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:是否建立索引,預設是 true
store:是否單獨儲存,預設是 false
analyzer:分詞器
searchAnalyzer:搜尋時的分詞器
  1. 5. 建立Repository介面
// Repository介面繼承ElasticsearchRepository,該介面提供了文件的增刪改查方法@RepositorypublicinterfaceProductRepositoryextendsElasticsearchRepository<Product,Integer> {}
  1. 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開頭,涉及查詢條件時,條件的屬性用條件關鍵字連線。
關鍵字
命名規則
解釋
示例
and
findByField1AndField2
根據Field1和Field2 獲得資料
findByTitleAndContent
or
findByField1OrField2
根據Field1或Field2 獲得資料
findByTitleOrContent
is
findByField
根據Field獲得資料
findByTitle
not
findByFieldNot
根據Field獲得補集資料
findByTitleNot
between
findByFieldBetween
獲得指定範圍的資料
findByPriceBetween
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(13);  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(12);  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(02,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, nullnull));     }if (productDesc != null) {      boolQuery.must(Queries.matchQueryAsQuery("productDesc", productDesc, nullnull));     }    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(03);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. 1. 假設專案中有一個500G大小的索引,但我們只有幾臺200G硬碟的伺服器,此時是不可能將索引放入其中某一臺伺服器中的。

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

在這裡插入圖片描述
  1. 3. 此時如果某個節點故障,則會造成叢集崩潰,所以每個節點的分片往往還會建立副本,存放在其他節點中,此時一個節點的崩潰就不會影響整個叢集的正常執行。

    在這裡插入圖片描述

    節點(node):一個節點是叢集中的一臺伺服器,是叢集的一部分。它儲存資料,參與叢集的索引和搜尋功能。叢集中有一個為主節點,主節點透過ES內部選舉產生。

叢集(cluster):一組節點組織在一起稱為一個叢集,它們共同持有整個的資料,並一起提供索引和搜尋功能。
分片(shards):ES可以把完整的索引分成多個分片,分別儲存在不同的節點上。
副本(replicas):ES可以為每個分片建立副本,提高查詢效率,保證在分片資料丟失後的恢復。

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

搭建叢集

在這裡插入圖片描述
安裝第一個ES節點
  1. 1. 修改系統程序最大開啟檔案數
#修改系統檔案vim /etc/security/limits.conf#新增如下內容es soft nofile 65535es hard nofile 131072
  1. 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
  1. 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
  1. 4. 啟動
#切換為es使用者:su es#後臺啟動第一個節點:ES_JAVA_OPTS="-Xms512m -Xmx512m" /usr/local/myes1/bin/elasticsearch -d
安裝第二個ES節點
  1. 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
  1. 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
  1. 3. 啟動
#切換為es使用者:su es#後臺啟動第二個節點:ES_JAVA_OPTS="-Xms512m -Xmx512m" /usr/local/myes2/bin/elasticsearch -d
安裝第三個ES節點
  1. 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
  1. 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
  1. 3. 啟動
#切換為es使用者:su es#後臺啟動第三個節點:ES_JAVA_OPTS="-Xms512m -Xmx512m" /usr/local/myes3/bin/elasticsearch -d
測試叢集訪問http://虛擬機器IP:9200/_cat/nodes檢視叢集是否搭建成功。
kibana連線es叢集
  1. 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
  1. 3. 訪問kibana:http://虛擬機器IP:5601

測試叢集狀態

  1. 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    }   }  }}
  1. 2. 檢視叢集狀態
# 檢視叢集健康狀態GET /_cat/health?v# 檢視索引狀態GET /_cat/indices?v# 檢視分片狀態GET /_cat/shards?v 

故障應對和水平擴容

  1. 1. 關閉一個節點,可以發現ES叢集可以自動進行故障應對。
  2. 2. 重新開啟該節點,可以發現ES叢集可以自動進行水平擴容。
  3. 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. 1. 解壓logstash-8.10.4-windows-x86_64
logstash要和elastisearch版本一致
  1. 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}"   }}
  1. 3. 在解壓路徑下開啟cmd黑視窗,執行命令:
bin\logstash -f config\mysql.conf
  1. 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"// 請求路徑delay100//請求延遲minLength1//最少輸入多少字元像伺服器傳送請求   })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
END
官方站點:www.linuxprobe.com
 Linux命令大全:www.linuxcool.com

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


相關文章