- shell 变量传递给 awk
举例:将shell的变量shellVarm传递给awk,在awk中变量名叫awkVarm
该shell语句的目的是:如果第一列等于a的话,则打印
shellVarm=a; echo "a b\na d\nc m" | awk -v awkVarm=${shellVarm} '$1==awkVarm {print $2}'
举例:将shell的变量shellVarm传递给awk,在awk中变量名叫awkVarm
该shell语句的目的是:如果第一列等于a的话,则打印
shellVarm=a; echo "a b\na d\nc m" | awk -v awkVarm=${shellVarm} '$1==awkVarm {print $2}'
创建一个索引
curl -XPUT http://10.95.116.44:9202/test11 -d '{
"settings":{
"number_of_shards": 5,
"number_of_replicas": 1
},
"mappings" : {
"prod": {
"dynamic": "strict",
"properties": {
"brand_code": {
"type": "integer"
},
"brand": {
"type": "string"
}
}
}
}
}'
查看索引的setting
curl -XGET http://10.95.116.44:9202/test11/_mapping
添加一条数据
curl -XGET "http://10.95.116.44:9202/megacorp/employee/1"
检索接口
curl -XGET "http://10.95.116.44:9202/megacorp/employee/_search?pretty"
{
"took" : 16,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 1.0,
"hits" : [ {
"_index" : "megacorp",
"_type" : "employee",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests" : [ "sports", "music" ]
}
} ]
}
}
5.1 使用查询语句进行
curl -XGET "http://10.95.116.44:9202/megacorp/employee/_search" -d '
{
"query": {
"match": {
"first_name": "John"
}
}
}'
5.2 更复杂的查询语句
curl -XGET "http://10.95.116.44:9202/megacorp/employee/_search" -d '
{
"query" : {
"bool": {
"must": {
"match" : {
"last_name" : "Smith"
}
},
"filter": {
"range" : {
"age" : { "gt" : 20 }
}
}
}
}
}
'
5.3 全文搜索
5.4 短语搜索 match_phrase
查看集群健康
/_cluster/health
当master节点都挂了以后,查看机器健康状态就有问题,会报错:
master_not_discovered_exception
取回一个文档
GET /website/blog/123?pretty
取回一个文档的一部分
GET /website/blog/123?pretty&_source=title,text
删除一个文档
DELETE /website/blog/123
判断一个文档是否存在
HEAD /website/blog/123
如果有status code = 200;
如果没有 status code = 404
处理冲突
在数据库领域中,有两种方法通常被用来确保并发更新时变更不会丢失:
乐观并发控制
采用compare_and_set的方式, Elasticsearch 使用这个 _version 号来确保变更以正确顺序得到执行。如果旧版本的文档在新版本之后到达,它可以被简单的忽略。冲突时返回的status code 是 409 Conflict HTTP 响应码。
悲观并发控制
采用加锁的方式来做
curl -XPOST "http://10.95.177.126:9201/megacorp/employee/_mapping" -d '
{
"employee": {
"properties": {
"onSale":{
"type":"string"
}
}
}
}'
新建、索引和删除文档
数据写入时如何是写入成功呢?
背面’'。规定的数量定义公式如下:
+
int( (primary + number_of_replicas) / 2 ) + 1`取回一个文档
问题:取回是从主分片取还是从副本取呢??
局部更新文档
在主分片上更新,同时并行转发给replicas分片。
多文档模式
https://elasticsearch.cn/book/elasticsearch_definitive_guide_2.x/distrib-multi-doc.html
空搜索
响应的字段取值
|字段取值|含义|
|—|—|
|took|告诉我们执行整个搜索请求耗费了多少毫秒
|请求中的timeout|默认情况下,搜索请求不会超时。 如果低响应时间比完成结果更重要,你可以指定 timeout 为 10 或者 10ms(10毫秒),或者 1s(1秒),在请求超时之前,Elasticsearch 将会返回已经成功从每个分片获取的结果。 timeout 不是停止执行查询,它仅仅是告知正在协调的节点返回到目前为止收集的结果并且关闭连接。在后台,其他的分片可能仍在执行查询即使是结果已经被发送了。使用超时是因为 SLA(服务等级协议)对你是很重要的,而不是因为想去中止长时间运行的查询。|
|响应中timed_out|表示是否超时|
|_shards| 告诉我们在查询中参与分片的总数,以及这些分片成功了多少个失败了多少个。正常情况下我们不希望分片失败,但是分片失败是可能发生的。如果我们遭遇到一种灾难级别的故障,在这个故障中丢失了相同分片的原始数据和副本,那么对这个分片将没有可用副本来对搜索请求作出响应。假若这样,Elasticsearch 将报告这个分片是失败的,但是会继续返回剩余分片的结果。|
|hits|它 包含 total 字段来表示匹配到的文档总数,并且一个 hits 数组包含所查询结果的前十个文档.在 hits 数组中每个结果包含文档的 _index 、 _type 、 _id ,加上 _source 字段。这意味着我们可以直接从返回的搜索结果中使用整个文档。这不像其他的搜索引擎,仅仅返回文档的ID,需要你单独去获取文档。
分页
参数from=0&size=1,
理解为什么深度分页是有问题的,我们可以假设在一个有 5 个主分片的索引中搜索。 当我们请求结果的第一页(结果从 1 到 10 ),每一个分片产生前 10 的结果,并且返回给 协调节点 ,协调节点对 50 个结果排序得到全部结果的前 10 个。
现在假设我们请求第 1000 页–结果从 10001 到 10010 。所有都以相同的方式工作除了每个分片不得不产生前10010个结果以外。 然后协调节点对全部 50050 个结果排序最后丢弃掉这些结果中的 50040 个结果。
可以看到,在分布式系统中,对结果排序的成本随分页的深度成指数上升。这就是 web 搜索引擎对任何查询都不要返回超过 1000 个结果的原因。
分析与分析器
分析 包含下面的过程:
之后,将这些词条统一化为标准格式以提高它们的”可搜索性”,或者 recall
分析器 实际上是将三个功能封装到了一个包里:
字符过滤器
首先,字符串按顺序通过每个 字符过滤器 。他们的任务是在分词前整理字符串。一个字符过滤器可以用来去掉HTML,或者将 & 转化成 and
。
Token 过滤器
最后,词条按顺序通过每个 token 过滤器 。这个过程可能会改变词条
对于一个例子
“Set the shape to semi-transparent by calling set_trans(5)”
+ 标准分析器:它根据 Unicode 联盟 定义的 单词边界 划分文本。删除绝大部分标点。最后,将词条小写。
`set, the, shape, to, semi, transparent, by, calling, set_trans, 5`
+ 简单分析器
简单分析器在任何不是字母的地方分隔文本,将词条小写
`set, the, shape, to, semi, transparent, by, calling, set, trans`
+ 空格分析器
空格分析器在空格的地方划分文本
`Set, the, shape, to, semi-transparent, by, calling, set_trans(5)`
+ 语言分析器
特定语言分析器可用于 很多语言。它们可以考虑指定语言的特点。例如, 英语 分析器附带了一组英语无用词(常用单词,例如 and 或者 the ,它们对相关性没有多少影响),它们会被删除。 由于理解英语语法的规则,这个分词器可以提取英语单词的 词干 .
`set, shape, semi, transpar, call, set_tran, 5`
### 什么时候使用分析器
全文查询,理解每个域是如何定义的,因此它们可以做 正确的事:
+ 当你查询一个 全文 域时, 会对查询字符串应用相同的分析器,以产生正确的搜索词条列表。
+ 当你查询一个 精确值 域时,不会分析查询字符串, 而是搜索你指定的精确值。
### 注意:
当Elasticsearch在你的文档中检测到一个新的字符串域 ,它会自动设置其为一个全文 字符串 域,使用 标准 分析器对它进行分析。
### 测试分析器
GET /_analyze
{
"analyzer": "standard",
"text": "Text to analyze"
}
这样得到分析结果
{
"tokens": [
{
"token": "text",
"start_offset": 0,
"end_offset": 4,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "to",
"start_offset": 5,
"end_offset": 7,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "analyze",
"start_offset": 8,
"end_offset": 15,
"type": "<ALPHANUM>",
"position": 3
}
]
}
Elasticsearch 支持 如下简单域类型:
+ 字符串: string
+ 整数 : byte, short, integer, long
+ 浮点数: float, double
+ 布尔型: boolean
+ 日期: date
JSON中类型与Elasticsearch类型对应
|Json类型|Elasticsearch类型|
|---|---|
|true/false|boolean|
|123|long|
|123.45|double|
|字符串,有效日期: 2014-09-15|date|
|字符串foo bar| string|
### string域
string 域映射的两个最重要 属性是 index 和 analyzer 。
index的取值
|index取值|含义|
|---|---|
| analyzed|首先分析字符串,然后索引它。换句话说,以全文索引这个域|
| not_analyzed|索引这个域,所以可以搜索到它,但索引指定的精确值。不对它进行分析|
| no|不索引这个域。这个域不会被搜索到|
analyzer的取值
对于 analyzed 字符串域,用 analyzer 属性指定在搜索和索引时使用的分析器。默认, Elasticsearch 使用 standard 分析器, 但你可以指定一个内置的分析器替代它,例如 whitespace 、 simple 和 `english`
复杂核心域类型
空域 null,[],[null]
多层级对象
内部对象 经常用于 嵌入一个实体或对象到其它对象中。
Elasticsearch 会动态 监测新的对象域并映射它们为 对象 ,在 properties 属性下列出内部域,
{
"gb": {
"tweet": {
"properties": {
"tweet": { "type": "string" },
"user": {
"type": "object",
"properties": {
"id": { "type": "string" },
"gender": { "type": "string" },
"age": { "type": "long" },
"name": {
"type": "object",
"properties": {
"full": { "type": "string" },
"first": { "type": "string" },
"last": { "type": "string" }
}
}
}
}
}
}
}
}
+ 内部对象是如何进行索引的。
Lucene 不理解内部对象。 Lucene 文档是由一组键值对列表组成的。为了能让 Elasticsearch 有效地索引内部类,它把我们的文档转化成这样:
{
"tweet": [elasticsearch, flexible, very],
"user.id": [@johnsmith],
"user.gender": [male],
"user.age": [26],
"user.name.full": [john, smith],
"user.name.first": [john],
"user.name.last": [smith]
}
+ 内部对象数组 最后,考虑包含 内部对象的数组是如何被索引的
假设我们有个 followers 数组:
{
"followers": [
{ "age": 35, "name": "Mary White"},
{ "age": 26, "name": "Alex Jones"},
{ "age": 19, "name": "Lisa Smith"}
]
}
这个文档会像我们之前描述的那样被扁平化处理,结果如下所示:
{
"followers.age": [19, 26, 35],
"followers.name": [alex, jones, lisa, smith, mary, white]
}
一个查询语句的典型结构:
{
QUERY_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
如果针对某个字段,其结构如下:
{
QUERY_NAME: {
FIELD_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
}
举个例子,你可以使用 match 查询语句 来查询 tweet 字段中包含 elasticsearch 的 tweet:
{
"match":{
"tweet":"elasticsearch"
}
}
完整的查询语句:
GET /_search
{
"query": {
"match": {
"tweet": "elasticsearch"
}
}
}
合并查询语句
查询语句Query Clasues就像一些简单的组合块,这些组合块可以彼此之间合并组成更复杂的查询。如下:
复合Compound语句:合并其他查询语句,比如一个bool语句 允许在你需要的时候组合其他语句,无论是must
,must not
, should
,同时它可以包含不评分的过滤器filters
{
"bool": {
"must": { "match": { "tweet": "elasticsearch" }},
"must_not": { "match": { "name": "mary" }},
"should": { "match": { "tweet": "full text" }},
"filter": { "range": { "age" : { "gt" : 30 }} }
}
}
一条复合语句可以合并 任何 其它查询语句,包括复合语句,了解这一点是很重要的。这就意味着,复合语句之间可以互相嵌套,可以表达非常复杂的逻辑。
查询与过滤
Elasticsearch使用的查询语言可以以无限组合的方式进行搭配,可以在以下两种情况下使用:过滤情况(filtering context)和查询情况(query context)。
查询情况:查询就变成了一个“评分”的查询。
但从 Elasticsearch 2.0 开始,过滤(filters)已经从技术上被排除了,同时所有的查询(queries)拥有变成不评分查询的能力
性能差异
过滤查询(Filtering queries)只是简单的检查包含或者排除,这就使得计算起来非常快。考虑到至少有一个过滤查询(filtering query)的结果是 “稀少的”(很少匹配的文档),并且经常使用不评分查询(non-scoring queries),结果会被缓存到内存中以便快速读取,所以有各种各样的手段来优化查询结果。
评分查询(scoring queries)不仅仅要找出 匹配的文档,还要计算每个匹配文档的相关性,计算相关性使得它们比不评分查询费力的多。同时,查询结果并不缓存。
如何选择过滤或评分
通常的规则是,使用 查询(query)语句来进行 全文 搜索或者其它任何需要影响 相关性得分 的搜索。除此以外的情况都使用过滤(filters)。
重要的查询
match_all
查询简单的 匹配所有文档match
查询:无论你在任何字段上进行的是全文搜索还是精确查询,match 查询是你可用的标准查询。
multi_match
查询:multi_match 查询可以在多个字段上执行相同的 match 查询
{
"multi_match": {
"query": "full text search",
"fields": [ "title", "body" ]
}
}
range
查询 查询找出那些落在指定区间内的数字或者时间,被允许的操作符:gt,lt,gte,lte
{
"range": {
"age": {
"gte": 20,
"lt": 30
}
}
}
term
查询 term 查询被用于精确值 匹配,这些精确值可能是数字、时间、布尔或者那些 not_analyzed 的字符串
{ "term": { "age": 26 }}
{ "term": { "date": "2014-09-01" }}
terms
查询:和 term 查询一样,但它允许你指定多值进行匹配。如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件
{ "terms": { "tag": [ "search", "full_text", "nosql" ] }}
组合查询
bool查询
,可以接收以下参数:
举例:下面的查询用于查找 title 字段匹配 how to make millions 并且不被标识为 spam 的文档。那些被标识为 starred 或在2014之后的文档,将比另外那些文档拥有更高的排名。如果 _两者_ 都满足,那么它排名将更高:
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }},
{ "range": { "date": { "gte": "2014-01-01" }}}
]
}
}
假如不想文档的时间影响评分,则可以增加带过滤器的查询
:
{
"bool": {
"must": { "match": { "title": "how to make millions" }},
"must_not": { "match": { "tag": "spam" }},
"should": [
{ "match": { "tag": "starred" }}
],
"filter": {
"range": { "date": { "gte": "2014-01-01" }}
}
}
}
所有查询都可以借鉴这种方式。将查询移到 bool 查询的 filter 语句中,这样它就自动的转成一个不评分的 filter 了。
constant_score
查询
constant_score 查询也是你工具箱里有用的查询工具。它将一个不变的常量评分应用于所有匹配的文档。它被经常用于你只需要执行一个 filter 而没有其它查询。
可以使用它来取代只有 filter 语句的 bool 查询。在性能上是完全相同的,但对于提高查询简洁性和清晰度有很大帮助。
term 查询被放置在 constant_score 中,转成不评分的 filter
{
"constant_score": {
"filter": {
"term": { "category": "ebooks" }
}
}
}
排序
为了按照相关性来排序,需要将相关性表示为一个数值。在 Elasticsearch 中, 相关性得分 由一个浮点数进行表示,并在搜索结果中通过 _score 参数返回, 默认排序是 _score 降序。
按照字段的值进行排序
我们可以使用 sort 参数进行实现
返回结果中_score
是不会被计算的。
GET /_search
{
"query" : {
"bool" : {
"filter" : { "term" : { "user_id" : 1 }}
}
},
"sort": { "date": { "order": "desc" }}
}
多级排序
GET /_search
{
"query" : {
"bool" : {
"must": { "match": { "tweet": "manage text search" }},
"filter" : { "term" : { "user_id" : 2 }}
}
},
"sort": [
{ "date": { "order": "desc" }},
{ "_score": { "order": "desc" }}
]
}
Elasticsearch 的相似度算法 被定义为检索词频率/反向文档频率, TF/IDF,包括以下内容:
创建一个索引
PUT /my_index
{
"settings": { ... any settings ... },
"mappings": {
"type_one": { ... any mappings ... },
"type_two": { ... any mappings ... },
...
}
}
action.auto_create_index: false
删除一个索引
DELETE /my_index
DELETE /my_index*
索引设置
两个最重要的设置
number_of_shards
number_of_replicas
每个主分片的副本数,默认值是 1 。对于活动的索引库,这个配置可以随时修改。
修改副本配置
PUT /my_temp_index/_settings
{
"number_of_replicas": 1
}
配置分析器
第三个重要的索引设置是 analysis 部分, 用来配置已存在的分析器或针对你的索引创建新的自定义分析器。
`standard `分析器是用于全文字段的默认分析器, 对于大部分西方语系来说是一个不错的选择。 它包括了以下几点:
+ standard 分词器,通过单词边界分割输入的文本。
+ standard 语汇单元过滤器,目的是整理分词器触发的语汇单元(但是目前什么都没做)。
+ lowercase 语汇单元过滤器,转换所有的语汇单元为小写。
+ stop 语汇单元过滤器,删除停用词--对搜索相关性影响不大的常用词,如 a , the , and , is 。
默认情况下,停用词过滤器是被禁用的。如需启用它,你可以通过创建一个基于 standard 分析器的自定义分析器并设置 stopwords 参数。 可以给分析器提供一个停用词列表,或者告知使用一个基于特定语言的预定义停用词列表
自定义分析器
一个 分析器 就是在一个包里面组合了三种函数的一个包装器
token过滤器:经过分词,作为结果的 词单元流 会按照指定的顺序通过指定的词单元过滤器。
格式:
PUT /my_index
{
"settings": {
"analysis": {
"char_filter": { ... custom character filters ... },
"tokenizer": { ... custom tokenizers ... },
"filter": { ... custom token filters ... },
"analyzer": { ... custom analyzers ... }
}
}
}
字符过滤器:将&替换为and,
"char_filter": {
"&_to_and": {
"type": "mapping",
"mappings": [ "&=> and "]
}},
token过滤器:
“filter”: {
"my_stopwords": {
"type": "stop",
"stopwords": [ "the", "a" ]
}
},
分析:
“analyzer”: {
"my_analyzer": {
"type": "custom",
"char_filter": [ "html_strip", "&_to_and" ],
"tokenizer": "standard",
"filter": [ "lowercase", "my_stopwords" ]
}
}
整体设置:
PUT /my_index
{
"settings": {
"analysis": {
"char_filter": {
"&_to_and": {
"type": "mapping",
"mappings": [ "&=> and "]
}},
"filter": {
"my_stopwords": {
"type": "stop",
"stopwords": [ "the", "a" ]
}},
"analyzer": {
"my_analyzer": {
"type": "custom",
"char_filter": [ "html_strip", "&_to_and" ],
"tokenizer": "standard",
"filter": [ "lowercase", "my_stopwords" ]
}}
}}}
索引被创建以后,使用 analyze API 来 测试这个新的分析器:
curl -XGET “http://10.95.177.126:9201/my_index/_analyze?analyzer=my_analyzer&pretty" -d “ The quick & brown fox”
{
"tokens" : [ {
"token" : "quick",
"start_offset" : 5,
"end_offset" : 10,
"type" : "<ALPHANUM>",
"position" : 1
}, {
"token" : "and",
"start_offset" : 11,
"end_offset" : 12,
"type" : "<ALPHANUM>",
"position" : 2
}, {
"token" : "brown",
"start_offset" : 13,
"end_offset" : 18,
"type" : "<ALPHANUM>",
"position" : 3
}, {
"token" : "fox",
"start_offset" : 19,
"end_offset" : 22,
"type" : "<ALPHANUM>",
"position" : 4
} ]
}
类型和映射
类型
在 Elasticsearch 中表示一类相似的文档映射
就像数据库中的 schema ,描述了文档可能具有的字段或 属性 、 每个字段的数据类型,以及Lucene是如何索引和存储这些字段的
避免类型陷阱
这导致了一个有趣的思想实验: 如果有两个不同的类型,每个类型都有同名的字段,但映射不同(例如:一个是字符串一个是数字),将会出现什么情况?
简单回答是,Elasticsearch 不会允许你定义这个映射。当你配置这个映射时,将会出现异常。
映射的最高一层被称为 根对象 ,它可能包含下面几项:
+ 一个 properties 节点,列出了文档中可能包含的每个字段的映射
+ 各种元数据字段,它们都以一个下划线开头,例如 _type 、 _id 和 _source
+ 设置项,控制如何动态处理新的字段,例如 analyzer 、 dynamic_date_formats 和 dynamic_templates
+ 其他设置,可以同时应用在根对象和其他 object 类型的字段上,例如 enabled 、 dynamic 和 include_in_all
### 属性
+ type:字段的数据类型,例如 string 或 date
+ index:字段是否应当被当成全文来搜索( analyzed ),或被当成一个准确的值( not_analyzed ),还是完全不可被搜索( no )
+ analyzer:确定在索引和搜索时全文字段使用的 analyzer
+ ip ,`geo_point` , `geo_shape`
### 元数据:`_source`字段
这个字段的存储几乎总是我们想要的,因为它意味着下面的这些:
+ 搜索结果包括了整个可用的文档——不需要额外的从另一个的数据仓库来取文档。
+ 如果没有 _source 字段,部分 update 请求不会生效。
+ 当你的映射改变时,你需要重新索引你的数据,有了_source字段你可以直接从Elasticsearch这样做,而不必从另一个(通常是速度更慢的)数据仓库取回你的所有文档。
+ 当你不需要看到整个文档时,单个字段可以从 _source 字段提取和通过 get 或者 search 请求返回。
+ 调试查询语句更加简单,因为你可以直接看到每个文档包括什么,而不是从一列id猜测它们的内容。
### 元数据: `_all`字段
_all 字段:一个把其它字段值 当作一个大字符串来索引的特殊字段
如果你不再需要 _all 字段,你可以通过下面的映射来禁用:
PUT /my_index/_mapping/my_type
{
"my_type": {
"_all": { "enabled": false }
}
}
### 元数据:文档标识
+ `_id`: 文档的 ID 字符串
+ `_type`: 文档的类型名
+ `_index`: 文档所在的索引
+ `_uid`: `_type` 和 `_id`连接在一起构造成 type#id
动态映射
当 Elasticsearch 遇到文档中以前 未遇到的字段,它用 dynamic mapping 来确定字段的数据类型并自动把新的字段添加到类型映射。
dynamic取值
my_type
出现新增字段就报异常,如果对于字段stash出现新增字段,则动态映射。PUT /my_index
{
"mappings": {
"my_type": {
"dynamic": "strict",
"properties": {
"title": { "type": "string"},
"stash": {
"type": "object",
"dynamic": true
}
}
}
}
}
自定义动态映射
https://elasticsearch.cn/book/elasticsearch_definitive_guide_2.x/custom-dynamic-mapping.html
缺省映射
https://elasticsearch.cn/book/elasticsearch_definitive_guide_2.x/default-mapping.html
重新索引你的数据
从ES2.3开始,可以使用reindex api,它能够对文档重建索引而不需要任何插件或外部工具。
索引别名和零停机
索引 别名 就像一个快捷方式或软连接,可以指向一个或多个索引,也可以给任何一个需要索引名的API来使用。别名 带给我们极大的灵活性,允许我们做下面这些:
举例:
PUT /my_index_v1 #创建索引my_index_v1
PUT /my_index_v1/_alias/my_index # 设置别名 my_index指向my_index_v1
使文本可搜索
倒排索引被写入磁盘后是 不可改变 的:它永远不会修改。 不变性有重要的价值:
下一个需要被解决的问题是怎样在保留不变性的前提下实现倒排索引的更新? 答案是: 用更多的索引。
被混淆的概念是,一个 Lucene 索引 我们在 Elasticsearch 称作 分片 。 一个 Elasticsearch 索引 是分片的集合。 当 Elasticsearch 在索引中搜索的时候, 他发送查询到每一个属于索引的分片(Lucene 索引),然后像 执行分布式检索 提到的那样,合并每个分片的结果到一个全局的结果集。
sync is a standard system call in the Unix operating system, which commits to disk all data in the kernel filesystem buffers, i.e., data which has been scheduled for writing via low-level I/O system calls. Higher-level I/O layers such as stdio may maintain separate buffers of their own.
要回答的问题是:
倒排索引写入的流程
—>写入内存buffer–>写入文件系统缓存(一个segment)
—>translog
数据写入时在写入内存buffer时,会同时写入translog;
内存buffer中的数据每隔refresh_interval(es设置,默认是1秒)写入文件系统缓存;这就是为什么我们说 Elasticsearch 是 近 实时搜索: 文档的变化并不是立即对搜索可见,但会在一秒之内变为可见
当translog到达一定阈值后
这个执行一个提交并且截断 translog 的行为在 Elasticsearch 被称作一次 flush 。 分片每30分钟被自动刷新
(flush),或者在 translog 太大的时候
也会刷新。请查看 translog 文档 来设置,它可以用来 控制这些阈值:
下面的图演示了一个完整的数据写入流程
段合并。由于自动刷新流程每秒会创建一个新的段 ,这样会导致短时间内的段数量暴增。而段数目太多会带来较大的麻烦。 每一个段都会消耗文件句柄、内存和cpu运行周期。更重要的是,每个搜索请求都必须轮流检查每个段;所以段越多,搜索也就越慢。
启动段合并不需要你做任何事。进行索引和搜索时会自动进行。会进行以下工作:
合并进程选择一小部分大小相似的段,并且在后台将它们合并到更大的段中。这并不会中断索引和搜索。
一旦合并结束,老的段被删除,
![合并结束老的段被删除](elas_合并结束老的段被删除.png)
通过optimize API大可看做是 强制合并 API 。它会将一个分片强制合并到 `max_num_segments` 参数指定大小的段数目。 这样做的意图是减少段的数量(通常减少到一个),来提升搜索性能
POST /logstash-2014-10/_optimize?max_num_segments=1
GO的web服务器的简单helloword 服务器就是使用net/http库的进行编写。需要看一下http.ListenAndServe的工作原理。
package main
import "net/http"
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`hello world`))
})
http.ListenAndServe(":3000", nil)
}
主要包括:
HandleFunc的第二个参数是func(ResponseWriter, *Request),会赋值给HandlerFunc类型
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
在src/net/http/server.go中使用HandleFunc
进行注册,会将其放入多路复用的结构体ServeMux
中。
src/net/http/server.go
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler))
}
// Handle registers the handler for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler))
}
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
func (mux *ServeMux) Handle(pattern string, handler Handler) {
}
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)
http请求的多路复用结构体ServeMux
// ServeMux is an HTTP request multiplexer.
// It matches the URL of each incoming request against a list of registered
// patterns and calls the handler for the pattern that
// most closely matches the URL.
//
// Patterns name fixed, rooted paths, like "/favicon.ico",
// or rooted subtrees, like "/images/" (note the trailing slash).
// Longer patterns take precedence over shorter ones, so that
// if there are handlers registered for both "/images/"
// and "/images/thumbnails/", the latter handler will be
// called for paths beginning "/images/thumbnails/" and the
// former will receive requests for any other paths in the
// "/images/" subtree.
//
// Note that since a pattern ending in a slash names a rooted subtree,
// the pattern "/" matches all paths not matched by other registered
// patterns, not just the URL with Path == "/".
//
// If a subtree has been registered and a request is received naming the
// subtree root without its trailing slash, ServeMux redirects that
// request to the subtree root (adding the trailing slash). This behavior can
// be overridden with a separate registration for the path without
// the trailing slash. For example, registering "/images/" causes ServeMux
// to redirect a request for "/images" to "/images/", unless "/images" has
// been registered separately.
//
// Patterns may optionally begin with a host name, restricting matches to
// URLs on that host only. Host-specific patterns take precedence over
// general patterns, so that a handler might register for the two patterns
// "/codesearch" and "codesearch.google.com/" without also taking over
// requests for "http://www.google.com/".
//
// ServeMux also takes care of sanitizing the URL request path,
// redirecting any request containing . or .. elements or repeated slashes
// to an equivalent, cleaner URL.
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
hosts bool // whether any patterns contain hostnames
}
// A Server defines parameters for running an HTTP server.
// The zero value for Server is a valid configuration.
type Server struct {
Addr string // TCP address to listen on, ":http" if empty
Handler Handler // handler to invoke, http.DefaultServeMux if nil
ReadTimeout time.Duration // maximum duration before timing out read of the request
WriteTimeout time.Duration // maximum duration before timing out write of the response
MaxHeaderBytes int // maximum size of request headers, DefaultMaxHeaderBytes if 0
TLSConfig *tls.Config // optional TLS config, used by ListenAndServeTLS
// TLSNextProto optionally specifies a function to take over
// ownership of the provided TLS connection when an NPN
// protocol upgrade has occurred. The map key is the protocol
// name negotiated. The Handler argument should be used to
// handle HTTP requests and will initialize the Request's TLS
// and RemoteAddr if not already set. The connection is
// automatically closed when the function returns.
// If TLSNextProto is nil, HTTP/2 support is enabled automatically.
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
// ConnState specifies an optional callback function that is
// called when a client connection changes state. See the
// ConnState type and associated constants for details.
ConnState func(net.Conn, ConnState)
// ErrorLog specifies an optional logger for errors accepting
// connections and unexpected behavior from handlers.
// If nil, logging goes to os.Stderr via the log package's
// standard logger.
ErrorLog *log.Logger
disableKeepAlives int32 // accessed atomically.
nextProtoOnce sync.Once // guards initialization of TLSNextProto in Serve
nextProtoErr error
}
其中ListenAndServe
函数的逻辑,其中tcpKeepAliveListener是开启了tcp的keep-alive
// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
// If srv.Addr is blank, ":http" is used.
// ListenAndServe always returns a non-nil error.
func (srv *Server) ListenAndServe() error {
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
在Serve中在Accept处于等待,当请求到来时,Accept会从Listener l中接收一个到来的连接并创建一个新的goroutine处理请求。这个goroutine会读取请求,调用Server.Handler来处理他们。
func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
....
for {
rw, e := l.Accept()
if e != nil {
....
return e
}
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve()
}
}
其中newConn是获得一个新连接
// Create new connection from rwc.
func (srv *Server) newConn(rwc net.Conn) *conn {
c := &conn{
server: srv,
rwc: rwc,
}
if debugServerConnections {
c.rwc = newLoggingConn("server", c.rwc)
}
return c
}
其中conn结构体在net/http/server.go文件中
// A conn represents the server side of an HTTP connection.
type conn struct {
// server is the server on which the connection arrived.
// Immutable; never nil.
server *Server
// rwc is the underlying network connection.
// This is never wrapped by other types and is the value given out
// to CloseNotifier callers. It is usually of type *net.TCPConn or
// *tls.Conn.
rwc net.Conn
// remoteAddr is rwc.RemoteAddr().String(). It is not populated synchronously
// inside the Listener's Accept goroutine, as some implementations block.
// It is populated immediately inside the (*conn).serve goroutine.
// This is the value of a Handler's (*Request).RemoteAddr.
remoteAddr string
// tlsState is the TLS connection state when using TLS.
// nil means not TLS.
tlsState *tls.ConnectionState
// werr is set to the first write error to rwc.
// It is set via checkConnErrorWriter{w}, where bufw writes.
werr error
// r is bufr's read source. It's a wrapper around rwc that provides
// io.LimitedReader-style limiting (while reading request headers)
// and functionality to support CloseNotifier. See *connReader docs.
r *connReader
// bufr reads from r.
// Users of bufr must hold mu.
bufr *bufio.Reader
// bufw writes to checkConnErrorWriter{c}, which populates werr on error.
bufw *bufio.Writer
// lastMethod is the method of the most recent request
// on this connection, if any.
lastMethod string
// mu guards hijackedv, use of bufr, (*response).closeNotifyCh.
mu sync.Mutex
// hijackedv is whether this connection has been hijacked
// by a Handler with the Hijacker interface.
// It is guarded by mu.
hijackedv bool
}
serve方法
// Serve a new connection.
func (c *conn) serve() {
c.remoteAddr = c.rwc.RemoteAddr().String()
defer func() {
... // defer处理
}()
... // tls connection
c.r = &connReader{r: c.rwc}
c.bufr = newBufioReader(c.r)
c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
for {
w, err := c.readRequest() //读取请求
...
if err != nil {
....
return
}
... // Expect 100 Continue support
// HTTP cannot have multiple simultaneous active requests.[*]
// Until the server replies to this request, it can't read another,
// so we might as well run the handler in this goroutine.
// [*] Not strictly true: HTTP pipelining. We could let them all process
// in parallel even if their responses need to be serialized.
serverHandler{c.server}.ServeHTTP(w, w.req)
... // finish operation
}
}
看一下这里面最核心的 serverHandler{c.server}.ServeHTTP(w, w.rseq)
处理逻辑
// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
srv *Server
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
}
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
最后的h,_:=mux.Handler
获取的是type HandlerFunc func(ResponseWriter, *Request)
,调用ServeHTTP也就是调用了我们注册的
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`hello world`))
})
$ cat /sys/block/*/queue/rotational
判断cat /sys/block//queue/rotational的返回值(其中为你的硬盘设备名称,例如sda等等),如果返回1则表示磁盘可旋转,那么就是HDD了;反之,如果返回0,则表示磁盘不可以旋转,那么就有可能是SSD了
这种方法有个问题,那就是/sys/block/下面不只有硬盘,还可能有别的块设备,它们都在干扰你的判断。
使用lsblk命令进行判断,参数-d表示显示设备名称,参数-o表示仅显示特定的列。
这种方法的优势在于它只列出了你要看的内容,结果比较简洁明了。还是那个规则,ROTA是1的表示可以旋转,反之则不能旋
$ lsblk -d -o name,rota
NAME ROTA
sda 1
sdb 1
物理cpu
插槽上的CPU个数,物理cpu数量等于不同physical id的个数。
cat /proc/cpuinfo | grep ‘pysical id’ | sort | uniq | wc -l
cpu核数
一颗CPU上面能处理数据的芯片组的数量
cat /proc/cpuinfo| grep “cpu cores”| uniq
一般来说,物理CPU个数 X cpu cores = 逻辑CPU的个数。
如果不相等的话,则表示服务器的CPU支持超线程技术。
cat /proc/cpuinfo | grep ‘processor’ | wc -l
超线程的数量 是 “siblings”/“cpu cores”
$ cat /proc/cpuinfo| grep “physical id”| sort| uniq| wc -l
2
$ cat /proc/cpuinfo| grep “cpu cores”| uniq
cpu cores : 12
$ cat /proc/cpuinfo| grep “siblings”| uniq
siblings : 24
$ cat /proc/cpuinfo| grep “processor”| wc -l
48
cpu的个数只与processor 、physical id、siblings 、cpu cores 四个参数有关,其他参数值可以不用考虑
在centos上使用free命令得到如下四行,其中行号1 2 3 4是我加上的
1 total used free shared buffers cached
2 Mem: 132047948 121586656 10461292 2824 89148 106314244
3 -/+ buffers/cache: 15183264 116864684
4 Swap: 7812092 97712 7714380
第一行解释:
total:除系统外,可以使用的内存总量;
used:已经使用的内存总量
free:空闲的内存总量
shared:共享内存使用总量
buffers:被OS buffer住的内存
cached:被OS cached住的内存
buffers和cached的区别是:
+ A buffer is something that has yet to be "written" to disk.
+ A cache is something that has been "read" from the disk and stored for later use.
+ 也就是说buffer是用于存放要输出到disk(块设备)的数据的,而cache是存放从disk上读出的数据。这二者是为了提高IO性能的,并由OS管理。
第二行解释:
输出是从操作系统(OS)来看的。也就是说,从OS的角度来看,计算机上一共有:
132047948(缺省时free的单位为KB)物理内存,即FO[2][1];
在这些物理内存中有121586656(即FO[2][2])被使用了;
还用10461292(即FO[2][3])是可用的;
这里得到第一个等式:
FO[2][1] = FO[2][2] + FO[2][3]
FO[2][5] 是OS buffer住的内存,FO[2][6]是OS cached住的内存。
Linux和其他成熟的操作系统(例如windows),为了提高IO read的性能,总是要多cache一些数据,这也就是为什么FO[2][6](cached memory)比较大,而FO[2][3]比较小的原因。
第三行解释:
是从一个应用程序的角度看系统内存的使用情况。
对于FO[3][2],即FO[2][2]-buffers/cache,表示一个应用程序认为系统被用掉多少内存;
对于FO[3][3],即FO[2][3]+buffers/cache,表示一个应用程序认为系统还有多少内存;
因为被系统cache和buffer占用的内存可以被快速回收,所以通常FO[3][3]比FO[2][3]会大很多
这里还用两个等式:
FO[3][2] = FO[2][2] - FO[2][5] - FO[2][6]
FO[3][3] = FO[2][3] + FO[2][5] + FO[2][6]
在这篇文章中描述了elasticsearch按照ik分词的步骤。
https://github.com/medcl/elasticsearch-analysis-ik
注意: