关于ElasticSearch
1.概述
Elasticsearch 是一个基于 Apache Lucene 构建的开源分布式搜索引擎和分析引擎。它专为云计算环境设计,提供了一个分布式的、高可用的实时分析和搜索平台。Elasticsearch 可以处理大量数据,并且具备横向扩展能力,能够通过增加更多的硬件资源来应对数据和查询量的增长。天生的分布式数据库
Elasticsearch 的核心特点包括:
-
全文搜索:支持对各种类型的数据(包括结构化、半结构化和非结构化文本数据)进行快速高效的全文本搜索。
-
分布式:数据在集群中的多个节点间分布和复制,确保高可用性和容错性,同时也支持水平扩展,以应对更大的数据量和更高的并发访问。
-
实时性:数据一旦写入 Elasticsearch,几乎可以立即被搜索到,提供近乎实时的搜索体验。
-
分析能力:内置丰富的数据分析工具,包括聚合分析(Aggregations)和其他统计功能,便于用户对数据进行深入挖掘和洞察。
-
多租户:支持索引级别的隔离,每个索引可以配置分片数量和副本数量,以满足不同业务场景的需求。
-
RESTful API:通过 HTTP/HTTPS 协议提供 JSON 格式的 REST API 接口,易于与其他系统集成,支持多种开发语言调用。
-
灵活的文档模型:无需预定义严格的表结构,而是采用动态 schema 或映射,可以根据文档内容自动识别数据类型和结构。
Elasticsearch 被广泛应用在日志分析、监测数据、企业搜索、电子商务搜索、实时分析等多个领域,并常与 Logstash(日志收集和处理工具)、Kibana(数据可视化平台)共同构成 Elastic Stack(原 ELK Stack),形成一套完整的日志管理和数据分析解决方案。
2.什么场景会用到Elasticsearch
-
全文搜索:
-
电商搜索:快速查找商品信息,支持模糊匹配、关键词高亮显示、过滤、排序等功能。
-
站内搜索:网站内部的页面、文章、博客等内容的搜索,提供类似Google的搜索体验。
-
文档管理系统:企业级文档搜索,如办公文档、合同、法律文件等的高效检索。
-
论坛和社交媒体:用户发表的内容搜索,如帖子、评论、话题等。
-
日志分析与监控:
-
服务器日志:收集、索引和分析服务器产生的各类日志,用于故障排查、性能优化、安全审计等。
-
应用日志:跟踪应用程序的行为,帮助开发人员迅速定位错误、诊断问题。
-
运维监控:收集系统指标、网络流量数据,实时或历史数据分析,可视化展示系统状态和趋势。
-
数据分析:
-
业务分析:实时或批量分析业务数据,生成报表,进行趋势分析、关联分析等。
-
时序数据分析:存储和分析时间序列数据,例如设备传感器数据、用户行为数据等。
-
NoSQL JSON文档数据库:
-
作为JSON文档数据库使用,存储和检索半结构化数据,支持地理位置查询和混合查询。
-
搜索推荐:
-
实现个性化搜索和推荐功能,根据用户的搜索历史和行为模式,智能推荐相关内容。
-
地理信息系统:
-
存储和查询带有地理位置信息的数据,构建地图应用、位置服务等相关功能。
-
大规模监控系统:
-
结合Logstash和Kibana,搭建ELK Stack,进行大规模分布式环境下的日志集中管理、实时分析和可视化展示。
3.基于docker安装
命令:
docker run -d --name es7 --restart=always -e ES_JAVA_POTS="-Xms256m -Xmx256m" -e "discovery.type=single-node" -v /opt/es7/data/:/usr/share/elasticsearch/data -p 9200:9200 -p 9300:9300 registry.cn-beijing.aliyuncs.com/all100/elasticsearch:7.14.0
注意:/opt/es7 下 data文件夹要开放权限 直接执行命令:chmod -R 777 /opt/es7/data
3.1.使用客户端UI工具,Edge浏览器扩展
4.分词器安装
4.1为什么要安装分词器
在 Elasticsearch 的 IK Analyzer 中,ik_smart和 ik_max_word 是IK 分词器针对中文分词提供的两种策略,但分词效果和粒度不同:
ik_smart: 这种模式更侧重于保持语义完整性,尽量进行较少的、更有意义的拆分,减少无意义的子词组合,提高搜索准确率,降低误报率。
示例:“中华人民共和国人民大会堂” 分词结果(ik_smart)可能只有:“中华人民共和国”、“人民大会堂”等较完整、更具实际意义的词语组合。
ik_max_word: 此模式致力于最大化地拆分文本,即尽可能多地生成可能的词语组合,包括单字、双字直至整个短语。它的特点是尽力穷举所有可能的词汇,提高召回率,但在某些情况下可能会造成噪声较多。
示例:“中华人民共和国人民大会堂” 分词结果(ik_max_word)可能包括:中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、大会堂、大会、会堂等。
简单来说,ik_max_word
更倾向于全面细致的分词,而 ik_smart
则偏向于精简和精准的分词。在实际应用场景中,选择哪种模式取决于项目的具体需求,如是否需要扩大搜索覆盖面还是提高搜索准确性。
4.2安装
官方插件下载地址:
https://github.com/medcl/elasticsearch-analysis-ik/releases
注:根据你安装的版本选择
执行命令
#第一步 copy 插件到容器
docker cp /opt/es7/elasticsearch-analysis-ik-7.14.0.zip 容器id:/usr/share/elasticsearch
#第二步进入你的容器
docker exec -it 容器id /bin/bash
#第三步执行如下命令,安装插件,中间会提示 Y or N,直接写 Y ,回车即可
elasticsearch-plugin install file:\/usr/share/elasticsearch/elasticsearch-analysis-ik-7.14.0.zip
#第四步退出容器
exit
#第五步重启容器
docker restart 容器ID
5.使用客户端查看
6.spring Boot使用EelasticSearch
6.1.导入依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</exclusion>
<exclusion>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cn.easy-es</groupId>
<artifactId>easy-es-boot-starter</artifactId>
<version>2.0.0-beta1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.14.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.14.0</version>
</dependency>
6.2.在对象上添加注解
@IndexName( aliasName = "es_product",shardsNum=2,replicasNum=2)
@Data
@Accessors(chain = true)
public class EsProduct {
@IndexId(type = IdType.CUSTOMIZE)
private Integer id;
@IndexField(fieldType= FieldType.TEXT, analyzer = Analyzer.IK_MAX_WORD,searchAnalyzer = Analyzer.IK_MAX_WORD)
@HighLight
private String name;
@IndexField( fieldType= FieldType.TEXT, analyzer = Analyzer.IK_MAX_WORD,searchAnalyzer = Analyzer.IK_MAX_WORD)
@HighLight //查询时高亮
private String subTitle;
@IndexField( fieldType= FieldType.INTEGER)
private Integer categoryId;
@IndexField( fieldType= FieldType.DOUBLE) // 12.56
private BigDecimal price;
@IndexField( fieldType= FieldType.TEXT, analyzer = Analyzer.IK_MAX_WORD)
@HighLight //查询时高亮
private String description; // 描述
@IndexField( fieldType= FieldType.KEYWORD)
private String img;
@IndexField( fieldType= FieldType.KEYWORD)
private String brandName;
@IndexField( fieldType= FieldType.TEXT, analyzer = Analyzer.IK_MAX_WORD)
private List<String> tags;
@IndexField( fieldType= FieldType.INTEGER) //198
private Integer highOpinion; //好评
@IndexField( fieldType= FieldType.INTEGER)
private Integer saleCount; // 销量
@IndexField( fieldType= FieldType.DATE)
private LocalDateTime productionDate; // 生产日期
}
6.2.新建Mapper类,类似Mybatis的dao
public interface ProductMapper extends BaseEsMapper<Product> {
}
6.3.配置ES
# Easy-Es配置部分
easy-es:
# 启用Easy-Es功能
enable: true
# 设置Elasticsearch服务器地址和端口
address: 127.0.0.1:9200
# 全局配置项,设置是否打印执行的DSL语句(便于调试)
global-config:
print-dsl: true
7.基本操作
7.1.新增
@SpringBootTest
class InsertTests {
@Autowired
private EsProductMapper esProductMapper;
@Test
void test1() {
EsProduct esProduct = new EsProduct().setId(1)
.setName("大学生自习室视频爆火鼻祖")
.setSubTitle("副标题有 玻璃 小米(MI)Redmi K70 至尊版 天玑9300+ IP68 小米龙晶 12GB+256GB 晴雪白 小米红米K70 Ultra 5G手机")
.setDescription("天玑 9300+\n" +
"MediaTek 迄今最强性能芯\n" +
"升级全大核 CPU 架构\n" +
"作为小米xMediaTek 联合实验室首款性能力作,RedmiK70 至尊版\n" +
"采用了 9300+旗舰芯片,主频高达3.4GHz,配备 Cortex-X4 大核\n" +
"架构。无论驾驭重载 3D游戏,还是重载应用,全然毫不费力。即\n" +
"便效果拉满,依旧狂暴持久。格力")
.setImg("https://www.baidu.com/log.png")
.setCategoryId(100)
.setBrandName("小米")
.setSaleCount(100)
.setHighOpinion(250)
.setPrice(new BigDecimal(10.28))
.setTags(CollUtil.newArrayList("小米", "手机","红米","老人机"));
Integer retNum = esProductMapper.insert(esProduct);
}
@Test
void insert2() {
EsProduct esProduct = new EsProduct().setId(2)
.setName("名称有-玻璃")
.setSubTitle("格力(GREE)空调 云佳【超级省电】新能效空调变频冷暖 自清洁 壁挂式空调挂机卧室变频空调冷暖空调冷暖 大1匹 一级能效 【适用10-15㎡】")
.setDescription("4.50 旧国标一级能效\n" +
"APF:\n" +
"空调APF能效等级是空调评价指标\n" +
"APF数值越高越省电。苹果")
.setImg("https://www.baidu.com")
.setCategoryId(120)
.setBrandName("格力")
.setSaleCount(50)
.setHighOpinion(300)
.setPrice(new BigDecimal(2000))
.setTags(CollUtil.newArrayList("格力", "夏天","避暑","节能","制冷","省电"));
Integer rowNum = esProductMapper.insert(esProduct);
}
@Test
void insert5() {
EsProduct esProduct = new EsProduct().setId(3)
.setName("理想汽车")
.setSubTitle("格力(GREE)空调 云佳【超级省电】新能效空调变频冷暖 自清洁 壁挂式空调挂机卧室变频空调冷暖空调冷暖 大1匹 一级能效 【适用10-15㎡】")
.setDescription(" 描述有-玻璃 4.50 旧国标一级能效\n" +
"APF:\n" +
"空调APF能效等级是空调评价指标\n" +
"APF数值越高越省电。苹果")
.setImg("https://www.baidu.com")
.setCategoryId(120)
.setBrandName("格力")
.setSaleCount(50)
.setHighOpinion(300)
.setPrice(new BigDecimal(2000))
.setTags(CollUtil.newArrayList("格力", "夏天","避暑","节能","制冷","省电"));
Integer rowNum = esProductMapper.insert(esProduct);
}
@Test
void insert3() {
List productList = new ArrayList<EsProduct>();
for (int i = 1; i <= 130000; i++) {
EsProduct esProduct = new EsProduct().setId(i)
.setName("Redmi K"+i)
.setSubTitle("小米(MI)Redmi K70 至尊版 天玑9300+ IP68 小米龙晶玻璃 12GB+256GB 晴雪白 小米红米K70 Ultra 5G手机")
.setDescription("天玑 9300+\n" +
"MediaTek 迄今最强性能芯\n" +
"升级全大核 CPU 架构\n" +
"作为小米xMediaTek 联合实验室首款性能力作,RedmiK70 至尊版\n" +
"采用了 9300+旗舰芯片,主频高达3.4GHz,配备 Cortex-X4 大核\n" +
"架构。无论驾驭重载 3D游戏,还是重载应用,全然毫不费力。即\n" +
"便效果拉满,依旧狂暴持久。")
.setImg("https://www.baidu.com")
.setBrandName("小米")
.setSaleCount(100)
.setCategoryId(100)
.setPrice(new BigDecimal(2599))
.setTags(CollUtil.newArrayList("小米", "手机","红米","老人机"))
.setProductionDate(LocalDateTime.now());
productList.add(esProduct);
}
Integer rowNum = esProductMapper.insertBatch(productList);
}
}
7.2.删除
@SpringBootTest
class DeleteTests {
@Autowired
private EsProductMapper esProductMapper;
@Test
void test1() {
Integer rowNum = esProductMapper.deleteBatchIds(CollUtil.newArrayList(13,12));
}
@Test
void test2() {
LambdaEsQueryWrapper wrapper = new LambdaEsQueryWrapper<EsProduct>()
.eq(EsProduct::getId, 11); //where id = 11
Integer rowNum = esProductMapper.delete(wrapper);
}
@Test
void test3() {
LambdaEsQueryWrapper wrapper = new LambdaEsQueryWrapper<EsProduct>()
.le(EsProduct::getSaleCount, 50); //where id = 11
Integer rowNum = esProductMapper.delete(wrapper);
}
}
7.3.修改
@SpringBootTest
class UpdateTests {
@Autowired
private EsProductMapper esProductMapper;
@Test
void update() {
EsProduct esProduct = new EsProduct().setId(1)
.setName("红米手机")
.setSubTitle("小米(MI)Redmi K70 至尊版 天玑9300+ IP68 小米龙晶玻璃 12GB+256GB 晴雪白 ")
.setDescription("天玑 9300+\n" +
"MediaTek 迄今最强性能芯\n" +
"升级全大核 CPU 架构\n" +
"作为小米xMediaTek 联合实验室首款性能力作,RedmiK70 至尊版\n" +
"采用了 9300+旗舰芯片,主频高达3.4GHz,配备 Cortex-X4 大核\n" +
"架构。无论驾驭重载 3D游戏,还是重载应用,全然毫不费力。即\n" +
"便效果拉满,依旧狂暴持久。格力")
.setImg("https://www.baidu.com")
.setCategoryId(100)
.setBrandName("小米")
.setSaleCount(1008)
.setPrice(new BigDecimal(1000))
.setTags(CollUtil.newArrayList("小米", "手机","红米","老人机"));
Integer rowNum = esProductMapper.updateById(esProduct);
}
@Test
//部分更改
void update2() {
EsProduct esProduct = new EsProduct().setId(1).setSaleCount(1009);
Integer rowNum = esProductMapper.updateById(esProduct);
}
@Test
//部分更改
void update3() {
LambdaEsUpdateWrapper<EsProduct> wrapper = new LambdaEsUpdateWrapper<EsProduct>()
.eq(EsProduct::getId, 1);
// .set(EsProduct::getSaleCount, 200);
EsProduct esProduct = new EsProduct().setSaleCount(305);
// set salecount = 300 where id = 1;
Integer update = esProductMapper.update(esProduct, wrapper);
}
}
7.4.查询
@SpringBootTest
class SelectTests {
@Autowired
private EsProductMapper esProductMapper;
@Test
void select() {
LambdaEsQueryWrapper<EsProduct> wrapper = new LambdaEsQueryWrapper<>();
wrapper.eq(EsProduct::getId, 3); //where id = 2
List<EsProduct> esProducts = esProductMapper.selectList(wrapper);
}
@Test
void select2() {
LambdaEsQueryWrapper<EsProduct> wrapper = new LambdaEsQueryWrapper<>();
wrapper.eq(EsProduct::getCategoryId, 200); //where category = 200
List<EsProduct> esProducts = esProductMapper.selectList(wrapper);
}
@Test
void select3() {
LambdaEsQueryWrapper<EsProduct> wrapper = new LambdaEsQueryWrapper<>();
wrapper.in(EsProduct::getCategoryId, 100,200); //where category in (100,200)
List<EsProduct> esProducts = esProductMapper.selectList(wrapper);
}
@Test
//对单个字段的查找
void select4() {
LambdaEsQueryWrapper<EsProduct> wrapper = new LambdaEsQueryWrapper<>();
wrapper.match(EsProduct::getBrandName, "格力"); //where brandName = "格力"
List<EsProduct> esProducts = esProductMapper.selectList(wrapper);
}
@Test
//对单个字段的查找
void select5() {
LambdaEsQueryWrapper<EsProduct> wrapper = new LambdaEsQueryWrapper<>();
wrapper.queryStringQuery("玻璃");
List<EsProduct> esProducts = esProductMapper.selectList(wrapper);
}
@Test
//对某些字段做模糊查询,并指定权重
void select6() {
//where categroyId in (100,120)
// and (name like '%玻璃%' or subTitle like '%玻璃%' or description like '%玻璃%')
String keyword = "玻璃";
LambdaEsQueryWrapper<EsProduct> wrapper = new LambdaEsQueryWrapper<>();
//指定类目
wrapper.in(EsProduct::getCategoryId,100,120); //where categroyId in (100,120)
//按照等级进行排序
wrapper.and(w->w.match(EsProduct::getName,keyword,5f)
.or().match(EsProduct::getSubTitle,keyword,3f)
.or().match(EsProduct::getDescription,keyword,1f));
List<EsProduct> esProducts = esProductMapper.selectList(wrapper);
}
@Test
//排序
void select7() {
//where categroyId in (100,120) order by salecount
String keyword = "玻璃";
boolean isDesc = true;
LambdaEsQueryWrapper<EsProduct> wrapper = new LambdaEsQueryWrapper<>();
//指定类目
wrapper.in(EsProduct::getCategoryId,100,120); //where categroyId in (100,120)
//排序
//wrapper.orderBy(isDesc,true,EsProduct::getSaleCount);
//按照等级进行排序
wrapper.and(w->w.match(EsProduct::getName,keyword,5f)
.or().match(EsProduct::getSubTitle,keyword,3f)
.or().match(EsProduct::getDescription,keyword,1f));
List<EsProduct> esProducts = esProductMapper.selectList(wrapper);
}
@Test
//排序
void select8() {
//where categroyId in (100,120) order by salecount
String keyword = "玻璃";
boolean isDesc = true;
LambdaEsQueryWrapper<EsProduct> wrapper = new LambdaEsQueryWrapper<>();
//指定类目
wrapper.in(EsProduct::getCategoryId,100,120); //where categroyId in (100,120)
//排序
wrapper.orderBy(isDesc,false,EsProduct::getSaleCount);
EsPageInfo<EsProduct> esProductEsPageInfo = esProductMapper.pageQuery(wrapper, 1, 2);
String source = esProductMapper.getSource(wrapper);
System.out.println(source);
}
}