mapping json中包含了诸如properties
,matadata(_id,_source,_type)
,settings(analyzer)
已经其余的settingshtml
PUT my_index { "mappings": { "my_index": { "properties": { "my_field1": { "type": "integer" }, "my_field2": { "type": "float" }, "my_field2": { "type": "scaled_float", "scaling_factor": 100 } } } } }
当咱们往ES中插入一条document时,它里面包含了多个fireld, 此时,ES会自动的将多个field的值,串联成一个字符串,做为_all属性,同时会创建索引,当用户再次检索却没有指定查询的字段 时,就会在这个_all中进行匹配前端
示例:node
# Example documents PUT my_index/_doc/1 { "title": "This is a document" } PUT my_index/_doc/2?refresh=true { "title": "This is another document", "body": "This document has a body" } GET my_index/_search { "query": { "terms": { "_field_names": [ "title" ] } } }
禁用:程序员
PUT tweets { "mappings": { "_doc": { "_field_names": { "enabled": false } } } }
shard_num = hash(_routing) % num_primary_shards
能够像下面这样定制路由规则es6
PUT my_index/_doc/1?routing=user1&refresh=true { "title": "This is a document" } GET my_index/_doc/1?routing=user1
禁用算法
PUT tweets { "mappings": { "_doc": { "_source": { "enabled": false } } } }
这个字段标识doc的类型,是一个逻辑上的划分, field中的value在顶层的lucene创建索引的时候,所有使用的opaque bytes类型,不区分类型的lucene是没有type概念的, 在document中,实际上将type做为一个document的field,什么field呢? _type
sql
ES会经过_type
进行type的过滤和筛选,一个index中是存放的多个type其实是存放在一块儿的,所以一个index下,不可能存在多个重名的typejson
首先一点,在ES5中容许建立多个index,这在ES6中继续被沿用,可是在ES7将被废弃,甚至在ES8中将被完全删除跨域
其次:在一开始咱们将Elastic的index必作Mysql中的database, 将type比做table,其实这种比喻是错误的,由于在Mysql中不一样表之间的列在物理上是没有关系的,各自占有本身的空间,可是在ES中不是这样,可能type=Student中的name和type=Teacher中的name在存储在彻底相同的字段中,换句话说,type是在逻辑上的划分,而不是在物理上的划分数组
这个copy_to其实是在容许咱们自定义一个_all字段, 程序员能够将多个字段的值复制到一个字段中,而后再次检索时目标字段就使用咱们经过copy_to建立出来的_all新字段中
它解决了一个什么问题呢? 假设咱们检索的field的value="John Smith",可是doc中存放名字的field却有两个,分别是firstName和lastName中,就意味着cross field检索,这样一来再通过TF-IDF算法一算,可能结果就不是咱们预期的样子,所以使用copy_to 作这件事
示例:
PUT my_index { "mappings": { "_doc": { "properties": { "first_name": { "type": "text", "copy_to": "full_name" }, "last_name": { "type": "text", "copy_to": "full_name" }, "full_name": { "type": "text" } } } } } PUT my_index/_doc/1 { "first_name": "John", "last_name": "Smith" } GET my_index/_search { "query": { "match": { "full_name": { "query": "John Smith", "operator": "and" } } } }
ES使用_type
来描述doc字段的类型,原来咱们直接往ES中存储数据,并无指定字段的类型,缘由是ES存在类型推断,默认的mapping中定义了每一个field对应的数据类型以及如何进行分词
null --> no field add true flase --> boolean 123 --> long 123.123 --> double 1999-11-11 --> date "hello world" --> string Object --> object
示例
PUT /my_index/ { "mappings":{ "dynamic":"strict" } }
PUT my_index { "mappings": { "_doc": { "date_detection": false } } } PUT my_index/_doc/1 { "create": "2015/09/02" }
PUT my_index { "mappings": { "_doc": { "dynamic_date_formats": ["MM/dd/yyyy"] } } } PUT my_index/_doc/1 { "create_date": "09/25/2015" }
PUT my_index { "mappings": { "_doc": { "numeric_detection": true } } } PUT my_index/_doc/1 { "my_float": "1.0", "my_integer": "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 } } } } }
date
示例:
PUT my_index { "mappings": { "_doc": { "properties": { "date": { "type": "date" } } } } } PUT my_index/_doc/1 { "date": "2015-01-01" }
string类型的字符串能够被ES解释成boolean
boolean
示例:
PUT my_index { "mappings": { "_doc": { "properties": { "is_published": { "type": "boolean" } } } } }
binary
示例
PUT my_index { "mappings": { "_doc": { "properties": { "name": { "type": "text" }, "blob": { "type": "binary" } } } } } PUT my_index/_doc/1 { "name": "Some binary blob", "blob": "U29tZSBiaW5hcnkgYmxvYg==" }
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" } }
对象类型,嵌套对象类型
示例:
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" }
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 ] }
更多内容参见官网**,点击进入
GET /index/_mapping/type
能够给现存的type添加field,可是不能修改,不然就会报错
PUT twitter { "mappings": { "user": { "properties": { "name": { "type": "text" , # 会被所有检索 "analyzer":"english" # 指定当前field使用 english分词器 }, "user_name": { "type": "keyword" }, "email": { "type": "keyword" } } }, "tweet": { "properties": { "content": { "type": "text" }, "user_name": { "type": "keyword" }, "tweeted_at": { "type": "date" }, "tweeted_at": { "type": "date" "index": "not_analyzeed" # 设置为当前field tweeted_at不能被分词 } } } } }
Object类型
{ "address":{ "province":"shandong", "city":"dezhou" }, "name":"zhangsan", "age":"12" }
转换
{ "name" : [zhangsan], "name" : [12], "address.province" : [shandong], "address.city" : [dezhou] }
Object数组类型
{ "address":[ {"age":"12","name":"张三"}, {"age":"12","name":"张三"}, {"age":"12","name":"张三"} ] }
转换
{ "address.age" : [12,12,12], "address.name" : [张三,张三,张三] }
搜索时,输入的value必须和目标彻底一致才算做命中
"query": { "match_phrase": { "address": "mill lane" } }, # 短语检索 address彻底匹配 milllane才算命中,返回
全文检索时存在各类优化处理以下:
示例
GET /_search { "query": { "match" : { "message" : "this is a test" } } }
倒排索引指向全部document分词的field
假设咱们存在这样两句话
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 来检索,通过分词后去上面索引中检索,doc12都会被检索出,可是doc1命中了更多的词,所以doc1得分会更高
doc value
实际上指向全部不分词的document的field
ES中,进行搜索动做时须要借助倒排索引,可是在排序,聚合,过滤时,须要借助正排索引,所谓正排索引就是其doc value在创建正排索引时一遍创建正排索引一遍创建倒排索引, doc value
会被保存在磁盘上,若是内存充足也会将其保存在内存中
正排索引大概长这样
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便可
doc value
存储压缩 -- column
压缩为了减小doc value占用内存空间的大小,采用column对其进行压缩, 好比咱们存在三个doc, 以下
doc 1: 550 doc 2: 550 doc 3: 500
合并相同值,doc1,doc2的值相同都是550,保存一个550标识便可
如: doc1: 24 doc2 :36 除以最大公约数 6 doc1: 4 doc2 : 6 保存下最大公约数6
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)
向量空间模式
ES会根据用户输入的词条在全部document中的评分状况计算出一个空间向量模型 vector model, 他是空间向量中的一个点
而后会针对全部的doc都计算出一个vector model出来, 将这个
若是存在多个term,那么就是一个多维空间向量之间的运算,可是咱们假设是二维的,就像下面这张图
一目了然,Doc2和目标词条之间的弧度小,因而认为他们最类似,它的得分也就越高
咱们使用分词将将一段话拆分红一个一个的单词,甚至进一步对分出来的单词进行词性的转换,师太的转换,单复数的转换的操做, 为何使用分词器? 就是为了提升检索时的召回率,让更多的doc被检索到
在一段文本在分词前先进行预处理,好比过滤html标签, 将特殊符号转换成123..这种阿拉伯数字等特殊符号的转换
进行分词,拆解句子,记录词条的位置(在当前doc中占第几个位置term position)及顺序
进行同义词的转换,去除同义词,单复数的转换等等
每个分片地位相同,都能接受请求,处理请求,当当用户的一个请求发送到某一个shard中后,这个shard会自动就请求路由到真正存储数据的shard上去,可是最终老是由接受请求的节点响应请求
图解: master的选举,容错,以及数据的恢复
如上图为初始状态图
假如,图上的第一个节点是master节点,而且它挂掉,在挂掉的一瞬间,整个cluster的status=red,表示存在数据丢失了集群不可用
下面要作的第一步就是完成master的选举,自动在剩下的节点中选出一个节点当成master节点, 第二步选出master节点后,这个新的master节点会将Po在第三个节点中存在一个replica shard提高为primary shard,此时cluster 的 status = yellow,表示集群中的数据是能够被访问的可是存在部分replica shard不可用,第三步,从新启动由于故障宕机的node,而且将右边两个节点中的数据拷贝到第一个节点中,进行数据的恢复
ES内部的多线程异步并发修改时,是经过_version版本号进行并发控制的,每次建立一个document,它的_version内部版本号都是1,之后对这个doc的修改,删除都会使这个版本号增1
ES的内部需在Primary shard 和 replica shard之间同步数据,这就意味着多个修改请求实际上是乱序的不必定按照前后顺序执行
相关语法:
PUT /index/type/2?version=1{ "name":"XXX" }
上面的命令中URL中的存在?version=1
,此时,若是存在其余客户端将id=2的这条记录修改过,致使id=2的版本号不等于1了,那么这条PUT语句将会失败并有相应的错误提示
基于external的版本号控制,ES提供了一个Futrue,也就是说用户可使用本身维护的版本号进行并发访问控制,好比:
PUT /index/type/2?version=1&version_type=external
假设当前ES中的版本号是1, 那么只有当用户提供的版本号大于1时,PUT才会成功
一个index被分红了多个shard,文档被随机的存在某一个分片上,客户端一个请求打向index中的一个分片,可是请求的doc可能不存在于这个分片上,接受请求的shard会将请求路由到真正存储数据的shard上,这个过程叫作数据路由
其中接受到客户端请求的节点称为coordinate node,协调节点,好比如今是客户端往服务端修改一条消息,接受A接受到请求了,那么A就是 coordnate node协调节点,数据存储在B primary shard 上,那么协调节点就会将请求路由到B primary shard中,B处理完成后再向 B replica shard同步数据,数据同步完成后,B primary shard响应 coordinate node, 最后协调节点响应客户端结果
shard = hash(routing) % number_of_primary_shards
其实这个公式并不复杂,能够将上面的routing当成doc的id,不管是用户执行的仍是自动生成的,反正确定是惟一,既然是惟一的通过每次hash获得的结果也是同样的, 这样一个惟一的数对主分片的数进行取余数,获得的结果就会在0-最大分片数之间
能够手动指定routing value的值,好比PUT /index/type/id?routing=user_id
,在保证这类doc必定被路由到指定的shard上,并且后续进行应用级负载均衡时会批量提高读取的性能
咱们在发送任何一个增删改查时,均可以带上一个 consistency 参数,指明咱们想要的写一致性是什么,以下
PUT /index/type/id?consistency=quorum
有哪些可选参数呢?
quorum数量的计算公式: int((primary+number_of_replicas)/2)+1, 算一算,假如咱们的集群中存在三个node,replica=1,那么cluster中就存在3+3*1=6个shard
int((3+1)/2)+1 = 3
结果显示,咱们只有当quorum=3,即replica_shard=3时,集群才是可用的,可是当咱们的单机部署时,因为ES不容许同一个server的primary_shard和replica_shard共存,也就是说咱们的replica数目为0,为何ES依然能够用呢? 这是ES提供了一种特殊的处理场景,即当number_of_replicas>1时才会生效
quorum不全时,集群进入wait()状态, 默认1分钟,,在等待期间,指望活跃的shard的数量能够增长,到最后都没有知足这个数量的话就会timeout
咱们在写入时也可使用timeout参数, 好比: PUT /index/type/id?timeout=30
经过本身设置超时时间来缩短超时时间
用户的写请求将doc写入内存缓冲区,写的动做被记录在translog日志文件中,每隔一秒中内存中的数据就会被刷新到index segment file中,index segment file中的数据随机被刷新到os cache中,而后index segement file处理打开状态,对外提供检索服务,ES会重复这个过程,每次重复这个过程时,都会先清空内存buffer,处理打开状态的 index segment file能够对外提供检索
直到translog日志文件体积太大了,就会进一步触发flush操做,这个flush操做会将buffer中所有数据刷新进新的segment file中,将index segment file刷新进os cache, 写一个commit point 到磁盘上,标注有哪些index segment,并将OS cache中的数据刷新到OS Disk中,完成数据的持久化
上面的flush动做,默认每隔30分钟执行一次,或者当translog文件体积过大时也会自动flush
数据恢复时,是基于translog文件和commit point二者判断,究竟哪些数据在日志中存在记录,却没有被持久化到OSDisk中,从新执行日志中的逻辑,等待下一次的flush完成持久化
看上面的图中,为了实现近实时的搜索,每1秒钟就会产生一个segment文件,文件数目会特别多,而恰巧对外提供搜索的就是这些segment文件,所以ES会在后台进行segement 文件的合并,在合并的时候,被标记deleted的docment会会被完全的物理删除
每次merge的操做流程