一、Elasticsearch 简介
Elasticsearch 是一个基于 Lucene 的全文检索服务器,提供了基于 RESTful web接口的分布式全文搜索引擎。
总结:
- Elasticsearch是一个基于 Lucene 的分布式全文检索服务器;
- Elasticsearch 对 Lucene 进行了封装,隐藏了 Lucene 的复杂性,对外提供 RESTful 风格的接口来进行索引和搜索;
二、Elasticsearch 工作原理
2.1 索引结构
右侧蓝色部分是原始文件,左边是文件的逻辑结构。Elasticsearch 根据一定的策略从原始文档中提取出一系列的关键字(field)保存在一个文档(document)中,用这个document文档来描述原始文件,然后从所有的document文档中提取出不重复的 field 作为 分词(trem)保存在分词列表(index)中。
分词列表特点:
- 分词列表中所有的分词(trem)是不重复的;
- 分词列表中不包括“的、地、啊、呀”等连词或语气词;
- 分词列表中不包括一些通常不会作为搜索关键字的内容作为分词,如:资源的URL等;
总结:分词列表中的分词是不重复的,分词列表中的分词都是可能作为搜索关键字的内容;
2.2 倒排索引
正排索引:将搜索内容作为关键字,在一系列的原始文件中,进行精确或模糊匹配,将包含关键字的原始文件作为目标文件(文档 --> 关键字);
倒排索引:从搜索内容中提取若干个分词,通过分词检索 document 文档,将和搜索内容所包含分词关联性最强的document文档对应的原始文件作为目标文件。(关键字 --> 文档)
三、Elasticsearch中数据存储的结构及简单入门
Elasticsearch中数据的存储结构和关系型数据库类似,可以类比记忆:
Elasticsearch | Mysql(关系型数据库) |
---|---|
index(索引库) | database(数据库) |
type(类型) | table(表) |
document(文档) | row(记录,一行数据) |
field(域) | column(字段) |
注意:ES 6.0 之前的版本中 type(类型)相当于关系型数据库的 table(表),6.0 版本之后 type 概念被弱化,并且ES官方打算在 7.0 版本废除 type 的概念。
3.1 index管理
1、创建index索引库:
语法:PUT /index_name
,如:
PUT /java06
{
"settings": {
"number_of_shards": 2,
"number_of_replicas": 1
}
}
number_of_shards:当索引库中数据量太大,一台ES存放不下,会将索引库拆分成多片分别存储在不同的节点中,number_of_shards 用来指定节点数量。
number_of_replicas:每个节点只有一台,一个节点挂掉整个索引库都会挂掉,为了提高系统的容错性,可以给每个节点设置备份机,number_of_replicas 用来指定每个节点的备份数量。如果只有一台机器,备份数量必须为0,如果机器挂掉了,主片和备份都会挂掉,备份就没有意义了;
2、修改index:
注意:索引库(index)一旦创建,primary shard 不可修改,只能修改 replica shard 数量。
语法:PUT /index_name/_settings
,如:
PUT /java06/_settings
{
"number_of_replicas" : 1
}
为什么 primary shard 不可修改?
索引库中添加数据(document)时,会根据数据的 id 获取哈希值 hash(id)
,然后对主片个数(primary shard)求余(shard = hash(id) %number_of_primary_shards
)来确定数据存放到那个分片中,一旦修改,会造成索引库中数据紊乱。
3、删除index:
语法:DELETE /index_name
,如:
DELETE /java06 //删除单个
DELETE /[index_name1, index_name2] //删除多个
3.2 mapping管理
创建映射的过程类似关系型数据库中建表的过程,确定 type 中有哪些 field ,分别是什么类型。
1、创建mapping:
语法:POST /index_name/type_name/_mapping
,如:
POST /java06/course/_mapping
{
"properties": {
"name": {
"type": "text"
},
"description": {
"type": "text"
},
"studymodel": {
"type": "keyword"
}
}
}
2、查询mapping:
查询所有索引的映射:
GET /java06/course/_mapping
3、更新mapping:
映射创建成功后,可以添加新的映射,不能更改已有映射。
4、删除mapping:
通过删除索引来删除映射。
3.3 document管理
1、创建document:
语法:POST /index_name/type_name/id
,如:
POST /java06/course/1
{
"name":"python从入门到放弃",
"description":"人生苦短,我用Python",
"studymodel":"201002"
}
POST /java06/course
{
"name":".net从入门到放弃",
"description":".net程序员谁都不服",
"studymodel":"201003"
}
创建document时如果不指定id,ES也会自动生成,但生成的id可读性较差,建议指定id。
2、修改document:
语法:PUT/index_name/type_name/id
,如:
PUT /java06/course/2
{
"name":"php从入门到放弃",
"description":"php是世界上最好的语言",
"studymodel":"201001"
}
如果修改的id对应的文档不存在,会自动创建文档document。
结果:
{
"_index": "test_index", 新增的 document 在什么 index 中,
"_type": "my_type", 新增的 document 在 index 中的哪一个 type 中。
"_id": "1", 指定的 id 是多少
"_version": 1, document 的版本是多少,版本从 1 开始递增,每次写操作都会+1
"result": "created", 本次操作的结果,created 创建,updated 修改,deleted 删除
"_shards": { 分片信息
"total": 2, 分片数量只提示 primary shard
"successful": 1, 数据 document 一定只存放在 index 中的某一个 primary shard 中
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
3、查询document:
语法:GET /index_name/type_name/id
GET /java06/course/1
4、删除Document:
语法:DELETE /index_name/type_name/id
DELETE /java06/course/3
结果:
{
"_index": "java06",
"_type": "course",
"_id": "2",
"_version": 2,
"result": "deleted",
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"_seq_no": 3,
"_primary_term": 1
}
ES 中执行删除操作时,ES先标记Document为deleted状态,而不是直接物理删除。当ES 存储空间不足或工作空闲时,才会执行物理删除操作,标记为deleted状态的数据不会被查询搜索到(ES 中删除 index ,也是标记。后续才会执行物理删除。所有的标记动作都是为了NRT(近实时)实现)
四、field详细介绍
4.1 field 的属性介绍
1、type:
type 属性用来指定 field 的类型。
field 常用类型:
字符串:text、keyword;
数字:integer、long、float、double;
text类型的字段需要通过analyzer 属性指定分词模式。
keyword类型的字段往索引目录写时是不进行分词的,比如:邮政编码、手机号码、身份证等。keyword字段通常用于过虑、排序、聚合等。
2、analyzer:
analyzer 属性用来指定分词模式。
可以在创建索引和搜索的时候指定不同的分词器。建议在创建索引时使用 ik_max_word 分词器进行细粒度的分词,搜索时使用 ik_smart 分词器进行粗粒度的分词。
"name": {
"type": "text",
"analyzer":"ik_max_word",#生成索引目录时
"search_analyzer":"ik_smart"#检索时
}
分词器:
指定分词模式就是选择分词器。由于Lucene 是由外国人开发,Lucene 自带的分词器对中文的分词效果很不好,比如自带的三种分词器:
StandardAnalyzer: 单字分词:就是按照中文一个字一个字地进行分词。如:“我爱中国”,效果:“我”、“爱”、“中”、“国”。
CJKAnalyzer: 二分法分词:按两个字进行切分。如:“我是中国人”,效果:“我是”、“是中”、“中国”、“国人”。
SmartChineseAnalyzer: 对中文支持较好,但扩展性差,扩展词库和禁用词库等不好处理。
因此,我们通常使用由第三方的分词器:如:IK分词器。
IK分词器有两种分词模式:ik_max_word和ik_smart模式。
- ik_max_word:会将文本做最细粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、华人、人民大会堂、人民、共和国、大会堂、大会、会堂等词语。
- ik_smart:会做最粗粒度的拆分,比如会将“中华人民共和国人民大会堂”拆分为中华人民共和国、人民大会堂。
IK分词器支持自定义词库,IK分词器安装目录里config路径下自带的main.dic的文件为扩展词典,stopword.dic为停用词典。
(注意将文件格式另存为utf-8(不要选择utf-8 BOM))
要使用自定义词库,需要在配置文件 IKAnalyzer.cfg.xml 中 配置main.dic和stopword.dic,
3、index:
index属性决定了该field能否作为检索条件。通过index属性指定是否对该 field 创建索引。
默认为 index=true,即要进行索引,只有 field 创建了索引,才可以通过该 field 从索引库搜索到内容。对于一些不需要索引的内容,可以将index设置为false。
"pic": {
"type":"text",
"index":false
}
4、source:
source 属性决定了该field的内容是否作为结果展示。
如果只想存储某几个字段的原始值到Elasticsearch,可以通过incudes参数来设置,在mapping中的设置如下:
POST /java06/course/_mapping
{
"_source": {
"includes":["description"]
}
}
同样,可以通过excludes参数排除某些字段:
POST /java06/course/_mapping
{
"_source": {
"excludes":["description"]
}
}
五、ES 集群管理
ES 通常以集群方式工作,这样不仅提高了ES的搜索能力,而且增加了系统的容错能力及高可用。
下面是一个ES集群的示意图:该集群有三个主片,每个主片有两个副本,某个节点挂了也不怕,其他节点上还有副本。
添加文档过程:
- 用户将添加请求发送到节点1;
- 通过求余算法得知该文档应添加到主分片2,节点1就会将请求转发到节点3;
- 节点3上的主分片2会添加文档,然后将数据同步到1、2节点上的副本2;
查询文档过程:
1、用户将查询请求发送到节点1;
2、经过求余算法得知数据存储在分片2上,此时有3种选择,节点1上的副本2、节点2上的副本2和节点3上的主分片2;
3、节点1通过负载均衡选中了节点2上的副本2,将请求转发给节点2;
4、节点2把数据返回给节点1,节点1将数据返回给用户;
ES集群的状态
集群的状态有3中颜色表示:
- green:表示集群中所有的主分片和副本都正常;
- yellow:表示集群中所有的主分片都正常,副本有部分挂掉;
- red:表示集群中主分片和副本都有部分挂掉;