Elasticsearch

学习途径:尚硅谷

前言

数据类型:

  • 结构化数据:关系型数据库,表结构
  • 非结构化数据:视频、图表等复杂的,保存在非关系型数据库
  • 半结构化数据:xml、txt文档等,保存在非关系型

ELK:

  • ElasticSearch:存储和搜索数据
  • Beats:采集数据
  • Logstash:传输数据
  • Kibana:展示数据

Lucene:lucene不能独立使用,需要在其基础上搭建服务器框架才可。ES和solr基于lucene开发。选es的原因:除了搜索外还需要进行分析查询;集群下es更好扩展、性能好;es会暴露更多关键性指标

安装:官网,启动bin目录下的.bat文件,9300端口为es集群间组件的通信端口,9200为访问的http协议的restful端口,访问:http://localhost:9200

概述

ES是一个开源的高扩展的分布式全文搜索引擎,优势在于灵活的分布式架构和强大的检索能力,但不适合强事务场景(如金融交易)。在实际应用中,常与数据库配合使用(数据库存核心数据,ES 存检索副本)。

典型使用场景:

  • 日志与监控:收集、存储和分析系统日志(如 ELK 栈)。
  • 全文检索:实现网站搜索、文档检索等功能。
  • 数据分析:通过聚合功能进行实时统计分析(如用户行为分析)。
  • 业务系统:替代传统数据库实现高并发的复杂查询。
    底层采用倒排索引:所以是分词拆解,模糊匹配

语法

索引创建&修改

索引创建:

js 复制代码
{
    "title": "小米14",
    "category": "小米",
    "images": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-shopapi-pms/pms_1698304477.56836008.png",
    "price": 3999.00
}

索引修改:

索引查询

索引查询:GET 请求

javascript 复制代码
http://127.0.0.1:9200/_cat/indices?v        //查询所有库
http://127.0.0.1:9200/shopping/_search      //查询指定库中所有
http://127.0.0.1:9200/shopping/_doc/1       //查询指定行

条件查询和分页查询

http://127.0.0.1:9200/shopping/_search,附带JSON体如下:

javascript 复制代码
//如品牌输入"小华",返回结果带回品牌有"小米"和华为的。
{
	"query": {
		"match": {
			"category": "小华"  //条件查询
		}
	}
}
==========================================
{
	"query":{
		"match_all":{}     //查询全部
	},
	"_source":["title"],   //指定查询想要的字段
	"from": 0,             //每页起始位置=(页码-1)* size
    "size": 1,             //每页显示多少条
    "sort": {              //排序
		"price": {
			"order": "desc"
		}
	}
}

多条件查询

must为必须全部满足,should满足一个即可

javascript 复制代码
{
	"query": {
        "bool": {         //条件查询
            "should": [
                {
                    "match": {
                        "category": "小米"
                    }
                },
                {
                    "match": {
                        "category": "华为"
                    }
                }
            ],
            "filter": {        //过滤条件
                "range": {     //范围查询
                    "price": {
                        "gt": 5000
                    }
                }
            }
        }
	}
}

精准匹配和高亮

javascript 复制代码
{
	"query": {
		"match_phrase": {    //精准匹配
			"category" : "小华"  //这样就查不到了
		}
	},
	"highlight": {
        "fields": {
            "category": {} // <----高亮此字段
        }
    }
}

聚合查询

javascript 复制代码
{
	"aggs": { // 聚合操作
        "price_group": { // 名称,随意起
            "terms": { // 分组
                "field": "price" // 分组字段
            }
        }
	},
  "size": 0 // 排除原始数据结果
}
===========================================
{
	"aggs": { 
        "price_avg": { 
            "avg": { // 平均值
                "field": "price" 
            }
        }
	},
    "size": 0 
}

映射关系

PUT http://127.0.0.1:9200/user新增数据库

javascript 复制代码
PUT http://127.0.0.1:9200/user/_mapping
{
    "properties": {
        "name": {
            "type": "text",  // 支持全文检索,即分词
            "index": true    //支持索引查询
        },
        "sex": {
            "type": "keyword", // 需要完全匹配
            "index": true
        },
        "tel": {
            "type": "keyword", // 需要完全匹配
            "index": false     //这个就不能查询
        }
    }
}

java API

依赖

xml 复制代码
<dependency>
   <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>7.8.0</version>
</dependency>
<!-- elasticsearch 的客户端 -->
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.8.0</version>
</dependency>

基本功能语句

java 复制代码
public class ESTest_Client {
    public static void main(String[] args) throws Exception {
        // 创建ES客户端
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost", 9200, "http"))
        );
 /**========================创建索引===================================**/
        CreateIndexRequest request = new CreateIndexRequest("user");
        CreateIndexResponse createIndexResponse =
                esClient.indices().create(request, RequestOptions.DEFAULT);
        // 响应状态
        boolean acknowledged = createIndexResponse.isAcknowledged();
        System.out.println("索引操作 :" + acknowledged);
/**=========================查看索引====================================**/
        GetIndexResponse getIndexResponse =
            esClient.indices().get(request, RequestOptions.DEFAULT);
	    System.out.println(getIndexResponse.getAliases());
	    System.out.println(getIndexResponse.getMappings());
	    System.out.println(getIndexResponse.getSettings());
/**=========================删除索引===================================**/
		DeleteIndexRequest request = new DeleteIndexRequest("user");
		AcknowledgedResponse response = 
		       esClient.indices().delete(request, RequestOptions.DEFAULT);
	    System.out.println(response.isAcknowledged());
/**=========================新增数据====================================**/
	   IndexRequest request = new IndexRequest();
	   request.index("user").id("1001");
	
	   User user = new User();
	   user.setName("zhangsan");
	   user.setAge(30);
	   user.setSex("男");
	
	   // 向ES插入数据,必须将数据转换位JSON格式
	   ObjectMapper mapper = new ObjectMapper();
	   String userJson = mapper.writeValueAsString(user);
	   request.source(userJson, XContentType.JSON);
	
	   IndexResponse response = 
	          esClient.index(request, RequestOptions.DEFAULT);
	   System.out.println(response.getResult());
/**=========================修改数据====================================**/
	   UpdateRequest request = new UpdateRequest();
	   request.index("user").id("1001");
	   request.doc(XContentType.JSON, "sex", "女");
	   UpdateResponse response = 
	         esClient.update(request, RequestOptions.DEFAULT);
	    System.out.println(response.getResult());
/**=========================查看数据====================================**/
	    GetRequest request = new GetRequest();
	    request.index("user").id("1001");
	    GetResponse response = 
	         esClient.get(request, RequestOptions.DEFAULT);	
	    System.out.println(response.getSourceAsString());
/**=========================删除数据====================================**/
		DeleteRequest request = new DeleteRequest();
		request.index("user").id("1001");
		DeleteResponse response = 
		         esClient.delete(request, RequestOptions.DEFAULT);
		System.out.println(response.toString());
/**=========================批量插入数据================================**/
	    BulkRequest request = new BulkRequest();
	    request.add(new IndexRequest().index("user").id("1001").source(XContentType.JSON, "name", "zhangsan", "age",30,"sex","男"));
	    request.add(new IndexRequest().index("user").id("1002").source(XContentType.JSON, "name", "lisi", "age",30,"sex","女"));
	    request.add(new IndexRequest().index("user").id("1003").source(XContentType.JSON, "name", "wangwu", "age",40,"sex","男"));	
	    BulkResponse response = 
	            esClient.bulk(request, RequestOptions.DEFAULT);
	    System.out.println(response.getTook());//花费时间
	    System.out.println(response.getItems());
/**===================================================================**/
        // 关闭ES客户端
        esClient.close();
    }
}

高级查询

java 复制代码
public class ESTest_Client {
    public static void main(String[] args) throws Exception {
        // 创建ES客户端
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost", 9200, "http"))
        );
        
	    SearchRequest request = new SearchRequest();
	    request.indices("user");
	    //查询全部数据
	    SearchSourceBuilder builder = new SearchSourceBuilder().query(QueryBuilders.matchAllQuery());
	    builder.from(0);  //分页
	    builder.size(3);
	    builder.sort("age", SortOrder.DESC);//排序
	    String[] excludes = {"age"};  //排除字段
	    String[] includes = {};       //包含字段
	    builder.fetchSource(includes, excludes); //过滤
	    request.source(builder);
	    //条件查询
	    //request.source(new SearchSourceBuilder().query(QueryBuilders.termQuery("age", 30)));
	    SearchResponse response = 
	          esClient.search(request, RequestOptions.DEFAULT);
	    SearchHits hits = response.getHits();
	    System.out.println(hits.getTotalHits());//总条数
	    System.out.println(response.getTook());//执行时间
	    for ( SearchHit hit : hits ) {
	        System.out.println(hit.getSourceAsString());//数据
	    }
	}
}

组合查询、范围查询、高亮

java 复制代码
public class ESTest_Client {
    public static void main(String[] args) throws Exception {
        // 创建ES客户端
        RestHighLevelClient esClient = new RestHighLevelClient(
                RestClient.builder(new HttpHost("localhost", 9200, "http"))
        );
	    SearchRequest request = new SearchRequest();
	    request.indices("user");
	    SearchSourceBuilder builder = new SearchSourceBuilder();
/**=========================组合查询====================================**/
	    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
	    //boolQueryBuilder.must(QueryBuilders.matchQuery("age", 30));
	    //boolQueryBuilder.must(QueryBuilders.matchQuery("sex", "男"));
	    //boolQueryBuilder.mustNot(QueryBuilders.matchQuery("sex", "男"));
	    boolQueryBuilder.should(QueryBuilders.matchQuery("age", 30));
	    boolQueryBuilder.should(QueryBuilders.matchQuery("age", 40));
	    builder.query(boolQueryBuilder);
/**=========================范围查询和高亮===============================**/
	    RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("age");
	    rangeQuery.gte(30);
	    rangeQuery.lt(50);
	    //高亮
	    HighlightBuilder highlightBuilder = new HighlightBuilder();
	    highlightBuilder.preTags("<font color='red'>");
	    highlightBuilder.postTags("</font>");
	    highlightBuilder.field("name");
	    builder.highlighter(highlightBuilder);
	    
	    builder.query(rangeQuery);
/**=========================模糊查询====================================**/
        //差异不超过两个字符的都可以查出来
        builder.query(QueryBuilders
              .fuzzyQuery("name", "wangwu").fuzziness(Fuzziness.TWO));
/**=========================聚合查询====================================**/
		AggregationBuilder aggregationBuilder = 
		          AggregationBuilders.max("maxAge").field("age");//最大年龄
		/**AggregationBuilder aggregationBuilder = 
		          AggregationBuilders.terms("ageGroup").field("age");*/
		builder.aggregation(aggregationBuilder);
/**====================================================================**/
	    request.source(builder);
	    SearchResponse response = 
	          esClient.search(request, RequestOptions.DEFAULT);
	    SearchHits hits = response.getHits();
	    for ( SearchHit hit : hits ) {
	        System.out.println(hit.getSourceAsString());//数据
	    }
	}
}

springData集成ES,通过ElasticsearchRepository(蕊抛自tory) 操作数据

windows集群配置

windows下:创建 elasticsearch-cluster 文件夹,在内部复制三个 elasticsearch 服务。修改集群文件目录中每个节点的 config/elasticsearch.yml 配置文件。

检测集群状态:http://127.0.0.1:1001/_cluster/health

yaml 复制代码
#集群名称,节点之间要保持一致
cluster.name: my-elasticsearch
#节点名称,集群内要唯一
node.name: node-1001
node.master: true
node.data: true
#ip 地址
network.host: localhost
#http 端口
http.port: 1001
#tcp 监听端口
transport.tcp.port: 9301
# 注释里面的在node2和node3里面需要配置,这是节点之间的关系
#discovery.seed_hosts: ["localhost:9301", "localhost:9302"]
#discovery.zen.fd.ping_timeout: 1m
#discovery.zen.fd.ping_retries: 5
#跨域配置
http.cors.enabled: true
http.cors.allow-origin: "*"

es系统架构

分片(shard):类似数据库的分表,提高吞吐量

副本(replicas):某个节点/分片宕掉,就需要副本来保证数据不丢失

分配:将分片分配给某个节点过程,包括分配主分片或者副本(他俩不能在一台机器)

执行PUT http://127.0.0.1:1001/users 设置分片和副本。

javascript 复制代码
{
    "settings":{
        "number_of_shards":3,
        "number_of_replicas":1
    }
}

使用浏览器扩展插件elasticsearch-head 查看集群情况。创建之初设置好了分片就不可以再改,后面可以改副本数量 http://127.0.0.1:1001/users/_settings

路由策略:存储数据猜出哈希算法,读数据可以任意节点,会找到对应节点,或者可以轮询

整体步骤:

  • 客户端请求集群任意节点
  • 协调节点通过路由策略将请求转换到指定节点
  • 主分片将数据保存
  • 主分片将数据发送给副本
  • 副本保存成功后反馈给主分片
  • 主分片给客户端反馈

consistency参数:参数值可以设为 one (只要主分片状态 ok 就允许执行写操作),all(必须要主分片和所有副本分片的状态没问题), 默认值为 quorum , 即大多数的分片副本状态没问题就ok。

倒排索引:先去词典查找词条---存在找到倒排列表,取文档id

  • 词条:索引中最小存储和查询单元
  • 词典:字典-词条的集合,B+、HashMap。key是词条,value存储倒排列表位置指针(该词条对应倒排列表的内存地址或磁盘偏移量)
  • 倒排表:关键词出现位置、频率、文档id

优化

  • 合理设置分片数量:需考虑node数量,一般都设置分片数不超过节点数的 3 倍

  • 推迟分片分配:对于节点瞬时中断的问题,默认情况,集群会等待一分钟来查看节点是否会重新加入,减少 ES 在自动再平衡可用分片时所带来的极大开销。

  • 优化硬件存储设备:ES 是一种密集使用磁盘的应用(段合并)

  • 合理使用段合并: ES 默认采用较保守的策略,让后台定期进行段合并

  • 减少 Refresh 的次数:延迟写入的策略,默认 1 秒触发一次 Refresh,然后 Refresh 会把内存中的的数据刷新到操作系统的文件缓存

  • 加大 Flush 设置:Flush 的主要目的是把文件缓存系统中的段持久化到硬盘,当 Translog 的数据量达到 512MB 或者 30 分钟时,会触发一次 Flush

  • 减少副本的数量:每个副本也会执行分析、索引及可能的合并过程,所以 Replicas 的数量会严重影响写索引的效率。当写索引时,需要把写入的数据都同步到副本节点,副本节点越多,写索引的效率就越慢。

文档

文档搜索

建立大的倒排索引并写入磁盘,倒排索引不可变。那么实现倒排索引的更新:就是用新的补充索引,通过新增索引来反应修改,而不是重写整个倒排索引,查完每个倒排索引后对结果进行合并。加标记代表被删除。

问题:索引越来越多、删除问题。ES 通过分段(Segment)机制+合并(Merge)策略。自动触发:当分段数量达到阈值(可配置)时,后台线程异步执行合并,会剔除标记为删除的数据,且合并后的大分段会替代参与合并的小分段,小分段随后被删除,释放磁盘空间。

文档近实时搜索:

文档分析(分析器)

  • 字符过滤器可以去掉HTML、将&转换为and等
  • 分词器
  • Token过滤器:大小写转换、删除无用词条、增加同义词

es内置分析器:GET http://localhost:9200/_analyze

javascript 复制代码
{
   "analyzer": "standard",
   "text": "text to analyze"
}
//可以加入中文分词器插件
{
   "analyzer": "ik_max_word",//细粒度拆分,ik_smart粗粒度
   "text": "测试单词"
}

扩展分词字典:在ik的config,创建custom.dic写入词条;修改IKAnalyzer.cfg.xml将custom.dic写入即可。

可以在创建索引的时候自定义分析器。

文档冲突

ES采用乐观锁version(if_seq_no&if_primary_term或者是version&version_type=external)

Kibana

Kibana下载,Kibana是一个免费且开放的用户界面,能够让你对Elasticsearch 数据进行可视化修改。 config/kibana.yml 文件

yml 复制代码
# 默认端口
server.port: 5601
# ES 服务器的地址
elasticsearch.hosts: ["http://localhost:9200"]
# 索引名
kibana.index: ".kibana"
# 支持中文
i18n.locale: "zh-CN"
相关推荐
大数据CLUB3 小时前
基于mapreduce的资金流入流出任务计算
大数据·hadoop·mapreduce
渡我白衣4 小时前
C++20 协程:在 AI 推理引擎中的深度应用
大数据·人工智能·c++20
躺着数星星4 小时前
Linux中安装es
linux·elasticsearch·jenkins
小树苗1935 小时前
Berachain稳定币使用指南:HONEY与跨链稳定币的协同之道
大数据·人工智能·区块链
电商API_180079052476 小时前
电商数据分析之自动获取数据的技术手段分享
大数据·数据库·数据挖掘·数据分析
Elastic 中国社区官方博客11 小时前
AutoOps:简单的 Elasticsearch 集群监控与管理现已支持本地部署
大数据·人工智能·elasticsearch·搜索引擎·云计算·全文检索
云手机掌柜13 小时前
技术深度解析:指纹云手机如何通过设备指纹隔离技术重塑多账号安全管理
大数据·服务器·安全·智能手机·矩阵·云计算
Turboex邮件分享14 小时前
Syslog日志集成搭建
运维·elasticsearch·集成测试
计算机毕设残哥16 小时前
基于Hadoop+Spark的人体体能数据分析与可视化系统开源实现
大数据·hadoop·python·scrapy·数据分析·spark·dash