一、安裝Elasticsearch相關(guān)插件
為了避免使用的Elasticsearch版本和SpringBoot采用的版本不一致導(dǎo)致的問題,盡量使用一致的版本。下表是對應(yīng)關(guān)系:

安裝Elasticsearch
Elasticsearch官網(wǎng),點(diǎn)擊跳轉(zhuǎn)即可
1、下載上面鏈接的安裝包
2、解壓到任意目錄
3、啟動es /bin/elasticsearch.bat
4、查看安裝結(jié)果,在網(wǎng)頁輸入localhost:9200,出現(xiàn)下圖即為成功

接下來我們要通過修改Elasticsearch安裝目錄下的/config/elasticsearch.yml處理一些問題
解決IP無法進(jìn)行訪問
#增加IP訪問配置
network.bind_host: 0.0.0.0
#注釋此配置
#cluster.initial_master_nodes: ["node-1", "node-2"]
#修改為單節(jié)點(diǎn)配置
cluster.initial_master_nodes: ["node-1"]
解決跨域問題
由于前后端分離開發(fā),所以會存在跨域問題,需要在服務(wù)端做CORS的配置。
#開啟跨域支持
http.cors.enabled: true
#允許所有人跨域訪問
http.cors.allow-origin: "*"
#瀏覽器允許在跨域請求中發(fā)送憑證(如 cookies、HTTP 認(rèn)證信息等)
http.cors.allow-credentials: true
#允許特定的請求頭
http.cors.allow-headers: Authorization,X-Requested-With,Content-Length,Content-Type
開啟集群身份認(rèn)證與用戶鑒權(quán)和集群內(nèi)部安全通信
# 集群身份認(rèn)證與用戶鑒權(quán)
xpack.security.enabled: true
# 集群內(nèi)部安全通信
xpack.license.self_generated.type: basic
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.keystore.path: D:\elasticsearch\elasticsearch-7.6.2\config\elastic-stack-ca.p12
xpack.security.transport.ssl.truststore.path: D:\elasticsearch\elasticsearch-7.6.2\config\elastic-stack-ca.p12
重啟Elasticsearch服務(wù)以使更改生效
Elasticsearch 的默認(rèn)賬戶為 elastic 默認(rèn)密碼為 changme
一、通過Kibana更改密碼
1、登錄到Kibana。
2、導(dǎo)航到“Management”(管理)> “Security”(安全)> “Users”(用戶)。
3、找到你想要修改密碼的用戶,點(diǎn)擊“Edit”(編輯)。
4、在“Change Password”(更改密碼)部分,輸入新密碼,然后點(diǎn)擊“Update”(更新)。
二、通過Elasticsearch REST API更改密碼
curl -u elastic:your_current_password -X POST "localhost:9200/_security/user/username/_password" -H 'Content-Type: application/json' -d'
{
"password" : "new_password"
}
替換your_current_password、username和new_password為實(shí)際的當(dāng)前密碼、用戶名和新密碼。
安裝es-head插件,方便查看ES中的索引及數(shù)據(jù)
安裝es-head插件需要把node和grunt配置好
node下載地址下載對應(yīng)環(huán)境的node版本安裝即可。
安裝過程結(jié)束后,在dos窗口查看是否安裝成功,使用命令:node -v,出現(xiàn)如下截圖,則說明安裝成功

在node安裝路徑下,使用命令安裝:npm install -g grunt-cli 安裝grunt。 安裝結(jié)束后,使用命令grunt-version查看是否安裝成功,出現(xiàn)如下截圖,說明安裝成功。

es-head下載地址
解壓elasticsearch-head-master
在該目錄下進(jìn)入cmd命令,執(zhí)行npm install

執(zhí)行完成后運(yùn)行命令 grunt server,
grunt server是啟動命令
如果出現(xiàn)報錯

則使用 cmd繼續(xù)執(zhí)行npm install grunt --save-dev。 這將向package.json添加最新版本。

如果不報錯,則命令窗顯示

瀏覽器輸入127.0.0.1:9100

安裝kibana,用途:便于通過rest api調(diào)試ES。
kibana官方下載地址
1、解壓
2、修改 kibana-7.12.0-windows-x86_64/config/kibana.yml 32行
3、改為elasticsearch.hosts: [“http://127.0.0.1:9200”]
4、保存之后,運(yùn)行bin/kibana.bat
5、瀏覽器中訪問kibana開發(fā)工具:http://localhost:5601/app/kibana#/dev_tools/console

如果想使用ip訪問kibana,需要修改 kibana-7.12.0-windows-x86_64/config/kibana.yml 7行
改為 server.host: "0.0.0.0"
如果想使用kibana漢化 需要修改 kibana-7.12.0-windows-x86_64/config/kibana.yml 最后一行
i18n.locale: "zh-CN"
安裝ik分詞器,注意:下載的ik分詞器版本號要和安裝的elasticsearch版本一致
把下載的ik分詞器解壓至Elasticsearch的安裝目錄/plugins/ik內(nèi)。


- 測試ik分詞器
- 重啟elasticsearch
- 重啟kibana
- 進(jìn)入kibana的開發(fā)工具中執(zhí)行命令測試 開發(fā)工具
ik_max_word 和 ik_smart 是 IK 分詞器的兩種分詞模式,主要用于中文分詞。以下是它們的區(qū)別:
-
ik_max_word- 特點(diǎn): 會將文本進(jìn)行最細(xì)粒度的拆分,盡可能多地拆出詞語。
- 適用場景: 當(dāng)需要盡可能多的關(guān)鍵詞匹配時使用,例如搜索引擎中的寬泛查詢。
-
示例: 對于字符串“中華人民共和國國歌”,
ik_max_word會拆分為:["中華人民共和國", "中華人民", "中華", "華人", "人民共和國", "人民", "共和國", "共和", "國", "國歌"]。
image.png
-
ik_smart- 特點(diǎn): 會以更智能的方式對文本進(jìn)行拆分,盡量減少冗余詞語,保留語義完整。
- 適用場景: 當(dāng)需要更精確的分詞結(jié)果時使用,適合對查詢性能要求較高的場景。
-
示例: 對于字符串“中華人民共和國國歌”,
ik_smart會拆分為:["中華人民共和國", "國歌"]。
image.png
我個人建議是:
-
analyzer = "ik_max_word"表示在索引階段使用ik_max_word模式進(jìn)行分詞。 -
searchAnalyzer = "ik_smart"表示在搜索階段使用ik_smart模式進(jìn)行分詞。
這種配置可以讓索引階段盡可能多地提取關(guān)鍵詞,而在搜索階段提供更精準(zhǔn)的匹配結(jié)果。
二、整合SpringBoot和Elasticearch
引入依賴
<!-- Spring Boot 對 Elasticsearch 的啟動器,提供在 Spring Boot 應(yīng)用中方便地使用 Elasticsearch 的方式。 -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
yml文件增加配置
spring:
# elasticsearch配置
elasticsearch:
rest:
# 用戶名
username: elastic
# 密碼
password: 123456
# 服務(wù)器地址,支持多個uri,用','隔開
uris: localhost:9200
# 連接超時時間
connection-timeout: 10s
# 讀取超時時間
read-timeout: 30s
# 最大連接數(shù)
max-connect-requests: 10
# 空閑連接的最大存活時間
idle-timeout: 60s
# 是否啟用自動重試
retry-on-failure: true
# 重試次數(shù)
max-retry-timeout: 3
BaseEntity實(shí)體類
package com.point.pack.entity.base;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.point.pack.constant.DateFormatConstant;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* Entity基類,提供了實(shí)體對象的基本屬性,如主鍵、創(chuàng)建時間和更新時間。
* 這是一個抽象類,用于定義所有實(shí)體對象共有的屬性和行為。
*
* @param <ID> 實(shí)體的主鍵類型,繼承自Serializable,允許不同的實(shí)體使用不同的主鍵類型。
* @return 無返回值
* @date 2020.03.27
*/
@Data
public abstract class BaseEntity<ID extends Serializable> implements Serializable {
@Id
@ApiModelProperty(value = "主鍵")
// MyBatis-Plus 注解,適用于 MySQL
@TableId(type = IdType.ASSIGN_ID)
// Spring Data Elasticsearch 注解,適用于 ES
@Field(name = BaseIndexField.ID, type = FieldType.Keyword)
@JsonSerialize(using = ToStringSerializer.class)
private ID id;
@ApiModelProperty(value = "創(chuàng)建時間")
// MyBatis-Plus 注解,適用于 MySQL
@TableField(fill = FieldFill.INSERT)
// Spring Data Elasticsearch 注解,適用于 ES
@Field(name = BaseIndexField.CREATE_TIME, type = FieldType.Date, format = DateFormat.date_time)
@DateTimeFormat(pattern = DateFormatConstant.DEFAULT_DATETIME_FORMAT)
@JsonFormat(pattern = DateFormatConstant.DEFAULT_DATETIME_FORMAT, timezone = "GMT+8")
private LocalDateTime createTime;
@ApiModelProperty(value = "更新時間")
// MyBatis-Plus 注解,適用于 MySQL
@TableField(fill = FieldFill.INSERT_UPDATE)
// Spring Data Elasticsearch 注解,適用于 ES
@Field(name = BaseIndexField.UPDATE_TIME, type = FieldType.Date, format = DateFormat.date_time)
@DateTimeFormat(pattern = DateFormatConstant.DEFAULT_DATETIME_FORMAT)
@JsonFormat(pattern = DateFormatConstant.DEFAULT_DATETIME_FORMAT, timezone = "GMT+8")
private LocalDateTime updateTime;
}
FileInfo實(shí)體類
package com.point.pack.entity.common;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.point.pack.entity.base.BaseEntity;
import com.point.pack.entity.es.FileInfoIndexField;
import com.point.pack.enums.StorageType;
import com.point.pack.enums.YesNo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName = FileInfoIndexField.INDEX_NAME)
@ApiModel(value = "FileInfo", description = "文件表")
public class FileInfo extends BaseEntity<Long> {
private static final long serialVersionUID = 1L; // 序列化ID,用于版本控制
@ApiModelProperty(value = "文件名稱")
@Field(name = FileInfoIndexField.FILE_NAME, analyzer = "ik_max_word", searchAnalyzer = "ik_smart", type = FieldType.Text)
private String fileName;
@ApiModelProperty(value = "文件字節(jié)大小")
@Field(name = FileInfoIndexField.FILE_SIZE, type = FieldType.Long)
@JsonSerialize(using = ToStringSerializer.class)
private Long fileSize;
@ApiModelProperty(value = "文件類型,如image/jpeg")
@Field(name = FileInfoIndexField.FILE_TYPE, type = FieldType.Keyword)
private String fileType;
@ApiModelProperty(value = "文件存儲路徑")
@Field(name = FileInfoIndexField.FILE_PATH, type = FieldType.Keyword)
private String fileSavePath;
@ApiModelProperty(value = "文件訪問路徑")
@Field(name = FileInfoIndexField.FILE_URL, type = FieldType.Keyword)
private String fileAccessUrl;
@ApiModelProperty(value = "存儲類型")
@Field(name = FileInfoIndexField.STORAGE_TYPE, type = FieldType.Keyword)
private StorageType storageType;
@ApiModelProperty(value = "備注")
@Field(name = FileInfoIndexField.REMARK, type = FieldType.Keyword)
private String remark;
@ApiModelProperty(value = "是否已刪除")
@Field(name = FileInfoIndexField.IS_DELETE, type = FieldType.Keyword)
private Integer isDelete = YesNo.NO.getValue();
}
FileInfoRepository類,這個非常重要
Spring Data Elasticsearch只有在應(yīng)用程序啟動時,帶有索引實(shí)體的ElasticsearchRepository發(fā)現(xiàn)該索引不存在時,才會自動寫入該索引的映射。
否則,不會對索引自動執(zhí)行任何操作。映射不會被重寫。因此,如果向?qū)嶓w添加某些屬性,即使實(shí)體上有注釋,也不會創(chuàng)建新映射。
您可以做的是,在添加屬性之后,在使用這個新屬性插入新數(shù)據(jù)之前,使用IndexOperations.putMapping()方法編寫更新的映射。
- 重要提示:只能向索引添加新屬性/字段,不能刪除或更新現(xiàn)有屬性/字段;這是Elasticsearch的限制。
package com.point.user.service.repository;
import com.point.pack.entity.common.FileInfo;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface FileInfoRepository extends ElasticsearchRepository<FileInfo, Long> {
}
FileInfoController類
package com.point.user.controller.web;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.point.common.annotation.ResponseResult;
import com.point.pack.entity.common.FileInfo;
import com.point.pack.exception.CustomException;
import com.point.user.dto.IdDTO;
import com.point.user.dto.IdsDTO;
import com.point.user.dto.fileInfo.FileInfoAddDTO;
import com.point.user.dto.fileInfo.FileInfoQueryPageDTO;
import com.point.user.dto.fileInfo.FileInfoUpdateDTO;
import com.point.user.service.FileInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@ResponseResult
@RequestMapping("/web/fileInfo")
@Api(value = "文件模塊相關(guān)接口", tags = "文件模塊相關(guān)接口")
public class FileInfoController {
@Resource
private FileInfoService fileInfoService;
@PostMapping("/queryPage")
@ApiOperation(value = "分頁查詢文件", notes = "分頁查詢文件")
public Page<FileInfo> queryPage(@Validated @RequestBody FileInfoQueryPageDTO condition) throws CustomException {
return fileInfoService.queryPage(condition);
}
@PostMapping("/add")
@ApiOperation(value = "添加文件", notes = "添加文件")
public void add(@Validated @RequestBody FileInfoAddDTO condition) throws CustomException {
fileInfoService.add(condition);
}
@PostMapping("/detail")
@ApiOperation(value = "查詢文件詳情", notes = "查詢文件詳情")
public FileInfo detail(@Validated @RequestBody IdDTO condition) throws CustomException {
return fileInfoService.detail(condition);
}
@PostMapping("/update")
@ApiOperation(value = "編輯文件", notes = "編輯文件")
public void update(@Validated @RequestBody FileInfoUpdateDTO condition) throws CustomException {
fileInfoService.update(condition);
}
@PostMapping("/deleteByIds")
@ApiOperation(value = "批量刪除文件", notes = "批量刪除文件")
public void deleteByIdList(@Validated @RequestBody IdsDTO condition) throws CustomException {
fileInfoService.deleteByIdList(condition);
}
}
FileInfoService類
package com.point.user.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.point.pack.entity.common.FileInfo;
import com.point.pack.exception.CustomException;
import com.point.user.dto.IdDTO;
import com.point.user.dto.IdsDTO;
import com.point.user.dto.fileInfo.FileInfoAddDTO;
import com.point.user.dto.fileInfo.FileInfoQueryPageDTO;
import com.point.user.dto.fileInfo.FileInfoUpdateDTO;
public interface FileInfoService {
/**
* 分頁查詢文件
*
* @param condition 分頁查詢條件,包含需要查詢的文件和分頁參數(shù)
* @return 返回查詢到的文件分頁對象
* @throws CustomException 當(dāng)查詢過程中發(fā)生錯誤時拋出自定義異常
*/
Page<FileInfo> queryPage(FileInfoQueryPageDTO condition) throws CustomException;
/**
* 添加文件
*
* @param condition 添加文件的DTO,包含所需信息
* @throws CustomException 添加過程中可能出現(xiàn)的自定義異常
*/
Long add(FileInfoAddDTO condition) throws CustomException;
/**
* 查詢文件詳情
*
* @param condition 文件詳情查詢條件,包含用于篩選查詢結(jié)果的具體參數(shù)
* @return 返回一個FileInfo對象,該對象包含查詢到的文件詳情
*/
FileInfo detail(IdDTO condition) throws CustomException;
/**
* 編輯文件
*
* @param condition 更新文件的DTO,包含所需信息
* @throws CustomException 更新過程中可能出現(xiàn)的自定義異常
*/
void update(FileInfoUpdateDTO condition) throws CustomException;
/**
* 批量刪除文件
*
* @param condition 包含待刪除文件項(xiàng)ID列表的DTO對象,用于指定刪除條件
* @throws CustomException 當(dāng)刪除操作失敗時拋出自定義異常,以便調(diào)用者處理錯誤情況
*/
void deleteByIdList(IdsDTO condition) throws CustomException;
}
FileInfoServiceImpl
package com.point.user.service.impl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.point.pack.entity.common.FileInfo;
import com.point.pack.entity.es.FileInfoIndexField;
import com.point.pack.enums.YesNo;
import com.point.pack.exception.CustomException;
import com.point.user.dto.IdDTO;
import com.point.user.dto.IdsDTO;
import com.point.user.dto.fileInfo.FileInfoAddDTO;
import com.point.user.dto.fileInfo.FileInfoQueryPageDTO;
import com.point.user.dto.fileInfo.FileInfoUpdateDTO;
import com.point.user.service.FileInfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.IdsQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
@Slf4j
@Service
public class FileInfoServiceImpl implements FileInfoService {
@Resource
private ElasticsearchRestTemplate elasticsearchRestTemplate;
/**
* 分頁查詢文件
*
* @param condition 分頁查詢條件,包含需要查詢的文件和分頁參數(shù)
* @return 返回查詢到的文件分頁對象
* @throws CustomException 當(dāng)查詢過程中發(fā)生錯誤時拋出自定義異常
*/
@Override
public Page<FileInfo> queryPage(FileInfoQueryPageDTO condition) throws CustomException {
// 構(gòu)造查詢條件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
queryBuilder.withFilter(QueryBuilders.termQuery(FileInfoIndexField.IS_DELETE, YesNo.NO.getValue()));
if (StringUtils.isNotBlank(condition.getFileName())) {
queryBuilder.withFilter(QueryBuilders.matchQuery(FileInfoIndexField.FILE_NAME, condition.getFileName()));
}
if (StringUtils.isNotBlank(condition.getFileType())) {
queryBuilder.withFilter(QueryBuilders.matchQuery(FileInfoIndexField.FILE_TYPE, condition.getFileType()));
}
if (condition.getStorageType() != null) {
queryBuilder.withFilter(QueryBuilders.matchQuery(FileInfoIndexField.STORAGE_TYPE, condition.getStorageType()));
}
// 設(shè)置分頁
Page<FileInfo> page = condition.getPage();
int current = (int) (page.getCurrent() - 1); // 顯式轉(zhuǎn)換為int
int size = (int) page.getSize(); // 顯式轉(zhuǎn)換為int
Pageable pageable = PageRequest.of(current, size);
queryBuilder.withPageable(pageable);
// 設(shè)置排序
queryBuilder.withSort(SortBuilders.fieldSort(FileInfoIndexField.ID).order(SortOrder.DESC));
NativeSearchQuery query = queryBuilder.build();
// 執(zhí)行查詢
SearchHits<FileInfo> searchHits = elasticsearchRestTemplate.search(query, FileInfo.class);
//設(shè)置總數(shù)
page.setTotal(searchHits.getTotalHits());
// 返回的行數(shù)
List<SearchHit<FileInfo>> searchHitList = searchHits.getSearchHits();
if (CollectionUtils.isNotEmpty(searchHitList)) {
List<FileInfo> records = new ArrayList<>();
for (SearchHit<FileInfo> searchHit : searchHits) {
FileInfo fileInfo = searchHit.getContent();
records.add(fileInfo);
}
page.setRecords(records);
}
return page;
}
/**
* 添加文件
*
* @param condition 包含新文件的數(shù)據(jù)傳輸對象
* @throws CustomException 如果添加過程中出現(xiàn)異常
*/
@Override
public Long add(FileInfoAddDTO condition) throws CustomException {
FileInfo fileInfo = new FileInfo();
fileInfo.setId(Math.abs(UUID.randomUUID().getMostSignificantBits()));
BeanUtils.copyProperties(condition, fileInfo);
elasticsearchRestTemplate.save(fileInfo);
return fileInfo.getId();
}
/**
* 查詢文件詳情
*
* @param condition 文件詳情查詢條件,包含用于篩選查詢結(jié)果的具體參數(shù)
* @return 返回一個FileInfo對象,該對象包含查詢到的文件詳情
*/
@Override
public FileInfo detail(IdDTO condition) throws CustomException {
// 根據(jù)ID從Elasticsearch中查詢文件
String id = String.valueOf(condition.getId());
FileInfo fileInfo = elasticsearchRestTemplate.get(id, FileInfo.class);
if (fileInfo == null) {
throw new CustomException("文件不存在");
}
if (Objects.equals(fileInfo.getIsDelete(), YesNo.YES.getValue())) {
throw new CustomException("文件已刪除");
}
return fileInfo;
}
/**
* 編輯文件
*
* @param condition 更新文件的DTO,包含所需信息
* @throws CustomException 更新過程中可能出現(xiàn)的自定義異常
*/
@Override
public void update(FileInfoUpdateDTO condition) throws CustomException {
// 獲取要編輯的文件
FileInfo updateFileInfo = elasticsearchRestTemplate.get(String.valueOf(condition.getId()), FileInfo.class);
if (updateFileInfo == null) {
throw new CustomException("文件不存在");
}
if (!Objects.equals(condition.getFileName(), updateFileInfo.getFileName()) || !Objects.equals(condition.getRemark(), updateFileInfo.getRemark())) {
// 將更新后的信息復(fù)制到原字典類型對象并保存
BeanUtils.copyProperties(condition, updateFileInfo);
elasticsearchRestTemplate.save(updateFileInfo);
}
}
/**
* 批量刪除文件
*
* @param condition 包含待刪除文件項(xiàng)ID列表的DTO對象,用于指定刪除條件
* @throws CustomException 當(dāng)刪除操作失敗時拋出自定義異常,以便調(diào)用者處理錯誤情況
*/
@Override
public void deleteByIdList(IdsDTO condition) throws CustomException {
// 構(gòu)造查詢條件
IdsQueryBuilder idsQueryBuilder = QueryBuilders.idsQuery();
for (Long id : condition.getIds()) {
idsQueryBuilder.addIds(String.valueOf(id));
}
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(idsQueryBuilder)
.build();
// 執(zhí)行查詢
SearchHits<FileInfo> searchHits = elasticsearchRestTemplate.search(query, FileInfo.class);
List<FileInfo> fileInfos = new ArrayList<>();
for (SearchHit<FileInfo> searchHit : searchHits) {
fileInfos.add(searchHit.getContent());
}
if (CollectionUtils.isEmpty(fileInfos)) {
return;
}
for (FileInfo fileInfo : fileInfos) {
fileInfo.setIsDelete(YesNo.YES.getValue());
}
elasticsearchRestTemplate.save(fileInfos);
}
}
Elasticsearch(ES)雖然是一款強(qiáng)大的分布式搜索和分析引擎,但它并不是為所有類型的數(shù)據(jù)存儲和管理場景設(shè)計(jì)的。以下是Elasticsearch不適合做數(shù)據(jù)存儲的一些原因:
1、事務(wù)支持不足
Elasticsearch 不支持傳統(tǒng)關(guān)系型數(shù)據(jù)庫中的 ACID 事務(wù)(Atomicity, Consistency, Isolation, Durability)。
它更適合最終一致性模型,而不是強(qiáng)一致性需求的場景。
2、高存儲成本
Elasticsearch 的倒排索引和分片機(jī)制會導(dǎo)致數(shù)據(jù)占用更多的存儲空間。
對于大規(guī)模寫入和存儲密集型的應(yīng)用,存儲成本會顯著增加。
3、復(fù)雜查詢性能問題
雖然 ES 在全文搜索和聚合查詢上表現(xiàn)優(yōu)異,但對于復(fù)雜的多表聯(lián)結(jié)(JOIN)或深度嵌套查詢,性能較差。
這些操作可能會導(dǎo)致資源消耗過高,影響整體系統(tǒng)穩(wěn)定性。
4、數(shù)據(jù)更新開銷大
Elasticsearch 是基于 LSM 樹(Log-Structured Merge Tree)的實(shí)現(xiàn),頻繁的更新操作會導(dǎo)致段合并(Segment Merging)壓力增大。
段合并會影響寫入性能,并可能引發(fā)磁盤 I/O 瓶頸。
5、缺乏嚴(yán)格的權(quán)限控制
默認(rèn)情況下,Elasticsearch 的安全性和權(quán)限控制功能較弱(需要額外購買 X-Pack 插件)。
對于敏感數(shù)據(jù)存儲,可能存在安全隱患。
6、不適用于結(jié)構(gòu)化數(shù)據(jù)
如果數(shù)據(jù)是高度結(jié)構(gòu)化的(如金融交易記錄、用戶信息等),關(guān)系型數(shù)據(jù)庫(如 MySQL、PostgreSQL)更為合適。
ES 更適合非結(jié)構(gòu)化或半結(jié)構(gòu)化數(shù)據(jù)(如日志、文檔、媒體元數(shù)據(jù))。
7、實(shí)時性限制
Elasticsearch 的近實(shí)時(Near Real-Time, NRT)特性意味著數(shù)據(jù)從寫入到可搜索之間存在延遲(通常為秒級)。
對于要求毫秒級實(shí)時性的場景,ES 可能無法滿足需求。
8、維護(hù)復(fù)雜度高
集群管理和調(diào)優(yōu)需要較高的技術(shù)門檻,包括分片分配、副本管理、內(nèi)存優(yōu)化等。
對于小型團(tuán)隊(duì)或資源有限的項(xiàng)目,維護(hù)成本較高。
總結(jié)
Elasticsearch 最適合用于全文搜索、日志分析、監(jiān)控數(shù)據(jù)處理等場景,而對于需要強(qiáng)一致性、復(fù)雜事務(wù)、嚴(yán)格權(quán)限控制或低成本存儲的場景,建議選擇其他更合適的數(shù)據(jù)庫解決方案(如關(guān)系型數(shù)據(jù)庫或?qū)iT的 NoSQL 數(shù)據(jù)庫)。

