Elasticsearch学习(一):基础概念
1、什么是Elasticsearch?
1.1、介绍
Elasticsearch(简称ES)是一个基于Apache Lucene构建的开源、分布式、RESTful接口的全文搜索引擎,Elasticsearch通过对Lunece的封装,隐藏了复杂性,提供了使用简单的RESTful Api。
Elasticsearch还是一个分布式文档数据库,其中每个字段均可被索引,而且每个字段的数据均可被搜索,因为对文档进行了分词处理。ES能够横向扩展至数以百计的服务器存储以及处理PB级的数据,可以在极短的时间内存储、搜索和分析大量的数据。
Elasticsearch 是用 Java 开发的,并作为 Apache 许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。
注:PB是数据存储容量的单位,它等于2的50次方个字节,或者在数值上大约等于1000个TB。
1.2、为什么要使用它
在业务开发中,基于ES的特性,通常有下面这些场景需要使用它:
- 存储大量数据。通过在使用mysql存储的时候,数据的单位是
G
。使用ES的时候,数据的单位是T
。由此可以看出ES使用于大数据量的存储场景,基于分布式特性,它也支持备份和容灾,并且可以很容易水平扩展容量。 - 分词搜索引擎。ES具有强大的分词能力,可以支持高性能的实时搜索。
- 高效数据分析。ES提供的聚合分析功能,可实现对保存的大量数据的近实时统计分析。
1.3、特性
- 分布式的实时文件存储,每个字段都被索引并可被搜索;
- 分布式的实时分析搜索引擎;
- 可以扩展到成百上千台服务器,处理PB级结构化或非结构化数据。
1.4、使用场景
- 搜索领域: 如百度、谷歌,全文检索等。
- 门户网站: 访问统计、文章点赞、留言评论等。
- 广告推广: 记录员工行为数据、消费趋势、员工群体进行定制推广等。
- 信息采集: 记录应用的埋点数据、访问日志数据等,方便大数据进行分析。
1.5、ElasticSearch与Solr的比较
- ES和Solr都是是基于Lucene的,它们都是成熟的产品,拥有强大而广泛的用户社区;
- Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能;
- Solr 支持更多格式的数据,而 Elasticsearch 仅支持json文件格式;
- Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多由第三方插件提供;
- Solr 在传统的搜索应用中表现好于 Elasticsearch,但在处理实时搜索应用时效率明显低于 Elasticsearch。
2、elasticsearch相关概念
es底层无更新数据操作,上层封装的更新实际是删除后再新增。
ES 和传统的关系型数据库有这么一种关系:
RDMS | Elasticsearch |
---|---|
数据库(database) | 索引(index) |
表(table) | 类型(type) |
行(row) | 文档(document) |
列(column) | 字段(field) |
表结构 | 映射 |
索引 | 全文索引 |
SQL | 查询DSL |
SELECT * FROM tablename | GET http://… |
UPDATE table SET | PUT http://… |
DELETE | DELETE http://… |
Elasticsearch是面向文档型数据库,一条数据在这里就是一个文档,用JSON作为文档序列化的格式,比如下面这条用户数据:
{
"name": "John",
"sex": "Male",
"age": 25,
"birthDate": "1990/05/01",
"about": "I love to go rock climbing",
"interests": ["sports","music"]
}
2.1、Index 索引
Elasticsearch 数据管理的顶层单位就叫做 Index(索引),相当于关系型数据库里的database的概念。另外,每个Index的名字必须是小写。一个索引是一个文档的集合(等同于solr中的集合)。每个索引有唯一的名字,通过这个名字来操作它。一个集群中可以有任意多个索引。
Elasticsearch的索引原理是通过倒排索引来实现的。
2.2、type 类型
这里指的是文档的类型,而不是字段的类型。type类似于MySQL数据库中 表 的概念。而ES中没有表的概念,这是ES和数据库的一个区别。在6.0版本之前,ES中有Type的概念,但是后来官方说这是一个设计上的失误。所以这个 type 从 7.x 开始就被移除了!系统默认使用_doc(现在8.x 版本就也不再支持修改这个类型了), 因为这个设计会降低 Lucene 压缩数据的能力,导致数据稀疏。
我们都知道elasticsearch是基于Lucene开发的搜索引擎,而ES中不同type下名称相同的filed最终在Lucene中的处理方式是一样的。举个例子,两个不同type下的两个user_name,在ES同一个索引下其实被认为是同一个filed,你必须在两个不同的type中定义相同的filed映射。否则,不同type中的相同字段名称就会在处理中出现冲突的情况,导致Lucene处理效率下降。从本质上来看,这个 Type是对索引进行逻辑分区,使用文档类型_type 和文档_id 组成_uid ,形成文档的唯一ID,对索引进行细分。而在 Lucene 中,我们这个字段域在索引中是唯一的,所以原本的字段也会被细分,导致字段域增多的同时,数据的密度也就降低了,压缩效率也就降低了,导致ES查询效率的降低。
在 5.X 版本中,一个 index 下可以创建多个 type;
在 6.X 版本中,一个 index 下只能存在一个 type(即:”_type” : “doc”);
在 7.X 版本中,直接去除了 type 的概念,就是说 index 不再会有 type。
8.0开始,将移除接受类型的API
ES 的Type 被废弃后,库表合一,Index 既可以被认为对应 MySQL 的 Database,也可以认为对应 table。
也可以这样理解:
- ES 实例:对应 MySQL 实例中的一个 Database。
- Index 对应 MySQL 中的 Table 。
- Document 对应 MySQL 中表的记录。
2.3、Document 文档
文档在ES中相当于传统数据库中的行的概念,即每一行的数据,ES中的数据都以JSON的形式来表示,在MySQL中插入一行数据和ES中插入一个JSON文档是一个意思。文档是Elasticsearch中的最小单位,每个索引都是有数量众多的文档组成的。文档由多个字段组成,每个字段的类型由mapping定义,每个字段的类型,可以是文本、数值、日期等。字段类型也可以是复杂类型,一个字段包含其他子文档或者数组,但 Elasticsearch 的文档中,相同字段必须有相同类型。下面的JSON数据表示一个包含7个字段的文档。
{
"_index": "user",
"_type": "_doc",
"_id": "qbuOs4AB1VH6WaY_OsFW",
"_version": 1,
"_score": 1,
"_source": {
"name": "张三",
"address": "广东省深圳市",
"remark": "他是一个程序员",
"age": 28,
"salary": 8800,
"birthDate": "1991-10-05",
"createTime": "2019-07-22T13:22:00.000Z"
}
}
上面为 ES 一条文档数据,而一个文档不只有基础数据,它还包含了元数据(metadata)——关于文档的信息,也就是用下划线开头的字段,它是官方提供的字段:
- _index :文档所属索引名称,即文档存储的地方。
- _type :文档所属类型名(此处已默认为_doc)。
- _id :文档的唯一标识。在写入的时候,可以指定该 Doc 的 ID 值,如果不指定,则系统自动生成一个唯一的 UUID 值。
- _score :顾名思义,得分,也可称之为相关性,在查询是 ES 会 根据一些规则计算得分,并根据得分进行倒排。除此之外,ES 支持通过 Function score query 在查询时自定义 score 的计算规则。
- _source :文档的原始 JSON 数据。
2.4、字段Field
相当于是数据表的字段,字段在ES中可以理解为JSON数据的键,是文档中的基本单位,以键值对的形式存在。在下面的JSON数据中,键都是一个字段。
{
"name": "张三",
"address": "广东省深圳市",
"remark": "他是一个程序员",
"age": 28,
"salary": 8800,
"birthDate": "1991-10-05",
"createTime": "2019-07-22T13:22:00.000Z"
}
2.5、映射mapping
相当于数据库中的schema,用来约束字段的数据类型,每一种数据类型都有对应的使用场景。mapping 中定义了一个文档所包含的所有 field 信息,每个文档都有映射,但是在大多数使用场景中,我们并不需要显示的创建映射,因为ES中实现了动态映射。我们在索引中写入一个下面的JSON文档:
{
"name":"jack",
"age":18,
"birthDate": "1991-10-05"
}
在动态映射的作用下,name会映射成text类型,age会映射成long类型,birthDate会被映射为date类型,映射的索引信息如下。
{
"mappings": {
"_doc": {
"properties": {
"age": {
"type": "long"
},
"birthDate": {
"type": "date"
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
自动判断的规则如下:
JSON Type | Field Type |
---|---|
Boolean:true、flase | boolean |
Whole number:123、456、876 | long |
Floating point:123.43、234.534 | double |
String,valid date:”2022-05-15” | date |
String:”Hello Elasticsearch” | string |
常见的ELasticSearch数据类型如下:
数据类型 | 具体类型 |
---|---|
字符串类型 | string,text,keyword |
整数类型 | integer,long,short,byte |
浮点类型 | double,float,half_float,scaled_float |
逻辑类型 | boolean |
日期类型 | date |
范围类型 | range |
二进制类型 | binary |
数组类型 | array |
对象类型 | object |
嵌套类型 | nested |
地理坐标类型 | geo_point |
地理地图 | geo_shape |
IP类型 | ip |
令牌计数类型 | token_count |
注意事项关于字符串类型:
string类型: 在ElasticSearch 旧版本中使用较多,从ElasticSearch 5.x开始不再支持string,由text和keyword类型替代。
- text类型的字段不用于排序,很少用于聚合,需要分词设置text类型
- keyword类型适用于索引结构化的字段,keyword类型的字段只能通过精确值搜索到。不需要分词设置keyword类型
补充:
对text类型的字段,会先使用分词器分词,生成倒排索引,用于之后的搜索。
对keyword类型的字段,不会分词,搜索时只能精确查找
2.6、集群Cluster
ElasticSearch 是一个分布式的搜索引擎,所以一般由多台物理机组成。而在这些机器上通过配置一个相同的cluster name,让其互相发现从而把自己组织成一个集群。
2.7、Node 节点
即一个elasticsearch 实例。节点也有自己的名称,默认在启动时会以一个随机的UUID的前七个字符作为节点的名字,你可以为其指定任意的名字。通过集群名在网络中发现同伴组成集群。一个节点也可是集群。(每个物理机器上可以有多个节点,使用不同的端口和节点名)。
node节点可以分为三类:
2.7.1、master节点
集群中的一个节点会被选为master节点,它将负责管理集群范畴的变更:处理创建,删除索引等请求,维护集群状态信息。master节点无需参与文档层面的变更和搜索,这意味着仅有一个master节点并不会因流量增长而成为瓶颈。任意一个节点都可以成为 master 节点。
2.7.2、data节点:
持有数据和倒排索引。默认情况下,每个节点都可以通过设定配置文件elasticsearch.yml中的node.data属性为true(默认)成为数据节点。如果需要一个专门的主节点,应将其node.data属性设置为false。
2.7.3、Client节点:
如果将node.master属性和node.data属性都设置为false,那么该节点就是一个客户端节点,扮演一个负载均衡的角色,将到来的请求路由到集群中的各个节点。
2.8、Shard 分片:
为了将数据添加到 Elasticsearch,我们需要索引(index)——一个存储关联数据的地方。实际上,索引只是一个用来指向一个或多个分片(shards)的 逻辑命名空间 (logical namespace)。
当你查询的索引分布在多个分片上时,ES会把查询发送给每个相关的分片,并将结果组合在一起,而应用程序并不知道分片的存在。即:这个过程对用户来说是透明的。
分片分为:主分片(Primary shard)和副本分片(Replica shard):
主分片Primary shard:
用于解决数据水平扩展的问题,通过主分片,可以将数据分布到集群内的所有节点之上,将一份索引数据划分为多小份的能力,允许水平分割和扩展容量。多个分片可以响应请求,提高性能和吞吐量。一个节点(Node)一般会管理多个分片。
副本分片Replica shard:
副本分片只是一个主分片的拷贝。 副本分片作为硬件故障时保护数据不丢失的冗余备份,并为搜索和返回文档等读操作提供服务。
分片的特点:
- 一个Index数据在物理上被分布在多个主分片中,每个主分片只存放部分数据,每个主分片可以有多个副本。
- 主分片的作用:对索引的扩容,使一个索引的容量可以突破单机的限制。
- 副本分片是对数据的保护,每个主分片对应一个或多个副本分片,当主分片所在节点宕机时,副本分片会被提升为对应的主分片使用。
- 一个主分片和它的副本分片,不会分配到同一个节点上。
- 一个分片就是一个Lucene实例,并且它本身就是一个完整的搜索引擎。应用程序不会和它直接通信。
- 当索引创建完成的时候,主分片的数量就固定了,如果要修改,需要重建索引,代价很高,但是复制分片的数量可以随时调整。
分片的设定:
对于生产环境中分片的设定,需要提前做好容量规划,主分片数是在索引创建的时候预先设定,事后无法修改。
分片数设置过小
- 导致后续无法增加节点实现水平扩展;
- 单个分片的数据量太大,导致数据重新分配耗时。
分片数设置过大,7.0开始,默认主分片设置成1,解决了over-sharding的问题
- 影响搜索结果的相关性打分,影响统计结果的准确性
- 单个节点上过多的分片,会导致资源浪费,同时也会影响性能
用图形表示出来可能是这样子的:
- Index 1:蓝色部分,有3个shard,分别是P1,P2,P3,位于3个不同的Node中,这里没有Replica。
- Index 2:绿色部分,有2个shard,分别是P1,P2,位于2个不同的Node中。并且每个shard有一个replica,分别是R1和R2。
基于系统可用性的考虑,同一个shard的primary和replica不能位于同一个Node中。这里Shard1的P1和R1分别位于Node3和Node2中,如果某一刻Node2发生宕机,服务基本不会受影响,因为还有一个P1和R2都还是可用的。因为是主备架构,当主分片发生故障时,需要切换,这时候需要选举一个副本作为新主,这里除了会耗费一点点时间外,也会有丢失数据的风险。
2.8.1、路由
Elasticsearch 如何知道一个文档应该存放到哪个分片中呢?实际上,这个过程是根据下面这个公式决定的:
shard = hash(routing) % number_of_primary_shards
routing 是一个可变值,唯一不可重复,默认是文档的 _id ,也可以设置成一个自定义的值。 routing 通过 hash 函数生成一个数字,然后这个数字再除以 number_of_primary_shards (主分片的数量)后得到余数 。这个分布在 0 到 number_of_primary_shards-1 之间的余数,就是我们所寻求的文档所在分片的位置。
这就解释了为什么我们要在创建索引的时候就确定好主分片的数量 并且永远不会改变这个数量:因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了。
所有的文档 API( get 、 index 、 delete 、 bulk 、 update 以及 mget )都接受一个叫做 routing 的路由参数 ,通过这个参数我们可以自定义文档到分片的映射。一个自定义的路由参数可以用来确保所有相关的文档(例如所有属于同一个用户的文档)都被存储到同一个分片中。
3、ElasticSearch中索引原理
我们知道ES的搜索是非常快的,并且比MySQL快很多,所以来看下两者的索引原理:
- MySQL的索引原理:B+Tree索引
ElasticSearch的索引原理:倒排索引
倒排索引:也叫反向索引,首先对文档数据按照id进行索引存储,然后对文档中的数据分词,记录对词条进行索引,并记录词条在文档中出现的位置。这样查找时只要找到了词条,就找到了对应的文档。概括来讲是先找到词条,然后看看哪些文档包含这些词条。通俗地来讲,正向索引是通过key找value,倒排索引则是通过value找key。跟MySQL中的索引回表查询有点类似。
还没有评论,来说两句吧...