Elasticsearch 动态创建mapping
如果在创建index的时候不指定mapping,那么ES会帮我们动态创建mapping,那么String数据类型的数据会被映射成text类型(这种类型默认是分词的,在搜索的时候只需要指定部分内容就可以搜到结果)。如果不动态创建mapping,指定字段类型是keyword,那么在搜索的时候需要精确匹配内容,才可以把结果搜出来(底层依然使用倒排索引,只不过索引的字段是整个内容)
Elasticsearch Object数据类型
我们新建一个index:
PUT article
{
"mappings": {
"type": {
"properties": {
"title": {
"type": "keyword"
},
"author": {
"properties": {
"name": {
"type": "keyword"
},
"age": {
"type": "long"
}
}
}
}
}
}
}
这里面的author字段属于Object类型,下面有name和age字段。
当我们添加一条数据时:
POST article/type
{
"title": "hello world",
"author": {
"name": "Bill Gates",
"age": 50
}
}
Object数据类型的底层存储形式:
{
"title": ["hello world"],
"author.name": ["Bill Gates],
"author.age": [50]
}
当添加的数据是如下形式:
POST article/type
{
"title": "CS",
"author": [
{"name":"Tuling","age":70},
{"name":"yaoqizhi","age":65}
]
}
那么Object的存储形式是:
{
"title": "CS",
"author.name": ["Tuling", "yaoqizhi"],
"author.age": [70, 65]
}
Term/Terms
term/terms查询会去倒排索引中查询确切的关键词,并不知道分词器的存在,这种查询适合keyword、date、numeric类型
Match
match 会对我们这个搜索的字段进行分词,然后在倒排索引中进行查找。同时所查找的字段的数据类型不能是keyword。
例如下面的index:
PUT article2
{
"mappings": {
"type": {
"properties": {
"title": {
"type": "text"
},
"author": {
"properties": {
"name": {
"type": "keyword"
},
"age": {
"type": "long"
}
}
}
}
}
}
}
添加数据
POST article2/type
{
"title": "system",
"author": {
"name": "Tom",
"age": 52
}
}
POST article2/type
{
"title": "operator",
"author": {
"name": "jerry",
"age": 12
}
}
使用match搜索:
GET article2/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "system operator"
}
}
]
}
}
}
搜索结果如下:
"hits": {
"total": 2,
"max_score": 0.2876821,
"hits": [
{
"_index": "article2",
"_type": "type",
"_id": "Dgu-oW0BKTSW7lTxmSgP",
"_score": 0.2876821,
"_source": {
"title": "system",
"author": {
"name": "Tom",
"age": 52
}
}
},
{
"_index": "article2",
"_type": "type",
"_id": "Dwu-oW0BKTSW7lTxoyhT",
"_score": 0.2876821,
"_source": {
"title": "operator",
"author": {
"name": "jerry",
"age": 12
}
}
}
]
}
此时,当title的类型是text时,使用match才会生效。
如果使用term查询:
GET article2/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"title": {
"value": "system operator"
}
}
}
]
}
}
}
这样是查询不到的,因为要精确匹配system operator
,而文档中没有这个词。
Multi_match
可以指定多个字段,matc只能指定一个字段
match_phrase
短语匹配,类似于英语中的短语
ik分词器
带有两个分词器:
- ik_max_word: 会将文本做最细粒度的拆分;尽可能多的拆分出词语
- ik_smart: 会做最粗粒度的拆分;已经被分出的词语将不会被其他词语占用
Filter
filter不计算相关性的,同时可以缓存,因此速度快与query。
新建index,并插入数据:
PUT order
{
"mappings": {
"type": {
"properties": {
"customsID": {
"type": "keyword"
},
"price": {
"type": "keyword"
},
"orderID": {
"type": "keyword"
}
}
}
}
}
POST order/type/_bulk
{"index":{}}
{"customsID":"001","price":"100", "orderID":"10000"}
{"index":{}}
{"customsID":"002","price":"40", "orderID":"10002"}
{"index":{}}
{"customsID":"002","price":"75", "orderID":"10003"}
查询价格是40的数据:
GET order/_search
{
"query": {
"bool": {
"filter": {
"term": {
"price": "40"
}
}
}
}
}
查询结果如下:
"hits": {
"total": 1,
"max_score": 0,
"hits": [
{
"_index": "order",
"_type": "type",
"_id": "EgvxoW0BKTSW7lTx3iiD",
"_score": 0,
"_source": {
"customsID": "002",
"price": "40",
"orderID": "10002"
}
}
]
}
既然可以用term,那么就可以使用terms
constant_score
当我们不关心检索词频率TF(Term Frequency)对搜索结果排序的影响时,可以使用constant_score将查询语句query或者过滤语句filter包装起来。
检索词频率:检索词在该字段出现的频率。出现频率越高,相关性也越高。字段中出现过5次要比只出现过1次的相关性高。
假设存在如下类型的文档:
{ "description": "wifi wifi garden pool" }
查询语句如下:
{
"query":{
"bool":{
"should": [
{ "constant_score": {
"query": { "match": { "description": "wifi" }}
}},
{ "constant_score": {
"query": { "match": { "description": "garden" }}
}},
{ "constant_score": {
"query": { "match": { "description": "pool" }}
}}
]
}
}
}
因为不考虑检索词频率,所以匹配文档的score等于该文档含有的不同检索词汇的个数。
score受到协调因子boost的影响:
{ "constant_score": {
"boost":2,
"query": { "match": { "description": "pool" }}
}}
出现pool的文档score加2。
score还需要考虑反向文档频率IDF的影响,最后得分可以不是整数。
反向文档频率:每个检索词在索引中出现的频率。频率越高,相关性越低。 检索词出现在多数文档中会比出现在少数文档中的权重更低, 即检验一个检索词在文档中的普遍重要性。
Rebalance
当新增一个节点时,ES集群会自动把一些shard分配到这个新的节点
节点对等
每一个节点(包括master节点和data节点)都可以接收请求,如果要查询的数据在当前节点的shard中不存在,那么会将请求路由到其他节点,其他节点查询后将结果返回给原来的节点,最后返回给应用程序。
primary shard和replica shard
每一个document肯定只存在于一个primary shard和对应的replica shard中,不可能存在多个primary shard中。
primary shard在创建index时之后就不可以改变了,而replica shard可以在后续更改
ES容错机制
当es集群中有一个节点宕机,并且这个节点包含primary shard,那么会把其余节点上对应的replica shard提升为primary shard
ES更新
es更新文档时,当出现并发问题时,内部使用乐观锁,也就是说通过版本控制(也就是说version)的方式,解决并发冲突的问题。此外如果使用retry_on_confict=次数
时会从新获取最新版本的文档数据,然后再去进行更新,如果失败之后,会再次尝试这一步骤,次数自己指定。
ES数据路由的原理
一个索引由多个primary shard构成。此时如果添加一个document,那么这个document一定会存储在某一个primary shard中的,ES通过数据路由机制确定这个document存储在那个shard中。
路由算法:
shard=hash(routing) % number_of_primary_shards
示例:一个索引,3个primary shard
- 当每次增删改查时,有一个routing值,默认是文档doc_id值
- 对这个routing值使用hash函数进行计算
- 算出的值再和primary_shards个数取余数
余数肯定在0~(number_of_primary_shards-1)之间,文档就在对应的shard上
如果手动指定doc_id对负载均衡和提高批量读写的性能都有帮助
ES增删改原理
示例:一个索引,三个分片,一个副本。
如下图:当我们往ES中添加一条数据时,可以任选一个节点进行添加数据操作,假设我们往node1中添加数据,那么此时node1被称为协调节点,因为我们要添加节点时,这个文档不一定是要存在node1中,而是需要根据数据路由算法来决定这个数据应该往哪个分片存。
协调节点会根据数据路由算法,算出文档应该存在哪个primary shard中。假设经过计算这条数据应该存在P1上。然后协调节点会将数据进行转发,将数据转发到node2中的P1上,到达P1后存储数据,并把数据同步到副本R1上。
一系列操作之后,由协调节点对应用程序进行响应。
写一致性原理和quorum机制
对es进行增删改都数据写操作,当进行写操作时,可以使用参数consistency=
consistency的取值有以下三种:
- one:(primary shard) 只要有一个primary shard是活跃的就可以执行
- all:(all shard)所有的primary shard和replica shard都是活跃的才能执行
- quorum:(default)默认值,大部分shards是活跃的才能执行,(例如共有6个shard,至少有3个shard活跃时才能执行写操作)
quorum机制:int((number_of_primary_shard + number_of_replica)/2) + 1
例如: 3个primary shard,1个replica
int((3+1)/2) + 1=3,因此至少要3个shard活跃时才可以写操作。
案例:当只有一个node,一个primary shard,一个replica时,此时集群状态时yellow,只有一个primary shard活跃,而quorum=(1+1)/2 + 1 = 2,因此此时无法进行增删改操作。
案例:当我们有两个node,一个primary shard,三个replica时,此时集群状态是yellow,而quorum=(1+3)/2 + 1 = 3,因此此时同样能无法进行增删改操作。
当活跃的shard的个数没有达到要求时,es默认会等待一分钟,如果在等待时间内活跃的shard数量没有增加,则显示timeout。
文档查询原理
第一步:查询请求发给任意一个节点,该节点就成了协调节点(coodinating node),然后该节点使用路由算法算出文档所在的primary shard
第二步:协调节点把请求转发给primary shard也可以转发给replica shard(使用轮训算法(Round-Robin-Schedule)把请求平均分配给primary shard和replica shard)
第三步:处理请求的节点把结果返回给协调节点,协调节点再返回给应用程序
特殊情况:请求的文档还在建立的过程中,primary shard上存在,而replica shard上不存在,但是请求被转发到了replica shard中,这时就会提示找不到文档。
返回数据根据设置的timeout来返回
当数据量比较大时,用户可以设置返回多长时间的数据。虽然不能返回所有的数据,但是可以提高响应速度。不至于让用户等待太长时间,而返回所有数据。