1.Elasticsearch 安装
1.1 下载软件
- Elasticsearch 的官方地址:https://www.elastic.co/cn/
- 这里选择下载 7.8.0 版本
- 下载地址:https://www.elastic.co/cn/downloads/past-releases#elasticsearch
Elasticsearch 分为 Linux 和 Windows 版本,这里我们只是做测试,因此下载 windows 版本即可。
1.2 安装软件
Windows 版的 Elasticsearch 的安装很简单,解压即安装完毕,解压后的 Elasticsearch 的目录结构如下:
目录 | 含义 |
---|---|
bin | 可执行脚本目录 |
config | 配置目录 |
jdk | 内置JDK目录 |
lib | 类库 |
logs | 日志目录 |
modules | 模块目录 |
plugins | 插件目录 |
1.3 安装 ik 中文分词器
🏠 下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.8.0
ik_max_word
:会将文本做最细粒度的拆分ik_smart
:会将文本做最粗粒度的拆分
将解压后的后的文件夹放入 ES
根目录下的 plugins
目录下。
1.4 启动ES
解压后,进入 bin 文件目录,点击 elasticsearch.bat 文件启动 ES 服务:
⚠️ 注意:
9300
端口为 Elasticsearch 集群间组件的通信端口。9200
端口为浏览器访问的 http 协议 RESTful 端口。
💻 浏览器访问地址:http://localhost:9200
1.5 问题解决
-
Elasticsearch 是使用 java 开发的,且 7.8 版本的 ES 需要 JDK 版本 1.8 以上,默认安装包带有 jdk 环境,如果系统配置 JAVA_HOME,那么使用系统默认的 JDK,如果没有配置使用自带的 JDK,一般建议使用系统配置的 JDK。
-
双击启动窗口闪退,通过路径访问追踪错误,如果是“空间不足”,请修改
config/jvm.options
配置文件# 设置 JVM 初始内存为 1G。此值可以设置与-Xmx 相同,以避免每次垃圾回收完成后 JVM 重新分配内存 # Xms represents the initial size of total heap space # 设置 JVM 最大可用内存为 1G # Xmx represents the maximum size of total heap space -Xms1g -Xmx1g
2.Kibana 安装 & 使用
Elasticsearch 的开源分析可视化工具,与存储在 Elasticsearch 中的数据进行交互。
2.1 下载软件
Elasticsearch 下载的版本是 7.8.0,这里我们选择同样的 7.8.0 版本
下载地址:https://www.elastic.co/cn/downloads/past-releases#kibana
2.2 安装软件
解压即安装完毕,解压后的 Kibana 的目录结构如下:
修改配置文件:kibana.yml
添加如下信息:
# 服务端口
server.port: 5601
# 国际化 - 中文
i18n.locale: "zh-CN"
# ES 服务主机地址
elasticsearch.hosts: ["http://localhost:9200"]
server.ssl.enabled: false
2.3 启动 Kibana
访问浏览器:http://localhost:5601/
3.实战开发-前提准备
3.1 开发背景
假设有一个博客模块,即有一个博客圈,用户可以通过搜索来获取与自己输入的关键字相关的数据列表,类似于CSDN的搜索。
由于只是 demo,因此这里会设计的很简单。
- 不支持通过作者进行搜索。
3.2 数据库表设计
# 如果存在 es_demo 数据库则删除
DROP DATABASE IF EXISTS `es_demo`;
# 创建新数据库
CREATE DATABASE `es_demo`;
# 创建一张博客表
CREATE TABLE `blog`(
`id` INT NOT NULL AUTO_INCREMENT COMMENT '主键id',
`user_id` BIGINT NOT NULL COMMENT '用户id(雪花算法生成)',
`title` VARCHAR(255) NOT NULL COMMENT '标题',
`tags` VARCHAR(64) NOT NULL COMMENT '标签',
`introduce` VARCHAR(512) NOT NULL COMMENT '介绍',
`content` TEXT NOT NULL COMMENT '文章内容',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY(`id`),
KEY `idx_user_create`(`user_id`,`create_time` DESC)
)ENGINE=INNODB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='博客信息表';
3.3 创建es索引与映射
PUT /blog
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"userId": {
"type": "long"
},
"title": {
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart",
"type": "text"
},
"tags": {
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart",
"type": "text"
},
"introduce":{
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart",
"type": "text"
},
"content":{
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart",
"type": "text"
},
"createTime":{
"format": "yyyy-MM-dd HH:mm:ss",
"type": "date"
},
"updateTime":{
"format": "yyyy-MM-dd HH:mm:ss",
"type": "date"
}
}
}
}
3.4 es文档数据
其实按照正常的逻辑,应当在往数据库表添加记录后也写入到es中,可以使用java代码实现。但这里为了简单起见,我们直接往es中添加数据进行测试。
POST /blog/_doc/1000
{
"id":1000,
"userId":1626989073847750657,
"title":"Java语言",
"introduce":"Java的起源",
"content":"Java最初是由任职于太阳微系统的詹姆斯-高斯林(James-Gosling)等人于1990年代初开发。最初被命名为Oak;当时发现Oak被其他公司注册了,不得不重新起名,当时他们正在咖啡馆喝着印尼爪哇(JAVA)岛出产的咖啡,有人提议就叫JAVA怎么样,然后就这样定下来了。",
"tags":"编程,java,语言",
"createTime":"2023-03-23 00:40:20",
"updateTIme":"2023-03-23 00:40:20"
}
POST /blog/_doc/1001
{
"id":1001,
"userId":1626989073847750657,
"title":"C语言",
"introduce":"C的起源",
"content":"C语言是由美国贝尔实验室的Dennis Ritchie于1972年设计发明的,最初在UNIX操作系统的DEC PDP-11计算机上使用。它由早期的编程语言BCPL(Basic Combind Programming Language)发展演变而来。",
"tags":"编程,C,语言",
"createTime":"2023-03-23 00:40:20",
"updateTIme":"2023-03-23 00:40:20"
}
POST /blog/_doc/1002
{
"id":1002,
"userId":1626989073847750657,
"title":"C与JAVA语言",
"introduce":"c和java关系",
"content":"C语言和Java都是计算机编程语言,但它们有很多不同之处。C语言是一种面向过程的编程语言,而Java是一种面向对象的编程语言。",
"tags":"C,java,语言",
"createTime":"2023-03-23 00:40:20",
"updateTIme":"2023-03-23 00:40:20"
}
4.实战开发-后端代码
4.1 pom.xml依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.fox</groupId>
<artifactId>elasticsearch-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.8.0</version>
</dependency>
<!--fastjson依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.33</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.7</version>
</dependency>
</dependencies>
</project>
4.2 application.yml配置
server:
# 服务端口
port: 9999
elasticsearch:
# es访问ip
hostname: 127.0.0.1
# es访问port
port: 9200
blog:
# 访问索引
index: blog
# 搜索返回字段
source_fields: id,userId,title,introduce,createTime
4.3 entity包
4.3.1 Result.java
package com.fox.es.entity;
import lombok.Data;
/**
* 统一返回对象
*
* @author 狐狸半面添
* @create 2023-03-22 18:34
*/
@Data
public class Result {
private Integer code;
private String msg;
private Object data;
private Result(Integer code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
private Result(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public static Result ok() {
return new Result(200, "success");
}
public static Result ok(Object data) {
return new Result(200, "success", data);
}
public static Result error(String msg) {
return new Result(500, msg);
}
public Result error(Integer code, String msg) {
return new Result(code, msg);
}
}
4.3.2 JacksonObjectMapper.java
package com.fox.es.entity;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
/**
* 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
*
* @author 狐狸半面添
* @create 2023-01-18 20:34
*/
public class JacksonObjectMapper extends ObjectMapper {
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public JacksonObjectMapper() {
super();
//收到未知属性时不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
//反序列化时,属性不存在的兼容处理
this.getDeserializationConfig().withoutFeatures(FAIL_ON_UNKNOWN_PROPERTIES);
SimpleModule simpleModule = new SimpleModule()
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
.addSerializer(BigInteger.class, ToStringSerializer.instance)
.addSerializer(Long.class, ToStringSerializer.instance)
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
//注册功能模块 例如,可以添加自定义序列化器和反序列化器
this.registerModule(simpleModule);
}
}
4.4 config包
4.4.1 ElasticsearchConfig.java
package com.fox.es.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author 狐狸半面添
* @create 2023-03-22 17:51
*/
@Configuration
public class ElasticsearchConfig {
@Value("${elasticsearch.hostname}")
private String hostname;
@Value("${elasticsearch.port}")
private Integer port;
@Bean
public RestHighLevelClient restHighLevelClient() {
RestClientBuilder builder = RestClient.builder(
new HttpHost(hostname, port, "http")
);
return new RestHighLevelClient(builder);
}
}
4.4.2 WebMvcConfig.java
package com.fox.es.config;
import com.fox.es.entity.JacksonObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List;
/**
* @author 狐狸半面添
* @create 2023-02-11 23:25
*/
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
messageConverter.setObjectMapper(new JacksonObjectMapper());
converters.add(0, messageConverter);
}
}
4.5 BlogSimpleInfoDTO.java
package com.fox.es.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* @author 狐狸半面添
* @create 2023-03-22 21:33
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BlogSimpleInfoDTO {
/**
* 主键id
*/
private Integer id;
/**
* 用户id
*/
private Long userId;
/**
* 标题
*/
private String title;
/**
* 介绍
*/
private String introduce;
/**
* 创建时间
*/
private LocalDateTime createTime;
}
4.5 主启动类
package com.fox.es;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author 狐狸半面添
* @create 2023-03-22 18:07
*/
@SpringBootApplication
public class ElasticsearchDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ElasticsearchDemoApplication.class, args);
}
}
4.6 ⭐测试是否连接 es 成功
package com.fox.es.controller;
import com.fox.es.entity.Result;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.MainResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.io.IOException;
/**
* @author 狐狸半面添
* @create 2023-03-22 18:33
*/
@RestController
public class TestController {
@Resource
private RestHighLevelClient restHighLevelClient;
/**
* 用于测试是否连接 es 成功
*
* @return 返回 es 的基本信息,等价于访问:http://127.0.0.1:9200
* @throws IOException 异常信息
*/
@GetMapping("/getEsInfo")
public Result getEsInfo() throws IOException {
MainResponse info = restHighLevelClient.info(RequestOptions.DEFAULT);
return Result.ok(info);
}
}
浏览器访问:http://localhost:9999/getEsInfo
4.7 ⭐搜索服务
4.7.1 controller层
package com.fox.es.controller;
import com.fox.es.entity.Result;
import com.fox.es.service.ShareResourceSearchService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author 狐狸半面添
* @create 2023-03-22 20:16
*/
@RestController
@RequestMapping("/search")
public class ShareResourceSearchController {
@Resource
private ShareResourceSearchService shareResourceSearchService;
/**
* 通过关键词获取数据列表
*
* @param keyWords 关键词
* @param pageNo 页码
* @param pageSize 每页大小
* @return 数据列表,按照相关性从高到低进行排序
*/
@GetMapping("/list")
public Result list(@RequestParam("keyWords") String keyWords,
@RequestParam("pageNo") Integer pageNo,
@RequestParam("pageSize") Integer pageSize) {
return shareResourceSearchService.list(keyWords, pageNo, pageSize);
}
}
4.7.2 service接口层
package com.fox.es.service;
import com.fox.es.entity.Result;
/**
* @author 狐狸半面添
* @create 2023-03-22 20:18
*/
public interface ShareResourceSearchService {
/**
* 通过关键词获取数据列表
*
* @param keyWords 关键词
* @param pageNo 页码
* @param pageSize 每页大小
* @return 数据列表,按照相关性从高到低进行排序
*/
Result list(String keyWords, int pageNo, int pageSize);
}
4.7.3 service实现层
package com.fox.es.service.impl;
import com.alibaba.fastjson.JSON;
import com.fox.es.dto.BlogSimpleInfoDTO;
import com.fox.es.entity.Result;
import com.fox.es.service.ShareResourceSearchService;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author 狐狸半面添
* @create 2023-03-22 20:18
*/
@Slf4j
@Service
public class ShareResourceSearchServiceImpl implements ShareResourceSearchService {
@Resource
private RestHighLevelClient restHighLevelClient;
@Value("${elasticsearch.blog.index}")
private String blogIndexStore;
@Value("${elasticsearch.blog.source_fields}")
private String blogFields;
public Result list(String keyWords, int pageNo, int pageSize) {
// 1.设置索引 - blog
SearchRequest searchRequest = new SearchRequest(blogIndexStore);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
// 2.source源字段过虑
String[] sourceFieldsArray = blogFields.split(",");
searchSourceBuilder.fetchSource(sourceFieldsArray, new String[]{});
// 3.关键字
if (StringUtils.hasText(keyWords)) {
// 哪些字段匹配关键字
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keyWords, "title", "tags", "introduce", "content");
// 设置匹配占比(表示最少匹配的子句个数,例如有五个可选子句,最少的匹配个数为5*70%=3.5.向下取整为3,这就表示五个子句最少要匹配其中三个才能查到)
multiMatchQueryBuilder.minimumShouldMatch("70%");
// 提升字段的Boost值
multiMatchQueryBuilder.field("title", 10);
multiMatchQueryBuilder.field("tags", 7);
multiMatchQueryBuilder.field("introduce", 5);
boolQueryBuilder.must(multiMatchQueryBuilder);
}
// 4.分页
int start = (pageNo - 1) * pageSize;
searchSourceBuilder.from(start);
searchSourceBuilder.size(pageSize);
// 布尔查询
searchSourceBuilder.query(boolQueryBuilder);
// 6.高亮设置
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags("<font color='red'>");
highlightBuilder.postTags("</font>");
// 设置高亮字段
ArrayList<HighlightBuilder.Field> fields = new ArrayList<>();
fields.add(new HighlightBuilder.Field("title"));
fields.add(new HighlightBuilder.Field("introduce"));
highlightBuilder.fields().addAll(fields);
searchSourceBuilder.highlighter(highlightBuilder);
// 请求搜索
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse;
try {
searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
log.error("博客搜索异常:{}", e.getMessage());
return Result.error(e.getMessage());
}
// 结果集处理
SearchHits hits = searchResponse.getHits();
SearchHit[] searchHits = hits.getHits();
// 记录总数
long totalHitsCount = hits.getTotalHits().value;
// 数据列表
List<BlogSimpleInfoDTO> list = new ArrayList<>();
for (SearchHit hit : searchHits) {
String sourceAsString = hit.getSourceAsString();
// json 转 对象
BlogSimpleInfoDTO blog = JSON.parseObject(sourceAsString, BlogSimpleInfoDTO.class);
// 取出高亮字段内容
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if (highlightFields != null) {
blog.setTitle(parseHighlightStr(blog.getTitle(), highlightFields.get("title")));
blog.setIntroduce(parseHighlightStr(blog.getIntroduce(), highlightFields.get("introduce")));
}
list.add(blog);
}
// 封装信息返回前端
HashMap<String, Object> resultMap = new HashMap<>(4);
// 页码
resultMap.put("pageNo", pageNo);
// 每页记录数量
resultMap.put("pageSize", pageSize);
// 总记录数
resultMap.put("total", totalHitsCount);
// 该页信息
resultMap.put("items", list);
return Result.ok(resultMap);
}
public String parseHighlightStr(String text, HighlightField field) {
if (field != null) {
Text[] fragments = field.getFragments();
StringBuilder stringBuilder = new StringBuilder();
for (Text str : fragments) {
stringBuilder.append(str.string());
}
return stringBuilder.toString();
} else {
return text;
}
}
}
这里我们使用 apipost7
进行测试:
5.源码获取
这个demo如果你按照我的前三步进行了执行,那么直接运行源码应该是没有问题的。
源码地址:Mr-Write/SpringbootDemo: 各种demo案例 (github.com)
对应的是 elasticsearch-demo
包模块。
6.存在的问题与进阶说明
我们还存在另外一个需要解决的重要问题:与MySQL的数据一致性问题
❓ 为什么不直接使用ES存储所有的项目数据呢?
- 非关系型表达,大量的反范式存储,导致维护基础数据异常(新增异常,修改异常,删除异常),需要MySQL做托底。
- 不支持事务,一致性需要自己维护。
- 从存储的角度来说,Elasticsearch是基于“字段”的,大行超多字段性能并不好。
数据同步的方式有很多种,比如通过ES的API进行增删改查,或者通过中间件像canal
、MQ
进行数据全量、增量的同步。
具体解决方案,可参考:docker环境安装mysql、canal、elasticsearch,基于binlog利用canal实现mysql的数据同步到elasticsearch中