1. ElasticSearch的介绍
ElasticSearch的简称是ES. ES是内存数据库的一种,其核心的内容是,分词,全文检索,高性能.使用的场景一般有商城,订单,日志收集等.日志收集一般会搭配logstash与Kibana一起使用,简称ELK.logstash的作用是收集数据,Kibana的作用是ES数据库的管理工具并且附带报表生成的功能.本文后半段会附带ELK的搭建.
2. ElasticSearch提供的查询方式
-
关键字匹配:Match , Mulit Match.
-
条件过滤(Filter):Term , Terms ,Range ,Geo Distance .
-
字段排序: Sort(on specific fields ) , Geo Distance Sorting .
-
打分排序: Script Based Sorting , Function Score , Rescoring .
-
或与非(Bool Query) : must , filter , should , must_not .
3. ElasticSearch的结构
为了更好的解释ES的结构,现在通过与关系型数据库的对比说明 .
以下是ES与关系型数据库的结构对应关系:
关系型数据库 | ElasticSearch |
---|---|
database(数据库) | index(索引库) |
table(表) | type(类型) |
row(行) | document(文档) |
column(列) | field(字段) |
注:
-
在ES8版本之后对type(类型)去掉了 , 库下面是直接放的document(文档) .
-
对某一字段做分词的时候,最好字段内容短一些 .
4. ElasticSearch的调用
ElasticSearch是由java写的, 底层是Lucene,封装后的ElasticSearch 具有了并发能力.
ElasticSearch 提供的接口风格是rest 的所以它可以跨语言使用 . 以下是它所提供的接口:
method | URL地址 | 描述 |
---|---|---|
PUT | localhost:9200/索引名称/类型名称/文档id | 创建文档(指定文档ID) |
POST | localhost:9200/索引名称/类型名称 | 创建文档(随机文档ID) |
POST | localhost:9200/索引名称/类型名称/文档id/_updata | 修改文档 |
DELETE | localhost:9200/索引名称/类型名称/文档id | 删除文档 |
GET | localhost:9200/索引名称/类型名称/文档id | 查询文档通过文档ID |
POST | localhost:9200/索引名称/类型名称/_search | 查询所有数据 |
5. 环境的搭建
现在提供是Docker的部署:
5.1. 单节点集群部署
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms100m -Xmx200m" elasticsearch:7.2.0
说明:
-
ES_JAVA_OPTS="-Xms100m -Xmx200m" 是内存使用
-
"discovery.type=single-node" 单点集群
安装成功后:访问(http://IP:9200/)
返回如下内容表示成功:
{
"name" : "695644ec6d0d",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "3BtyZmyJSDeoJwYZ7x3q8g",
"version" : {
"number" : "7.2.0",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "508c38a",
"build_date" : "2019-06-20T15:54:18.811730Z",
"build_snapshot" : false,
"lucene_version" : "8.0.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
通过网页插件Elasticvue 登录界面
1.jpg
Kibana 的安装
docker run -p 5601:5601 -d -e ELASTICSEARCH_URL=http://IP:9200 -e ELASTICSEARCH_HOSTS=http://IP:9200 kibana:7.2.0
说明:
- 其中的IP为上一步安装ES的IP地址.
安装成功后:访问(http://IP:5601/)
可以在Console 中执行简单的命令 : 具体查看(4. ElasticSearch的调用)的调用集合.
例如:增加 .其他的查看 4. ElasticSearch的调用
PUT myes/_doc/1
{
"Name" = "sss",
}
6. ElasticSearch的基本操作
6.1 创建索引插入数据
#删除索引库
DELETE /testindex
#创建索引库"type": "keyword"字符串类型不做分词,"type": "text"字符串类型做分词
PUT /testindex
{
"mappings": {
"properties": {
"name":{
"type": "keyword"
},
"address":{
"type": "text"
},
"age":{
"type": "integer"
}
}
}
}
#插入数据 可以自己生成id //
POST /testindex/_doc
{
"name":"张三",
"address":"山西",
"age":18
}
POST /testindex/_doc
{
"name":"李四",
"address":"河北",
"age":19
}
POST /testindex/_doc
{
"name":"王五",
"address":"山东",
"age":37
}
POST /testindex/_doc
{
"name":"赵六",
"address":["陕西","山东"],
"age":25
}
6.2 排序
以下是排序的查询:
GET testindex/_doc/_search
{
"query":{
"match": {
"address": "山西"
}
},
"_source":["name", "age" ,"address"],
"sort":[
{"age":{"order":"desc"}},
{"name":{"order":"asc"}}
]
}
注意:
- 查询条件是 address ,而address text类型的数据,ES中会自动的做分词,所以出来的结果,有山西,山东。
6.3 分页查询
分页查询多了两个字段:from :开始位置,size :条数
GET testindex/_doc/_search
{
"query":{
"match": {
"address": "山西"
}
},
"_source":["name"],
"sort":[
{"age":{"order":"desc"}},
{"name":{"order":"asc"}}
],
"from":0,
"size":10
}
6.4 BOOL值查询
and 逻辑使用 must
GET testindex/_doc/_search
{
"query":{
"bool": {
"must": [
{
"match": {
"address": "山西"
}
},{
"match": {
"name": "张三"
}
}
]
}
},
"_source":["name"],
"sort":[
{"age":{"order":"desc"}},
{"name":{"order":"asc"}}
],
"from":0,
"size":2
}
or 逻辑使用 should
GET testindex/_doc/_search
{
"query":{
"bool": {
"should": [
{
"match": {
"address": "魏国"
}
},{
"match": {
"name": "曹操"
}
}
]
}
},
"_source":["name"],
"sort":[
{"age":{"order":"desc"}},
{"name":{"order":"asc"}}
],
"from":0,
"size":10
}
6.5 排除查询
must_not 与之不匹配的查询
#查询名字不是张三的信息
GET testindex/_doc/_search
{
"query":{
"bool": {
"must_not": [
{
"match": { "name": "张三"}
}
]
}
}
}
6.6 FILTER数据过滤
gt 大于, gte 大于等于, lt 小于 ,lte 小于等于
GET testindex/_doc/_search
{
"query":{
"bool": {
"must": [
{
"match": {"name": "张三"}
}
],
"filter": {
"range": {
"age": {
"gte": 10,
"lte": 20
}
}
}
}
}
}
6.7 配备多个条件查询
查询是山东,又是山西的
GET testindex/_doc/_search
{
"query":{
"match":{"address":"山东 山西 "}
}
}
6.8 高亮查询
- 高亮查询
GET testindex/_doc/_search { "query":{ "match": { "name": "张三" } }, "highlight":{ "fields": { "name": {} } } } # 返回结果是:<em> 张三</em>
-
自定义高亮风格
就是在返回的结果中,加上自定义的标签,属性。
GET testindex/_doc/_search { "query":{ "match": { "name": "张三" } }, "highlight":{ # 前面加的内容 "pre_tags": "<p class='zhangs'>", # 后面加的内容 "post_tags": "</p>", "fields": { "name": {} } } } # 返回结果是:<p class='zhangs'> 张三</p>
- 多条件多字段高亮查询
```json
GET testindex/_doc/_search
{
"query":{
"bool": {
"must": [
{
"match": {
"address": "山西"
}
},{
"match": {
"name": "张三"
}
}
]
}
},
"highlight":{
"pre_tags": "<p class='zhangs'>",
"post_tags": "</p>",
"fields": [
{"name": {}},
{"address": {}}
]
}
}
```
6.9 SQL 查询
- .Net代码对接中使用的方式。
- 进入容器 docker exec -it 容器ID /bin/bash ,然后进入 bin 文件夹执行:elasticsearch-sql-cli, 然后就可以执行SQL语句了。
- 注意,可以进行跨库子查询,但是不能做连接查询,像左连接,右连接等。
7. 聚合查询
准备数据创建索引库,以音乐库为例:ID ,作者,歌名,点赞数,语言等。
PUT /music
{
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"author": {
"type": "text",
"analyzer": "english",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"language": {
"type": "text",
"analyzer": "english",
"fielddata": true
},
"tags": {
"type": "text",
"analyzer": "english"
},
"length": {
"type": "long"
},
"likes": {
"type": "long"
},
"isRelease": {
"type": "boolean"
},
"releaseDate": {
"type": "date"
}
}
}
}
增加数据:使用批量增加,_bulk。注意批量增加每条数据之前的加上 { "index": {}}。
POST /music/_doc/_bulk
{ "index": {}}
{ "author" : "zhangsan", "name" : "red", "content" : "honda", "language":"ch","tags":"tags","length":3,"likes":10,"isRelease":true,"releaseDate" : "2021-10-28" }
{ "index": {}}
{ "author" : "lisi", "name" : "blue", "content" : "honda", "language":"en","tags":"tags","length":3,"likes":100,"isRelease":true,"releaseDate" : "2021-10-28" }
{ "index": {}}
{ "author" : "zhangsan1", "name" : "red", "content" : "honda", "language":"ch","tags":"tags","length":30,"likes":10,"isRelease":true,"releaseDate" : "2021-11-28" }
{ "index": {}}
{ "author" : "lisi1", "name" : "blue", "content" : "honda", "language":"en","tags":"tags","length":30,"likes":100,"isRelease":true,"releaseDate" : "2021-11-28" }
{ "index": {}}
{ "author" : "zhangsan2", "name" : "red", "content" : "honda", "language":"ch","tags":"tags","length":80,"likes":10,"isRelease":true,"releaseDate" : "2021-11-28" }
{ "index": {}}
{ "author" : "lisi2", "name" : "blue", "content" : "honda", "language":"en","tags":"tags","length":80,"likes":100,"isRelease":true,"releaseDate" : "2022-10-28" }
{ "index": {}}
{ "author" : "zhangsan2", "name" : "red", "content" : "honda", "language":"ch","tags":"tags","length":80,"likes":10,"isRelease":true,"releaseDate" : "2022-10-28" }
{ "index": {}}
{ "author" : "lisi2", "name" : "blue", "content" : "honda", "language":"en","tags":"tags","length":80,"likes":100,"isRelease":true,"releaseDate" : "2022-10-28" }
-
统计每种语言的歌曲数量
GET /music/_doc/_search { "size": 0, "aggs": { "song_qty_by_language": { "terms": { "field": "language" } } } }
- size:0 表示只要统计后的结果,原始数据不展现,如果是大于0的,则会返回原数据
- aggs:固定语法 ,聚合分析都要声明aggs
- song_qty_by_language:聚合的名称,可以随便写,建议规范命名
- terms:按什么字段进行分组
- field:具体的字段名称
-
按语种统计歌曲的平均时长,最大时长,最短时长,总时长
GET /music/_doc/_search { "size": 0, "aggs": { "color": { "terms": { "field": "language" }, "aggs": { "length_avg": { "avg": { "field": "length" } }, "length_max": { "max": { "field": "length" } }, "length_min": { "min": { "field": "length" } }, "length_sum": { "sum": { "field": "length" } } } } } }GET /music/_doc/_search { "size": 0, "aggs": { "lang": { "terms": { "field": "language" }, "aggs": { "length_avg": { "avg": { "field": "length" } } } } } }
-
按时长分段统计统计歌曲平均时长
以30秒为一段,看各段区间的平均值。interval:30表示分的区间段为[0,30),[30,60),[60,90),[90,120)
GET /music/_doc/_search { "size": 0, "aggs": { "sales_price_range": { "histogram": { "field": "length", "interval": 30 }, "aggs": { "length_avg": { "avg": { "field": "length" } } } } } }
-
按日期分段统计歌曲数量
GET /music/_doc/_search { "size": 0, "aggs": { "sales": { "date_histogram": { "field": "releaseDate", "interval": "month", "format": "yyyy-MM-dd", "min_doc_count": 0, "extended_bounds": { "min": "2000-10-01", "max": "2088-12-31" } } } } }
8. .NET 的代码对接
Nuget .安装Nest 下面是以单节点集群连接
-
原始方式的查询
var settings = new ConnectionSettings(new Uri(“http://IP:9200/”)).DefaultIndex("people"); var client = new ElasticClient(settings); //查询张三 var searchResponse = client.Search<Person>(s => s.From(0).Size(10).Query(q => q.Match(m => m.Field(f => f.FirstName).Query("张三")))); var people = searchResponse.Documents; foreach (var item in people) { Console.WriteLine($"id:{item.Id},firstname:{item.FirstName},lastname:{item.LastName}"); }
缺点:学习查询方式成本高。虽然可以使用ElasticHD.exe 负责但是成本还个很高。
-
以ES提供的SQL模式查询:
使用:http://IP:9200_xpack/sql?format=csv
//调用:SendByWebRequest.GetDataBySql("select * from testindex where address like '%山%' ");
public static DataTable GetDataBySql(string sql, string url = "http://IP:9200/_xpack/sql?format=csv")
{
DataTable dataTable = new DataTable();
try
{
// 调用通过sql去查询结果
string jsonstr = Post(sql,url);
var lines = jsonstr.Split("\r\n");
foreach (string item in lines[0].Split(","))
{
//可以做类型转换
dataTable.Columns.Add(item, typeof(System.String));
}
for (int i = 1; i < lines.Length - 1; i++)
{
var filedvalue = lines[i].Split(",");
var row = dataTable.NewRow();
for (int j = 0; j < dataTable.Columns.Count; j++)
{
row[j] = filedvalue[j];
}
dataTable.Rows.Add(row);
}
}
catch (Exception ex)
{
throw;
}
return dataTable;
}
public static string Post(string queryParam, string url )
{
HttpWebRequest request = null;
try
{
request = (HttpWebRequest)WebRequest.Create(url);
var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(queryParam));
request.Method = "POST";
request.ContentType = "application/json"; // 设置请求数据的ContentType
request.ContentLength = data.Length;
request.Timeout = 90000;
// 设置入参
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
// 发送请求
var response = (HttpWebResponse)request.GetResponse();
// 读取出参
using (var resStream = response.GetResponseStream())
{
using (var reader = new StreamReader(resStream, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
}
catch (Exception ex)
{
return null;
}
finally
{
// 释放连接
if (request != null) request.Abort();
}
}
缺点:
1. "select * from testindex where address like '%山%' " 。不能写出 * 需要写出具体的字段。
2. 可以进行跨库子查询,但是不能做连接查询,像左连接,右连接等。
-
使用插件
下载插件地址:https://github.com/NLPchina/elasticsearch-sql注意插件对应与ES版本。
把下载SQL插件拷贝到home/es/sql 然后docker 挂载到plugins/sql下面。
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -v /home/es/sql:/usr/share/elasticsearch/plugins/sql -e ES_JAVA_OPTS="-Xms100m -Xmx200m" elasticsearch:7.6.1
调用:Post("select * from testindex where address like '%山%'");
public static string Post(string queryParam, string url = "http://IP:9200/_nlpcn/sql")
{
HttpWebRequest request = null;
try
{
request = (HttpWebRequest)WebRequest.Create(url);
var data = Encoding.UTF8.GetBytes(queryParam);
request.Accept = "application/json; charset=UTF-8"; // 设置响应数据的ContentType
request.Method = "POST";
request.ContentType = "application/json"; // 设置请求数据的ContentType
request.ContentLength = data.Length;
request.Timeout = 90000;
// 设置入参
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
// 发送请求
var response = (HttpWebResponse)request.GetResponse();
// 读取出参
using (var resStream = response.GetResponseStream())
{
using (var reader = new StreamReader(resStream, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
}
catch (Exception ex)
{
return null;
}
finally
{
// 释放连接
if (request != null) request.Abort();
}
}
9. 分词
由于原ES做分词是针对英文的,针对汉语分词必须的加载iK分词器
9.1 分词器的安装
- 下载分析器:
wget [https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v5.6.12/elasticsearch-analysis-ik-5.6.12.zip](https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v5.6.12/elasticsearch-analysis-ik-5.6.12.zip)
-
进入 ES 容器 docker exec -it b246c /bin/bash
找到plugins 目录 创建 iK目录,
-
把下载的iK分析器解压到iK中。
-
重启ES .
以下是示例:
{
"analyzer": "iK_max_word",
"test":"北京天安门"
}
9.2 自定义扩展分词
-
进入iK分词器下面的config目录
-
创建my.doc 文件在其中加入要分的词
-
在IKAnalyzer.cfg.xml中加入自己的创建的分词文件
-
重启ES。
以下是截图示例:
111.png
10. 倒排索引
倒排索引是相对于正排索引而言的。
正排索引,就是一般SQL保存数据的格式,Key ,Value 的结构。如下面的结构:
Key | Value |
---|---|
青花瓷 | 天青色等烟雨......... |
菊花台 | 你的笑容以泛黄..... |
正排索引,可以通过《青花瓷》来检索到歌词具体的内容。但是经常遇到的问题就是,通过歌词中的某一个词去检索整首歌曲,这样传统的正排索引就比较费劲了,如果数据较少还可以通过比对内容去实现,但是数据成千上百万,用这种方式就不现实了。而ES实现的就是全文索引。所以这种方式是不可取的。而它采取的是倒排索引的数据保存方式。如下面的结构:
key | Value |
---|---|
天 | 天青色等烟雨......... |
青色 | 天青色等烟雨......... |
烟雨 | 天青色等烟雨......... |
雨 | 天青色等烟雨......... |
采用倒排的方式就可以解决了,但是随之而来的问题就是,这样的拆分会导致数据量的成倍的增长。数据量大同样的导致数据查询慢的问题,为了解决这一问题,ES使用通过大量使用内存,咋就是做索引压缩。
11. 索引压缩
压缩Key :索引压缩称为FST,
假设对aaz,abyz,acdyz,be构建FST。做一个排序如下图:
3.png
使用共享的首尾对应硬盘的地址。比如:要针对aaz ,查询,通过排序就包含了,abyz,acdyz,查询的范围增大,但是也会有个范围不是全盘去扫描,如果是针对a*yz 去查,则检索范围进一步缩小。返回在缩小的范围进一步去检索,就可以快速的找到对应的key保存在磁盘中的位置。
压缩Value:
对于Value巨大的数据量也是需要压缩的压缩的方式如下图:
4.png如图所示:保存 73 300 302 332 343 372 如果是用一般的方式就是长度为6的int类型的数组,所用的空间是24个字节。
第二步:通过连加的方式:缩小了数据的大小,可以用6个short类型的数组保存数据,所用的空间就减小了一半成为了12个字节。
第三步:对于一些突变的数据可以做跟段处理。然后在一段之内数据选择最大的,申请bit数组如上图左边,最大是227 就是8个bit ,三个数组就是 3个字节,同理右半段就是2个字节,加上前面的一个数字代表的bit长度,一共就是占用7个字节。
整个算下来就是压缩了1/3空间大小。
12. 联合查询
由于倒排索引的保存方式会导致,主键Key 的不同但是Value相同。所以联合查询的时候会导致查询的结果是相同的。例如:查询条件是,where key=‘天’ and key = ‘青色’ 的就会导致相同的结果,所以用到了取交集的方式,同理也会遇到取并集、补集的方式。ElasticSearch 的实现思路和Redis 的Zset 实现的思路是一致的可以查看Redis那篇。也是通过跳跃表完成的。
13. 分布式原理
13.1 分部式的搭建
以下是es的yml 文件,配置文件的说明。
# 配置es的集群名称, es会自动发现在同一网段下的es,如果在同一网段下有多个集群,就可以用这个属性来区分不同的集群。
cluster.name: elasticsearch
# 节点名称
node.name: node-001
# 指定该节点是否有资格被选举成为node
node.master: true
cluster.initial_master_nodes: ["127.0.0.1:9300"]
# 指定该节点是否存储索引数据,默认为true。
node.data: true
# 设置绑定的ip地址还有其它节点和该节点交互的ip地址,本机ip
network.host: 127.0.0.1
# 指定http端口,你使用head、kopf等相关插件使用的端口
http.port: 9200
# 设置节点间交互的tcp端口,默认是9300。
transport.tcp.port: 9300
#设置集群中master节点的初始列表,可以通过这些节点来自动发现新加入集群的节点。
#因为下两台elasticsearch的port端口会设置成9301 和 9302 所以写入两台#elasticsearch地址的完整路径
discovery.zen.ping.unicast.hosts: ["127.0.0.1:9300","127.0.0.1:9301","127.0.0.1:9302"]
#如果要使用head,那么需要解决跨域问题,使head插件可以访问es
http.cors.enabled: true
http.cors.allow-origin: "*"
以下提供三个yml文件做三个节点的集群示例:
示例的内容:是在同一宿主机上的三个不同的端口开启三个节点,生产环境中是不同的宿主机部署。
es1.yml
cluster.name: elasticsearch-cluster
node.name: es-node1
network.bind_host: 0.0.0.0
network.publish_host: 192.168.1.101
http.port: 9200
cluster.initial_master_nodes: ["192.168.1.101:9300"]
transport.tcp.port: 9300
http.cors.enabled: true
http.cors.allow-origin: "*"
node.master: true
node.data: true
discovery.zen.ping.unicast.hosts: ["192.168.1.101:9300","192.168.1.101:9301","192.168.1.101:9302"]
discovery.zen.minimum_master_nodes: 2
es2.yml
cluster.name: elasticsearch-cluster
node.name: es-node2
network.bind_host: 0.0.0.0
network.publish_host: 192.168.1.101
http.port: 9201
cluster.initial_master_nodes: ["192.168.1.101:9300"]
transport.tcp.port: 9301
http.cors.enabled: true
http.cors.allow-origin: "*"
node.master: true
node.data: true
discovery.zen.ping.unicast.hosts: ["192.168.1.101:9300","192.168.1.101:9301","192.168.1.101:9302"]
discovery.zen.minimum_master_nodes: 2
es3.yml
cluster.name: elasticsearch-cluster
node.name: es-node3
network.bind_host: 0.0.0.0
network.publish_host: 192.168.1.101
http.port: 9202
cluster.initial_master_nodes: ["192.168.1.101:9300"]
transport.tcp.port: 9302
http.cors.enabled: true
http.cors.allow-origin: "*"
node.master: true
node.data: true
discovery.zen.ping.unicast.hosts: ["192.168.1.101:9300","192.168.1.101:9301","192.168.1.101:9302"]
discovery.zen.minimum_master_nodes: 2
以下是执行docker :
# 没有挂载的
docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 -v /root/es/es1.yml:/usr/share/elasticsearch/config/elasticsearch.yml --name es01 elasticsearch:7.2.0
docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9201:9201 -p 9301:9301 -v /root/es/es2.yml:/usr/share/elasticsearch/config/elasticsearch.yml --name es02 elasticsearch:7.2.0
docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9202:9202 -p 9302:9302 -v /root/es/es3.yml:/usr/share/elasticsearch/config/elasticsearch.yml --name es03 elasticsearch:7.2.0
#有挂载的
docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 -v /home/es/config/es1.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /home/es/data1:/usr/share/elasticsearch/data --name es01 docker.elastic.co/elasticsearch/elasticsearch:7.10.0
docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9201:9201 -p 9301:9301 -v /home/es/config/es2.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /home/es/data2:/usr/share/elasticsearch/data --name es02 docker.elastic.co/elasticsearch/elasticsearch:7.10.0
docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9202:9202 -p 9302:9302 -v /home/es/config/es3.yml:/usr/share/elasticsearch/config/elasticsearch.yml -v /home/es/data3:/usr/share/elasticsearch/data --name es03 docker.elastic.co/elasticsearch/elasticsearch:7.10.0
注意:
-
必须在宿主机创建data文件夹,然后配置777权限,文件夹单独配置
-
调高jvm线程数限制
-
方法1:
vim /etc/sysctl.conf
加入
vm.max_map_count=262144
执行生效
sysctl –p
方法2:
sysctl -w vm.max_map_count=262144
13.2 主节点的职责
-
索引
创建索引
删除索引
删除索引Template
删除索引Mapping
Mapping更新
Mapping创建
删除索引Warmer
索引Warmer存在
获取索引Warmer
创建Warmer
索引别名
索引存在
打开索引
关闭索引
-
节点或集群
集群信息
集群健康
集群状态
集群配置
搜索分片
节点关闭
集群路由
-
发布集群状态
由于主节点职责多,所以主节点一般不保存数据。
14. 工作机制
14.1 节点发现
节点的发现是通过配置文件中实现的,它们每个节点之间都是都是相互发现的如下图:
5.png
如果其中有一个节点断开其他的节点是知道的。
14.2 节点故障
-
节点故障,
当主节点与节点有连续三次以上的心跳不通后,主节点就会把该节点移除,认为该节点故障,然后重新的分配片区。如下图:
6.png -
主节点故障
当主节点与所有节点断开后,默认主节点宕机,主节点宕机后,其他的剩余的节点重新选举主节点。如下图:
7.png
14.3 Master选举
主节点的选举,不是按照票数的多少来进行的,而是按照节点的开启创建的cluster_uuid来的,cluster_uuid越小就越有机会选举成主节点。
"cluster_uuid" : "3BtyZmyJSDeoJwYZ7x3q8g",
public MasterCandidate electMaster(Collection<MasterCandidate> candidates) {
assert hasEnoughCandidates(candidates);
List<MasterCandidate> sortedCandidates = new ArrayList<>(candidates);
sortedCandidates.sort(MasterCandidate::compare);
return sortedCandidates.get(0);
}
public static int compare(MasterCandidate c1, MasterCandidate c2) {
// we explicitly swap c1 and c2 here. the code expects "better" is lower in a sorted
// list, so if c2 has a higher cluster state version, it needs to come first.
int ret = Long.compare(c2.clusterStateVersion, c1.clusterStateVersion);
if (ret == 0) {
ret = compareNodes(c1.getNode(), c2.getNode());
}
return ret;
}
14.4 脑裂
配置文件中配置 discovery.zen.minimum_master_nodes: 2(形成集群所需要的最小节点数)
- 一般是(所有节点数-主节点数)/2 + 1
- 尽量多的节点放到和主节点一个网段下面
14.5 集群状态更新
集群的状态更新,是由主节点主动的推送到分节点,再由节点去完成相应的任务,如下图:
8.png
15. 分片
集群搭建完成最重要的就是负载均衡,也就是做分片。elasticsearch的分片原理和Kafka一致的。基本的原则就是:首先,满足节点数等于分片数,其次一个分片最好保存20G左右的数据,再次就是一个节点最多两个分片(过多会影响性能)。
#Kibana 执行
PUT /my_temp_index/_settings
{
"number_of_shards": 1,
"number_of_replicas": 5
}
-
分片机制弹性扩容
分片的扩容:新的节点接入的时候会从新的分片,做到任何一个节点宕机后,不会影响数据的完整性。
-
分片机制节点故障
旧的节点宕机后,剩下的节点会从新的分片,保证剩下的节点任何一个节点宕机后不会影响数据的完整性。
16. 集群的代码连接方式
var uris = new[]
{
new Uri("http://localhost:9200"),
new Uri("http://localhost:9201"),
new Uri("http://localhost:9202"),
};
var connectionPool = new SniffingConnectionPool(uris);
var settings = new ConnectionSettings(connectionPool).DefaultIndex("people");
var client = new ElasticClient(settings);
把连接字符串变成集群就可以了。
网友评论