Elasticsearch 笔记
集群(cluster)、节点(node)和分片(shard)
Elasticsearch也是使用Java编写并使用Lucene来建立索引并实现搜索功能,但是它的目的是通过简单连贯的RESTful API让全文搜索变得简单并隐藏Lucene的复杂性。
Elasticsearch的优点:
Elasticsearch不仅仅是Lucene和全文搜索引擎,它还提供:
分布式的实时文件存储,每个字段都被索引并可被搜索
实时分析的分布式搜索引擎
可以扩展到上百台服务器,处理PB级结构化或非结构化数据
ELasticsearch提供两种内置客户端用于你的代码:
(1)节点客户端(node client):
节点客户端以无数据节点(none data node)身份加入集群,换言之,它自己没有数据,但是知道什么数据位于集群的哪个节点上,能够直接转发请求到对应的节点上。
(2)传输客户端(Transport client):
更轻量的传输客户端能够发送请求到远程集群,它自己不加入集群,只是简单转发请求给集群中的节点。
两个Java客户端都通过9300端口与集群交互,使用Elasticsearch传输协议(Elasticsearch Transport Protocol)。集群中的节点也通过9300端口通信。如果此端口未开放,你的节点将不能形成集群。
在Elasticsearch中存储数据的行为叫做索引(indexing)
---------------------------------------------------------
和传统数据库对比:
关系数据库(RelationalDB)->库(Databases)->表(Tables) ->行(Rows) ->列(Columns)
Elasticsearch-> 索引(Indices) ->类型(Types)->文档(Documents)->字段(Fields)
---------------------------------------------------------
Elasticsearch集群可以包含多个索引(indices)(数据库),这些库可以包含多个类型(types)(表),这些类型包含多个文档(documents)(行),然后每个文档包含多个字段(Fields)(列)。
索引(名词)
如上文所述,一个索引(index)像是传统关系数据库中的数据库,它是相关文档存储的地方,index的复数是indices或indexes。
索引(动词)
“索引一个文档”表示存储一个文档在索引(名词)里,以便它可以被检索或者查询。这很像SQL中的"INSERT"关键字,差别是,如果文档已经存在,新的文档将覆盖旧的文档。
反向索引
传统数据库为特定列增加一个索引(index),例如多路搜索树(B-Tree)索引来加速检索。Elasticsearch和Lucene使用一种叫做反向索引(inverted index)的结构来实现相同目的。
通常文档中的所有字段会被索引(拥有反向索引),因此他们可以被搜索。如果一个字段没有反向索引不能被搜索。
Elasticsearch查询关键字:
match
{
"query" : {
"match":{ "last_name":"Smith" } //查询姓中包含smith的记录
}
}
姓氏为“Smith”的员工,但是我们只想得到年龄大于30岁的员工:
{
"query" : {
"filtered" : {
"filter" : {
"range" : {
"age" : { "gt" : 30 }
}
},
"query" : {
"match":{ "last_name":"Smith" } //查询姓中包含smith的记录
}
}
}
}
查询结果中_score代表相关度,ES查询后会按相关度进行排序
短语(phrases)
我们想要查询 about 包
含完整短语“rock climbing”的员工。
为了实现以上效果,我们将查询 match 变更为 match_phrase :
{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
}
}
高亮(highlight)匹配到的关键字
{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
},
"highlight": {
"fields" : {
"about" : {}
}
}
}
结果会得到一个新的叫做 highlight 的部分,这里包括了 about 字段中匹
配的文本片段,并且用<em></em>包围匹配到的单词。
Elasticsearch把这项功能叫做聚合(aggregations),它允许你在数据基础上生成复杂的统计。它很像SQL中的"GROUP BY" 但是功能更强大
例如:按interests分组,然后统计每组下的平均年龄:
{
"aggs" : {
"all_interests" : {
"terms" : { "field" : "interests" },
"aggs" : {
"avg_age" : {
"avg" : { "field" : "age" }
}
}
}
}
}
es扩展:
横向扩展:购买更多的服务器 它通过增加节点来传播负载和增加可靠性
纵向扩展:购买更好的服务器
一个节点(node)就是一个Elasticsearch实例,而一个集群(cluster)由一个或多个节点组成,它们具有相同的cluster.name,
它们协同工作,分享数据和负载。当有新的节点加入或者删除节点,集群就会感知到并平衡数据。
集群中一个节点会被选举为主节点(master),它用来管理集群中的一些变更,例如新建或删除索引、增加或移除节点等。主节点不需要参与文档级别的更改或搜索,
意味着只有一个主节点不会随着流量的增长而成为集群的瓶颈
status 字段提供一个整体的标识来指示集群的功能是否可用。三种颜色表示:
green 所有主要和复制的分片都可用
yellow 所有主分片可用,但不是所有复制分片都可用
red 不是所有的主分片都可用
索引(index)——一个存储关联数据的地方。实际上,索引只是一个用来指向一个或多个分片(shards)的“逻辑命名空间(logical namespace)”.
一个分片(shard)是一个最小级别“工作单元(worker unit)”,它只是保存索引中所有数据的一小片,分片是一个单一的Lucene实例
文档存储和被索引在分片中,程序不知道如何直接与分片通信,他们直接与索引通信
分片用于Elasticsearch在你的集群中分配数据。想象把分片当作数据的容器。文档存储在分片中,然后分片分配给你集群中的节点上。当你的集群扩容或缩小,Elasticsearch将会自动在你的节点间迁移分片,
以使集群保持平衡。
分片可以是主分片(primary shard)或者复制分片(replica shard)。
你索引中的每个文档属于一个单独的主分片,所以主分片的数量决定了你最多能存储多少数据。
复制分片只是主分片的一个副本,它用于提供数据的冗余副本,在硬件故障之后提供数据保护,同时服务于像搜索和检索等只读请求。
一个索引默认指派5个主分片,但是为了演示的目的,我们只指派3个主分片和一个复制分片(每个主分片有一个复制分片对应):
{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 1
}
}
同一个节点上保存相同的数据副本是没有必要的,如果这个节点故障了,那所有的数据副本也会丢失
一些新的被索引的文档将首先被存储在主分片中,然后平行复制到关联的复制节点上。这可以确保我们的数据在主节点和复制节点上都可以被检索。
主分片的数量在创建索引时已经给定,复制分片的数量可以在运行中的集群中动态地变更,这允许我们可以根据需求扩大或者缩小规模
一个文档(行)不只有数据。它还包含了元数据(metadata)——关于文档的信息。三个必须的元数据节点是:
_index 文档存储的地方
_type 文档代表的对象的类
_id 文档的唯一标识
如下面的结构:
_index _type _id _score id name funciton
------------------------------------------------------------------------
indexdemo123 typedemo123 AUxohiLEldvtLz1fv7mt 1 3 感冒灵颗粒 功能主治:解热镇痛。头痛 ,清热。
_index,_type,_id,_score是自动加上去的(_score代表数据的相关度,猜测待验证),真正的业务数据只有id,name,function三列。
_index 、_type 、_id三者唯一确定一个文档
GET请求返回的响应内容包括{"found":true}。这意味着文档已经找到。如果我们请求一个不存在的文档,依旧会得到一个JSON,不过found值变成了false。
Elasticsearch中每个文档都有版本号,每当文档变化(包括删除)都会使 _version 增加
可以使用 upsert 参数定义文档来使其不存在时被创建
mget 允许我们一次性检索多个文档
mget API参数是一个 docs 数组,数组的每个节点定义一个文档的 _index 、 _type 、 _id 元数据。如果你只想检索一个或几个确定的字段,也可以定义一个_source参数:
GET /_mget
{
"docs" : [
{
"_index":"website",
"_type" :"blog",
"_id" : 2
},
{
"_index" : "website",
"_type" : "pageviews",
"_id" : 1,
"_source": "views"
}
]
}
响应体也包含一个 docs 数组,每个文档还包含一个响应,它们按照请求定义的顺序排列。每个这样的响应与单独使用 get request响应体相同:
bulk API允许我们使用单一请求来实现多个文档的create、index 、update或delete
POST /_bulk
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }}
{ "title": "My first blog post" }
{ "index": { "_index": "website", "_type": "blog" }}
{ "title": "My second blog post" }
{ "update": { "_index": "website", "_type": "blog", "_id": "123", "_retry_on_conflict" : 3} }
{ "doc" : {"title" : "My updated blog post"}}
Elasticsearch响应包含一个 items 数组,它罗列了每一个请求的结果,结果的顺序与我们请求的顺序相同
Elasticsearch使用一种叫做倒排索引(inverted index)的结构来做快速的全文搜索。倒排索引由在文档中出现的唯一的单词列表,以及对于每个单词在文档中的位置组成。
词(terms)或者表征(tokens)
查询子句就像是搭积木一样,可以合并简单的子句为一个复杂的查询语句,比如:
简单子句(leaf clauses)(比如 match 子句)用以在将查询字符串与一个字段(或多字段)进行比较
复合子句(compound)用以合并其他的子句。例如, bool 子句允许你合并其他的合法子句,无论是must ,must_not 还是should,复合子句可以相互嵌套,从而实现非常复杂的逻辑
{
"bool": {
"must": {"match":{ "email":"business opportunity" }},
"should":[
{ "match":{ "starred": true }},
{ "bool": {
"must":{"folder":"inbox" }},
"must_not": { "spam": true }}
}}
],
"minimum_should_match": 1
}
}
结构化查询(Query DSL)和结构化过滤(Filter DSL)
--------------------------------------------------------------------------------------------------------------
最重要的查询过滤语句
term 过滤
term 主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed 的字符串(未经分析的文本数据类型):
{ "term": { "age": 26 }}
{ "term": { "date":"2014-09-01" }}
{ "term": { "public": true}}
{ "term": { "tag": "full_text" }}
terms 过滤 跟 term 有点类似,但 terms 允许指定多个匹配条件。如果某个字段指定了多个值,那么文档需要一起去做匹配:
{
"terms": {
"tag": [ "search", "full_text", "nosql" ]
}
}
range 过滤允许我们按照指定范围查找一批数据:
{
"range": {
"age": {"gte": 20,"lt":30}
}
}
范围操作符包含:
gt :: 大于
gte :: 大于等于
lt :: 小于
lte :: 小于等于
exists和missing过滤:
exists 和 missing 过滤可以用于查找文档中是否包含指定字段或没有某个字段,类似于SQL语句中的 IS_NULL 条件
{
"exists":{
"field": "title"
}
}
这两个过滤只是针对已经查出一批数据来,但是想区分出某个字段是否存在的时候使用。
bool 过滤
bool 过滤可以用来合并多个过滤条件查询结果的布尔逻辑,它包含一下操作符:
must :: 多个查询条件的完全匹配,相当于 and 。
must_not :: 多个查询条件的相反匹配,相当于 not 。
should :: 至少有一个查询条件匹配, 相当于 or 。
这些参数可以分别继承一个过滤条件或者一个过滤条件的数组:
{
"bool": {
"must": { "term": { "folder": "inbox" }},
"must_not": { "term": { "tag": "spam" }},
"should": [
{ "term": { "starred": true}},
{ "term": { "unread": true}}
]
}
}
match_all 查询
使用 match_all 可以查询到所有文档,是没有查询条件下的默认语句。
{
"match_all": {}
}
此查询常用于合并过滤条件。 比如说你需要检索所有的邮箱,所有的文档相关性都是相同的,所以得到的 _score 为1
match查询
match 查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。
如果你使用 match 查询一个全文本字段,它会在真正查询之前用分析器先分析 match 一下查询字符:
{
"match": {
"tweet": "About Search"
}
}
如果用 match 下指定了一个确切值,在遇到数字,日期,布尔值或者not_analyzed的字符串时,它将为你搜索你给定的值:
{ "match": { "age":26}}
{ "match": { "date":"2014-09-01"}}
{ "match": { "public":true}}
{ "match": { "tag":"full_text" }}
提示: 做精确匹配搜索时,你最好用过滤语句,因为过滤语句可以缓存数据。
不像我们在《简单搜索》中介绍的字符查询, match 查询不可以用类似"+usid:2 +tweet:search"这样的语句。它只能就指定某个确切字段某个确切的值进行搜索,而你要做的就是为它指定正确的字段名以避免语法错误。
multi_match查询
multi_match查询允许你做 match 查询的基础上同时搜索多个字段:
{
"multi_match": {
"query": "full text search",
"fields":[ "title", "body" ]
}
}
bool查询与bool过滤相似,用于合并多个查询子句。不同的是,bool过滤可以直接给出是否匹配成功,而bool查询要计算每一个查询子句的_score(相关性分值)。
must :: 查询指定文档一定要被包含。
must_not :: 查询指定文档一定不要被包含。
should :: 查询指定文档,有则可以为文档相关性加分。
以下查询将会找到title字段中包含 "how to make millions",并且 "tag" 字段没有被标为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 子句,那至少应该有一个 should 子句。但是 如果有 must 子句,那么没有 should 子句也可以进行查询。
node是es的运行实例,shard是一个单独的lucene实例
primary shard: 每个document都存储在一个单独的primary shard中。当为一个document建索引时,首先在primary shard上建立,然后在该primary shard的所有replica shards上面建。
replica shard: 每个primary shard有零或多个repica shard,replica是primary的拷贝,有两个目的,
a) 提高恢复能力:当primary挂掉时,replica可以变成primary
b) 提高性能:get和search请求既可以由primary又可以由replica处理
默认的,每个primary有一个replica,但一个索引的replica的数量可以动态地调整。replica从不与它的primary在同一个node上启动。
routing: 当为某个document建立索引的时候,索引存储在某个primary shard上。该shard是通过哈希routing value选出来的。默认的,routing value通过document ID得到,或者当该文档有特定的父文档,从父文档的ID得到(这是为了保证子文档和父文档存储在相同的shard)。该value可以在建索引时指定,或者在mapping中通过routing field给定
Analyzer在Es中分为index_analyzer和search_analyzer
Index_analzyer:指的是索引过程中采用的分词器
Search_analyzer:指的是检索过程中采用的分词器
ES索引优化篇主要从两个方面解决问题,一是索引数据过程;二是检索过程。