ElasticSearch简介
Elaticsearch,简称为es,可以看成一个数据库,可以存储数据、搜索数据
- es是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据
- 本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据,1PB=1024TB
- es也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能
- 但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单
全文检索
1.结构化数据与非结构化数据
- 结构化数据:指具有固定格式或有限长度的数据,如数据库sql【结构化查询语言】,元数据等。
- 非结构化数据:指不定长或无固定格式的数据,如邮件,word文档等磁盘上的文件
将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。这部分从非结构化数据中提取出的然后重新组织的信息,我们称之索引。
数据库中的数据存储是有规律的,有行有列而且数据格式、数据长度都是固定的,所以 数据库搜索很容易
2.全文检索的实现流程
先建立索引,再对索引进行搜索的过程就叫全文检索。
- 索引创建:将现实世界中所有的结构化和非结构化数据提取信息,创建索引的过程。
- 搜索索引:就是得到用户的查询请求,搜索创建的索引,然后返回结果的过程。
虽然创建索引的过程也是非常耗时的,但是索引一旦创建就可以多次使用,全文检索主要处理的是查询,所以耗时间创建索引是值得的
3.倒排索引结构 --面向词汇
索引的目的是为了搜索,最终要实现只搜索被索引的语汇单元从而找到Document(文档)。
正常:先找文档,再在文件内容中匹配搜索关键字
倒排索引:根据内容(词语)锁定文档。
倒排索引结构也叫反向索引结构,包括索引和文档两部分,索引即词汇表,它的规模较小,而文档集合较大
4.Elasticsearch与mysql的区别
ElasticSearch和MySQL分工不同,MySQL负责存储数据,ElasticSearch负责搜索数据。
- 数据库响应时间不可接受(企业级)
- 数据库不支持分词
- 数据库不支持相关性搜索(行为分析)
ElasticSearch安装与启动
1、安装ElasticSearch服务
下载中心 - Elastic 中文社区 (elasticsearch.cn)
学习的是ElasticSearch的Java客户端的使用,安装较为简便的Window版本
1、解压elasticsearch-7.8.0-windows-x86_64.zip到 英文且没有空格的目录
2、启动es服务,双击bin目录下的elasticsearch.bat
3、 通过浏览器访问ElasticSearch服务器:http://localhost:9200
2、Kibana客户端
1、解压kibana-7.8.0-windows-x86_64.zip
2、进入config目录修改kibana.yml第2、28行,配置自身端口和连接的ES服务器地址。
server.port: 5601 elasticsearch.hosts: ["http://localhost:9200"]
3、进入kibana的bin目录,双击kibana.bat启动
.4、访问:http://localhost:5601
http://localhost:5601/app/kibana#/dev_tools/console
然后点左边那三个横杠,下拉选择 dev-tools。这里就是之后使用的地方了
3、Elasticsearch head客户端
ElasticSearch head就是一款能连接ElasticSearch搜索引擎,并提供可视化的操作页面对ElasticSearch搜索引擎进行各种设置和数据检索功能的管理插件,如在head插件页面编写RESTful接口风格的请求,就可以对ElasticSearch中的数据进行增删改查、创建或者删除索引等操作。
elasticsearch-head,插件安装好后怎么使用
点击后跳转效果
集群健康值颜的色说明:
颜色 | 说明 |
绿色 | 最健康的状态,代表所有的分片包括备份都可用 |
黄色 | 基本的分片可用,但是备份不可用(也可能是没有备份) |
红色 | 部分的分片可用,表明分片有一部分损坏。执行查询部分数据仍然可以查到,遇到这种情况,还是赶快解决比较好 |
灰色 | 未连接到elasticsearch服务 |
Elasticsearch重要概念
1、分词器
字符串由text和keyword类型替代
- text,(可以分词的文本),比如产品描述
- keyword,(精确值,不可分词),比如email地址
IK分词器的俩种模式
ik_max_word(最细切分,细粒度)
#方式一ik_max_wordGET /_analyze{ "analyzer": "ik_max_word", "text": "乒乓球明年总冠军" }
会将“乒乓球明年总冠军”拆分为:乒乓球、乒乓、球、明年、总冠军、冠军
ik_smart(粗粒度分词)
#方式二ik_smartGET /_analyze{ "analyzer": "ik_smart", "text": "乒乓球明年总冠军" }
会将“乒乓球明年总冠军”拆分为乒乓球、明年、总冠军
2、索引和文档
index:一些具有相似特征的文档的集合
索引 index
ElasticSearch 中的索引更像是一种数据存储集合,即用于存储文档
文档存储的地方,类似于MySQL中的数据库。建立"索引"之后,可以直接往 索引 中写入"文档"
Elasticsearch是面向文档型数据库,一条数据在这里就是一个文档,用JSON作为文档序列化的格式
文档 document
文档是ES中信息存储和检索的最小单位,以json的形式存储
{ "name":"", "age":18, "gender":1 }
一个文档不只有数据。它还包含了元数据(metadata)——关于文档的信息。三个必须的元数据节点是
节点
说明
_index
文档存储的地方
_type
文档代表的对象的类
_id
文档的唯一标识
字段 Field
每个文档包含多个字段,字段是ES中JSON数据的键,相当于Mysql数据表的字段
3、映射 mapping
mapping映射是对文档中每个字段的类型进行定义,mappings 映射相当于表结构
每个文档都有映射,但是在大多数使用场景中,我们并不需要显示的创建映射,因为ES中实现了动态映射
查看 mapping 信息:
GET renzu/_mapping
4、集群与节点
单节点集群
Elasticsearch head 查看当前集群状态
一个集群(Cluster)就是由一个或多个节点(node)组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能。集群默认名“elasticsearch”
一个节点(node)就是一个运行的 ES 实例,一个机器可以有多个实例,所以并不是说一台机器就是一个node,大多数情况下,每个node运行在一个独立的环境或者虚拟机上。
一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每个节点都会被安排加入到一个叫做“elasticsearch”的集群中
5、分片 Shard
单个节点由于物理机硬件限制,存储的文档是有限的,如果一个索引包含海量文档,则不能在单个节点存储。ES 提供分片( Shard)机制,同一个索引可以存储在不同分片(数据容器)中。
分片有助于横向扩展,N个分片会被尽可能平均地(rebalance)分配在不同的节点上(例如你有2个节点,4个主分片(不考虑备份),那么每个节点会分到2个分片,后来你增加了2个节点,那么你这4个节点上都会有1个分片,这个过程叫relocation,ES感知后自动完成)
当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。
PUT /users
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
一个分片只能存放 Integer.MAX_VALUE - 128 = 2,147,483,519 个 docs。
主分片primary shard
主分片会被尽可能平均地 (rebalance) 分配在不同的节点上
6、副本 replica
从分片只是主分片的一个副本,它用于提供数据的冗余副本,从分片和主分片不会出现在同一个节点上(防止单点故障),默认情况下一个索引创建 5 个主分片,每个主分片会有一个从分片
为了提升访问压力过大是单机无法处理所有请求的问题,Elasticsearch集群引入了副本策略replica。副本策略对index中的每个分片创建冗余的副本,处理查询时可以把这些副本当做主分片来对待(primary shard),此外副本策略提供了高可用和数据安全的保障,当分片所在的机器宕机,Elasticsearch可以使用其副本进行恢复,从而避免数据丢失。
注意:对于一个索引,除非重建索引否则不能调整主分片的数目 (number_of_shards),但可以随时调整 replica 的数目 (number_of_replicas)。
kibana客户端操作
1、操作索引
1.查看全部索引库
#查看es中有哪些索引库
GET /_cat/indices?v
es 中会默认存在一个名为.kibana和.kibana_task_manager的索引
表头的含义
字段名 | 含义说明 |
health | green(集群完整) yellow(单点正常、集群不完整) red(单点不正常) |
status | 是否能使用 |
index | 索引名 |
uuid | 索引统一编号 |
pri | 主节点几个 |
rep | 从节点几个 |
docs.count | 文档数 |
docs.deleted | 文档被删了多少 |
store.size | 整体占空间大小 |
pri.store.size | 主节点占 |
2.对索引的增删改查
# 创建索引
PUT person
# 查询索引
GET person
# 删除索引
DELETE person
-------------------------------------------------
# 参数可选:指定分片及副本,默认分片为3,副本为2
PUT /atguigu
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2
}
}
3.映射的添加和查看
# 查询映射
GET person/_mapping
# 添加映射(先创建索引库再添加映射)
PUT person/_mapping
{
"properties":{
"name":{
"type":"keyword"
},
"age":{
"type":"integer"
}
}
}
# ------------------------------------------------
# 创建索引并添加映射
PUT person
{
"mappings": {
"properties": {
"name":{
"type": "keyword"
},
"age":{
"type":"integer"
}
}
}
}
4.创建映射字段
在索引库中创建映射字段
PUT /索引库名/_mapping{ "properties": { "字段名": { "type": "类型", "index": true, "store": true, "analyzer": "分词器" } } }
字段名:类似于列名,properties下可以指定许多字段。
每个字段可以有很多属性。例如:
- type:类型,String(text keyword) Numeric(long integer float double) date boolean
- index:是否索引,默认为true
- store:是否存储,默认为false,即使为false也会存储到_source中,如果为true则会额外存储一份
- analyzer:分词器,这里使用ik分词器:
ik_max_word
或者ik_smart
举例如下:
# 索引库中添加字段PUT person/_mapping{ "properties":{ "address":{ "analyzer": "ik_max_word", "type":"text" } } }
2、操作文档
1.指定id添加查看文档
#------------文档操作----------------------
# 查询索引
GET person
# 添加文档,指定id
PUT person/_doc/1
{
"name":"太一",
"age":20000,
"address":"太阳星"
}
# 查询文档
GET person/_doc/1
_source
:源文档信息,所有的数据都在里面
_id
:这条文档的唯一标识,与文档自己的id字段没有关联
2.查询所有文档
# 查询所有文档
GET person/_search
# 添加文档,不指定id,自动生成id
POST person/_doc/
{
"name":"帝俊",
"age":20001,
"address":"太阳星"
}
# 查询指定id文档
GET person/_doc/1
3.文档删改操作
#------------文档操作----------------------
# 删除文档
DELETE person/_doc/1
# 修改文档 根据id修改 ,相当于把原来的删除,再把这个新添加进去。
#即如果修改的时候只有一个属性,那么修改后也就只有一个属性
PUT person/_doc/1
{
"name":"东皇太一",
"age":20000,
"address":"太阳星"
}
4.智能的mapping映射
事实上Elasticsearch非常智能,你不需要给索引库设置任何mapping映射,它也可以根据你输入的数据来判断类型,动态添加数据映射。
特别注意:如果是字符串类型的数据,会添加两种类型:text + keyword。
3、花里胡哨的查询
elasticsearch作为搜索引擎,最复杂最强大的功能就是搜索查询功能。包括:匹配查询、词条查询、模糊查询、组合查询、范围查询、高亮、排序、分页等等查询功能。
查询语法和结果结构分析
基本查询语法如下:
GET /索引库名/_search{ "query":{ "查询类型":{ "查询条件":"查询条件值" } } }
这里的query代表一个查询对象,里面可以有不同的查询属性
- 查询类型:
- 例如:
match_all
,match
,term
,range
等等
- 查询条件:查询条件会根据类型的不同,写法也有差异
举例:
GET person/_search
{
"query": {
"match": {
"address": "太阳星"
}
}
}
查询结果宏观来看
- took:查询花费时间,单位是毫秒
- time_out:是否超时
- _shards:分片信息
- hits:搜索结果总览对象
- total:搜索到的总条数
- max_score:所有结果中文档得分的最高分
- hits:搜索结果的文档对象数组,每个元素是一条搜索到的文档信息
-----------------------------------
点开hits,查看具体的一个文档对象
- _index:索引库
- _type:文档类型
- _id:文档id
- _score:文档得分
- _source:文档的源数据
1.match查询-条件分词
match查询会分析查询条件,先将查询条件进行分词,然后查询,求并集,返回符合指定条件的文档
# match 先会对查询的字符串进行分词,在查询,求并集GET person/_search{ "query": { "match": { "address": "太阳星" } } }
默认的match查询多个词是or的关系,当然们可以修改这个关系如下
GET person/_search{ "query": { "match": { "address": { "query": "太阳星", "operator": "and" } } } }
2. term查询-条件不分词
词条查询不会分析查询条件(即查询条件不分词,归结为精确查找),只有当词条和查询字符串完全匹配时才匹配搜索
先看看person索引库中的数据
请注意此时的address,2个是太阳星,1个是太阴星
执行term词条查询
GET person/_search{ "query": { "term": { "address": { "value": "太阳星" } } } }
遗憾的是,上面并未查找出来,出乎意料了吧
原因分析:
根据address字段,建立倒排索引时,需要对其分词,产生多个词条,而词条集合中没有"太阳星"的词条,故而查询不到数据,
如果你这么查,就不一样了,结果会把帝俊和太一查出来,也是意料之中
GET person/_search
{
"query": {
"term": {
"address": {
"value": "太阳"
}
}
}
}
3. bool组合查询
bool
把各种其它查询通过must
(与)、must_not
(非)、should
(或)的方式进行组合
索引库renzu中内容如下
查询年龄大于20岁的女性
GET renzu/_search
{
"query": {
"bool": {
"must": [
{
"range": {
"age": {
"gte": 20
}
}
},
{
"match": {
"sex": {
"query": "女"
}
}
}
]
}
}
}
一个字段匹配2次找不同的值
GET renzu/_search
{
"query":{
"match":{
"name": "张三 李四"
}
}
}
4.filter过滤和sort排序
所有的查询都会影响到文档的评分及排名。如果我们需要在查询结果中进行过滤,并且不希望过滤条件影响评分,那么就不要把过滤条件作为查询条件来用。而是使用filter
方式:
#bool查询表示多条件,must和filter都表示且的关系
GET renzu/_search
{
"query":{
"bool":{
"filter":{
"range":{
"age":{
"gt":20
}
}
},
"must":{
"match":{
"sex":"女"
}
}
}
}
}
排序sort
sort
可以让我们按照不同的字段进行排序,并且通过order
指定排序的方式
GET /atguigu/_search
{
"query": {
"match": {
"title": "小米手机"
}
},
"sort": [
{
"price": { "order": "desc" }
},
{
"_score": { "order": "desc"}
}
]
}
5. 高亮显示
# 搜索条件高亮显示
GET renzu/_search
{
"query": {
"match": {
"name": "张三 李四"
}
},
"highlight": {
"fields": {
"name": {}
}
}
}
结果是多一点标签一样的东西
"highlight" : {
"name" : [
"<em>张</em><em>三</em>"
]
}
6.聚合查询
Elasticsearch中的聚合,包含多种类型,最常用的两种,一个叫桶
,一个叫度量
桶(bucket )
桶的作用,是按照某种方式对数据进行分组,每一组数据在ES中称为一个
桶
Elasticsearch中提供的划分桶的方式有很多:
- Date Histogram Aggregation:根据日期阶梯分组,例如给定阶梯为周,会自动每周分为一组
- Histogram Aggregation:根据数值阶梯分组,与日期类似
- Terms
- Range Aggregation:数值和日期的范围分组,指定开始和结束,然后按段分组
bucket aggregations 只负责对数据进行分组,并不进行计算,因此往往bucket中往往会嵌套另一种聚合:metrics aggregations即度量
聚合为桶
- size: 查询条数,这里设置为0,因为我们不关心搜索到的数据,只关心聚合结果,提高效率
- aggs:声明这是一个聚合查询,是aggregations的缩写
- brands:给这次聚合起一个名字,任意。
- terms:划分桶的方式,这里是根据词条划分
- field:划分桶的字段
GET /atguigu/_search{ "size": 0, "aggs": { "brands": { "terms": { "field": "attr.brand.keyword" } } } }
查询的结果
- aggregations:聚合的结果
- brands:我们定义的聚合名称
- buckets:查找到的桶,每个不同的品牌字段值都会形成一个桶
- key:这个桶对应的品牌字段的值
- doc_count:这个桶中的文档数量
度量(metrics)
分组完成以后,我们一般会对组中的数据进行聚合运算,例如求平均值、最大、最小、求和等,这些在ES中称为
度量
比较常用的一些度量聚合方式:
- Avg Aggregation:求平均值
- Max Aggregation:求最大值
- Min Aggregation:求最小值
- Percentiles Aggregation:求百分比
- Stats Aggregation:同时返回avg、max、min、sum、count等
- Sum Aggregation:求和
- Top hits Aggregation:求前几
- Value Count Aggregation:求总数
桶内度量
度量
的运算会基于桶
内的文档进行,我们为刚刚的聚合结果添加 求价格平均值的度量:
- aggs:我们在上一个aggs(brands)中添加新的aggs。可见
度量
也是一个聚合- avg_price:聚合的名称
- avg:度量的类型,这里是求平均值
- field:度量运算的字段
GET /atguigu/_search{ "size": 0, "aggs": { "brands": { "terms": { "field": "attr.brand.keyword" }, "aggs": { "avg_price": { "avg": { "field": "price" } } } } } }
结果
可以看到每个桶中都有自己的
avg_price
字段,这是度量聚合的结果
-----------
桶内嵌套桶
事实上桶不仅可以嵌套运算, 还可以再嵌套其它桶。也就是说在每个分组中,再分更多组。
比如:我们想统计每个品牌都生产了那些产品,按照
attr.category.keyword
字段再进行分桶
GET /atguigu/_search{ "size" : 0, "aggs" : { "brands" : { "terms" : { "field" : "attr.brand.keyword" }, "aggs":{ "avg_price": { "avg": { "field": "price" } }, "categorys": { "terms": { "field": "attr.category.keyword" } } } } } }
7.指定查询结果字段
#指定响应的字段
GET renzu/_doc/1001?_source=id,name
等价于
#指定响应的字段
GET renzu/_search
{
"query": {
"match": {
"id": "1001"
}
},
"_source": ["id","name"]
}
8.判断文档是否存在
如果我们只需要判断文档是否存在,而不是查询文档内容,那么可以这样:
HEAD renzu/_doc/1001
存在返回:200 - OK
不存在返回:404 – Not Found
9.批量查询
有些情况下可以通过批量操作以减少网络请求。如:批量查询、批量插入数据。
#批量查询 等价于in()
POST renzu/_doc/_mget
{
"ids" : [ "1001", "1003" ]
}
#1006 这条数据不存在,则found的值是false
POST renzu/_doc/_mget
{
"ids" : [ "1001", "1006" ]
}
10._bulk操作
在Elasticsearch中,支持批量的插入、修改、删除操作,都是通过_bulk的api完成的。
批量插入数据:
# 批量插入数据POST _bulk{"create":{"_index":"renzu","_id":2001}}{"id":2001,"name":"name1","age": 21,"sex": "男"}{"create":{"_index":"renzu","_id":2002}}{"id":2002,"name":"name2","age": 22,"sex": "男"}{"create":{"_index":"renzu","_id":2003}} {"id":2003,"name":"name3","age": 23,"sex": "女"}
批量删除:
由于delete没有请求体,所以,action的下一行直接就是下一个action。
#批量删除POST _bulk{"delete":{"_index":"renzu","_id":2001}}{"delete":{"_index":"renzu","_id":2002}}{"delete":{"_index":"renzu","_id":2003}}
11.分页查询
Elasticsearch接受 from 和 size 参数:
size: 结果数,默认10
from: 跳过开始的结果数,默认0
#跳过2个,显示2个
GET renzu/_search?size=2&from=2
也可以这么写
#跳过2个,显示2个
POST renzu/_search
{
"query" : {
"match_all": {}
},
"from": 2,
"size": 2
}
12. terms查询
terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去做匹配。老师说了,都不会分词。。(我也没验证过对不对)
#批量查询 等价于in()
POST renzu/_doc/_mget
{
"ids" : [ "1001", "1003" ]
}
POST renzu/_search
{
"query" : {
"terms" : {
"id" : [1001,1003]
}
}
}
13. range查询
range 过滤允许我们按照指定范围查找一批数据
范围操作符包含:
gt 大于gte大于等于lt小于lte小于等于
#按照指定范围查找一批数据
POST renzu/_search
{
"query": {
"range": {
"age": {
"gte": 20,
"lte": 30
}
}
}
}
14. exits查询
exists 查询可以用于查找文档中是否包含指定字段或没有某个字段
# "exists": 是否包含指定字段
POST renzu/_search
{
"query": {
"exists": {
"field": "email"
}
}
}
没有命中的结果如下: