Elastic Stack概念:
-
官网:Elasticsearch 平台 — 大规模查找实时答案 | Elastic
包含了数据的整合 => 提取 => 存储 => 使用
-
相当于一个数据库,进行增删改查等操作
安装:
elasticsearch:Set up Elasticsearch | Elasticsearch Guide [7.17] | Elastic
kibana:Kibana—your window into Elastic | Kibana Guide [7.17] | Elastic
运行:
-
在 bin 目录下运行elasticsearch
-
浏览器地址栏输入GET 请求:http://localhost:9200/
-
打开kibana,在 bin 目录下运行kibana.bat,浏览器输入地址http://localhost:5601/,使用devtools
ES 的启动端口:
-
9200:给外部用户(给客户端调用)的端口
-
9300:给 ES 集群内部通信的(外部调用不了的)
只要是一套技术,所有版本必须一致
-
下载解压
-
启动 elasticsearch在 bin 目录下运行elasticsearch.bat
-
启动 kibana.bat
在 bin 目录下运行kibana.bat
-
索引:
-
Index 索引 => MySQL 里的表(table)
-
jiu
正向索引:
-
理解为数据的目录,可以快速帮你找到对应的内容(怎么根据页码找到文章)
倒排索引:
-
根据内容找到文章
-
例:
文章A:你好,我是 你爹
文章B:儿子你好,我是你爷
切词:
你好,我是,你爹
儿子,你好,我是,你爹
构建倒排索引表:
词 | 内容ID |
---|---|
你好 | 文章A,B |
我是 | 文章A,B |
你爹 | 文章A |
儿子 | 文章B |
你爷 | 文章B |
用户搜:“儿子你爹”
ES 先切词:儿子,你爹
去倒排索引表找对应文章:文章A,B
配置 kibana 可视化看板:
-
创建索引
-
导入数据
-
创建索引模式
-
选择图标、托拉拽
-
保存
数据同步:
-
⼀般情况下,如果做查询搜索功能,使⽤ ES 来模糊搜索,但是数据是存放在数据库 MySQL ⾥的,所以说我们需要把 MySQL 中的数据和 ES 进⾏同步,保证数据⼀致(以 MySQL 为主)。MySQL => ES (单向),避免es和mysql数据不一致,以mysql为主再去把es的值给覆盖⾸次安装完 ES,把 MySQL 数据全量同步到 ES ⾥,写⼀个单次脚本(详见FullSyncPostToEs类)
-
4 种方式,全量同步(首次)+ 增量同步(新数据):
-
定时任务,比如 1 分钟 1 次,找到 MySQL 中过去几分钟内(至少是定时周期的 2 倍)发生改变的数据,然后更新到 ES。
优点:简单易懂、占用资源少、不用引入第三方中间件
缺点:有时间差 应用场景:数据短时间内不同步影响不大、或者数据几乎不发生修改
-
双写:写数据的时候,必须也去写 ES;更新删除数据库同理。(事务:建议先保证 MySQL 写成功,如果 ES 写失败了,可以通过定时任务 + 日志 + 告警进行检测和修复(补偿))
-
用 Logstash 数据同步管道(一般要配合 kafka 消息队列 + beats 采集器):
Java操作ES:
3 种:
-
ES 官方的 Java API
Introduction | Elasticsearch Java API Client [7.17] | Elastic
快速开始:Connecting | Elasticsearch Java API Client [7.17] | Elastic
-
ES 以前的官方 Java API,HighLeavelRestClient(已废弃,不建议用)
-
Spring Data Elasticsearch
spring-data 系列:spring 提供的操作数据库的框架
spring-data-redis:操作 redis 的一套方法
spring-data-mongodb:操作 mongodb的一套方法
spring-data-elasticsearch:操作 elasticsearch 的一套方法
ES 实现搜索接口:
步骤:
-
ES建表(建立索引)
-
根据Mysql中的数据库表来建
-
id(可以不放到字段设置里)
ES 中,尽量存放需要用户筛选(搜索)的数据
-
例:
Mysql数据库里的表结构是 -- 帖子表create table if not exists post( id bigint auto_increment comment 'id' primary key, title varchar(512) null comment '标题', content text null comment '内容', tags varchar(1024) null comment '标签列表(json 数组)', thumbNum int default 0 not null comment '点赞数', favourNum int default 0 not null comment '收藏数', userId bigint not null comment '创建用户 id', createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间', updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', isDelete tinyint default 0 not null comment '是否删除', index idx_userId (userId)) comment '帖子' collate = utf8mb4_unicode_ci;
则Es建表:PUT post_v1{ "aliases": { "post": {} }, "mappings": { "properties": { "title": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_smart", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "content": { "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_smart", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "tags": { "type": "keyword" }, "userId": { "type": "keyword" }, "createTime": { "type": "date" }, "updateTime": { "type": "date" }, "isDelete": { "type": "keyword" } } }}因为要根据存放需要用户筛选(搜索)的数据来建表所以不常见的搜索的字段就不建了aliases:别名(两个字段都可以搜索到这个表,为了后续方便数据迁移)
字段类型是 text,这个字段是可被分词的、可模糊查询的;而如果是 keyword,只能完全匹配、精确查询。
如果想要让 text 类型的分词字段也支持精确查询,可以创建 keyword 类型的子字段:例如:"fields": { "keyword": { "type": "keyword", "ignore_above": 256 // 超过字符数则忽略查询 }
analyzer(存储时生效的分词器):用 ik_max_word,拆的更碎、索引更多,更有可能被搜出来
search_analyzer(查询时生效的分词器):用 ik_smart,更偏向于用户想搜的分词
-
引入 jar 包
<!-- elasticsearch--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency>
-
编写实体类对象PostEsDTO
-
用来接收实体类数据 和mysql类似原理
-
@Document(indexName = "这里可以填建的索引的名字也可以是别名,别名最好就是mysql里面的表名")
-
-
在 application.yml 中配置 Elasticsearch
-
使用 Elasticsearch
-
第一种方式:ElasticsearchRepository<PostEsDTO, Long>,默认提供了简单的增删改查,多用于可预期的、相对没那么复杂的查询、自定义查询,返回结果相对简单直接。
-
例:PostEsDaoTest测试类中的testAdd方法
执行成功后使用GET post/_doc/1在kibana查询返回{ "_index" : "post", "type" : "doc", "_id" : "1", "_version" : 1, "_seq_no" : 2, "_primary_term" : 2, "found" : true, "_source" : { "_class" : "com.one.model.dto.post.PostEsDTO", "id" : 1, "title" : "顶针是妈妈省的", "content" : "雪豹闭嘴", "tags" : [ "java", "python" ], "userId" : 1, "createTime" : "2023-10-30T06:53:20.679Z", "updateTime" : "2023-10-30T06:53:20.679Z", "isDelete" : 0 }}
发现source里的id和外面的id值一样是因为在.PostEsDTO类里的id属性使用了@Id注解把数据库里的组件和es里的组件值绑定了
-
-
-
-
-
-
-
Es分词器IK:
使用IK分词器:
下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.17.7(注意版本一致)
-
ik_smart 是智能分词,尽量选择最像一个词的拆分方式,比如“小”、“黑子”
-
ik_max_word 尽可能地分词,可以包括组合词,比如 “小黑”、“黑子”
-
可以做一个自定义的词典做到能根据自己的想法来对指定的索引分词,这是可优化的点
-
ik_smart例子
POST _analyze{ "analyzer": "ik_smart", "text": "你好我是顶针,我不是小黑子,我是坤坤真爱粉"}结果:你好、我、是、顶针、我、不是、小、黑子、我、是、坤、坤、真爱、粉
-
ik_max_word例子
POST _analyze { "analyzer": "ik_max_word", "text": "你好我是顶针,我不是小黑子,我是坤坤真爱粉" }
结果:你好、我、是、顶针、我、不是、小黑、黑子、我、是、坤、坤、真爱、粉
-
对比Mysql的优势:
搜索优势:
-
当搜索例如搜索詹姆斯洛杉矶湖人队球员时没办法搜到詹姆斯是洛杉矶湖人队球员,但是其实用户是想搜索到詹姆斯是洛杉矶是洛杉矶湖人队球员的,因为Mysql是使用like查询的like 是包含查询,查询语句里的字符串必须完全一样才能搜索出来,除了搜索语句完全符合要想搜索到就必须要分词詹姆斯,洛杉矶湖人队球员 ,且分词规则也不好确定,究竟在哪里断词,Es能够自动帮我们做分词,能够非常高效、灵活的查询内容。
-
建表优势:
-
ES有的Mapping结构,可以先插数据再指定表结构,而不是像mysql先制定表结构再插数据,不用先想好要有哪些字段再建表
-
-
例:
显式创建 mapping:// 建表PUT user{ "mappings": { "properties": { "age": {"type": "integer"}, "email": {"type": "keyword"}, "name": {"type": "text"} } }}
// 查看表结构GET user/_mapping
-
{"type": "integer"}手动指定type类型是防止当我们添加字段且没有指定类型的时候es自动给我们分配的类型和我们预期的不一样
-
-
-
-
Kibana:
作用:
-
相当于一个控制台,在上面进行对Elastis Search的增删改查等操作
ES操作:
Mapping结构:
-
理解为数据库的表结构,有哪些字段、字段类型,ES 支持动态 mapping,表结构可以动态改变,而不像 MySQL 一样必须手动建表,没有的字段就不能插入
-
Mapping表结构:
-
显式创建 mapping:
-
虽然DSL语法建的表新插入表之前没有的字段时可以自动给该字段分配类型,但是会出现自动分配的字段类型不符合我们预期的可能,mapping就是指定类型创建,可以根据该字段可能的查询方式指定类型,例如名字字段可以进行模糊查询则定义类型为text
-
keyword类型的字段不可进行分词,例如分类字段就可以定义为keyword,因为分类要精确搜索
-
建表
// 建表PUT user{ "mappings": { "properties": { "age": {"type": "integer"}, "email": {"type": "keyword"}, "name": {"type": "text"} } }}
-
查看表结构
// 查看表结构GET user/_mapping
-
-
-
-
文档:
[Quick start | Elasticsearch Guide 7.17] | Elastic
DSL语法:
-
json格式,好理解,和 http 请求最兼容,应用最广
建表,插入,查询,删除数据:
-
例子
-
建表PUT 表名或者下面
POST logs-my_app-default(表名)/_doc { "title":"aa",
"@timestamp": "2099-05-06T16:21:15.000Z", "event": { "original": "192.0.2.42 - - [06/May/2099:16:21:15 +0000] \"GET /images/bg.jpg HTTP/1.0\" 200 24736" } }
-
POST后/_doc是表名
-
成功后会返回
{ "_index" : ".ds-logs-my_app-default-2023.10.29-000001", "type" : "doc", "_id" : "iWfBeosB6QubWZ_lI4xn", "_version" : 1, "result" : "created", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 0, "_primary_term" : 1} "_id" : "iWfBeosB6QubWZ_lI4xn",id值没有指定的话es会自己帮你设置一个,就像mysql的id
-
-
根据 id 查询GET post/_doc/iWfBeosB6QubWZ_lI4xn
-
查询所有数据DSL语法:Query DSL | Elasticsearch Guide [7.17] | Elastic(忘了就查,不用背)GET logs-my_app-default/_search{ "query": { "match_all": { } }, "sort": [ { "@timestamp": "desc" } ]}
-
"match_all": { }代表查询所有类型的数据
-
-
-
-
修改
POST 表名/_doc/id值{ "title": "2", "desc": "2"}
-
- 里面直接写字段名加内容 •
-
删除
DELETE /表名
-
条件查询,查询的字段里必须有哪些语句
-
文档: [Query and filter context | Elasticsearch Guide 7.17] | Elastic
GET /_search{ "query": { "bool": { "must": [ 必须都满⾜ { "match": { "title": "Search" }}, // match 模糊查询 { "match": { "content": "Elasticsearch" }} ], "filter": [ { "term": { "status": "published" }}, // term 精确查询 { "range": { "publish_date": { "gte": "2015-01-01" }}} ] } }}
-
-
Boolean query(匹配匹配其他查询的布尔组合的文档的查询。Bool 查询映射到 Lucene Boolean Query。它使用一个或多个布尔子句构建,每个子句具有类型化的匹配项。
-
参考文档:[Boolean query | Elasticsearch Guide 7.17] | Elastic
-
这个 Boolean query变成java语言就是postserviceImpl里的searchFromEs方法
-
工作流程就是先模糊筛选静态数据,查出数据后,再根据查到的内容 id 去数据库查找到动态数据,es的作用就是帮忙做搜索
-
DSL的boolean query查询写法是 GET 表名/_search { "query": { "bool" : { //组合条件 "must" : { //必须满足的条件 "term" : { "user.id" : "kimchy" } }, "filter": { "term" : { "tags" : "production" } }, "must_not" : { "range" : { "age" : { "gte" : 10, "lte" : 20 } } }, "should" : [ //查询的结果只要tags包含env1或者deployed其中一个就可以 { "term" : { "tags" : "env1" } }, { "term" : { "tags" : "deployed" } } ], "minimum_should_match" : 1, //符合shold里面的term要查询的字段值最小值 "boost" : 1.0 } } "from":0 ,//分⻚ "size":5,//分⻚ "_source":["name","_createtime","desc","reviewStatus","priority","tags"] ,//要查的字段 "sort":[ //排序 { "priority":{ "order":"desc" } }, { "_score":{ "order":"desc" } }, { "publishTime":{ "order":"desc" } } ] } •
-
-
-