# elasticsearch
## 下载与安装
### ES
> 下载:https://www.elastic.co/cn/downloads/elasticsearch

>下载后解压即安装,需要在配置文件中添加以下配置
```yml
作用:添加以下配置后可以使用ES-head来操作ES
http.cors.enabled: true
http.cors.allow-origin: "*"
```
>修改配置文件后可能出现的启动闪退的情况,这是只需要检查拼写、编码、分割符即可
### ES-head(辅助工具)
> https://github.com/mobz/elasticsearch-head

> 解压后进入文件夹打开命令窗口,执行以下命令。即可访问
```
npm install
npm start
```
### kibana(辅助工具)
> https://www.elastic.co/cn/kibana
```
修改配置文件(中文)
i18n.locale: "zh-CN"
```
>双击bin目录下的.bat文件即可启动
## ES的概念
**索引(index)**:ElasticSearch存储数据的地方,可以理解成关系型数据库中的数据库概念。
**映射(mapping)**:mapping定义了每个字段的类型、字段所使用的分词器等。相当于关系型数据库中的表结构。
**文档(document)**: Elasticsearch中的最小数据单元,常以json格式显示。一个document相当于关系型数据库中的一行数据。
**倒排索引**: 一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,对应一个包含它的文档id列表。
**类型(type)**: 一种type就像一类表。如用户表、角色表等。在Elasticsearch7.X默认type为_doc
- ES 5.x中一个index可以有多种type。
- ES 6.x中一个index只能有一种type。
- ES 7.x以后,将逐步移除type这个概念,现在的操作已经不再使用,默认_doc
**分片(shard)**:索引可以被拆分为不同的部分进行存储,称为分片。在集群环境下,一个索引的不同分片可以拆分到不同的节点中。
**分片与自平衡**:当节点挂掉后,挂掉的节点分片会自平衡到其他节点中
注意:分片数量一旦确定好,不能修改。
**主分片(Primary shard)**:相对于副本分片的定义。
**副本分片(Replica shard)**每个主分片可以有一个或者多个副本,数据和主分片一样。
## RESTful风格介绍
(Representational State Transfer),表述性状态转移,是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是RESTful。就是一种定义接口的规范。
## 分词器
> 什么是分词器

如果要使用中文,建议使用ik分词器
> https://elasticsearch.cn/download/
下载完成之后在ES的plugins目录下把压缩包解压即可

给ik分词器增加自己的配置

添加之前

添加之后

## ES数据类型
1. **简单数据类型**
- 字符串
聚合:相当于mysql 中的sum(求和)
```text
text:会分词,不支持聚合
keyword:不会分词,将全部内容作为一个词条,支持聚合
```
- 数值
- 布尔:boolean
- 二进制:binary
- 范围类型
```
integer_range, float_range, long_range, double_range, date_range
```
- 日期:date
2. **复杂数据类型**
•数组:[ ] Nested: `nested` (for arrays of JSON objects 数组类型的JSON对象)
•对象:{ } Object: object(for single JSON objects 单个JSON对象)
## 关于索引的基本操作

## 关于文档的操作
### 简单操作
所有的查询都用GET
> 添加数据
```
PUT /hezhong/user/1
{
"name":"秦始皇",
"age":25,
"desc":"吃饭睡觉打游戏"
}
```

> 查询数据 GET
```
GET hezhong/user/1
```
> 更新数据 put

用put更新数据时没有写的字段会被置空
> 使用post的updat方法可以避免
其他值不会被覆盖,灵活性更高



score:分数,匹配度越高,值越大,越靠前
### 复杂操作(搜索)
#### matchAll
```
# 默认情况下,es一次展示10条数据,通过from和size来控制分页
# 查询结果详解
GET goods/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 100
}
GET goods
```
```java
@Test
public void matchAll() throws IOException {
//2. 构建查询请求对象,指定查询的索引名称
SearchRequest searchRequest=new SearchRequest("goods");
//4. 创建查询条件构建器SearchSourceBuilder
SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
//6. 查询条件
QueryBuilder queryBuilder= QueryBuilders.matchAllQuery();
//5. 指定查询条件
sourceBuilder.query(queryBuilder);
//3. 添加查询条件构建器 SearchSourceBuilder
searchRequest.source(sourceBuilder);
// 8 . 添加分页信息 不设置 默认10条
// sourceBuilder.from(0);
// sourceBuilder.size(100);
//1. 查询,获取查询结果
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
//7. 获取命中对象 SearchHits
SearchHits hits = searchResponse.getHits();
//7.1 获取总记录数
Long total= hits.getTotalHits().value;
System.out.println("总数:"+total);
//7.2 获取Hits数据 数组
SearchHit[] hits1 = hits.getHits();
//获取json字符串格式的数据
List
for (SearchHit searchHit : hits1) {
String sourceAsString = searchHit.getSourceAsString();
//转为java对象
Goods goods = JSON.parseObject(sourceAsString, Goods.class);
goodsList.add(goods);
}
for (Goods goods : goodsList) {
System.out.println(goods);
}
}
```
#### termQuery
查询text类型的数据时:只要其中出现了查询的关键字就会返回结果
查询keyword时:因为关键字不分词,需要查询条件和字段值完全匹配才可以
term Query 会去倒排索引中寻找确切的term,它并不知道分词器的存在。适合keyword、numeric、date
#### matchQuery
会对条件进行分词、将分词后的查询条件和词条进行匹配、默认取交集(OR)
match Query 知道分词器的存在。并且理解时如何被分词的
#### 模糊查询
wildcard查询:会对查询条件进行分词。还可以使用通配符 ?(任意单个字符) 和 * (0个或多个字符)
```
"*华*" 包含华字的
"华*" 华字后边多个字符
"华?" 华字后边多个字符
"*华"或"?华" 会引发全表(全索引)扫描 注意效率问题
```
```json
# wildcard 查询。查询条件分词,模糊查询
GET goods/_search
{
"query": {
"wildcard": {
"title": {
"value": "华*"
}
}
}
}
```
#### 正则查询
```
W:匹配包括下划线的任何单词字符,等价于 [A-Z a-z 0-9_] 开头的反斜杠是转义符
+号多次出现
(.)*为任意字符
正则查询取决于正则表达式的效率
```
```json
GET goods/_search
{
"query": {
"regexp": {
"title": "\w+(.)*"
}
}
}
```
#### 前缀查询
对keyword类型支持比较好
```json
# 前缀查询 对keyword类型支持比较好
GET goods/_search
{
"query": {
"prefix": {
"brandName": {
"value": "三"
}
}
}
}
```
#### 范围&排序查询
```json
# 范围查询
GET goods/_search
{
"query": {
"range": {
"price": {
"gte": 2000,
"lte": 3000
}
}
},
"sort": [
{
"price": {
"order": "desc"
}
}
]
}
```
#### queryString查询
queryString 多条件查询
•会对查询条件进行分词。
•然后将分词后的查询条件和词条进行等值匹配
•默认取并集(OR)
•可以指定多个查询字段
query_string:识别query中的连接符(or 、and)
```
# queryString
GET goods/_search
{
"query": {
"query_string": {
"fields": ["title","categoryName","brandName"],
"query": "华为 AND 手机"
}
}
}
```
simple_query_string:不识别query中的连接符(or 、and),查询时会将 “华为”、"and"、“手机”分别进行查询
```
GET goods/_search
{
"query": {
"simple_query_string": {
"fields": ["title","categoryName","brandName"],
"query": "华为 AND 手机"
}
}
}
```
query_string:有default_operator连接符的脚本
```json
GET goods/_search
{
"query": {
"query_string": {
"fields": ["title","brandName","categoryName"],
"query": "华为手机 "
, "default_operator": "AND"
}
}
}
```
注意:query中的or and 是查询时 匹配条件是否同时出现----or 出现一个即可,and 两个条件同时出现
default_operator的or and 是对结果进行 并集(or)、交集(and)
#### 布尔查询-脚本
boolQuery:对多个查询条件连接。连接方式:
•must(and):条件必须成立
•must_not(not):条件必须不成立
•should(or):条件可以成立
•filter:条件必须成立,性能比must高。不会计算得分
**得分:**即条件匹配度,匹配度越高,得分越高
```json
# boolquery
#must和filter配合使用时,max_score(得分)是显示的
#must 默认数组形式
GET goods/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"brandName": {
"value": "华为"
}
}
}
],
"filter":[
{
"term": {
"title": "手机"
}
},
{
"range":{
"price": {
"gte": 2000,
"lte": 3000
}
}
}
]
}
}
}
#filter 单独使用 filter可以是单个条件,也可多个条件(数组形式)
GET goods/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"brandName": {
"value": "华为"
}
}
}
]
}
}
}
```
#### 聚合查询-脚本
•指标聚合:相当于MySQL的聚合函数。max、min、avg、sum等
•桶聚合:相当于MySQL的 group by 操作。不要对text类型的数据进行分组,会失败。
```json
# 聚合查询
# 指标聚合 聚合函数
GET goods/_search
{
"query": {
"match": {
"title": "手机"
}
},
"aggs": {
"max_price": {
"max": {
"field": "price"
}
}
}
}
# 桶聚合 分组
GET goods/_search
{
"query": {
"match": {
"title": "手机"
}
},
"aggs": {
"goods_brands": {
"terms": {
"field": "brandName",
"size": 100
}
}
}
}
```
#### 高亮查询-脚本
高亮三要素:
•高亮字段
•前缀
•后缀
默认前后缀 :em
```html
手机
```
```json
GET goods/_search
{
"query": {
"match": {
"title": "电视"
}
},
"highlight": {
"fields": {
"title": {
"pre_tags": "",
"post_tags": ""
}
}
}
}
```
#### 重建索引&索引别名
```json
#查询别名 默认别名无法查看,默认别名同索引名
GET goods/_alias/
#结果
{
"goods" : {
"aliases" : { }
}
}
```
1.新建student_index_v1索引
```json
# -------重建索引-----------
# 新建student_index_v1。索引名称必须全部小写
PUT student_index_v1
{
"mappings": {
"properties": {
"birthday":{
"type": "date"
}
}
}
}
#查看 student_index_v1 结构
GET student_index_v1
#添加数据
PUT student_index_v1/_doc/1
{
"birthday":"1999-11-11"
}
#查看数据
GET student_index_v1/_search
#添加数据
PUT student_index_v1/_doc/1
{
"birthday":"1999年11月11日"
}
```
2.重建索引:将student_index_v1 数据拷贝到 student_index_v2
```json
# 业务变更了,需要改变birthday字段的类型为text
# 1. 创建新的索引 student_index_v2
# 2. 将student_index_v1 数据拷贝到 student_index_v2
# 创建新的索引 student_index_v2
PUT student_index_v2
{
"mappings": {
"properties": {
"birthday":{
"type": "text"
}
}
}
}
# 将student_index_v1 数据拷贝到 student_index_v2
# _reindex 拷贝数据
POST _reindex
{
"source": {
"index": "student_index_v1"
},
"dest": {
"index": "student_index_v2"
}
}
GET student_index_v2/_search
PUT student_index_v2/_doc/2
{
"birthday":"1999年11月11日"
}
```
3.创建索引库别名:
注意:DELETE student_index_v1 这一操作将删除student_index_v1索引库,并不是删除别名
```json
# 思考: 现在java代码中操作es,还是使用的实student_index_v1老的索引名称。
# 1. 改代码(不推荐)
# 2. 索引别名(推荐)
# 步骤:
# 0. 先删除student_index_v1
# 1. 给student_index_v2起个别名 student_index_v1
# 先删除student_index_v1
#DELETE student_index_v1 这一操作将删除student_index_v1索引库
#索引库默认的别名与索引库同名,无法删除
# 给student_index_v1起个别名 student_index_v11
POST student_index_v2/_alias/student_index_v11
#测试删除命令
POST /_aliases
{
"actions": [
{"remove": {"index": "student_index_v1", "alias": "student_index_v11"}}
]
}
DELETE student_index_v1
# 给student_index_v2起个别名 student_index_v1
POST student_index_v2/_alias/student_index_v1
#查询别名
GET goods/_alias/
GET student_index_v1/_search
GET student_index_v2/_search
```
### ES集群
ES天然支持集群、设计隐藏了分布式本身的复杂性
集群:多个人做同一件事
分布式:多个人做不同的事
集群解决的问题:让系统高可用、分担请求压力
分布式解决的问题:分担存储和计算压力,提速
#### 相关概念
•集群(cluster):一组拥有共同的 cluster name 的 节点。
•节点(node) :集群中的一个 Elasticearch 实例
•索引(index) :es存储数据的地方。相当于关系数据库中的database概念
•分片(shard):索引可以被拆分为不同的部分进行存储,称为分片。在集群环境下,一个索引的不同分片可以拆分到不同的节点中
•主分片(Primary shard):相对于副本分片的定义。
•副本分片(Replica shard)每个主分片可以有一个或者多个副本,数据和主分片一样。
##### 路由原理
•文档存入对应的分片,ES计算分片编号的过程,称为路由。
•Elasticsearch 是怎么知道一个文档应该存放到哪个分片中呢?
•查询时,根据文档id查询文档, Elasticsearch 又该去哪个分片中查询数据呢?
•路由算法 :shard_index = hash(id) % number_of_primary_shards
##### 脑裂
ES集群的正常状态
• 一个正常es集群中只有一个主节点(Master),主节点负责管理整个集群。如创建或删除索引,跟踪哪些节点是群集的一部分,并决定哪些分片分配给相关的节点。
•集群的所有节点都会选择同一个节点作为主节点。
什么是脑裂现象
• 一个正常es集群中只有一个主节点(Master),主节点负责管理整个集群。如创建或删除索引,跟踪哪些节点是群集的一部分,并决定哪些分片分配给相关的节点。
•集群的所有节点都会选择同一个节点作为主节点。
**脑裂产生的原因:**
1.网络原因:网络延迟
一般es集群会在内网部署,也可能在外网部署,比如阿里云。
内网一般不会出现此问题,外网的网络出现问题的可能性大些。
2.节点负载
主节点的角色既为master又为data。数据访问量较大时,可能会导致Master节点停止响应(假死状态)。
•node.master: true
•node.data: false
3.JVM内存回收
当Master节点设置的JVM内存较小时,引发JVM的大规模内存回收,造成ES进程失去响应。
**避免脑裂**:
1.网络原因:discovery.zen.ping.timeout 超时时间配置大一点。默认是3S
2.节点负载:角色分离策略
•候选主节点配置为
•node.master: true
•node.data: false
•数据节点配置为
•node.master: false
•node.data: true
3.JVM内存回收:修改 config/jvm.options 文件的 -Xms 和 -Xmx 为服务器的内存一半。



