ElasticSearch分散式搜索引擎——從入門到精通

来源:https://www.cnblogs.com/buchizicai/archive/2023/02/05/17093719.html
-Advertisement-
Play Games

Elasticsearch是位於 Elastic Stack 核心的分散式搜索和分析引擎。Elasticsearch 是索引、搜索和分析魔法發生的地方。lasticsearch 為所有類型的數據提供近乎實時的搜索和分析。無論您擁有結構化或非結構化文本、數字數據還是地理空間數據,Elasticsear... ...


ES分散式搜索引擎

註意: 在沒有創建庫的時候搜索,ES會創建一個庫並自動創建該欄位並且設置為String類型也就是text

什麼是elasticsearch?

  • 一個開源的分散式搜索引擎,可以用來實現搜索、日誌統計、分析、系統監控等功能

什麼是elastic stack(ELK)?

  • 是以elasticsearch為核心的技術棧,包括beats、Logstash、kibana、elasticsearch

什麼是Lucene?

  • 是Apache的開源搜索引擎類庫,提供了搜索引擎的核心API

elasticsearch是一款非常強大的開源搜索引擎,具備非常多強大功能,可以幫助我們從海量數據中快速找到需要的內容

ELK技術棧

本文只使用了elasticsearch,以及kibana做可視化界面

elasticsearch結合kibana、Logstash、Beats,也就是elastic stack(ELK)。被廣泛應用在日誌數據分析、實時監控等領域:

image

而elasticsearch是elastic stack的核心,負責存儲、搜索、分析數據。

image

初識elasticsearch

1. elasticsearch背景介紹

elasticsearch底層是基於lucene來實現的。

Lucene是一個Java語言的搜索引擎類庫,是Apache公司的頂級項目,由DougCutting於1999年研發。官網地址:https://lucene.apache.org/

image

elasticsearch的發展歷史:

  • 2004年Shay Banon基於Lucene開發了Compass
  • 2010年Shay Banon 重寫了Compass,取名為Elasticsearch。

image

2. 倒排索引

倒排索引的概念是基於MySQL這樣的正向索引而言的。

2.1 正向索引

設置了索引的話挺快的,但要是模糊查詢則就很慢!

那麼什麼是正向索引呢?例如給下表(tb_goods)中的id創建索引:

image

如果是根據id查詢,那麼直接走索引,查詢速度非常快。

但如果是基於title做模糊查詢,只能是逐行掃描數據,流程如下:

1)用戶搜索數據,條件是title符合"%手機%"

2)逐行獲取數據,比如id為1的數據

3)判斷數據中的title是否符合用戶搜索條件

4)如果符合則放入結果集,不符合則丟棄。回到步驟1

逐行掃描,也就是全表掃描,隨著數據量增加,其查詢效率也會越來越低。當數據量達到數百萬時,就是一場災難。

2.2 倒排索引

倒排索引中有兩個非常重要的概念:

  • 文檔(Document):用來搜索的數據,其中的每一條數據就是一個文檔。例如一個網頁、一個商品信息
  • 詞條(Term):對文檔數據或用戶搜索數據,利用某種演算法分詞,得到的具備含義的詞語就是詞條。例如:我是中國人,就可以分為:我、是、中國人、中國、國人這樣的幾個詞條

創建倒排索引是對正向索引的一種特殊處理,流程如下:

  • 將每一個文檔的數據利用演算法分詞,得到一個個詞條
  • 創建表,每行數據包括詞條、詞條所在文檔id、位置等信息
  • 因為詞條唯一性,可以給詞條創建索引,例如hash表結構索引

如圖:

image

倒排索引的搜索流程如下(以搜索"華為手機"為例):

1)用戶輸入條件"華為手機"進行搜索。

2)對用戶輸入內容分詞,得到詞條:華為手機

3)拿著詞條在倒排索引中查找,可以得到包含詞條的文檔id:1、2、3。

4)拿著文檔id到正向索引中查找具體文檔。

如圖:

image

雖然要先查詢倒排索引,再查詢倒排索引,但是無論是詞條、還是文檔id都建立了索引,查詢速度非常快!無需全表掃描。

2.3 正向和倒排對比

概念區別:

  • 正向索引是最傳統的,根據id索引的方式。但根據詞條查詢時,必須先逐條獲取每個文檔,然後判斷文檔中是否包含所需要的詞條,是根據文檔找詞條的過程

  • 倒排索引則相反,是先找到用戶要搜索的詞條,根據詞條得到保護詞條的文檔的id,然後根據id獲取文檔。是根據詞條找文檔的過程

優缺點:

正向索引

  • 優點:
    • 可以給多個欄位創建索引
    • 根據索引欄位搜索、排序速度非常快
  • 缺點:
    • 根據非索引欄位,或者索引欄位中的部分詞條查找時,只能全表掃描。

倒排索引

  • 優點:
    • 根據詞條搜索、模糊搜索時,速度非常快
  • 缺點:
    • 只能給詞條創建索引,而不是欄位
    • 無法根據欄位做排序

3. ES資料庫基本概念

elasticsearch中有很多獨有的概念,與mysql中略有差別,但也有相似之處。

3.1.文檔和欄位

一個文檔就像資料庫里的一條數據,欄位就像資料庫里的列

elasticsearch是面向文檔(Document)存儲的,可以是資料庫中的一條商品數據,一個訂單信息。文檔數據會被序列化為json格式後存儲在elasticsearch中:

image

而Json文檔中往往包含很多的欄位(Field),類似於mysql資料庫中的列

3.2.索引和映射

索引就像資料庫里的表,映射就像資料庫中定義的表結構

索引(Index),就是相同類型的文檔的集合【類似mysql中的表

例如:

  • 所有用戶文檔,就可以組織在一起,稱為用戶的索引;
  • 所有商品的文檔,可以組織在一起,稱為商品的索引;
  • 所有訂單的文檔,可以組織在一起,稱為訂單的索引;

image

因此,我們可以把索引當做是資料庫中的表。

資料庫的表會有約束信息,用來定義表的結構、欄位的名稱、類型等信息。因此,索引庫中就有映射(mapping),是索引中文檔的欄位約束信息,類似表的結構約束。

3.3.mysql與elasticsearch

各自長處:

  • Mysql:擅長事務類型操作,可以確保數據的安全和一致性

  • Elasticsearch:擅長海量數據的搜索、分析、計算

我們統一的把mysql與elasticsearch的概念做一下對比

MySQL Elasticsearch 說明
Table Index 索引(index),就是文檔的集合,類似資料庫的表(table)
Row Document 文檔(Document),就是一條條的數據,類似資料庫中的行(Row),文檔都是JSON格式
Column Field 欄位(Field),就是JSON文檔中的欄位,類似資料庫中的列(Column)
Schema Mapping Mapping(映射)是索引中文檔的約束,例如欄位類型約束。類似資料庫的表結構(Schema)
SQL DSL DSL是elasticsearch提供的JSON風格的請求語句,用來操作elasticsearch,實現CRUD

在企業中,往往是兩者結合使用:

  • 對安全性要求較高的寫操作,使用mysql實現
  • 對查詢性能要求較高的搜索需求,使用elasticsearch實現
  • 兩者再基於某種方式,實現數據的同步,保證一致性

image

4. 安裝es、kibana、分詞器

分詞器的作用是什麼?

  • 創建倒排索引時對文檔分詞
  • 用戶搜索時,對輸入的內容分詞

IK分詞器有幾種模式?

  • ik_smart:智能切分,粗粒度
  • ik_max_word:最細切分,細粒度

IK分詞器如何拓展詞條?如何停用詞條?

  • 利用config目錄的IkAnalyzer.cfg.xml文件添加拓展詞典和停用詞典
  • 在詞典中添加拓展詞條或者停用詞條

4.1 部署單點es

4.1.1.創建網路

因為我們還需要部署kibana容器,因此需要讓es和kibana容器互聯。這裡先創建一個網路:

docker network create es-net
4.1.2.載入鏡像

這裡我們採用elasticsearch的7.12.1版本的鏡像,這個鏡像體積非常大,接近1G。不建議大家自己pull。

課前資料提供了鏡像的tar包:

image

大家將其上傳到虛擬機中,然後運行命令載入即可:

# 導入數據
docker load -i es.tar

註意:同理還有kibana的tar包也需要這樣做。

4.1.3.運行

運行docker命令,部署單點es:

docker run -d \
	--name es \
    -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
    -e "discovery.type=single-node" \
    -v es-data:/usr/share/elasticsearch/data \
    -v es-plugins:/usr/share/elasticsearch/plugins \
    --privileged \
    --network es-net \
    -p 9200:9200 \
    -p 9300:9300 \
elasticsearch:7.12.1

命令解釋:

  • -e "cluster.name=es-docker-cluster":設置集群名稱
  • -e "http.host=0.0.0.0":監聽的地址,可以外網訪問
  • -e "ES_JAVA_OPTS=-Xms512m -Xmx512m":記憶體大小
  • -e "discovery.type=single-node":非集群模式
  • -v es-data:/usr/share/elasticsearch/data:掛載邏輯捲,綁定es的數據目錄
  • -v es-logs:/usr/share/elasticsearch/logs:掛載邏輯捲,綁定es的日誌目錄
  • -v es-plugins:/usr/share/elasticsearch/plugins:掛載邏輯捲,綁定es的插件目錄
  • --privileged:授予邏輯捲訪問權
  • --network es-net :加入一個名為es-net的網路中
  • -p 9200:9200:埠映射配置

在瀏覽器中輸入:http://192.168.194.131/:9200 即可看到elasticsearch的響應結果:

image

4.2.部署kibana

kibana可以給我們提供一個elasticsearch的可視化界面,便於我們學習。

4.2.1.部署

創建網路後,導入kibana壓縮包,然後創建並啟動相應容器。【和前面部署單點es一樣做法】

再運行docker命令,部署kibana

docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=es-net \
-p 5601:5601  \
kibana:7.12.1
  • --network es-net :加入一個名為es-net的網路中,與elasticsearch在同一個網路中
  • -e ELASTICSEARCH_HOSTS=http://es:9200":設置elasticsearch的地址,因為kibana已經與elasticsearch在一個網路,因此可以用容器名直接訪問elasticsearch
  • -p 5601:5601:埠映射配置

kibana啟動一般比較慢,需要多等待一會,可以通過命令:

docker logs -f kibana

查看運行日誌,當查看到下麵的日誌,說明成功:

image

此時,在瀏覽器輸入地址訪問:http://192.168.194.131:5601,即可看到結果如下圖:

image

kibana左側中提供了一個DevTools界面:

image

這個界面中可以編寫DSL來操作elasticsearch。並且對DSL語句有自動補全功能。

4.3.安裝IK分詞器

4.3.1.線上安裝ik插件(較慢)
# 進入容器內部
docker exec -it elasticsearch /bin/bash

# 線上下載並安裝
./bin/elasticsearch-plugin  install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip

#退出
exit
#重啟容器
docker restart elasticsearch
4.3.2.離線安裝ik插件(推薦)

1)查看數據捲目錄

安裝插件需要知道elasticsearch的plugins目錄位置,而我們用了數據捲掛載,因此需要查看elasticsearch的數據捲目錄,通過下麵命令查看:

docker volume inspect es-plugins

顯示結果:

[
    {
        "CreatedAt": "2022-05-06T10:06:34+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/es-plugins/_data",
        "Name": "es-plugins",
        "Options": null,
        "Scope": "local"
    }
]

說明plugins目錄被掛載到了:/var/lib/docker/volumes/es-plugins/_data 這個目錄中。

2)解壓縮分詞器安裝包

下麵我們需要把課前資料中的ik分詞器解壓縮,重命名為ik

image

3)上傳到es容器的插件數據捲中

也就是/var/lib/docker/volumes/es-plugins/_data

image

4)重啟容器

# 4、重啟容器
docker restart es
# 查看es日誌
docker logs -f es

5)測試:

IK分詞器包含兩種模式:

  • ik_smart:最少切分

  • ik_max_word:最細切分

在kibana的Dev tools中輸入以下代碼:

”analyzer“ 就是選擇分詞器模式

GET /_analyze
{
  "analyzer": "ik_max_word",
  "text": "黑馬程式員學習java太棒了"
}

結果:

{
  "tokens" : [
    {
      "token" : "黑馬",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "CN_WORD",
      "position" : 0
    },
    {
      "token" : "程式員",
      "start_offset" : 2,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 1
    },
    {
      "token" : "程式",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "CN_WORD",
      "position" : 2
    },
    {
      "token" : "員",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "CN_CHAR",
      "position" : 3
    },
    {
      "token" : "學習",
      "start_offset" : 5,
      "end_offset" : 7,
      "type" : "CN_WORD",
      "position" : 4
    },
    {
      "token" : "java",
      "start_offset" : 7,
      "end_offset" : 11,
      "type" : "ENGLISH",
      "position" : 5
    },
    {
      "token" : "太棒了",
      "start_offset" : 11,
      "end_offset" : 14,
      "type" : "CN_WORD",
      "position" : 6
    },
    {
      "token" : "太棒",
      "start_offset" : 11,
      "end_offset" : 13,
      "type" : "CN_WORD",
      "position" : 7
    },
    {
      "token" : "了",
      "start_offset" : 13,
      "end_offset" : 14,
      "type" : "CN_CHAR",
      "position" : 8
    }
  ]
}
4.3.3 擴展詞詞典

隨著互聯網的發展,“造詞運動”也越發的頻繁。出現了很多新的詞語,在原有的辭彙列表中並不存在。比如:“奧力給”,“白嫖” 等。

所以我們的辭彙也需要不斷的更新,IK分詞器提供了擴展辭彙的功能。

1)打開IK分詞器config目錄:

image

2)在IKAnalyzer.cfg.xml配置文件內容添加:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
        <comment>IK Analyzer 擴展配置</comment>
        <!--用戶可以在這裡配置自己的擴展字典 *** 添加擴展詞典-->
        <entry key="ext_dict">ext.dic</entry>
</properties>

3)新建一個 ext.dic,可以參考config目錄下複製一個配置文件進行修改

白嫖
奧力給

4)重啟elasticsearch

docker restart es

# 查看 日誌
docker logs -f elasticsearch

image

日誌中已經成功載入ext.dic配置文件

5)測試效果:

GET /_analyze
{
  "analyzer": "ik_max_word",
  "text": "傳智播客Java就業超過90%,奧力給!"
}

註意當前文件的編碼必須是 UTF-8 格式,嚴禁使用Windows記事本編輯

4.3.4 停用詞詞典

在互聯網項目中,在網路間傳輸的速度很快,所以很多語言是不允許在網路上傳遞的,如:關於宗教、政治等敏感詞語,那麼我們在搜索時也應該忽略當前辭彙。

IK分詞器也提供了強大的停用詞功能,讓我們在索引時就直接忽略當前的停用辭彙表中的內容。

1)IKAnalyzer.cfg.xml配置文件內容添加:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
        <comment>IK Analyzer 擴展配置</comment>
        <!--用戶可以在這裡配置自己的擴展字典-->
        <entry key="ext_dict">ext.dic</entry>
         <!--用戶可以在這裡配置自己的擴展停止詞字典  *** 添加停用詞詞典-->
        <entry key="ext_stopwords">stopword.dic</entry>
</properties>

3)在 stopword.dic 添加停用詞

大帥逼

4)重啟elasticsearch

# 重啟服務
docker restart es
docker restart kibana

# 查看 日誌
docker logs -f elasticsearch

日誌中已經成功載入stopword.dic配置文件

5)測試效果:

GET /_analyze
{
  "analyzer": "ik_max_word",
  "text": "我是真的會謝Java就業率超過95%,大帥逼都點贊白嫖,奧力給!"
}

註意當前文件的編碼必須是 UTF-8 格式,嚴禁使用Windows記事本編輯

索引庫操作

索引庫就類似資料庫表,mapping映射就類似表的結構。

我們要向es中存儲數據,必須先創建“庫”和“表”。

1. Mapping映射屬性

mapping是對索引庫中文檔的約束,常見的mapping屬性包括:

  • type:欄位數據類型,常見的簡單類型有:

    • 字元串:text(可分詞的文本)、keyword(精確值,例如:品牌、國家、ip地址)

      keyword類型只能整體搜索,不支持搜索部分內容

    • 數值:long、integer、short、byte、double、float、

    • 布爾:boolean

    • 日期:date

    • 對象:object

  • index:是否創建索引,預設為true

  • analyzer:使用哪種分詞器

  • properties:該欄位的子欄位

例如下麵的json文檔:

{
    "age": 21,
    "weight": 52.1,
    "isMarried": false,
    "info": "真相只有一個!",
    "email": "[email protected]",
    "score": [99.1, 99.5, 98.9],
    "name": {
        "firstName": "柯",
        "lastName": "南"
    }
}

對應的每個欄位映射(mapping):

  • age:類型為 integer;參與搜索,因此需要index為true;無需分詞器
  • weight:類型為float;參與搜索,因此需要index為true;無需分詞器
  • isMarried:類型為boolean;參與搜索,因此需要index為true;無需分詞器
  • info:類型為字元串,需要分詞,因此是text;參與搜索,因此需要index為true;分詞器可以用ik_smart
  • email:類型為字元串,但是不需要分詞,因此是keyword;不參與搜索,因此需要index為false;無需分詞器
  • score:雖然是數組,但是我們只看元素的類型,類型為float;參與搜索,因此需要index為true;無需分詞器
  • name:類型為object,需要定義多個子屬性
    • name.firstName;類型為字元串,但是不需要分詞,因此是keyword;參與搜索,因此需要index為true;無需分詞器
    • name.lastName;類型為字元串,但是不需要分詞,因此是keyword;參與搜索,因此需要index為true;無需分詞器

2. 索引庫的CRUD

CRUD簡單描述:

  • 創建索引庫:PUT /索引庫名
  • 查詢索引庫:GET /索引庫名
  • 刪除索引庫:DELETE /索引庫名
  • 修改索引庫(添加欄位):PUT /索引庫名/_mapping

這裡統一使用Kibana編寫DSL的方式來演示。

2.1 創建索引庫和映射

基本語法:

  • 請求方式:PUT
  • 請求路徑:/索引庫名,可以自定義
  • 請求參數:mapping映射

格式:

PUT /索引庫名稱
{
  "mappings": {
    "properties": {
      "欄位名":{
        "type": "text",
        "analyzer": "ik_smart"
      },
      "欄位名2":{
        "type": "keyword",
        "index": "false"
      },
      "欄位名3":{
        "properties": {
          "子欄位": {
            "type": "keyword"
          }
        }
      },
      // ...略
    }
  }
}

示例:

PUT /conan
{
  "mappings": {
    "properties": {
      "column1":{
        "type": "text",
        "analyzer": "ik_smart"
      },
      "column2":{
        "type": "keyword",
        "index": "false"
      },
      "column3":{
        "properties": {
          "子欄位1": {
            "type": "keyword"
          },
          "子欄位2": {
            "type": "keyword"
          }
        }
      },
      // ...略
    }
  }
}

2.2 查詢索引庫

基本語法

  • 請求方式:GET

  • 請求路徑:/索引庫名

  • 請求參數:無

格式

GET /索引庫名

示例

image

2.3 修改索引庫

這裡的修改是只能增加新的欄位到mapping中

倒排索引結構雖然不複雜,但是一旦數據結構改變(比如改變了分詞器),就需要重新創建倒排索引,這簡直是災難。因此索引庫一旦創建,無法修改mapping

雖然無法修改mapping中已有的欄位,但是卻允許添加新的欄位到mapping中,因為不會對倒排索引產生影響。

語法說明

PUT /索引庫名/_mapping
{
  "properties": {
    "新欄位名":{
      "type": "integer"
    }
  }
}

示例

image

2.4 刪除索引庫

語法:

  • 請求方式:DELETE

  • 請求路徑:/索引庫名

  • 請求參數:無

格式:

DELETE /索引庫名

在kibana中測試:

image

文檔操作

文檔操作有哪些?

  • 創建文檔:POST /{索引庫名}/_doc/文檔id
  • 查詢文檔:GET /{索引庫名}/_doc/文檔id
  • 刪除文檔:DELETE /{索引庫名}/_doc/文檔id
  • 修改文檔:
    • 全量修改:PUT /{索引庫名}/_doc/文檔id
    • 增量修改:POST /{索引庫名}/_update/文檔id { "doc": {欄位}}

1. 文檔的CRUD

1.1 新增文檔

語法:

POST /索引庫名/_doc/文檔id
{
    "欄位1": "值1",
    "欄位2": "值2",
    "欄位3": {
        "子屬性1": "值3",
        "子屬性2": "值4"
    },
    // ...
}

示例:

POST /heima/_doc/1
{
    "info": "真相只有一個!",
    "email": "[email protected]",
    "name": {
        "firstName": "柯",
        "lastName": "南"
    }
}

響應:

image

1.2 查詢文檔

根據rest風格,新增是post,查詢應該是get,不過查詢一般都需要條件,這裡我們把文檔id帶上。

語法:

GET /{索引庫名稱}/_doc/{id}
//批量查詢:查詢該索引庫下的全部文檔
GET /{索引庫名稱}/_search

通過kibana查看數據:

GET /heima/_doc/1

查看結果:

image

1.3 刪除文檔

刪除使用DELETE請求,同樣,需要根據id進行刪除:

語法:

DELETE /{索引庫名}/_doc/id值

示例:

# 根據id刪除數據
DELETE /heima/_doc/1

結果:

image

1.4 修改文檔

修改有兩種方式:

  • 全量修改:直接覆蓋原來的文檔
  • 增量修改:修改文檔中的部分欄位
1.4.1 全量修改

全量修改是覆蓋原來的文檔,其本質是:

  • 根據指定的id刪除文檔
  • 新增一個相同id的文檔

註意:如果根據id刪除時,id不存在,第二步的新增也會執行,也就從修改變成了新增操作了。

語法:

PUT /{索引庫名}/_doc/文檔id
{
    "欄位1": "值1",
    "欄位2": "值2",
    // ... 略
}

示例:

PUT /heima/_doc/1
{
    "info": "黑馬程式員高級Java講師",
    "email": "[email protected]",
    "name": {
        "firstName": "雲",
        "lastName": "趙"
    }
}
1.4.2 增量修改

增量修改是只修改指定id匹配的文檔中的部分欄位。

語法:

POST /{索引庫名}/_update/文檔id
{
    "doc": {
         "欄位名": "新的值",
    }
}

示例:

POST /heima/_update/1
{
  "doc": {
    "email": "[email protected]"
  }
}

RestAPI

ES官方提供了各種不同語言的客戶端,用來操作ES。這些客戶端的本質就是組裝DSL語句,通過http請求發送給ES。官方文檔地址:https://www.elastic.co/guide/en/elasticsearch/client/index.html

其中的Java Rest Client又包括兩種:

  • Java Low Level Rest Client
  • Java High Level Rest Client

image

我們使用的是Java HighLevel Rest Client客戶端API

API操作索引庫

JavaRestClient操作elasticsearch的流程基本類似。核心是client.indices()方法來獲取索引庫的操作對象。

索引庫操作的基本步驟:【可以根據發送請求那步的第一個參數,發過來判斷需要創建什麼XXXXRequest】

  • 初始化RestHighLevelClient
  • 創建XxxIndexRequest。XXX是Create、Get、Delete
  • 準備DSL( Create時需要,其它是無參)
  • 發送請求。調用RestHighLevelClient#indices().xxx()方法,xxx是create、exists、delete

1. mapping映射分析

根據MySQL資料庫表結構(建表語句),去寫索引庫結構JSON。表和索引庫一一對應

註意:地理坐標、組合欄位。索引庫里的地理坐標是一個欄位:坐標:維度,精度 。copy_to組合欄位作用是供用戶查詢(輸入關鍵字可以查詢多個欄位)

創建索引庫,最關鍵的是mapping映射,而mapping映射要考慮的信息包括:

  • 欄位名
  • 欄位數據類型
  • 是否參與搜索
  • 是否需要分詞
  • 如果分詞,分詞器是什麼?

其中:

  • 欄位名、欄位數據類型,可以參考數據表結構的名稱和類型
  • 是否參與搜索要分析業務來判斷,例如圖片地址,就無需參與搜索
  • 是否分詞呢要看內容,內容如果是一個整體就無需分詞,反之則要分詞
  • 分詞器,我們可以統一使用ik_max_word

來看下酒店數據的索引庫結構:

PUT /hotel
{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "name":{
        "type": "text",
        "analyzer": "ik_max_word",
        "copy_to": "all"
      },
      "address":{
        "type": "keyword",
        "index": false
      },
      "price":{
        "type": "integer"
      },
      "score":{
        "type": "integer"
      },
      "brand":{
        "type": "keyword",
        "copy_to": "all"
      },
      "city":{
        "type": "keyword",
        "copy_to": "all"
      },
      "starName":{
        "type": "keyword"
      },
      "business":{
        "type": "keyword"
      },
      "location":{
        "type": "geo_point"
      },
      "pic":{
        "type": "keyword",
        "index": false
      },
      "all":{
        "type": "text",
        "analyzer": "ik_max_word"
      }
    }
  }
}

幾個特殊欄位說明:

  • location:地理坐標,裡面包含精度、緯度
  • all:一個組合欄位,其目的是將多欄位的值 利用copy_to合併,提供給用戶搜索

地理坐標說明:

image

copy_to說明:

image

2.初始化RestClient

在elasticsearch提供的API中,與elasticsearch一切交互都封裝在一個名為RestHighLevelClient的類中,必須先完成這個對象的初始化,建立與elasticsearch的連接。

分為三步:

1)引入es的RestHighLevelClient依賴:

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>

2)因為SpringBoot預設的ES版本是7.6.2,所以我們需要覆蓋預設的ES版本:

<properties>
    <java.version>1.8</java.version>
    <elasticsearch.version>7.12.1</elasticsearch.version>
</properties>

3)初始化RestHighLevelClient:這裡一般在啟動類或者配置類里註入該Bean,用於告訴Java 訪問ES的ip地址

初始化的代碼如下:

@Bean
public RestHighLevelClient client(){
    return new RestHighLevelClient(RestClient.builder(
        HttpHost.create("http://192.168.150.101:9200")
	));
}

這裡為了單元測試方便,我們創建一個測試類HotelIndexTest,然後將初始化的代碼編寫在@BeforeEach方法中:

package cn.itcast.hotel;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;

public class HotelIndexTest {
    private RestHighLevelClient client;

    @BeforeEach
    void setUp() {
        this.client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://192.168.150.101:9200")
        ));
    }

    @AfterEach
    void tearDown() throws IOException {
        this.client.close();
    }
}

3. 索引庫CRUD

3.1 創建索引庫

代碼分為三步:

  • 1)創建Request對象。因為是創建索引庫的操作,因此Request是CreateIndexRequest。
  • 2)添加請求參數,其實就是DSL的JSON參數部分。因為json字元串很長,這裡是定義了靜態字元串常量MAPPING_TEMPLATE,讓代碼看起來更加優雅。
  • 3)發送請求,client.indices()方法的返回值是IndicesClient類型,封裝了所有與索引庫操作有關的方法。

創建索引庫的API如下:

image

代碼:

在hotel-demo的cn.itcast.hotel.constants包下,創建一個類,定義mapping映射的JSON字元串常量:

package cn.itcast.hotel.constants;

public class HotelConstants {
    public static final String MAPPING_TEMPLATE = "{\n" +
            "  \"mappings\": {\n" +
            "    \"properties\": {\n" +
            "      \"id\": {\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"name\":{\n" +
            "        \"type\": \"text\",\n" +
            "        \"analyzer\": \"ik_max_word\",\n" +
            "        \"copy_to\": \"all\"\n" +
            "      },\n" +
            "      \"address\":{\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"index\": false\n" +
            "      },\n" +
            "      \"price\":{\n" +
            "        \"type\": \"integer\"\n" +
            "      },\n" +
            "      \"score\":{\n" +
            "        \"type\": \"integer\"\n" +
            "      },\n" +
            "      \"brand\":{\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"copy_to\": \"all\"\n" +
            "      },\n" +
            "      \"city\":{\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"copy_to\": \"all\"\n" +
            "      },\n" +
            "      \"starName\":{\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"business\":{\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"location\":{\n" +
            "        \"type\": \"geo_point\"\n" +
            "      },\n" +
            "      \"pic\":{\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"index\": false\n" +
            "      },\n" +
            "      \"all\":{\n" +
            "        \"type\": \"text\",\n" +
            "        \"analyzer\": \"ik_max_word\"\n" +
            "      }\n" +
            "    }\n" +
            "  }\n" +
            "}";
}

在hotel-demo中的HotelIndexTest測試類中,編寫單元測試,實現創建索引:

@Test
void createHotelIndex() throws IOException {
    // 1.創建Request對象
    CreateIndexRequest request = new CreateIndexRequest("hotel");
    // 2.準備請求的參數:DSL語句
    request.source(MAPPING_TEMPLATE, XContentType.JSON);
    // 3.發送請求
    client.indices().create(request, RequestOptions.DEFAULT);
}
3.2 刪除索引庫

三步走:

  • 1)創建Request對象。這次是DeleteIndexRequest對象
  • 2)準備參數。這裡是無參
  • 3)發送請求。改用delete方法

刪除索引庫的DSL語句非常簡單:

DELETE /hotel

在hotel-demo中的HotelIndexTest測試類中,編寫單元測試,實現刪除索引:

@Test
void testDeleteHotelIndex() throws IOException {
    // 1.創建Request對象
    DeleteIndexRequest request = new DeleteIndexRequest("hotel");
    // 2.發送請求
    client.indices().delete(request, RequestOptions.DEFAULT);
}
3.3 查詢索引庫

三步走:

  • 1)創建Request對象。這次是GetIndexRequest對象
  • 2)準備參數。這裡是無參
  • 3)發送請求。改用exists方法

判斷索引庫是否存在,本質就是查詢,對應的DSL是:

GET /hotel
@Test
void testExistsHotelIndex() throws IOException {
    // 1.創建Request對象
    GetIndexRequest request = new GetIndexRequest("hotel");
    // 2.發送請求
    boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
    // 3.輸出
    System.err.println(exists ? "索引庫已經存在!" : "索引庫不存在!");
}

API操作文檔

這裡更多的是先讀取Mysql中的數據,然後再存進ES中。

文檔操作的基本步驟:【可以根據發送請求那步的第一個參數,發過來判斷需要創建什麼XXXXRequest】

  • 初始化RestHighLevelClient
  • 創建XxxRequest。XXX是Index、Get、Update、Delete、Bulk
  • 準備參數(Index、Update、Bulk時需要)
  • 發送請求。調用RestHighLevelClient#.xxx()方法,xxx是index、get、update、delete、bulk
  • 解析結果(Get時需要)

1. 初始化RestClient

在elasticsearch提供的API中,與elasticsearch一切交互都封裝在一個名為RestHighLevelClient的類中,必須先完成這個對象的初始化,建立與elasticsearch的連接。

分為三步:

1)引入es的RestHighLevelClient依賴:

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>

2)因為SpringBoot預設的ES版本是7.6.2,所以我們需要覆蓋預設的ES版本:

<properties>
    <java.version>1.8</java.version>
    <elasticsearch.version>7.12.1</elasticsearch.version>
</properties>

3)初始化RestHighLevelClient:這裡一般寫在最前面,用於告訴Java 訪問ES的ip地址

初始化的代碼如下:

RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
        HttpHost.create("http://192.168.150.101:9200")
));

這裡為了單元測試方便,我們創建一個測試類HotelIndexTest,然後將初始化的代碼編寫在@BeforeEach方法中:

package cn.itcast.hotel;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;

public class HotelIndexTest {
    private RestHighLevelClient client;

    @BeforeEach
    void setUp() {
        this.client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://192.168.150.101:9200")
        ));
    }

    @AfterEach
    void tearDown() throws IOException {
        this.client.close();
    }
}

2. 文檔CRUD

2.0 批量導入文檔

三步走:

  • 1)創建Request對象。這裡是BulkRequest
  • 2)準備參數。批處理的參數,就是其它Request對象,這裡就是多個IndexRequest
  • 3)發起請求。這裡是批處理,調用的方法為client.bulk()方法

案例需求:利用BulkRequest批量將資料庫數據導入到索引庫中。

步驟如下:

  • 利用mybatis-plus查詢酒店數據

  • 將查詢到的酒店數據(Hotel)轉換為文檔類型數據(HotelDoc)

  • 利用JavaRestClient中的BulkRequest批處理,實現批量新增文檔

語法說明:

批量處理BulkRequest,其本質就是將多個普通的CRUD請求組合在一起發送。

其中提供了一個add方法,用來添加其他請求:

image

可以看到,能添加的請求包括:

  • IndexRequest,也就是新增
  • UpdateRequest,也就是修改
  • DeleteRequest,也就是刪除

因此Bulk中添加了多個IndexRequest,就是批量新增功能了。示例:

image

我們在導入酒店數據時,將上述代碼改造成for迴圈處理即可。

在hotel-demo的HotelDocumentTest測試類中,編寫單元測試:

@Test
void testBulkRequest() throws IOException {
    // 批量查詢酒店數據
    List<Hotel> hotels = hotelService.list();

    // 1.創建Request
    BulkRequest request = new BulkRequest();
    // 2.準備參數,添加多個新增的Request
    for (Hotel hotel : hotels) {
        // 2.1.轉換為文檔類型HotelDoc
        HotelDoc hotelDoc = new HotelDoc(hotel);
        // 2.2.創建新增文檔的Request對象
        request.add(new IndexRequest("hotel")
                    .id(hotelDoc.getId().toString())
                    .source(JSON.toJSONString(hotelDoc), XContentType.JSON));
    }
    // 3.發送請求
    client.bulk(request, RequestOptions.DEFAULT);
}
2.1 批量新增文檔

四步走:

  • 0)創建索引庫實體類
  • 1)創建Request對象
  • 2)準備請求參數,也就是DSL中的JSON文檔
  • 3)發送請求 (註意:這裡直接使用client.xxx()的API,不再需要client.indices()了)

我們要將資料庫的酒店數據查詢出來,寫入elasticsearch中。

1)創建索引庫實體類

一般實體類里包含經緯度都需要創建一個新的實體類,將經緯度拼成一個欄位

資料庫查詢後的結果是一個Hotel類型的對象。結構如下:

@Data
@TableName("tb_hotel")
public class Hotel {
    @TableId(type = IdType.INPUT)
    private Long id;
    private String name;
    private String address;
    private Integer price;
    private Integer score;
    private String brand;
    private String city;
    private String starName;
    private String business;
    private String longitude;
    private String latitude;
    private String pic;
}

與我們的索引庫結構存在差異:

  • longitude和latitude需要合併為location

因此,我們需要定義一個新的類型,與索引庫結構吻合:

package cn.itcast.hotel.pojo;

import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class HotelDoc {
    private Long id;
    private String name;
    private String address;
    private Integer price;
    private Integer score;
    private String brand;
    private String city;
    private String starName;
    private String business;
    private String location;
    private String pic;

    public HotelDoc(Hotel hotel) {
        this.id = hotel.getId();
        this.name = hotel.getName();
        this.address = hotel.getAddress();
        this.price = hotel.getPrice();
        this.score = hotel.getScore();
        this.brand = hotel.getBrand();
        this.city = hotel.getCity();
        this.starName = hotel.getStarName();
        this.business = hotel.getBusiness();
        this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
        this.pic = hotel.getPic();
    }
}

2)新增代碼

新增文檔的DSL語句如下:

POST /{索引庫名}/_doc/1
{
    "name": "Jack",
    "age": 21
}

對應的java代碼如圖:

image

我們導入酒店數據,基本流程一致,但是需要考慮幾點變化:

  • 酒店數據來自於資料庫,我們需要先查詢出來,得到hotel對象
  • hotel對象需要轉為HotelDoc對象
  • HotelDoc需要序列化為json格式

在hotel-demo的HotelDocumentTest測試類中,編寫單元測試:

@Test
void testAddDocument() throws IOException {
    // 批量查詢酒店數據
    List<Hotel> hotels = hotelService.list();

    // 1.創建Request
    BulkRequest request = new BulkRequest();
    // 2.準備參數,添加多個新增的Request
    for (Hotel hotel : hotels) {
        // 2.1.轉換為文檔類型HotelDoc
        HotelDoc hotelDoc = new HotelDoc(hotel);
        // 2.2.創建新增文檔的Request對象
        request.add(new IndexRequest("hotel")
                    .id(hotelDoc.getId().toString())
                    .source(JSON.toJSONString(hotelDoc), XContentType.JSON));//實體類轉JSON,指定JSON格式
        request.add(new IndexRequest("xxx")...)
    }
    // 3.發送請求
    client.bulk(request, RequestOptions.DEFAULT);
}
2.2 查詢文檔

查詢文檔是根據id查詢的,所以沒有批量查詢

三步走:

  • 1)準備Request對象。這次是查詢,所以是GetRequest
  • 2)發送請求,得到結果。因為是查詢,這裡調用client.get()方法
  • 3)解析結果,就是對JSON做反序列化

查詢的DSL語句如下:

GET /hotel/_doc/{id}

非常簡單,因此代碼大概分兩步:

  • 準備Request對象
  • 發送請求

不過查詢的目的是得到結果,解析為HotelDoc,因此難點是結果的解析。完整代碼如下:

image

可以看到,結果是一個JSON,其中文檔放在一個_source屬性中,因此解析就是拿到_source,使用工具反序列化為Java對象即可。

在hotel-demo的HotelDocumentTest測試類中,編寫單元測試:

@Test
void testGetDocumentById() throws IOException {
    // 1.準備Request
    GetRequest request = new GetRequest("hotel", "61082");
    // 2.發送請求,得到響應
    GetResponse response = client.get(request, RequestOptions.DEFAULT);
    // 3.解析響應結果
    String json = response.getSourceAsString();

    HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
    System.out.println(hotelDoc);
}
2.3 批量刪除文檔

三步走:

  • 1)準備Request對象,因為是刪除,這次是DeleteRequest對象。要指定索引庫名和id
  • 2)準備參數,無參
  • 3)發送請求。因為是刪除,所以是client.delete()方法

刪除的DSL為是這樣的:

DELETE /hotel/_doc/{id}

在hotel-demo的HotelDocumentTest測試類中,編寫單元測試:

@Test
void testDeleteDocument() throws IOException {
    //0.查詢資料庫中的數據
    List<Hotel> list = hotelService.list();
    // 1.創建Request
    BulkRequest request = new BulkRequest();
    //2.批量轉換實體類,順便寫入到ES中
    for (Hotel hotel : list) {
        //2.1轉換實體類
        HotelDoc hotelDoc =new HotelDoc(hotel);
        //2.2寫入ES
        request.add(new DeleteRequest("hotel")
                    .id(hotel.getId().toString()));
    }
    //3.發送請求
    client.bulk(request,RequestOptions.DEFAULT);
}
2.4 批量修改文檔

三步走:

  • 1)準備Request對象。這次是修改,所以是UpdateRequest
  • 2)準備參數。也就是JSON文檔,裡面包含要修改的欄位
  • 3)更新文檔。這裡調用client.update()方法

修改有兩種方式:

  • 全量修改:本質是先根據id刪除,再新增
  • 增量修改:修改文檔中的指定欄位值

在RestClient的API中,全量修改與新增的API完全一致,判斷依據是ID:

  • 如果新增時,ID已經存在,則修改
  • 如果新增時,ID不存在,則新增

只演示增量修改:

代碼示例如圖:

image

在hotel-demo的HotelDocumentTest測試類中,編寫單元測試:

@Test
void testUpdateDocument() throws IOException {
    //0.查詢資料庫中的數據
    List<Hotel> list = hotelService.list();
    // 1.創建Request
    BulkRequest request = new BulkRequest();
    //2.批量轉換實體類,順便寫入到ES中
    for (Hotel hotel : list) {
        //2.1轉換實體類
        HotelDoc hotelDoc =new HotelDoc(hotel);
        //2.2寫入ES
        request.add(new UpdateRequest("hotel",hotel.getId().toString())
                    .doc(
                        "price", "952",
                        "starName", "四鑽"
                    ));
    }
    //3.發送請求
    client.bulk(request,RequestOptions.DEFAULT);
}

ES搜索引擎

elasticsearch的查詢依然是基於JSON風格的DSL來實現的。

1. DSL設置查詢條件

1.1 DSL查詢分類

Elasticsearch提供了基於JSON的DSL(Domain Specific Language)來定義查詢。常見的查詢類型包括:

  • 查詢所有:查詢出所有數據,一般測試用。例如:match_all

  • 全文檢索(full text)查詢:利用分詞器對用戶輸入內容分詞,然後去倒排索引庫中匹配。例如:

    • match_query
    • multi_match_query
  • 精確查詢:根據精確詞條值查找數據,一般是查找keyword、數值、日期、boolean等類型欄位。例如:

    • ids
    • range
    • term
  • 地理(geo)查詢:根據經緯度查詢。例如:

    • geo_distance
    • geo_bounding_box
  • 複合(compound)查詢:複合查詢可以將上述各種查詢條件組合起來,合併查詢條件。例如:

    • bool
    • function_score

查詢的語法基本一致:

GET /indexName/_search
{
  "query": {
    "查詢類型": {
      "查詢條件": "條件值"
    }
  }
}

我們以查詢所有為例,其中:

  • 查詢類型為match_all
  • 沒有查詢條件
// 查詢所有
GET /indexName/_search
{
  "query": {
    "match_all": {
    }
  }
}

其它查詢無非就是查詢類型查詢條件的變化。

1.2 全文檢索查詢

match和multi_match的區別是什麼?

  • match:根據一個欄位查詢【推薦:使用copy_to構造all欄位】
  • multi_match:根據多個欄位查詢,參與查詢欄位越多,查詢性能越差

註:搜索欄位越多,對查詢性能影響越大,因此建議採用copy_to,然後單欄位查詢的方式。

1.2.1 使用場景

全文檢索查詢的基本流程如下:

  • 對用戶搜索的內容做分詞,得到詞條
  • 根據詞條去倒排索引庫中匹配,得到文檔id
  • 根據文檔id找到文檔,返回給用戶

比較常用的場景包括:

  • 商城的輸入框搜索
  • 百度輸入框搜索

例如京東:

image

因為是拿著詞條去匹配,因此參與搜索的欄位也必須是可分詞的text類型的欄位。

常見的全文檢索查詢包括:

  • match查詢:單欄位查詢
  • multi_match查詢:多欄位查詢,任意一個欄位符合條件就算符合查詢條件
1.2.2 match查詢

match查詢語法如下:

GET /indexName/_search
{
  "query": {
    "match": {
      "FIELD": "TEXT"
    }
  }
}

match查詢示例:

image

1.2.3 mulit_match查詢

mulit_match語法如下:

GET /indexName/_search
{
  "query": {
    "multi_match": {
      "query": "TEXT",
      "fields": ["FIELD1", " FIELD12"]
    }
  }
}

multi_match查詢示例:

image

1.3 精準查詢

精準查詢類型:

  • term查詢:根據詞條精確匹配,一般搜索keyword類型、數值類型、布爾類型、日期類型欄位
  • range查詢:根據數值範圍查詢,可以是數值、日期的範圍

精確查詢一般是查找keyword、數值、日期、boolean等類型欄位。所以不會對搜索條件分詞。常見的有:

  • term:根據詞條精確值查詢
  • range:根據值的範圍查詢
1.3.1 term查詢

因為精確查詢的欄位搜時不分詞的欄位,因此查詢的條件也必須是不分詞的詞條。查詢時,用戶輸入的內容跟自動值完全匹配時才認為符合條件。如果用戶輸入的內容過多,反而搜索不到數據。

語法說明:

// term查詢
GET /indexName/_search
{
  "query": {
    "term": {
      "FIELD": {
        "value": "VALUE"
      }
    }
  }
}

示例:

當我搜索的是精確詞條時,能正確查詢出結果:

image

但是,當我搜索的內容不是詞條,而是多個詞語形成的短語時,反而搜索不到:

image

1.3.2 range查詢

範圍查詢,一般應用在對數值類型做範圍過濾的時候。比如做價格範圍過濾。

基本語法:

// range查詢
GET /indexName/_search
{
  "query": {
    "range": {
      "FIELD": {
        "gte": 10, // 這裡的gte代表大於等於,gt則代表大於
        "lte": 20 // lte代表小於等於,lt則代表小於
      }
    }
  }
}

示例:

image

1.4 地理坐標查詢

所謂的地理坐

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • REST和SpringMVC映射請求數據 7.REST-優雅的url請求風格 7.1REST基本介紹 REST風格詳細介紹 REST:即 Representational State Transfer,表述性狀態傳遞。它結構清晰,同時可以隱藏行為。 通過一個url來直觀展示傳統風格與REST風格的區 ...
  • @RequestMapping 1.基本使用 @RequestMapping註解可以指定 控制器(處理器) 的某個方法的請求url 2.@RequestMapping其他使用方式 2.1修飾方法和類 @RequestMapping註解可以修飾方法,還可以修飾類。 當同時修飾類和方法時,請求的url就 ...
  • 面向對象 面向對象:以類的方式組織代碼,以對象組織數據 特性: 封裝 繼承 多態 類:抽象概念 對象:具體事物 面向對象是java學習的重中之重,畢竟java就是一個面向對象的語言~ 類 = 屬性+方法 面向對象的概念適合複雜系統、多人協作 從巨集觀上來說,java是面向對象的,但在微觀上是面向過程的 ...
  • 一、RabbitMQ是什麼 RabbitMQ是一種常用的消息中間件,是基於AMQP協議,採用erlang語言開發的面向消息服務的中間件,是一個獨立的系統應用程式,可以管理伺服器計算資源和網路通信。一般可作為同構或異構系統間的數據交換平臺,由於erlang語言的高併發特性,使得RabbitMQ的性能較 ...
  • ​ ​編輯 列表和元組 list 是一種有序、可變的數據類型,可添加刪除其中的元素。 len()函數:可以獲取列表元素的個數 classmates = ['Micheal' , 'Bob' , 'James'] print(classmates) print(len(classmates)) Ter ...
  • 突發奇想,爬取p站圖片做個壁紙圖庫(bukemiaoshu),當然這裡有許多的門檻,但是為了實現理想,暫時沒想那麼多了,直接開乾(不是專業做測試和自動化的,如有大佬請評論指教!!!) 1.進入登錄頁由於p站是需要登錄的,聽說p站反爬,requests應該不是那麼好使,於是使用selenium模擬人工 ...
  • 這篇文章主要描述分散式系統的發展歷程和分散式系統的衡量指標。發展歷程包括了單機模式、數據並行模式和任務並行模式。分散式系統的衡量指標包括性能、資源占用、可用性和可擴展性。 ...
  • Java JDK1.5: 泛型 新特性的講解說明 每博一文案 聽到過這樣一句話:“三觀沒有標準。在烏鴉的世界里,天鵝也有罪。” 環境、閱歷的不同,造就了每個人獨有的世界觀、人生觀、價值觀。 三觀並無對錯高下,只有同與不同。恰如飛鳥不用和游魚同行,高山不必同流水相逢。 總用自己的尺子去度量別人,無疑是 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...