ElasticSearch

Es学习

搭建##

前置条件##

下文的所有功能均按照这些版本开发和描述,电脑版本Win11

  • Java的JDK:java version "1.8.0_181",安装ES之前需要先准备好Java的环境,因为Elasticsearch 使用 Java 构建。
  • ElasticSearch的安装包:elasticsearch-8.8.1-windows-x86_64
  • Node.js环境:Kibana需要node.js环境,判断是否有安装node。可以在cmd输入 node-v
  • Kibana的安装包:kibana的版本与ES的版本需要完全一致,如果不一致则无法正常使用
  • IK分词器:版本与ES的版本需要完全一致,如果不一致则无法正常使用

安装ElasticSearch##

最新安装包地址##

更多版本安装包地址##

安装教程##

  1. 将下载好的Zip文件解压到自定义的目录,建议文件夹名称为英文

    解压后的文件夹构成:

    • bin:存放关于命令、ES服务启动/关闭的文件夹(elasticsearch.bat)
    • config:存放配置ES的文件夹(elasticsearch.yml)
    • lib:存放Es的jar包依赖库
    • logs:日志存放
    • modules:es依赖的组件,在启动的时候加载
    • plugins:存放第三方插件,如IK分词器
  2. 双击bin文件夹中的elasticsearch.bat文件,会弹出一个控制台窗体,等待大约1分钟左右,启动后不要关闭窗体,关闭窗体代表关闭ES服务。

    注意:

    • 首次启动的时候,ES会默认启用身份验证和授权,会在控制台打印出默认的用户名和密码,如果打印的信息过多,可以全文搜索password这个字段,后面那串就是用户名和密码,记得把他保存下来
  3. 在浏览器访问http://localhost:9200,如果能正常访问,要求输入密码或返回一大段json则代表启动成功,如果提示失败,请查看控制台的错误信息

    注意:启动成功后会有2个端口一个是9200,为客户端访问端口,http协议。一个是9300,集群节点间通讯端口,接受tcp协议。

  4. 可以将ElasticSearch 安装为在后台运行或在启动时自动启动的服务,无需用户交互

    • 在控制台cd到bin文件夹目录,执行命令elasticsearch-service.bat install(如果之前有注册,install改为remove)
    • 继续执行命令elasticsearch-service.bat manager,选择java-勾选use default,选择general,下面的startup type 改为automatic,然后点击start
    • 打开控制面板(win+r-services.msc),查看服务是否成功运行

常用配置(elasticsearch.yml文件)##

  • 修改索引存放位置

    增加节点:path.data: 自定义目录,

  • 修改日志存放位置

    增加节点:path.logs: 自定义目录

  • 允许跨域

    增加节点:http.cors.enabled: true ,http.cors.allow-origin: "*"

安装Kibana##

最新安装包地址##

注意:Kibana需要Node.js的环境,需要和ES的版本一致,否则无法使用,下载的时候确定好对应的关系!!!!!!!!!

更多版本安装包地址##

安装教程##

  1. 将下载好的Zip文件解压到自定义的目录,建议文件夹名称为英文
  2. 双击bin文件夹下的kibana.bat,成功启动后,会出现提示,在浏览器输入http://localhost:5601/,如果能正常出现界面则代表启动成功,注意,需要先启动ES。
  3. kibana默认根据es服务启动了身份认证,所以按照提示获取token来注册
    • 首先,在es的bin文件夹下执行命令elasticsearch-create-enrollment-token.bat --scope kibana 获取密钥
    • 第二,在kibana的bin文件夹下执行命令kibana-verification-code.bat,获取验证码
    • 第三,输入es第一次启动的时候给的默认账户和密码

常用配置(config文件夹下的kibana.yml)##

  • 修改系统语言为中文

    修改节点:i18n.locale: "zh-CN"

  • 配置ES端口

    修改节点:elasticsearch.url: "https://localhost:9200"

配置中文分词器##

下载地址##

版本与ES的版本需要完全一致,如果不一致则无法正常使用

安装##

  1. 在ES的plugins创建文件夹ik,将配置文件之类的复制进去
  2. 重启ES服务,ES的控制台会打印loaded plugin [analysis-ik]这段信息即代表成功

介绍/注意##

介绍分词器作用

ES分词的操作是在添加数据的时候完成的!同样的,在查询索引数据的时候,也会先根据查询的关键词进行分词然后匹配, 在分词后,会在底层产生倒排索引,倒排索引就是Es能提供快速查询的核心,倒排索引表包含: 单词id,单词,文档频率(在整个索引中有多少个文档包含了单词),倒排列表<文档id,出现的次数,位置>

  1. ik_max_word:最细粒度的分割

    比如分割“我喜欢吃好吃的苹果”分割为“我喜欢、好吃、好吃的苹果、吃的苹果、苹果、喜欢、我”等,会穷尽各种可能的组合,建议添加数据的时候使用

  2. ik_smart: 会做最粗粒度的分割

    比如分割“我喜欢吃好吃的苹果”分割为“我喜欢吃好吃的苹果、好吃的苹果”,建议查询数据的时候使用

注意

由于ES中(5.0以后),文本类型分为Text和KeyWord2种类型,其中对于Text会进行分词处理,一般用于存储长文本,不能用于过滤,排序,聚合等操作,一般使用全文搜索,会在倒排索引中插入数据,对于KeyWord不会分词,一般用于短文本,用于过滤,排序,聚合等操作,一般使用术语级查询,会生成一个带有.keyword的子字段用于保存数据

Nest使用##

调试分词器Demo,判断ik分词器是否起作用了

        /// <summary>
        /// 调试分词器
        /// </summary>
        /// <param name="value"></param>
        public async Task<dynamic> TestAnalyze(string value)
        {
            // 分词器包含standard,whitespace
            AnalyzeResponse analyzeResponse = await _elasticClient.Indices.AnalyzeAsync(a => a
                                                                    .Tokenizer("ik_max_word") // 指定的分词器
                                                                    .Filter("lowercase", "stop") // 指定token处理器,小写/去除停用词
                                                                    .Text(value) // 需要处理的文本                                                
                                                         );

            return analyzeResponse.Tokens;
        }

配置分词器到指定模型上Demo

配置后,

[Text(Name = nameof(Content), Index = true, Analyzer = "ik_max_word")]
 public string? Content { get; set; }

常用配置文件##

在config文件夹下面很多的文件

  • main.dic / extra_main.dic:储存的中文分词/拓展的中文分词,只要是文本在里面的词,都会被分词

  • surname.dic:存储的姓氏分词,如刘、夏、莫等

  • surname.dic:存储的后缀分词,如省、市、海、湖、巷

  • quantifier.dic:存储的量词分词,如年、月、平米、千米、男、女、眼等

  • preposition.dic:存储的介词分词,如但、的、又、却等

  • extra_single_word.dic:存储的常见单字分词

  • extra_single_word_low_freq.dic:存储的少见的分词,比如一些繁体字或者文言文字体,当然我们也可以把一些网络用语或者谐音字放在这里面

  • extra_single_word_full.dic:存储的全部常见单字分词

  • extra_stopword.dic / stopword.dic:拓展 / 英文停用词字典

    为,在字典内配置的词语不会出现在分词器中,比如“和”、“虽然”、“啊”等语气助词不需要建立倒排索引就可以放在拓展停用词字典里面,因为默认不启用extra_stopword.dic,所以需要在ik的配置文件IKAnalyzer.cfg.xml中配置extra_stopword.dic

  • remote_ext_dict / remote_ext_stopwords:远程拓展字典 / 远程停用字典

    配置在几点节点

    可以将需要自动更新的热词放在一个UTF-8编码的.txt文件里,放在nginx或其他简单的http服务器下,当.txt文件修改时,http服务器会在客户端请求该文件时自动返回相应的 Last-Modified 和 ETag。还可以做一个工具来从业务系统中提取相关词汇,并更新这个 .txt 文件。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
	<comment>IK Analyzer 扩展配置</comment>
	<!--用户可以在这里配置自己的扩展字典 -->
	<entry key="ext_dict"></entry>
	 <!--用户可以在这里配置自己的扩展停止词字典-->
	<entry key="ext_stopwords"></entry>
	<!--用户可以在这里配置远程扩展字典 -->
	<!-- <entry key="remote_ext_dict">指向文件的地址</entry> -->
	<!--用户可以在这里配置远程扩展停止词字典-->
	<!-- <entry key="remote_ext_stopwords">指向文件的地址</entry> -->
</properties>

数据结构:##

由于ES是面向文档的,

Index索引:##

这个索引不要代入到我们数据库当中的索引

注意!!!!

  • 索引的名称必须为小写
  • 一个集群可以有多个索引!
  • 索引具有mapping和setting的概念,mapping决定索引中Document的映射关系,setting决定索引的数据分布的配置!

Document文档:##

存储在Index中的单元,一个Document代表一行数据(Json的形式存储),一个文档除了保存我们放入的数据之外,还会额外保存一些元数据

注意!!!

  • 文档包含字段,每个字段类型都有数据类型,如布尔、数字、字符串等,如果在创建的时候没有指定,默认自动匹配!
  • 每个文档都有主键,默认拿名称为Id的字段,也可以手动指定
  • 文档除了保存放入的数据外,还会保存一些额外的元数据
{
  "_index": "postindex", // 归属索引
  "_id": "443140808372293", // 主键Id
  "_version": 1, // 版本,即代表这条数据有无被修改过,如果被修改了则+1,在Es中,是没有修改的概念的,他是删除了在增加一条
  "_score": 1, // 相关性打分,这个作用可以在后续的文章中介绍
  "fields": {   
    "Title": [
      "测试"
    ],
    "Id": [
      443140808372293
    ]
  }
}

Shard分片:##

分片的原因是,当文档的数量太大的时候,响应可能会很慢,所以我们在创建索引的时候,确定好分片数量,比如3个分片,每个分片放在不同的服务器中,那么当我们往索引中插入一条文档的时候,ES会将这个文档分为很多部分,分别放在不同的分片中,当你发送请求的时候,ES会分别从3个分片中查询数据,然后组合在一起返回给用户。

分片分为主分片和副本分片,主分片用于主要的查询和存储功能,副本分片用于存储数据,这样当主分片挂掉了,副本分片能及时顶上去工作,一个索引最多拥有5个主分片,一个主分片可以有多个副本分片,在创建完索引后,主分片的数量不可更改,副本数量可以调整。

因为插入文档的时候,先插入主分片然后再插入副分片,那么我们可以就考虑主存副读来实现读写分离。

如果一个索引的Index大小不会超过20GB,那么无需考虑多个分片,设置为一个主分片,1/2的副本分片即可。

字段数据类型##

通常情况下,如果我们创建Index的时候指定了自动映射,那么ES会自动完成ES与C#数据类型的映射关系

只介绍常用类型,更多类型查看官网

特性:Nest中的特性

ES数据类型 C#中的数据类型 注意事项
Text string 分词器于Text会进行分词处理,一般用于存储长文本,不能用于过滤,排序,聚合等操作,可以通过特性TextAttribute指定属性映射为Text类型
KeyWord string 分词器对于KeyWord不会分词,一般用于短文本,用于过滤,排序,聚合等操作,可以通过特性KeywordAttribute指定属性映射为KeyWord类型。
long long long类型可能会查询后返回的数据存在精度丢失的问题,长度最好不能超过16,否则会丢失后面,存是没问题的,查就有问题了,所以当超过16位的时候,最好设置他为keyword。
doubel和float类型 double/float 使用聚合函数sum等查询的时候会出现精度丢失的问题
date DateTime ES 同步时间与真实时间相差8个小时,因为ES采取的格式位UTC,如果是时间还是推荐存为long类型时间戳
boolean bool 布尔类型,
Array List array 在 Elasticsearch 中,没有专用的array数据类型。默认情况下,任何字段都可以包含零个或多个值,但是数组中的所有值必须具有相同的数据类型。例如:
integer int
short short
byte byte
binary 可以用来存储Base64字符串,属于这个类型的字段默认不可以被搜索到
object object
nested 嵌套类型

安装包Nest##

在NetCore我们我们操作ElasticSearch通常通过Nest这个包

版本:7.17.5

NEST 是一个高级 Elasticsearch .NET 客户端,它仍然与原始 Elasticsearch API 非常接近。所有请求和响应都通过类型公开,使其成为快速启动和运行的理想选择。
在幕后,NEST 使用Elasticsearch.Net 低级客户端来分派请求和响应,使用和扩展 Elasticsearch.Net 中的许多类型。低级别客户端本身仍然通过该属性暴露在高级客户端上。

连接池:获取ElasticClient##

概念##

在了解IElasticClient之前,我们先了解一下,ES的连接池的概念。

连接池是一种内部机制,负责注册集群中有哪些节点以及哪些 NEST 可用于发出客户端调用。尽管有这个名称,NEST 中的连接池并不像您在使用 ADO.Net 与数据库交互时所熟悉的连接池 ;例如,NEST 中的连接池不负责管理与 Elasticsearch 的 TCP 连接的底层池,这是由 Desktop CLR 中的 ServicePointManager 处理的。

那么,NEST 中的连接池负责什么呢?它负责管理 Elasticsearch 集群中可以建立连接的节点,并且有一个的实例IConnectionPool与 的实例关联ConnectionSettings。由于建议在应用程序的生命周期内使用单个客户端和连接设置实例,因此单个连接池实例的生命周期也将与应用程序的生命周期绑定。

简单来说,ES的连接池不是用来管理客户端与ES的连接的,而是用于管理ES可以连接的节点列表,比如ES集群有2个节点,那么ES的连接池就有2个连接。当客户端发送查询等请求的时候,会从连接池中取得一个状态比较好的稳定的节点连接来进行操作,如果节点因为死机、断网等原因,没有办法提供服务的时候,连接池就会标记该节点为死节点(可通过配置尝试重连),所以ES的连接池是用来管理节点的而非连接的。

ConnectionPool 和ConnectionSettings是配套使用的,一个pool对应一个settings

连接池一共有5种:

  • 单节点连接池:SingleNodeConnectionPool
  • 云连接池:CloudConnectionPool
  • 静态连接池:StaticConnectionPool
  • 嗅探连接池:SniffingConnectionPool
  • 粘性连接池:

单节点连接池##

适用场景:当你的节点只有一个的时候,那么请采用单节点连接池

注意:ES官方的建议是将ElasticClient注册为单例模式,因为ElasticClient是线程安全的,为什么要注册为单例呢,是为了缓存ConnectionSettings。且SingleNodeConnectionPool不会标记连接是否为死结点(即不会触发嗅探和ping操作),默认永远调用这个节点的连接。

demo:

单节点应用池一共有2种写法(ES官方推荐使用第一种写法)

  • 第一种写法:需要显示的将连接池传递给ConnectionSettings的构造函数,这样做的目的是为了让所有的请求统一通过连接池来完成调用
  • 第二种写法:无需传递ConnectionSettings,通过Url来连接到ES,所有的请求也由Uri来发起调用,而非连接池。这种写法内部实现依旧采用SingleNodeConnectionPool实现
using Nest;
using Elasticsearch.Net;
namespace Test
{
    public class ElasticSearchHelper : IElasticSearchHelper
    {
        // 实现获取ElasticClient的方法
        public IElasticClient GetClient()
        {
            // Es节点连接
            Uri uri = new("http://localhost:9200");
            #region 第一种
            // 构造单节点应用池
            SingleNodeConnectionPool pool = new(uri);
            // 构造配置文件
            ConnectionSettings settings = new(pool);
            // 构造连接池
            ElasticClient client1 = new(settings);
            #endregion

            #region 第二种
            // 直接传入uri
            ElasticClient client2 = new(uri);
            #endregion
            return client1;
        }
    }
}

静态连接池##

ping操作:ping操作指的是ES会定期向节点发送ping请求,如果节点未响应则将节点连接移除连接池或标记为死节点,以达到集群的稳定性和可用性。

适用场景:如果您有一个已知的小型集群并且不想启用嗅探来找出集群拓扑,那么静态连接池非常有用。

注意:静态连接池不支持嗅探操作,但是支持配置ping操作来检测连接是否为死结点

demo:

using Nest;
using Elasticsearch.Net;
namespace Test
{
    public class ElasticSearchHelper : IElasticSearchHelper
    {
        // 实现获取ElasticClient的方法
        public IElasticClient GetClient()
        {
            #region 第一种使用uri来
            Uri[] uris = new Uri[] { new Uri("http://localhost:9200"), new Uri("http://localhost:9100") };
            // 构建静态连接池
            StaticConnectionPool pool1 = new(uris);
            // 构建配置文件
            ConnectionSettings settings1 = new(pool1);
            // 返回客户端
            return new ElasticClient(settings1);
            #endregion

            #region 第二种使用node
            Node[] nodes = new Node[] { new Uri("http://localhost:9200"), new Uri("http://localhost:9100") };
            // 构建静态连接池
            StaticConnectionPool pool2 = new(uris);
            // 构建配置文件
            ConnectionSettings settings2 = new(pool2);
            // 返回客户端
            return new ElasticClient(settings2);
            #endregion
        }
    }
}

嗅探连接池##

嗅探:嗅探是指的ES会主动嗅探整个集群的状态,获取集群中的节点并把他们添加到客户端中,好处就是无需手动配置所有节点,ES会自动查找、更新节点的状态。嗅探只在设置为true的时候启用。

适用场景:StaticConnectionPool嗅探连接池派生的池允许在运行时重新播种。它带来了非常小的开销ReaderWriterLockSlim来确保线程安全。

注意:支持嗅探操作和ping操作,建议节点结合使用已知的集合

demo:

using Nest;
using Elasticsearch.Net;
namespace Test
{
    public class ElasticSearchHelper : IElasticSearchHelper
    {
        // 实现获取ElasticClient的方法
        public IElasticClient GetClient()
        {
            // 获取端口范围
            var uris = Enumerable.Range(9200, 5).Select(port => new Uri($"http://localhost:{port}"));
            // 构建嗅探连接池
            SniffingConnectionPool pool = new(uris);
            ConnectionSettings settings = new(pool);
            return new ElasticClient(settings);

            // 使用node
            var nodes = uris.Select(u => new Node(u));
            var pool2 = new SniffingConnectionPool(nodes);
            var client1 = new ElasticClient(new ConnectionSettings(pool2));
            return client1;
        }
    }
}

连接配置:ConnectionSettings##

使用Elasticsearch.Net和NEST连接到 Elasticsearch很容易,但您完全有可能想要更改默认连接行为。有许多配置选项可用于ConnectionConfiguration低级别客户端和ConnectionSettings高级客户端,可用于控制客户端与 Elasticsearch 的交互方式。

在上文中,我们反复提到了这个类,这个类就是用来配置一些我们一些连接行为。

以下settings指的是ConnectionSettings的实例,配置并不完整,我只列举大概所用到的。

方法 作用 备注 是否为常用配置
settings.RequestTimeout(TimeSpan.FromSeconds(15)); 允许连接超时时间 单位毫秒,默认60
settings.ConnectionLimit(80); 限制可以向端点打开的并发连接数。 默认为80
settings.DisablePing(false); 禁止ping,当一个节点第一次使用时,或者当它被标记为死亡后第一次使用时,会向该节点发送一个超时时间非常低的 ping,以确保当它仍然死亡时,它会以最快的速度报告它。可能的。如果您希望在可能较慢的原始请求上失败。 默认false
settings.PingTimeout(TimeSpan.FromSeconds(15)); 设置 ping 请求的默认 ping 超时(以毫秒为单位),用于确定节点是否处于活动状态。Ping 应尽快失败。 单位毫秒,给的值不应该太大。
settings.DefaultIndex(indexName); 配置这个连接默认使用使用操作的的索引。 indexName指的是索引名称
settings.EnableDebugModel(); 配置后,可以在ISearchResponse.ApiCall.DebugInformation查看更多的调试信息(就是我们使用查询后返回的结果) 与DisableDirectStreaming()或者PrettyJson()使用,可以获取完整的报错信息。
settings.DisableDirectStreaming(true); 禁止流处理,确保响应字节始终可用ElasticsearchResponse<T> 默认为false,设置为true的时候,,这可能会导致响应首先缓冲在内存中,从而可能影响性能。
settings.PrettyJson(true); 强制所有请求附加 ?pretty=true 查询字符串参数,这个参数是用来决定ES默认返回json 默认为false
settings.DefaultFieldNameInferrer(p => p); 采用模型(我们自己编写的Index实体类)的属性名称,因为Nest默认默认是使用驼峰属性名称,即首字母小写,如果我们在使用查询的时候,传入指定字段名称,比如Name,由于Nest默认是name,就可能导致翻译的DSL语句不是我们想要的正确翻译。
settings.DefaultMappingFor(p => p) 指定如何为给定的 CLR 类型推断映射。该映射可以推断给定 CLR 类型的索引、id 和关系名称,以及控制 CLR 属性的序列化行为。
settings.DefaultDisableIdInference(true); 禁用ES自动推断ID,默认ES会采用属性名称为Id的字段作为主键,我们可以设置为true。并结合[ElasticsearchType(IdProperty = "自定义ID名称")]这个特性来设置Index的主键
settings.BasicAuthentication("用户名", "密码"); 与所有请求一起发送到 Elasticsearch 的基本身份验证凭据
settings.EnableHttpCompression(false); 启用 gzip 压缩请求和响应 默认为false,需要在 Elasticsearch 上配置 http 压缩才能使用此功能
settings.GlobalHeaders(); 设置每个请求需要携带的请求头
settings.GlobalQueryStringParameters(nameValue); 设置全局查询字符串,比如我们常用的假删除过滤器。设置后每个查询都会携带此条件 完整用法:
NameValueCollection nameValue = new();
nameValue.Add("Key", "Value");
settings.GlobalQueryStringParameters(nameValue);
settings.IncludeServerStackTraceOnError(true); 强制所有请求附加 ?error_trace=true 查询字符串参数,导致 Elasticsearch 返回堆栈跟踪作为序列化异常的一部分 默认为false
settings.MaximumRetries(1); 给定请求的最大重试次数
settings.OnRequestCompleted(apiCallDetails =>
{
// 回调事件
});
注册ES服务端接收到了请求所需要触发的事件。 这个功能可以实现日志记录啊之类的功能。可以注册多个回调事件
settings.OnRequestDataCreated(apiCallDetails =>
{
// 回调事件
});
注册在ES服务端处理完数据后,即响应的时候被触发的事件。 可以注册多个回调事件

作者:boise

出处:https://www.cnblogs.com/boise/p/18104280

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Ghetto_richh  阅读(42)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示
主题色彩