环境搭建
docker 上部署环境
# 下载elasticsearch
docker pull elasticsearch:7.4.2
# 可视化es 监控
docker pull kibana:7.4.2
# 创建文件夹(数据挂载使用)
mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data
# 文件elasticsearch.yml插入 --> http.host: 0.0.0.0
echo "http.host: 0.0.0.0" > /mydata/elasticsearch/config/elasticsearch.yml
# 修改权限
chmod -R 777 /mydata/elasticsearch/
# 启动es
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2
# 开机自启
docker update elasticsearch --restart=always
# 启动kibana
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.56.10:9200 -p 5601:5601 -d kibana:7.4.2
# 开机自启
docker update kibana --restart=always
ui 页面
- 访问elasticsearch
http://192.168.56.10:9200/
- 访问kibana
http://192.168.56.10:5601/
使用基本语法
基本请求
# 查看健康状态
http://192.168.56.10:9200/_cat/health
# 查看节点信息
http://192.168.56.10:9200/_cat/nodes
# 查看主节点信息
http://192.168.56.10:9200/_cat/master
# 查看所有索引
http://192.168.56.10:9200/_cat/indices
基本增删改查
get 查询
http://192.168.56.10:9200/costomer/external/3
{ "_index": "costomer", // 那个索引下
"_type": "external", // 索引类型
"_id": "1", // 索引分类下id
"_version": 2, // 版本 (2更新一次)
"_seq_no": 1, // 乐观锁使用(操作数据+1)
"_primary_term": 1, // 同上,主分片重新分配,如重启就会发生变化
"found": true, // 找到数据 true
"_source": { // 真正查询数据
"name": "jack"
}
}
post put 新增 post put 修改都能为之前对象增加属性
http://192.168.56.10:9200/costomer/external/3
{
"name":"张三"
}
post
带id 有数据就修改,没有数据就是更新
不带id就是新增 随机生成id
put
带id 有数据就修改,没有数据就是更新
不带id 错误码406 没有对应的请求
模拟高并发 乐观锁
使用了乐观锁,修改不成功错误码 409
- http://192.168.56.10:9200/costomer/external/1?if_seq_no=1&if_primary_term=1
{
"name":"111"
}
- http://192.168.56.10:9200/costomer/external/1?if_seq_no=1&if_primary_term=1
{
"name":"222"
}
同时修改比较 seq_no primary_term
修改用 _update
http://192.168.56.10:9200/costomer/external/3/_update
//json 格式必须是 {"doc":{主体}}
{
"doc": {
"name": "123"
}
}
会与原来数据对比 数据一致 (version和_seq_no)都不变
不带update 参数
{
"name":"123456"
}
每次都会更改成功
删除
删除指定id
http://192.168.56.10:9200/costomer/external/1
删除成功: result:deleted
删除失败: result:not_found
删除整个索引
http://192.168.56.10:9200/costomer
成功:{“acknowledged”: true}
失败:error 状态404
路径格式不能是
http://192.168.56.10:9200/costomer/external
以下测试均在kibana下测试
批量
语法
{action:{metadata}}\n
{request body }\n
{action:{metadata}}\n
{request body }\n
执行多条数据
POST customer/external/_bulk
{"index":{"_id":"1"}} //标识
{"name":"John Doe"} //数据
{"index":{"_id":"2"}}
{"name":"John Doe"}
-
这里的批量操作,当发生某一条执行发生失败时,其他的数据仍然能够接着执行,也就是说彼此之间是独立的。
bulk api以此按顺序执行所有的action(动作)。如果一个单个的动作因任何原因失败,它将继续处理它后面剩余的动作。当bulk api返回时,它将提供每个动作的状态(与发送的顺序相同),所以您可以检查是否一个指定的动作是否失败了。
对于整个索引执行批量操作
POST /_bulk
{"delete":{"_index":"website","_type":"blog","_id":"123"}}
{"create":{"_index":"website","_type":"blog","_id":"123"}}
{"title":"my first blog post"}
{"index":{"_index":"website","_type":"blog"}}
{"title":"my second blog post"}
{"update":{"_index":"website","_type":"blog","_id":"123"}}
{"doc":{"title":"my updated blog post"}}
- 运行结果
{
"took" : 608, // 执行时间
"errors" : false, // 异常
"items" : [ // 执行块
{
"delete" : {
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 1,
"result" : "not_found",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1,
"status" : 404 //执行失败
}
},
{
"create" : {
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 2, //版本
"result" : "created", //什么操作
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1,
"status" : 201 //成功
}
},
{
"index" : {
"_index" : "website",
"_type" : "blog",
"_id" : "MCOs0HEBHYK_MJXUyYIz",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 2,
"_primary_term" : 1,
"status" : 201
}
},
{
"update" : {
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 3,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 3,
"_primary_term" : 1,
"status" : 200
}
}
]
}
执行 : POST bank/account/_bulk
数据
检索
search Api
ES支持两种基本方式检索;
- 通过REST request uri 发送搜索参数 (uri +检索参数);
- 通过REST request body 来发送它们(uri+请求体);
uri+请求参数
GET bank/_search?q=*&sort=account_number:asc
uri+请求体进行检索
GET /bank/_search
{
"query": { "match_all": {} },
"sort": [
{ "account_number": "asc" },
{"balance":"desc"}
]
}
took
– Elasticsearch运行查询多长时间(以毫秒为单位)timed_out
–搜索请求是否超时_shards
–搜索了多少个分片,以及成功,失败或跳过了多少个分片。max_score
–找到的最相关文件的分数hits.total.value
-找到了多少个匹配的文档hits.sort
-文档的排序位置(不按相关性得分排序时)hits._score
-文档的相关性得分(使用时不适用match_all
)
Query DSL
基本语法格式
Elasticsearch提供了一个可以执行查询的Json风格的DSL。这个被称为Query DSL,该查询语言非常全面。
一个查询语句的典型结构
QUERY_NAME:{
ARGUMENT:VALUE,
ARGUMENT:VALUE,...
}
如果针对于某个字段,那么它的结构如下:
{
QUERY_NAME:{
FIELD_NAME:{
ARGUMENT:VALUE,
ARGUMENT:VALUE,...
}
}
}
GET bank/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 5,
"sort": [
{
"account_number": {
"order": "desc"
}
}
]
}
query定义如何查询;
- match_all查询类型【代表查询所有的所有】,es中可以在query中组合非常多的查询类型完成复杂查询;
- 除了query参数之外,我们可也传递其他的参数以改变查询结果,如sort,size;
- from+size限定,完成分页功能;
- sort排序,多字段排序,会在前序字段相等时后续字段内部排序,否则以前序为准;
返回部分字段
_source
GET bank/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 5,
"sort": [
{
"account_number": {
"order": "desc"
}
}
],
"_source": ["balance","firstname"]
}
match匹配查询
基本类型(非字符串),精确控制
GET bank/_search
{
"query": {
"match": {
"account_number": "20"
}
}
}
字符串,全文检索
// 全文检索 安装评分进行排序 , 会对检索条件进行分词
GET bank/_search
{
"query": {
"match": {
"address": "kings"
}
}
}
加上keyword 属性 ,就精确匹配 不进行分词
GET bank/_search
{
"query": {
"match": {
"address.keyword": "198 Mill Lane"
}
}
}
match_phrase [短句匹配]
将需要匹配的值当成一整个单词(不分词)进行检索 , 精确短语匹配
GET bank/_search
{
"query": {
"match_phrase": {
"address": "mill road"
}
}
}
multi_math【多字段匹配】
city或者address中包含Mill 或者 Movico,并且在查询过程中,会对于查询条件进行分词。
GET bank/_search
{
"query": {
"multi_match": {
"query": "Mill Movico",
"fields": ["city","address"]
}
}
}
bool用来做复合查询
复合语句可以合并,任何其他查询语句,包括符合语句。这也就意味着,复合语句之间
可以互相嵌套 ,可以表达非常复杂的逻辑。
must:必须达到must所列举的所有条件
must_not,必须不匹配must_not所列举的所有条件。
should,应该满足should所列举的条件。 不满足也行,满足得分更高
GET bank/_search
{
"query": {
"bool": {
"must": [
{"match": {
"gender": "M"
}}
],
"must_not": [
{"match": {
"age": "32"
}}
],
"should": [
{"match": {
"lastname": "Adams"
}}
]
}
}
}
Filter【结果过滤】
并不是所有的查询都需要产生分数,特别是哪些仅用于filtering过滤的文档。为了不计算分数,elasticsearch会自动检查场景并且优化查询的执行。
分词查询 addresss
过滤 age 在18-25之间的
GET bank/_search
{
"query": {
"bool": {
"must": [
{"match": {
"address": "Road"
}}
],
"filter": {
"range": {
"age": {
"gte": 18,
"lte": 25
}
}
}
}
}
}
在boolean查询中,must
, should
和must_not
元素都被称为查询子句 。 文档是否符合每个“must”或“should”子句中的标准,决定了文档的“相关性得分”。 得分越高,文档越符合您的搜索条件。 默认情况下,Elasticsearch返回根据这些相关性得分排序的文档。
“must_not”子句中的条件被视为“过滤器”。
它影响文档是否包含在结果中, 但不影响文档的评分方式。 还可以显式地指定任意过滤器来包含或排除基于结构化数据的文档。
must_not 和 filter 区别 : filter 不会计算相关性得分
term
单个字段精确查询 使用term , 多文本分词查询使用match
GET bank/_search
{
"query": {
"term": {
"age": "23"
}
}
}
Aggregation(执行聚合)
语法
"aggs":{
"aggs_name这次聚合的名字,方便展示在结果集中":{
"AGG_TYPE聚合的类型(avg,term,terms)":{}
}
},
搜索address中包含mill的所有人的年龄分布以及平均年龄,但不显示这些人的详情
GET bank/_search
{
"query": {
"match": {
"address": "Mill"
}
},
"aggs": {
"ageAgg": { //单个聚和取名字
"terms": {
"field": "age", //操作那一列进行分组
"size": 10 //显示条数
}
},
"aggs": {
"ss": {
"avg": {
"field": "balance" //分组结果查询 平均工资
}
}
},
"ageAvg": {
"avg": {
"field": "age" //查看所有平均年龄
}
},
"balanceAvg": {
"avg": {
"field": "balance" //查看所有平均薪资
}
}
},
"size": 0 //显示0条
}
Mapping
字段类型
示例博客
映射
Mapping(映射)
Maping是用来定义一个文档(document),以及它所包含的属性(field)是如何存储和索引的。比如:使用maping来定义:
-
哪些字符串属性应该被看做全文本属性(full text fields);
-
哪些属性包含数字,日期或地理位置;
-
文档中的所有属性是否都嫩被索引(all 配置);
-
日期的格式;
-
自定义映射规则来执行动态添加属性;
查看mapping信息
GET bank/_mapping
//输出
{
"my_index" : {
"mappings" : {
"properties" : {
"age" : {
"type" : "integer"
},
"email" : {
"type" : "keyword"
},
"employee-id" : {
"type" : "keyword",
"index" : false
},
"name" : {
"type" : "text"
}
}
}
}
}
创建映射
创建索引并指定映射
PUT /my_index
{
"mappings": {
"properties": {
"age": {
"type": "integer"
},
"email": {
"type": "keyword"
},
"name": {
"type": "text"
}
}
}
}
输出:
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "my_index"
}
- 如果以存在
{
"error": {
"root_cause": [
{
"type": "resource_already_exists_exception",
"reason": "index [my_index/C1GonM4UQ2GEDkPRVubULg] already exists",
"index_uuid": "C1GonM4UQ2GEDkPRVubULg",
"index": "my_index"
}
],
"type": "resource_already_exists_exception",
"reason": "index [my_index/C1GonM4UQ2GEDkPRVubULg] already exists",
"index_uuid": "C1GonM4UQ2GEDkPRVubULg",
"index": "my_index"
},
"status": 400
}
添加映射
PUT /my_index/_mapping
{
"properties": {
"employee-id": {
"type": "keyword",
"index": false //不是进行索引字段
}
}
}
更新映射
对于已经存在的字段映射,我们不能机组更新。更新必须创建新的索引,进行数据迁移。
创建映射
PUT /newbank
{
"mappings": {
"properties": {
"account_number": {
"type": "long"
},
"address": {
"type": "text"
},
"age": {
"type": "integer"
},
"balance": {
"type": "long"
},
"city": {
"type": "keyword"
},
"email": {
"type": "keyword"
},
"employer": {
"type": "keyword"
},
"firstname": {
"type": "text"
},
"gender": {
"type": "keyword"
},
"lastname": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"state": {
"type": "keyword"
}
}
}
}
数据迁移
将bank中的数据迁移到newbank中
POST _reindex
{
"source": {
"index": "bank",
"type": "account"
},
"dest": {
"index": "newbank"
}
}
新版本改变
ElasticSearch7-去掉type概念
-
关系型数据库中两个数据表示是独立的,即使他们里面有相同名称的列也不影响使用,但ES中不是这样的。elasticsearch是基于Lucene开发的搜索引擎,而ES中不同type下名称相同的filed最终在Lucene中的处理方式是一样的。
- 两个不同type下的两个user_name,在ES同一个索引下其实被认为是同一个filed,你必须在两个不同的type中定义相同的filed映射。否则,不同type中的相同字段名称就会在处理中出现冲突的情况,导致Lucene处理效率下降。
- 去掉type就是为了提高ES处理数据的效率。
-
Elasticsearch 7.x URL中的type参数为可选。比如,索引一个文档不再要求提供文档类型。
-
Elasticsearch 8.x 不再支持URL中的type参数。
-
解决:
将索引从多类型迁移到单类型,每种类型文档一个独立索引将已存在的索引下的类型数据,全部迁移到指定位置即可。详见数据迁移
ik 分词器
安装
下载与es对应版本
https://github.com/medcl/elasticsearch-analysis-ik/releases
把ik 解压到 文件夹 /mydata/elasticsearch/plugins/ 下
设置权限
chmod -R 777 ik/
配置nginx使用ik
-
随便启动一个nginx实例,只是为了复制出配置
docker run -p80:80 --name nginx -d nginx:1.10
-
将容器内的配置文件拷贝到/mydata/nginx/conf/ 下
mkdir -p /mydata/nginx/html mkdir -p /mydata/nginx/logs mkdir -p /mydata/nginx/conf docker container cp nginx:/etc/nginx/* /mydata/nginx/conf/ #由于拷贝完成后会在config中存在一个nginx文件夹,所以需要将它的内容移动到conf中 mv /mydata/nginx/conf/nginx/* /mydata/nginx/conf/ rm -rf /mydata/nginx/conf/nginx
-
终止原容器:
docker stop nginx
-
执行命令删除原容器:
docker rm nginx
-
创建新的Nginx,执行以下命令
docker run -p 80:80 --name nginx \ -v /mydata/nginx/html:/usr/share/nginx/html \ -v /mydata/nginx/logs:/var/log/nginx \ -v /mydata/nginx/conf/:/etc/nginx \ -d nginx:1.10
-
设置开机启动nginx
docker update nginx --restart=always
-
创建“/mydata/nginx/html/index.html”文件,测试是否能够正常访问
echo '<h2>hello nginx!</h2>' >index.html
访问:http://ngix所在主机的IP:80/index.html
配置分词
vi /mydata/elasticsearch/plugins/ik/config/IKAnalyzer.cfg.xml
在添加分词
html/es/fenci.txt
重启es测试