本文已参与「新人创作礼」活动,一起开启掘金创作之路。
引言
上节我们聊了一下倒排索引的原理和如何压缩文档ID,在es里面叫做posting list。那么除了posting list的压缩来节省空间,es还有什么可以进行压缩来节省空间呢?我们知道文档中有各种元数据,我们可以先试想一下,压缩,无非就是制定规则来对某些数据进行可复原的删减
。那么,如果文档中相似的,重复的的数据,是不是我们可以重点关注的对象?为什么这么说呢?就像前缀树
,不仅可以压缩、更可以快速的查找。因此,在Elasticsearch中使用的压缩算法的重要组成部分是字符串去重
。
关于Elasticsearch的压缩算法LZ4
和DEFLATE
我们后面详细进行讨论。今天先开始我们的本节学习,索引的Mapping。
Mapping是什么
还记得我们聊到的索引的两部分元数据,setting
和mapping
,当时我们简单地聊到了setting用来定义索引的行为,例如分片、备份、感知等等,Mapping则是定义了索引的结构,更加准确的来说Mapping是用来描述一个文档,文档中包含的字段,如何存储和如何索引(分词)的。
正如我们聊到的,Mapping
是一系列的字段集合,当我们在Kibana的Dev Tools执行下列命令的时候。如果你不记得此部分内容可以跳转至核心概念之索引和文档回味一下。
PUT /es-learn-doc-person/_create/1
{
"name":"zhang san",
"age":29
}
# 在es-learn-doc-person索引中创建一个文档
复制代码
不知道你有没有发现,在创建这个文档之前,我们并没有创建es-learn-doc-person
这个索引,由此我们知道,我们创建文档的时候,发现索引并不存在,因此Elasticsearch帮我们创建了索引,如果把这个操作类比成向关系型数据库插数据的话,相当于系统自动进行建表。如果Elasticsearch自动帮我们创建了索引,那么索引的mapping又是如何确认的呢?
GET /es-learn-doc-person/_mapping
# 查看索引的mapping
复制代码
输出结果:
{
"es-learn-doc-person" : {
"mappings" : {
"properties" : {
"age" : {
"type" : "long"
},
"name" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
复制代码
我们尝试解读一下mapping里面的一些定义。mapping里的properties
,便是字段的集合,每一个property里有着type
来描述着字段的类型。age的type是long,name的type是text,当类型是text的时候还有fields的属性。由此不得不引发我们的一些思考:
第一,Elasticsearch里面的mapping的字段有着哪些数据类型?
第二,Elasticsearch是如何确定我们创建的文档中的字段是什么类型的?
字段数据类型
每一个字段都有字段的数据类型,或者是字段类型
。这一句话是什么意思呢?拿上面的es-learn-doc-person
来说,age这个字段的数据类型是数字类型long,那么这个字段的数据类型是text,同时name这个字段下还有一些衍生字段keyword,类型是keyword。es是这么使用他们的,es的text用来全文检索,而keyword用来排序和过滤。目前,在es中只有text
和keyword
是两个类型,他们有类型簇,其余的类型都是只有单个的类型。
我们先粗略的整理一下数据类型表
详细信息可以参考es字段数据类型大全
动态映射
通过上面,我们大抵解决了第一个问题,字段的数据类型有哪些,现在我们面向第二个问题,在默认创建索引的时候的Mapping,他是如何知道字段的相关类型的?答案其实很简单,es创建了一个动态映射。Dynaimic Mapping
会指定一定的规则,根据你输入的文档格式来动态的检测出推荐的数据类型。接下来,我们来看看数据检测的规则是什么样的。
数据类型 | 检测规则 | 备注 |
---|---|---|
null | 忽略这个字段,并不加入mapping中 | |
true或者false | boolean | |
double | float | 优化存储空间 |
long | long | |
object | object | nested等更耗空间性能 |
array | 由数组中第一个非空的值决定 | 如果类型不一致会存储失败 |
string | 先进行date格式检验,通过了就是date | |
string | 再进行数据校验,通过了是float或者long | |
string | text类型,并且创建子属性keyword |
相信你也能看出来一些端倪,es尽可能的考量了数据类型的可能性,但是正如es说到的那样,没有人比你更懂你的数据,因此es提供了静态映射,让你自己来定义你的mapping。
静态映射
PUT /my-index-000001
{
"mappings": {
"properties": {
"age": { "type": "integer" },
"email": { "type": "keyword" },
"name": { "type": "text" },
"ip_addr": {"type": "ip"}
}
}
}
复制代码
在这段代码中我们可以看到,如果让es动态映射的话,可能email和ip推荐的是text的类型,但正如我们经验那般,email和ip进行全文检索的可能性并不大,所以大多数情况下,email设置成keyword,ip设置成ip就足够了,并且能够更有效的利用空间、性能。
问题
如果仅止步于此,不管是es,还是我们自身都会觉得意犹未尽,大家不妨来想这样一个问题。对于ip和邮箱的存储,是不是每一次我们创建mapping都必须要使用静态映射呢?如果不是,你会用什么方式呢?
如果你有任何疑问,欢迎跟我讨论