一、导读#
本篇是白日梦的第三篇ES笔记,前面已经跟大家分享过两篇ES笔记了,分别是:
其实这个专题相对来说质量还是比较不错的,看过前面两篇文章之后基本上大家可以上手使用ES了,包括对一些花里花哨的查询相关的写法也有所了解。然后这一篇文章会和大家调过头来重新巩固一下基础概念上的扫盲。
二、彩蛋福利:账号借用#
三、ES的Index、Shard及扩容机制#
首先你看下这个表格(ES6):
Elasticsearch | 关系型数据库 |
Document | 行 |
type(ES7中被取消) | 表 |
index | Database |
在ES中的Index的地位相当于是MySQL中的database。所以你让ES帮你存储数据你总得
先创建一个Index吧,如果你手动的定制创建Index,你还可以为Index指定shard。
那什么是shard呢?下文马上说。
下面是对Index操作的Case:
# 创建索引 PUT my_index { # 设置index的shard信息 "settings": { "number_of_shards": 3, "number_of_replicas": 2 }, # 设置index中各个字段的类型,属性(下文细讲) "mapping":{ ... } } # 修改索引 PUT /my_index/_settings { # 只能改number_of_replicas,不能改number_of_shards "number_of_replicas":3 } # 删除索引 DELETE /my_index DELETE /my_index1,my_index2 DELETE /my_* DELETE /_all # 删掉所有索引 # 如果不想让ES可以一下子删除所有索引,可以通过配置文件设置 elasticsearch.yml action.destructive_requires_name:true
shard分为primary shard和replica shard ,其中的primary shard可以接受读/写请求,replica shard可以接受读请求,起到一个负载的作用。默认情况下我创建的索引都有: number_of_shards = 5 和number_of_replicas = 1
。表示一共有五个primary shard,并且每个primary 都有一个副本。也就是 5+5*1 =10个shard。
但是当你启动单台ES实例时,架构其实是下面这样:
你会发现,其实系统中就有5个shard。不存在上面计算的10个shard。原因是因为ES要求Primary Shard 和它的备份 replica shard不能同时存在于一个Node上。所以你单个Node启动后,就只有5个primary shard。并且这时你去看集群的状态,会发现整个集群处于yellow状态,表示集群整体可用,但是存在replica shard不可用的情况。
然后你会不会好奇,假设我有2个Node(两个ES实例)组成的ES集群,你怎样做,才能让系统中的Shard是如何负载均衡分布在两个Node上呢?
回答:其实你不用操心,ES自己会帮你完成的。当你增加或减少节点时,ES会自动的进行rebalance,使数据平均分散在不同的节点中。
举个例子:假设你真的又启动了一个Node,这个Node会自动的加入到上面那个ES中去,自动组成一个有两个Node的集群,如果你依然使用的默认配置即:
number_of_shards = 5 和 number_of_replicas = 1
。这时ES会自动将系统rebalance成下图这样:
此时你再去看集群的状态,会发现为green。表示集群中所有shard都可用。
Node2中会存在5个replica shard,他们是Node1中的Primary的备份。每个shard相当于是一个luncene实例,拥有完整的检索数据、处理请求的能力。所以shard的数量越多,一定意义上意味着ES的吞吐量就越大。
但是你需要注意的是,primary shard的数量是不能改变的,但是它的副本的数量可以改变。
至于为什么primary shard的数量是不能改变的,下文中的路由原理会说的。
所以当你想对现在有的ES集群进行扩容的时,就存在两种选择:
1、纵向扩容:你不改变集群的总shard数,然后去买配置更高,存储更大的机器跑这些shard。
2、横向扩容:你扩大replica shard的数量,然后去多购置几个配置低的机器,你只需要写好配置文件,再启动Node,它自己会加入到现有的集群中。因为每个shard的都能对外提供服务嘛,所以你这样扩容系统的性能肯定有提升。
根据现在云服务器实例的市场行情来看,方案二会更省钱一些。
当然了如果你想让ES集群有最好的性能,还是使用默认的配置:number_of_shards = 5 和number_of_replicas = 1
,这时你需要10台机器。每个集群上都启动一个ES实例,让这10个实例组建集群。就像下图这样:
这时每个shard都独享操作系统的所有资源,性能自然会最好。
四、ES支持的核心数据类型#
参考官网 https://www.elastic.co/guide/en/elasticsearch/reference/6.2/mapping-types.html
4.1、数字类型#
long、integer、short、byte、double、float、half_float、scaled_float
示例:
PUT my_index { "mappings": { "_doc": { "properties": { "number_of_bytes": { "type": "integer" }, "time_in_seconds": { "type": "float" }, "price": { "type": "scaled_float", "scaling_factor": 100 } } } } }
4.2、日期类型#
date
示例:
PUT my_index { "mappings": { "_doc": { "properties": { "birthday ": { "type": "date" } } } } } PUT my_index/_doc/1 { "date": "2015-01-01" }
4.3、boolean类型#
string类型的字符串可以被ES解释成boolean。
boolean
示例:
PUT my_index { "mappings": { "_doc": { "properties": { "is_published": { "type": "boolean" } } } } }
4.4、二进制类型#
binary
示例
PUT my_index { "mappings": { "_doc": { "properties": { "name": { "type": "text" }, "blob": { "type": "binary" } } } } } PUT my_index/_doc/1 { "name": "Some binary blob", "blob": "U29tZSBiaW5hcnkgYmxvYg==" }
4.5、范围#
integer_range、float_range、long_range、double_range、date_range
示例
PUT range_index { "settings": { "number_of_shards": 2 }, "mappings": { "_doc": { "properties": { "expected_attendees": { "type": "integer_range" }, "time_frame": { "type": "date_range", "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" } } } } } PUT range_index/_doc/1?refresh { "expected_attendees" : { "gte" : 10, "lte" : 20 }, "time_frame" : { "gte" : "2015-10-31 12:00:00", "lte" : "2015-11-01" } }
4.6、复杂数据类型#
对象类型,嵌套对象类型
示例:
PUT my_index/_doc/1 { "region": "US", "manager": { "age": 30, "name": { "first": "John", "last": "Smith" } } }
在ES内部这些值被转换成这种样式
{ "region": "US", "manager.age": 30, "manager.name.first": "John", "manager.name.last": "Smith" }
4.7、Geo-type#
ES支持地理上的定位点。
PUT my_index { "mappings": { "_doc": { "properties": { "location": { "type": "geo_point" } } } } } PUT my_index/_doc/1 { "text": "Geo-point as an object", "location": { "lat": 41.12, "lon": -71.34 } } PUT my_index/_doc/4 { "text": "Geo-point as an array", "location": [ -71.34, 41.12 ] }
五、精确匹配与全文检索#
精确匹配和全文检索是ES提供的两种检索方式,都不难理解。
5.1、精确匹配:exact value#
搜索时输入的value必须和目标完全一致才算作命中。
"query": { # match_phrase 短语精确匹配的关键字 # 只有name字段 完全等于 “白日梦”的doc 才算命中然后返回 "match_phrase": { "name": "白日梦" } }
5.2、全文检索:full text#
全文检索时存在各种优化处理如下:
- 缩写转换: cn == china
- 格式转换 liked == like == likes
- 大小写转换 Tom == tom
- 同义词转换 like == love
示例
GET /_search { "query": { # match是全文检索的关键字 # 白日梦可以被分词器分成:白、白日、白日梦 # 所以当你使用:白、白日、白日梦、我是白日梦、白日梦是我 等等词条检索,都可以检索出结果 "match" : { "name" : "白日梦" } } }
六、倒排索引 & 正排索引#
6.1、倒排索引 inverted index#
其实正排索引和倒排索引都是人们取的名字而已。主要是你理解它是什么东西就好了。
正排索引:以doc为维度,记录doc中出现了哪些词。
倒排索引:以把doc打碎成一个个的词条,以词语为维度。记录它在哪些doc中出现过。
倒排索引要做的事就是将一篇文章通过分词器打散成很多词,然后记录各个词分别在哪篇doc中出现过。用户在使用的时候输入一串搜索串,这串字符串同样会使用一样的分词器打散成很多词。再拿着这些词去方才建立的倒排索引中匹配。同时结合相关性得分找到。
假设我们存在这样两句话。
doc1 : hello world you and me doc2 : hi world how are you
建立倒排索引就是这样
词条 | doc1(*表示出现过) | doc2(-表示不曾出现过) |
hello | * | - |
world | * | * |
you | * | * |
and | * | - |
me | * | - |
hi | - | * |
how | - | * |
are | - | * |
这时,我们拿着hello world you 来检索,同样需要先经过分词器分词,然后可以得到分出来的三个单词:hello、world、you,然后拿着这三个单词去上面的倒排索引表中找,
你可以看到:
- hello在doc1中出现过。
- world在doc1、doc2中出现过。
- you在doc1、doc2中出现过。
最终doc1、doc2都会被检索出,但是doc1命中了更多的词,因此doc1得分会更高,排名越靠前。
6.2、正排索引 doc value#
doc value 是指所有不分词的document的field。
在建立索引的时候,一方面会建立倒排索引,以供搜索用。一方面会建立正排索引,也就是doc values,以供排序,聚合,过滤等操作使用。
正排索引大概长这样:
document | name | age |
doc1 | 张三 | 12 |
doc2 | 李四 | 34 |
os cache会缓存正排索引,以提高访问doc value
的速度。当OS Cache中内存大小不够存放整个正排索引时,doc value
中的值会被写入到磁盘中。
关于性能方面的问题:ES官方建议,大量使用OS Cache来进行缓存和提升性能。不建议使用jvm内存来缓存数据,那样会导致一定的gc开销,甚至可能导致oom问题。所以官方的建议给JVM更小的内存,给OS Cache更大的内存。假如我们的机器64g,只需要给JVM 16g即可。
6.3、禁用doc value
#
假设我们不使用聚合、排序等操作,为了节省空间,在创建mappings
时,可以选择禁用doc value
,不创建正排索引。
PUT /index { "mappings":{ "my_type":{ "properties":{ "my_field":{ "type":"text", "doc_values":false # 禁用doc value } } } } }
七、简述相关性评分#
relevance score 相关度评分算法, 直白说就是算出一个索引中的文本和搜索文本之间的相似程度。
Elasticsearch使用的是 TF-IDF算法 (term-frequency / inverser document frequency)。
- term-frequency: 表示你搜索的词条在当前doc中出现的次数,出现的次数越多越相关。
- inverse document frequency : 表示搜索文本中的各个词条在整个index中所有的document中出现的次数,出现的次数越多越不相关。
- field-length: field长度越长,越不相关。