Elasticsearch和solr面试题
搜索引擎
搜索引擎的概念
是指根据一定的策略、运用特定的计算机程序从互联网上搜集信息,在对信息进行组织和处理后,为用户提供检索服务,将用户检索相关的信息展示给用户的系统。
这里的搜索引擎是指提供数据收集,建立索引数据库,数据检索功能的框架
全文搜索引擎的概念
全文检索:文档存储时把文档中的内容拆分成若干个关键词,然后根据关键词创建索引。查询时,根据关键词查询索引,最终找到包含关键词的文档。这样根据文档词条来检索文档的过程称为全文检索
结构化数据: 指具有固定格式或有限长度的数据,如数据库,元数据等。
非结构化数据: 指不定长或无固定格式的数据,如邮件,word文档等。
全文搜索引擎:提供数据收集,根据文档中的词条建立索引,根据词条来检索文档的框架
全文搜索引擎的应用场景
对数据量大的非结构化数据的搜索可以使用全文搜索引擎
如:社区网站的关键字搜索
商品网站的首页关键字搜索
日志信息查询等
倒排索引的概念
倒排索引是以关键词作为索引项来索引文档的一种机制,常被用于全文检索系统中
通过倒排索引,可以根据单词快速获取包含这个单词的文档列表。
倒排索引主要由两个部分组成:“单词词典”和“倒排表”
Term:是索引里最小的存储和查询单元,对于英文来说一般是指一个单词,对于中文来说一般是指一个分词后的词。
单词词典:搜索引擎的通常索引单位是单词,单词词典是由文档集合中分词后的单词构成的字符串集合,单词词典内每条索引项记载单词本身的一些信息以及指向“倒排列表”的指针。单词词典常用的数据结构包括哈希+链表结构和树形词典结构。包含了文档中所有不重复词的列表,词典通常是有序的,一个分片对应一个单词词典。
倒排表:倒排列表记载了出现过某个单词的所有文档的文档列表及单词在该文档中出现的位置信息,每条记录称为一个倒排项(Posting)。根据倒排列表,即可获知哪些文档包含某个单词。对于每个词,都有一个与之对应的倒排表。倒排表中的每个条目通常包含文档的ID以及词在文档中的位置信息(如词频、位置偏移等)。单词词典对倒排表是1对多的 关系。
倒排文件(Inverted File):所有单词的倒排列表往往顺序地存储在磁盘的某个文件里,这个文件即被称之为倒排文件,倒排文件是存储倒排索引的物理文件。
正排索引(正向索引)
正排表是以文档的ID为关键字进行查询,表中记录文档中单词的出现次数和出现位置。
当根据单词进行搜索时需要对所有的文档进行查询,找出所有包含关键词的文档,这样就使得检索时间大大延长,检索效率低下。
分析器
分析器说明:
分析器(Analyzer)的作用就是分析(Analyse),创建索引时为数据建立倒排索引,搜索时把文本处理成可被搜索的词。
分析器由一个分词器(Tokenizer)和零个或多个标记过滤器(TokenFilter)组成,也可以包含零个或多个字符过滤器(Character Filter)。
字符过滤器:进行符号转换为单词,移出html标签等
标记过滤器:移出停用词,转换为同义词等
分析器执行过程:
对于需要被分析的字段,首先,字符过滤器对分析(analyzed)文本进行过滤和处理,例如从原始文本中移除HTML标记,根据字符映射替换文本等,如把字符“&”转换为单词“and”
过滤之后的文本被分词器接收,分词器把文本分割成标记流,也就是一个接一个的标记,
然后,标记过滤器对标记流进行过滤处理,例如,移除停用词,把词转换成其词干形式,把词转换成其同义词等,最终,过滤之后的标记流被存储在倒排索引中
ElasticSearch引擎在收到用户的查询请求时,会使用分析器对查询条件进行分析,根据分析的结构,重新构造查询,以搜索倒排索引,完成全文搜索请求
es预定义分析器
分析器说明:分析器不止能用在可以进行分词的text数据类型上,其他数据类型上也可以用分析器,比如用分析器的字符过滤器功能
Standard Analyzer - 默认分词器(标准分析器)
英文按照空格切分,中文一个字分一个词,同时大写转小写,移除停用词
没有配置字符过滤器,对于标点符号在分词器处理的时候标点符号会被忽略掉不会进入分词
Simple Analyzer
先按照空格进行分词,英文大写转小写,中文不进行分词
Stop Analyzer
英文大写转小写,停用词过滤(the,a,is)
Whitespace Analyzer
按照空格切分,中文不进行分词
Keyword Analyzer
不分词,直接将输入当作输出
Patter Analyzer -
正则表达式,默认\W+(非字符分割)
IK分词器
IK分词器的类型
中华人民共和国国徽
ik_max_word 细粒度:会分为:中华、人民、共和国、国徽、华人、中华人民、中华人民共和国等
Ik_smart粗粒度:会分为:中华人民共和国、国徽
非smart模式所做的就是将能够分出来的词全部输出;smart模式下,IK分词器则会根据内在方法输出一个认为最合理的分词结果
smart模式是在全部分词的基础上使用歧义消除算法做消除岐义
歧义消除算法:
IK分词器从子分词器中取出不相交的分词集合,这些分词集合按照它们在文本中出现的位置进行排序。然后,IK分词器会尝试不同的分词组合,通过一定的算法和策略来评估每个组合的合理性。
我们用的是ik_max_word,在设置index的映射mappings可以指定字段使用什么类型的ik分词器
IK分词器原理
本质上是词典分词,在内存中初始化一个词典,然后在分词过程中逐个读取字符,和字典中的字符相匹配,把文档中的所有词语拆分出来的过程
es中如何使用
首先要在机器上安装ik分词器,之后给索引定义mapping的时候指定使用ik分析器,如果要结合pinyin过滤器,可以给索引定义settings文件时自定义分析器来组合ik和pinyin
lucene
说一下Lucene
lucene是一个java编写的开源的全文检索引擎,Lucene本身不是一个完整的应用,它只是一个类库,提供了一些基本API让开发者更加简单的在应用中集成搜索功能。
提供了分词,索引的增删改查,相关性分数计算等功能
lucene并不是像solr或elastic那样提供现成的、直接部署可用的系统,而是一套jar包。
现在很多的全文检索引擎框架都是基于lucene的,如solr和elasticsearch都是基于lucene的企业级应用框架
Lucene主要模块:
analysis:主要负责词法分析及语言处理,也就是我们常说的分词,通过该模块可最终形成存储或者搜索的最小单元 Term。
index 模块:主要负责索引的创建工作。
store 模块:主要负责索引的读写,主要是对文件的一些操作,其主要目的是抽象出和平台文件系统无关的存储。
queryParser 模块:主要负责语法分析,把我们的查询语句生成 Lucene 底层可以识别的条件。
search 模块:主要负责对索引的搜索工作。
similarity 模块:主要负责相关性打分和排序的实现。
Lucene涉及的概念
Term:是索引里最小的查询单元,对于英文来说一般是指一个单词,对于中文来说一般是指一个分词后的词。
段(Segment):索引中最小的独立存储单元。一个索引由一个或者多个段组成。在 Luence 中的段有不变性,也就是说段一旦生成,在其上只能有读操作,不能有写操作。一个Segment有多个document(段就是一个物理文件)
更新:更新的操作其实就是删除和新增的组合,先在.del文件中记录旧数据,再在新段中添加一条更新后的数据。
段只读的优缺点
只读,是写入的数据是只读的
优点:
- 不需要锁:因为数据不会更新,所以不用考虑多线程下的读写不一致情况。
- 可以常驻内存:段在被加载到内存后,由于具有不变性,所以只要内存的空间足够大,就可以长时间驻存,大部分查询请求会直接访问内存,而不需要访问磁盘,使得查询的性能有很大的提升。
- 缓存友好:在段的生命周期内始终有效,不需要在每次数据更新时被重建。
- 增量创建:分段可以做到增量创建索引,可以轻量级地对数据进行更新,由于每次创建的成本很低,所以可以频繁地更新数据,使系统接近实时更新。
段不可变性的缺点如下:
- 删除:当对数据进行删除时,旧数据不会被马上删除,而是在 .del 文件中被标记为删除。而旧数据只能等到段合并时才能真正地被移除,这样会有大量的空间浪费。
- 更新:更新数据由删除和新增这两个动作组成。若有一条数据频繁更新,则会有大量的空间浪费。
- 新增:由于索引具有不变性,所以每次新增数据时,都需要新增一个段来存储数据。当段段数量太多时,对服务器的资源(如文件句柄)的消耗会非常大,查询的性能也会受到影响。
- 过滤:在查询后需要对已经删除的旧数据进行过滤,这增加了查询的负担。
Lucene提供的功能
提供索引的基本操作功能(读写删除等),支持常用的搜索方式(词条搜索,范围搜索,模糊搜索,相识度搜索,组合搜索等),支持分页搜索,高亮显示,对搜索计算分数等。
lucene索引搜索器分类
(1)单词搜索
搜索单个关键字
(2)前缀搜索
按照对应的前缀进行搜索
(3)范围搜索
搜索指定一个范围
(4)语句模糊搜索
使用内置分词器或中文分词器对查询语句进行分词并根据分词结果模糊搜索
(5)条件模糊搜索
在语句模糊查询基础上加上OR或者AND进行条件搜索
补充:只支持OR、AND两种条件搜索
(6)组合搜索
可以组合上面几种搜索器进行混合搜索
Lucene内置的Query对象
lTermQuery 词条搜索
lNumericRangeQuery 范围搜索
lMatchAllDocsQuery 匹配所有搜索
lWildcardQuery 模糊搜索
lFuzzyQuery 相似度搜索(编辑距离算法:从一个字符串变换到另一个字符串所需要的最少变化操作步骤。)
lBooleanQuery 布尔搜索(组合查询)(必须包括,不能包括,或)
Elasticsearch
说一下Elasticsearch
es是一个开源的基于Lucene的分布式全文搜索引擎产品,同一个索引的
支持分布式存储:
- 节点类型分为主节点(维护集群的状态信息:所有节点的状态、分片的分配情况、索引的元数据等;主节点会定期更新这些信息,并将其广播给集群中的其他节点。分片的分配与负载均衡;集群节点的增加、删除、集群信息设置等)、数据节点、路由节点。
- 索引分片数据可以建立副本提供高可用和分担查询压力
可以提供近实时的查询功能:
使用es可以快速地储存、搜索和分析海量数据,基于es可以做网站的搜索引擎,日志分析系统等。
近实时概念:表示从文档被索引(即存储并使其可搜索)到该文档真正能够被搜索到之间存在一个短暂的延迟,这个延迟通常默认为1秒
记忆:是什么,作用
lucene,solr,elasticsearch比较
共同点+不同点+应用场景
solr和elasticsearch都是使用java开发基于lucene的企业级搜索引擎
lucene提供了搜索引擎开发的基本api
Lucene专注于搜索底层的建设,而Solr和elasticsearch专注于企业应用。
在es中每个分片都是一个独立的Lucene实例。
solr和elasticsearch对比:
Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能。
Solr提供多种格式数据建立索引,可以导入数据库表、xml、json、csv各种格式的数据信息。而 Elasticsearch 仅支持json文件格式。
es可以提供近实时搜索(默认1s刷新到文件系统缓存就可以被查询到),solr不能(需要持久化磁盘上才能被搜索到)
solr建立索引时进行查询效率会比较低(因为产生了io阻塞),es变化不大。
随着数据量的增加,Solr的搜索效率会变得更低,而Elasticsearch却没有明显的变化。
Solr 对已有数据进行搜索时效率更高对实时搜索效率不高, Elasticsearch 更适用于实时搜索应用。
Elasticsearch提供的功能
文档存储:
分布式的文档存储,支持索引的分片,副本,支持对文档的增删改查。
如博客系统不需要事务操作的场景,使用es作为持久存储文档型数据库
数据检索:
精确查询,通配符查询,正则查询,范围查询,分页查询,分组查询,排序查询,聚合查询等功能
数据分析:
es可以通过aggs模块来完成聚合分析,进行Count计算数量,Sum求和,Min获取最小,Max获取最大,AVG求平均等指标统计
查询支持
query查询:
match_all查询所有,也就是查询所有,默认查询10条,可以通过设置from和size指定查询哪些数据
{
"query": {
"match_all": {}
}
}
term查询:
代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇
{
"query": {
"term": {
"schoolId": "2811000226000000678"
}
}
}
terms查询:
多词语查询,查找符合词语列表的数据。如果要查询的字段索引为not_analyzed类型,则terms查询非常类似于关系型数据库中的in查询。
{
"query": {
"terms": {
"studentNo": [
"1",
"3"
]
}
}
}
bool查询:
Bool(布尔)查询是一种复合型查询,它可以结合多个其他的查询条件。主要有3类逻辑查询:
must:查询结果必须符合该查询条件(列表)。
should:类似于in的查询条件。如果bool查询中不包含must查询,那么should默认表示必须符合查询列表中的一个或多个查询条件。
must_not:查询结果必须不符合查询条件(列表)。
{
"query": {
"bool": {
"must": [
{
"term": {
"classNo": "2"
}
},
{
"term": {
"isLeader": "true"
}
}
]
}
}
}
ids查询:
通过指定一个ID列表来检索多个文档,想要检索ID为1、3和5的文档。
{
"query": {
"ids": {
"values": ["1", "3", "5"]
}
}
}
Prefix Query前缀查询:
{
"query": {
"prefix": {
"name": "赵"
}
}
}
range query:
{
"query": {
"range": {
"age": {
"gte": "18", // 表示>=
"lte": "20" // 表示<=
}
}
}
}
Wildcard Query通配符查询:
通配符查询,是简化的正则表达式查询,包括下面两类通配符:
* 代表任意(包括0个)多个字符
? 代表任意一个字符
查找名字的最后一个字是“亮”的同学,查询结果是学号为5的诸葛亮。
{
"query": {
"wildcard": {
"name": "*亮"
}
}
}
Regexp Query 正则表达式查询
{
"query": {
"regexp": {
"address": ".*长沙市.*" // 这里的.号表示任意一个字符
}
}
}
match查询:
用于全文搜索,Match查询默认会考虑字段的分析器设置。这意味着如果字段在索引时被配置了一个特定的分析器,那么查询时也会使用相同的分析器来处理查询字符串。它默认使用OR逻辑,即只要文档包含查询中的一个或多个分词,就会被视为匹配。不考虑这些单词的顺序或位置。
GET /products/_search
{
"query": {
"match": {
"description": "智能手机"
}
}
}
//operator指定多个词条之间的匹配逻辑"AND"表示所有词条都必须匹配,"OR"表示至少有一个词条匹配即可。默认值是"OR"。
{
"query": {
"match": {
"description": {
"query": "智能手机 快充",
"operator": "AND"
}
}
}
}
//fuzziness:这个参数用于模糊匹配,可以指定一个编辑距离来允许查询中的词与文档中的词有一定的差异。
{
"query": {
"match": {
"title": {
"query": "iphne",
"fuzziness": 2
}
}
}
}
//prefix_length:这个参数用于模糊匹配时,指定从词的前多少个字符开始不允许被模糊。默认值是0,表示从词的开始就可以进行模糊匹配。
{
"query": {
"match": {
"brand": {
"query": "appl",
"fuzziness": 2,
"prefix_length": 2 //从“appl”的前两个字符“ap”开始不允许被模糊,所以只会匹配到像“apple”这样的词。
}
}
}
}
//minimum_should_match:这个参数用于指定文档中至少包含多少个关键词才算匹配成功。它可以帮助控制返回的文档与查询的相关度。
{
"query": {
"match": {
"features": {
"query": "防水 防尘 抗震",
"minimum_should_match": "2<3" //这个查询会返回features字段中至少包含“防水”、“防尘”和“抗震”这三个词中的两个词的文档。
}
}
}
}
//zero_terms_query:当查询语句被分析后没有剩余词条时,这个参数用来控制查询的行为。默认是"NONE",表示不返回任何文档;也可以设置为"ALL",表示返回所有文档。
//Match查询还提供了其他一些参数,如analyzer(用于指定分析器)、lenient(控制查询对格式错误的容忍度)等。
Match_phrase 查询:
它要求分词在文档中的顺序必须与查询中的顺序相同,并且通常要求这些分词是相邻的。
我们想要搜索标题中精确包含“Elasticsearch 搜索”这个短语的文档,我们应该使用match_phrase查询。
{
"query": {
"match_phrase": {
"title": "Elasticsearch 搜索"
}
}
}
Elasticsearch会查找标题字段中精确包含“Elasticsearch”后面紧跟着“搜索”这两个词的文档。如果这两个词在文档中是分开的,或者它们的顺序与查询中的不同,那么该文档将不会被匹配。
Filter 查询
filter查询方式都可以通过设置_cache为true来缓存数据。如果下一次恰好以相同的查询条件进行查询并且该缓存没有过期,就可以直接从缓存中读取数据。缓存的清理:缓存信息不够用时会按照最近最少用的先清理原则。
filter不支持Wildcard查询(通配符查询)。
term查询:
{
"filter": {
"term": {
"name": "诸葛亮",
"_cache" : true // 与query主要是这里的区别,可以设置数据缓存
}
}
}
bool查询:
{
"filter": {
"bool": {
"must": [
{
"term": {
"classNo": "2"
}
},
{
"term": {
"isLeader": "true"
}
}
]
}
}
}
exists查询
存在查询,查询指定字段至少包含一个非null值的数据。如果字段索引为not_analyzed类型,则查询sql中的is not null查询方式。
在query范围内不支持使用exists,必须放在filter的下面
{
"filter": {
"exists": {
"field": "address"
}
}
}
Missing查询
缺失值查询,与Exists查询正好相反。该查询也是必须放在filter里面。
查询地址不存在的学生
{
"filter": {
"missing": {
"field": "address"
}
}
}
Prefix查询(前缀查询)
{
"filter": {
"prefix": {
"name": "赵"
}
}
}
range查询
{
"filter": {
"range": {
"age": {
"gte": "18",
"lte": "20"
}
}
}
}
terms查询
多词语查询,查找符合词语列表的数据。如果要查询的字段索引为not_analyzed类型,则terms查询非常类似于关系型数据库中的in查询
{
"filter": {
"terms": {
"studentNo": [
"1",
"3"
]
}
}
}
Regexp查询(正则表达式查询)
{
"filter": {
"regexp": {
"address": ".*长沙市.*"
}
}
}
query与filter嵌套
在Elasticsearch 7.x及更高版本中,建议使用bool查询结合filter子句,因为filter子句在更早期的版本中是一个顶层的查询参数,而在后续版本中它通常被放置在bool查询内部。
{
"query": {
"bool": {
"must": [
{ "match": { "title": "Search" }},
{ "match": { "content": "Elasticsearch" }}
],
"filter": [
{ "term": { "status": "published" }},
{ "range": { "publish_date": { "gte": "2015-01-01" }}}
]
}
}
}
query 参数表示整个语句是处于 query context 中
bool 和 match 语句被用在 query context 中,也就是说它们会计算每个文档的匹配度(_score)
filter 参数则表示这个子查询处于 filter context 中
filter 语句中的 term 和 range 语句用在 filter context 中,它们只起到过滤的作用,并不会计算文档的得分。
聚合查询
语法:
GET /<index_name>/_search
{
"size": 0, // 设置返回的文档数量为0,因为我们通常只对聚合结果感兴趣
"query": {
// ... 查询条件 ...
},
"aggs": {
"<aggregation_name>": {
"<aggregation_type>": {
// ... 聚合参数 ...
},
"aggs": {
// ... 子聚合 ...
}
}
}
}
聚合类型:
Terms 聚合
按字段的值进行分组。
"aggs": {
"groups": {
"terms": {
"field": "<field_name>",
"size": 10, // 返回的桶的最大数量
"order": { // 排序桶
"_key": "asc"
},
"include": "<value>", // 包含特定的桶
"exclude": "<value>", // 排除特定的桶
"min_doc_count": 1, // 桶中文档的最小数量
"shard_size": 200, // 每个分片返回的桶的最大数量
"show_term_doc_count_error": false // 是否显示文档计数的近似误差
}
}
}
GET /sales/_search
{
"size": 0, // 不返回文档,只返回聚合结果
"aggs": {
"products": { // 聚合的名称
"terms": { // 使用terms聚合按字段分组
"field": "product_id"
},
"aggs": {
"total_sales": { // 内部聚合的名称
"sum": { // 使用sum聚合计算销售额的总和
"field": "sales_amount"
}
}
}
}
}
}
Filters 聚合
根据多个过滤条件定义桶。
"aggs": {
"filters": {
"filters": {
"<filter_name>": { "match": { "<field_name>": "<value>" } },
// ... 其他过滤条件 ...
}
}
}
Range 聚合
对数值字段进行范围分组。
"aggs": {
"prices": {
"range": {
"field": "<numeric_field>",
"ranges": [
{ "from": 0, "to": 100 },
{ "from": 100, "to": 200 }
// ... 其他范围 ...
]
}
}
}
Metrics 聚合
用于计算数值字段的统计信息,如平均值、总和、最大值、最小值等。
"aggs": {
"average_price": {
"avg": {
"field": "<numeric_field>"
}
},
"total_sales": {
"sum": {
"field": "<numeric_field>"
}
}
// ... 其他metrics聚合 ...
}
子聚合
你可以在聚合内部嵌套其他聚合,称为子聚合,以便对分组后的数据进行进一步的分析。
"aggs": {
"products": {
"terms": {
"field": "product_id"
},
"aggs": {
"average_price": {
"avg": {
"field": "price"
}
}
// ... 其他子聚合 ...
}
}
}
es的应用场景
全文检索:我想搜索商品名称包含某个关键字的商品
结构化检索:我想搜索商品分类为日化用品的商品都有哪些
数据分析:每一个商品分类下有多少个商品;电商网站统计销售排名前10的商家
监控:利用es的实时性,可以根据日志做系统监控
报表:利用es的数据分析能力,对数据进行整合计算分析
日志分析:日志信息存储到es中,可以使用kiana或者api封装来完成日志的可视化分析
es特点
优点:
分布式:
可以将数据分散到多台服务器上去存储和检索
近实时(对海量数据进行近实时的处理):
从写入数据到数据可以被搜索到有大概1秒的延时
支持较复杂的条件查询group by、排序等
高可用:
提供副本(Replica)机制,一个分片可以设置多个副本,即使在某些服务器宕机后,集群仍能正常工作。
支持众多的插件:
展示用的head插件,中文分词插件ik分词器,各种数据同步插件等。
自动发现(Discovery):
Zen 发现机制是ElasticSearch中默认的用来发现新节点的功能模块,而且集群启动后默认生效。
缺点:
不支持事务
联合查询效率低下
es近实时:
为了提高索引性能,Elasticsearch 在写入数据时候,采用延迟写入的策略,即数据先写到内存buffer中,当超过默认 1 秒 (index.refresh_interval)会进行一次写入操作,就是将内存中 segment 数据刷新到文件系统缓存中,此时我们才能将数据搜索出来,所以这就是为什么 Elasticsearch 提供的是近实时搜索功能,而不是实时搜索功能。
es高可用:
除了分片副本机制,还有操作日志记录translog可以帮助恢复数据,还提供了整体数据备份与恢复功能
快照和还原:可用于备份单个索引或整个集群。您可以将这些备份自动存储在共享文件系统上的存储库中。
跨集群复制(CCR):可用于将远程集群中的索引复制到本地集群。您可以使用跨集群复制从主集群的故障中恢复,也可以基于地理位置邻近性在本地提供数据。
去中心化:
主节点是对内部集群来说的,对外来讲访问集群中任何一个节点都一样所以是去中心化的;
索引和文档
es中的索引和文档是分开存储的,不过同一个分片上的索引指向的文档都在相同的分片上。
es中的段(Segment)
关联查询
关联查询:
最好是先在 Java 系统里就完成关联,将关联好的数据直接写入 ES 中。搜索的时候,就不需要利用 ES 的搜索语法来完成 Join 之类的关联搜索了。join 查询,一直是 Elasticsearch 的弱项,使用关联查询,索引结构变得复杂,索引数据的速度同样受到影响。
传统搜索和实时搜索
传统搜索跟实时搜索的差别就在信息的实时性上,实时搜索可以马上搜索到刚产生的数据,而传统搜索做不到
elasticsearch涉及的概念
Cluster
一个集群由一个或多个具有相同集群名称的节点组成,每个集群都有一个主节点,集群会自动选择该主节点,如果当前主节点发生故障,则可以替换该主节点。
Node
节点是属于Elasticsearch集群的的运行实例,可以在单个服务器上启动多个节点,但是通常每个服务器应该有一个节点。
在启动时,节点将使用单播来发现具有相同集群名称的现有集群,并将尝试加入该集群。
单播特点:一个单个的发送者和一个接受者之间通过网络进行的通信。
Shard
代表索引分片,ES可以把一个完整的索引分成多个分片
索引数据可以利用分片进行分布式存储,分布式检索
一个分片默认最大文档数量是20亿
Shard有两种类型:primary和replica,即主shard及副本shard。Primary shard用于文档存储,index一旦创建完成,其Primary shard的数量将不可更改。Replica shard是Primary Shard的副本,用于容错及负载均衡。
在开始阶段, 一个好的方案是根据你的节点数量按照1.5~3倍的原则来创建分片. 例如,如果你有3个节点, 则推荐你创建的分片数最多不超过9(3x3)个
一个ElasticSearch的Shard本质上是一个Lucene实例。
Replia
代表分片副本,es可以设置零个或多个分片的副本
副本的作用一是高可用,当某个节点某个分片损坏或丢失时可以从副本中恢复。二是提高es的查询效率,es会自动对搜索请求进行负载均衡。
index(索引)是elasticsearch数据管理的顶层单位
一个index包含很多document,一个index就代表了一类类似的或者相同的document。
索引是一个逻辑命名空间,它映射到一个或多个主分片,并且可以具有零个或多个分片副本。
Type:类型,每个索引里都可以有一个或多个type,type是index中的一个逻辑数据分类。
类型已弃用,正在删除中,在7.0的API中不推荐使用类型,在 6.0 的时候,已经默认只能支持一个索引一个 type 了
lucene是没有type的概念的,在document中,实际上将type作为一个document的field来存储,即type,es通过type来进行type的过滤和筛选。
Document 每个文档是存储在es中的json对象,一个document里面有多个field,每个field就是一个数据字段。
为什么弃用type
在lucene中是没有type的,type是做为一个字段存储在文档中的,所以一个index的不同的type的数据都是放在一块进行存储的。
去掉type能够使数据存储在独立的index中,避免不同type相同名字字段不同数据类型出现数据冲突
在同一个索引的不同type下存储字段数不一样的实体会导致存储中出现稀疏数据,影响Lucene压缩文档的能力,导致ES查询效率的降低。
稀疏数据:在数据集中很多的字段没有数据
es速度快的原因
- 数据分布式存储,相比单节点压力小,可以提供更高的性能,而且可以并行在多个分片上查询
- 主分片与副本分片都可以提供查询功能,可以分担查询压力
- 索引,对于需要作为查询条件的字段,都会建立对应数据类型的索引(text倒排索引,其他类型其他索引方式)可以高效的查询文档信息
- 缓存机制,使用filter查询能够使用查询缓存,平常也会使用到文件系统缓存,这样就避免了磁盘查询提高效率
- 异步持久化,数据写入后不会马上持久化,会优先写入文件系统缓存,就可以提供查询功能。
es架构

主要模块说明:
Transport Client/Node Client/REST API:三种访问es集群的方式
Transport(Netty):通信模块,数据传输,底层采用netty框架
Index、Search…:支持搜索,索引等常用操作
Discovery:节点发现,集群之间通信的基石
Plugins:很多服务以插件形式提供,官方和社区支持的ik、head、river、discovery gce…
Script:提供脚本支持,内置painless,groovy等,默认painless性能还可以
Store/Snapshot:文件存储与访问,快照创建和恢复
translog、cluster state、segments:es主要文件类型,其中translog、cluster state是es添加的数据,多个segments段组成一个完整的lucene索引
Monitor:监控模块,监控jvm,文件系统,操作系统等运行情况
File System:es支持可以在多种文件系统上运行,本地、共享型、HDFS、亚马逊云平台等
es的模块及作用
1)cluster集群管理模块
cluster模块是主节点执行集群管理和封装的实现,管理集群状态,维护集群层面的配置信息。
管理集群状态,将新生成的集群状态发布到集群中所有的节点
调用allocation模块执行分片分配,决策哪些分片应该分配到哪个节点
在集群各节点中直接迁移分片,保持数据平衡
2)allocation分片分配模块
封装了分片分配相关的功能和策略,包括主分片的分配和副分片的分配,本模块由主节点调用。创建新索引、集群完全重启,节点的加入和移出等都需要分片分配的过程
3)discovery 自动发现模块
发现模块负责发现集群中的节点,以及选举主节点,当节点加入或者退出集群是,主节点会采取行动(选主或者是集群拓扑)。
4)gateway 模块
负责对收到主节点广播下来的集群状态数据的持久化存储,并在集群完全重启时恢复它们 。
集群级元数据: 主要是所有节点信息, 所有索引模板信息,所有索引状态信息
索引级元数据: 主要是numberOfShards,mappings等
分片级元数据: 主要是version,indexUUID,primary
gateway负责集群级和索引级的状态数据,allocation负责分片及分片状态数据
5)Indices 索引管理模块
索引模块管理全局级的索引设置,不包括索引级的(索引设置分为全局级和每个索引级)。 它还封装了索引数据恢复功能 。 集群启动阶段需要的主分片恢复和副分片恢复就是在这个模块实现的 。
6)HTTP模块
HTTP 模块允许通过 JSON over HTTP 的方式访问 ES 的 API, HTTP 模块本质上是完全异步的,这意味着没有阻塞线程等待响应 。
7)Transport 传输模块
传输模块用于集群内节点之间的内部通信 。 从一个节点到另-个节点的每个请求都使用传输模块。 如同 HTTP 模块 ,传输模块本质上也是完全异步的。传输模块使用 TCP 通信,每个节点都与其他节点维持若干 TCP 长连接。内部节点间的所有通信都是本模块承载的。
8)Engine
Engine 模块封装了对 Lucene 的操作及 translog 的调用,它是对一个分片读写操作的最终提供者 。
Es支持的数据类型
|
一级分类 |
二级分类 |
具体类型 |
|
核心类型
|
字符串类型 |
string(已经不用了),text,keyword |
|
整数类型 |
integer,long,short,byte |
|
|
浮点类型 |
double,float,half_float,scaled_float |
|
|
逻辑类型 |
boolean |
|
|
日期类型 |
date |
|
|
范围类型 |
range |
|
|
二进制类型 |
binary |
|
|
复合类型 |
数组类型 |
array |
|
对象类型 |
object |
|
|
嵌套类型 |
nested |
|
|
地理类型 |
地理坐标类型 |
geo_point |
|
地理地图 |
geo_shape |
|
|
特殊类型 |
IP类型 |
ip |
|
范围类型 |
completion |
|
|
令牌计数类型 |
token_count |
|
|
附件类型 |
attachment |
|
|
抽取类型 |
percolator |
一些数据类型的说明:
字符串类型
- string
从ElasticSearch 5.x开始不再支持string(使用string会报错),由text和keyword类型替代。
- text
文本数据类型,使用文本数据类型的字段,它们会被分词
text类型的字段不用于排序和聚合。如果需要聚合需要设置fielddata:true,只要不具备唯一性的字符串一般都可以使用text,例如:电子邮件正文,商品介绍,个人简介
- keyword
关键字数据类型,使用keyword类型的字段,其不会被分析
keyword类型适用于索引结构化的字段,比如email地址、住址、主机名、状态码和标签。keyword类型的字段只能通过精确值搜索到。
什么情况下使用keyword 数据类型:
如果字段需要进行过滤(比如查找已发布博客中status属性为published的文章)、排序、聚合。
具有唯一性的字符串,例如:电子邮件地址、MAC地址、身份证号、状态代码
keyword类型的最大支持的长度为——32766个UTF-8类型的字符,超过给定长度后的数据将不被索引
日期数据类型
一般在写入es的时候,会以json的方式写入,由于json中没有日期数据类型,所以日期如何存储显示,是由es决定的,也就是说es会进行隐式的类型转换。
es中的日期可以是:
格式化日期的字符串,例如"2019-12-30"或"2019/12/30 12:10:30"。
毫秒值。
秒值。
es默认存储时间的格式是UTC时间(即零时区),中国的时间(东八区)是等于UTC时间+8小时
如果我们查询es然后获取时间日期默认的数据,会发现跟当前的时间差8个小时
处理方案:
写数据时,时间数据转换为通用时间格式的字符串(yyyy-MM-dd HH:mm:ss)。
或写数据时,时间数据转换为带有时区信息的日期字符串,如:
“2016-07-15T12:58:17.136+0800”
如果是已经存入UTC格式时区问题,按时间进行分组查询时指定时区,普通查询转为long型再转为指定格式
哪些数据类型可以设置分析器:
text
如果没有指定字段的类型默认是什么类型:
字段的属性
常用属性:
①analyzer:指明该字段用于索引时和搜索时的分析字符串的分析器(使用search_analyzer可覆盖它)。 默认为标准分析器(英文按照空格切分,同时大写转小写,中文一个字分一个词)
②fielddata:指明该字段是否可以使用内存中的fielddata进行排序,聚合或脚本编写,默认值为false,可取值true或false。
③fields:设置该字段为"多字段",可以对一个字段设置多种索引模式,如一个分词一个不分词;。
PUT my_index
{
"mappings": {
"properties": {
"city": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
}
}
}
}
city.raw就是一个keyword类型的属性,对存入city字段的内容,也会存入city.raw
city字段可以用于全文索引,city.raw字段可以用于排序和聚合
④index:设置该字段是否可以用于搜索。默认为true,表示可以用于搜索。index:控制某个字段是否生成索引,设置为false,表明该字段不能被查询,如果查询会报错。
⑤store:设置该字段是否可以用于独立存储,默认为false,当某个数据字段很大,我们可以指定其它字段store为true,这样就不用从_source中取数据。 store 的意思是,是否在 _source 之外再独立存储一份。这么做的目的主要是针对内容比较多的字段,避免获取到_source内容太大占用带宽影响性能,只有需要这个字段数据时再去返回
_source 表示源文档,存储着文档原始json数据,当你创建文档时, elasticsearch 会保存一份源文档到 _source
⑥search_analyzer:设置在搜索时,用于分析该字段的分析器,默认analyzer
Es的配置说明
config目录下的elasticsearch.yml
node.master:true
指定该节点是否有资格被选举成为master,默认是true
node.data:true
指定该节点是否存储索引文档数据,默认为true。
index.number_of_shards:5
设置默认索引分片个数,默认为5片。(看具体版本,7.5的默认为1)
index.number_of_replicas:1
设置默认索引副本个数,默认为1个副本。
discovery.zen.ping.unicast.hosts:["host1", "host2:port","host3[portX-portY]"]
建议设置单播host列表为所有的master节点,默认是127.0.0.1,[::1],格式为数组或以逗号分隔的字符串,每个值的格式都应该为host或host:port,它应该去尝试连接的节点列表。一旦这个节点联系到组播列表中的一员,它就会得到整个集群所有节点的状态,然后它会联系master节点,并加入集群。
分片和副本的配置
分片数量的选择:
根据节点数量和es的jvm内存大小来选择:
一般以(节点数*1.5或3倍)来计算,比如有4个节点,分片数量一般是6个到12个
控制每个分片占用的硬盘容量不超过 ES 的最大 JVM 的堆空间设置(一般设置不超过 32 G),估算出要存储的索引数据的大小比如400G就设置10个
分配太多会让数据分布太分散,每个分片都是一个lucene实例,消耗cpu及内存;(主分片和副本分片不会分配到同一个节点,容易数据丢失)
分配太少一个分片上数据太多,检索效率降低
副本数量:
每个分片一般分配一个副本
主分片数量为何不能修改:
跟路由规则有关,shard= hash(routing)% number_of_primary_shards;如果修改路由到的位置就不一样了,就找不到数据了
es的jvm内存配置
配置文件config/jvm.options
设置最大内存为不超过物理内存的50%,剩余的内存能够作为文件系统缓存提高数据查询效率
es配置的内存缓存的数据越多,但垃圾收集时间越长,最佳实践为不超过物理内存50%
es索引数据结构
默认情况下,Elasticsearch对每个字段中的所有数据建立索引(如果index设置为false,代表字段不用于查询,不会建立索引),并且每个索引字段都具有专用的优化数据结构。
例如,text类型会建立倒排索引,其他类型(包括keyword)会用其他的索引结果,数字字段和地理字段(geo_point)存储在红黑树中。使用按字段数据结构组合并返回搜索结果的能力使Elasticsearch如此之快。
es的节点数量
没有必须是奇数个,一般建议是奇数个
zookeeper要求奇数台:
集群超过过半的机器是正常工作的,集群就对外是可用的
Zookeeper的大部分操作都是通过选举产生的,选举要求有超过半数的节点统一
zookeeper也可以按照偶数台,但对于2n和2n-1台机器的失效节点容忍度是一样的,都是n-1,所以为了更加高效,没有必要增加一个不必要的节点。
es的节点类型
- client 节点 路由节点
当node.master和node.data配置都设置为false的时候
- data节点 数据节点
node.data为true代表为数据节点
数据节点对cpu,内存,io要求较高, 在优化的时候需要监控数据节点的状态,当资源不够的时候,需要在集群中添加新的节点。
- 主节点
当node.master设置为true,代表可以参与主节点的选举
主节点由选举产生
配置建议:
如果你的节点数量小于10个,小集群,那所有的node,就不要做额外的配置了,每个节点
node.master: true
node.data: true
超过10个node的集群再单独拆分master和data node
主节点的作用
- 集群的状态维护:包括集群中所有节点的状态、分片的分配情况、索引的元数据等。主节点会定期更新这些信息,并将其广播给集群中的其他节点(所有节点都会被广播到)。
- 分片分配与负载均衡:当新的索引或分片被创建时,主节点会负责将其分配到合适的节点上。同时,主节点还会监控集群的负载情况,并根据需要进行分片的重新平衡,以确保集群的性能和稳定性。(对于索引的新增、删除主节点会协调数据节点对索引数据的处理)
- 处理集群级别操作:如添加或删除节点、更新集群设置等。
数据节点的作用
- 存储索引数据
- 执行索引操作:索引的增删改查
路由节点的作用
- 接收客户端请求、分发请求到相应的数据节点,并汇总返回结果
任何节点(包括主节点和数据节点)都可以作为路由节点/协调节点。
负载均衡:虽然主节点具有负载均衡的功能,但它并不直接处理数据请求。相反,负载均衡的任务主要由协调节点或路由节点完成,它们根据集群的状态和节点的负载情况,将请求分发到合适的数据节点上。
请求的处理过程
客户端发送请求到任意一个协调节点或路由节点,该节点计算并确定主分片的位置,然后将请求直接发送到存储相关数据的数据节点。数据节点执行实际的搜索或数据处理操作,并将结果返回给协调节点或路由节点,最后由协调节点或路由节点将结果汇总并返回给客户端。
索引的打开和关闭
打开、关闭索引接口允许关闭一个打开的索引或者打开一个关闭的索引。
关闭的索引只能显示索引元数据信息,不能进行读写文档
针对不使用的index,建议close,减少内存占用。因为只要索引处于open状态,索引库中的segement就会占用系统内存,close之后就只会占用磁盘空间了。
动态映射和静态映射
(1)动态映射
文档写入ElasticSearch时,根据文档字段自动识别类型,这种机制称之为动态映射。(会自动预测可能的数据类型,可能最终结果不是我们想要的)
(2)静态映射
在ElasticSearch中可以事先定义好映射(也就是定义好mappings信息),包含文档的各个字段及其类型等,这种方式称之为静态映射。
索引的settings和mappings
settings指定该索引的分片数量,副本数量等
Mappings指定该索引的字段类型,字段属性等
默认的settings信息:
-
index.number_of_shards:主分片的数量。默认值为1。 -
index.number_of_replicas:每个主分片的副本数。默认值为1,意味着每个主分片将有一个副本。副本用于提高读取性能和容错能力。 -
index.refresh_interval:索引刷新的时间间隔。默认值是1秒,这决定了文档从索引到可以被检索的延迟时间。 -
index.codec:默认使用的编解码器。它定义了索引中数据的压缩和存储方式。 -
index.mapping.total_fields.limit:一个索引中可以拥有的最大字段数。默认值是1000,但可以根据需要调整。 -
index.translog.flush_threshold_size:事务日志(translog)的刷新阈值大小,默认512M。当事务日志的大小超过此阈值时,会触发一个刷新操作。这有助于确保在发生故障时数据不会丢失太多。
索引别名说明
使用索引别名:
索引无缝切换:无缝的从一个索引切换到另一个索引
使用索引别名进行查询,查询会透明的下发到别名下挂的所有索引执行。
使用场景:
无缝切换:
旧的索引a定义别名为c,新的索引索引为b,解除a与别名c的关系,给b定义索引别名为c,以c对外提供搜索服务。(解除关系和建立关系可以使用一个命令完成)(比如索引的字段类型创建错误,如果要原来的数据,先把旧数据导入新的索引,之后再切换)
给多个索引分类:(例如, last_three_months)比如按月创建的索引,我们可以通过别名构造出一个最近3个月的索引
注意
写:不能直接对索引别名进行写入。所以在写数据的时候,要直接使用普通索引。索引写入请求只能以单个索引为目标。
读:查询,对索引别名进行查询,查询会透明的下发到别名下挂的所有索引执行,设置的路由也会随之下发。
一个index 可以有多个别名,一个别名又可以包含多个index
索引别名的原理
原理:索引别名只是物理索引的软链接名称(类似快捷方式)而已。同样的条件下使用别名和基于索引效率一样
索引模板说明
索引模板可以为预建立的索引指定setting和mapping,和模板匹配规则相符的索引都会走索引模板的设置,而不需要每次建立索引的时候指定索引的设置,索引模板中也可以直接指定索引别名
模板仅在索引创建时应用。更改模板不会对现有索引产生影响。
当存在多个索引模板时并且某个索引两者都匹配时,settings和mpapings将合成一个配置应用在这个索引上。合并的顺序可由索引模板的order属性来控制。
order说明多模板匹配的优先级,越大优先级越高,order默认值为0
es路由规则
默认采用哈希值取余,根据路由规则确定索引信息存储在哪个分片上。
shard= hash(routing)% number_of_primary_shards
routing是一个字符串,默认是索引的_id值,也可以自己定义。
这个routing字符串通过哈希函数生成一个数字,然后除以主分盘片的数量得到一个余数,
余数的范围永远是0到number_of_primary_shards - 1,这个数字就是特定文档所在的分片。
默认路由规则:
它会将文档的ID值作为依据将其哈希到相应的主分片上,这种算法基本上会保持所有数据在所有分片上的一个平均分布
自定义路由:
我们创建索引时可以指定routing,会根据routing的值进行hash,之后决定分片的位置
POST/ecommerce/product?routing=ecommerce
查询时同样可以指定routing:
GET /ecommerce/product/4?routing=ecommerce
自定义路由规则的缺点:
自定义路由可能会导致索引分配不均,大量的索引路由到一个分片上,导致这个分片的索引和查询性能降低。
可以创建索引的时候指定routing_partition_size,routing_partition_size的值必须是一个大于1但是小于number_of_shards设置的分片数量的一个整数。具体公式如下:
shard_num = (hash(_routing) + hash(_id) % routing_partition_size) % num_primary_shards
默认路由的查询过程:
- 这个搜索的请求会根据负载均衡被发送到一个节点,该节点被称为协调节点
- 接收到这个请求的节点,将这个查询广播到这个索引的每个分片上(可能是主分片,也可能是复制分片)
- 每个分片执行这个搜索查询并返回结果
- 结果在协调节点上合并、排序并返回给用户
被分发到主分片和其副本上请求会随机选择一个处理请求,不对都进行处理请求
为什么默认路由会查询所有分片:
因为我们查询的时候并没有把id指定,所以es也不知道怎么路由
自定义路由的查询:
不会把查询请求发送给所有分片,只会在路由到的分片上执行查询
Query查询和filter查询
Query查询会计算相关性分数
filter是不计算相关性分数的(返回的_score为0),同时可以缓存,因此,filter速度要快于query
缓存说明:
基于LRU策略,当缓存满了的情况下,会自动去除一个最近最少被使用的Query Cache。
LRU:Least Recently Used,最近最少使用。根据数据的历史访问记录来进行淘汰数据
配置文件可以对Query Cache进行设置大小及是否开启,默认是开启的
适用场景
原则上来说,使用查询语句做全文本搜索或其他需要进行相关性评分的时候,剩下的全部用过滤语句,一般应用时,应先使用过滤操作过滤数据,然后使用查询匹配数据。
es内存使用
es中可以设置要使用的jvm的内存,用于query cache,Field data Cache,Shard Request Cache,Indexing Buffer等
Lucene 缓存倒排索引的数据空间使用的是文件系统缓存不使用jvm缓存
Indexing Buffer就是写索引数据先要写入的那个buffer
es缓存说明
查询缓存:
query cache也称为filter cache
每个节点都有一个由所有分片共享的查询缓存。
缓存实施LRU逐出策略:当缓存已满时,会将最近最少使用的数据逐出,以腾出空间供新数据使用
并不是任何查询都会缓存,像term query就不会被缓存,rang query会被缓存,而且会根据查询的次数和查询结果的大小都有要求,达到条件才会进行缓存
字段缓存:
Field data Cache
主要用于sort以及aggs的字段。这会把字段的值加载到内存中,以便于快速访问。
分片缓存:
Shard Request Cache,分片级别的缓存,主要用于缓存size=0的聚合信息
文件系统缓存
fileSystem cache也可以叫做OS cache
索引数据最终会写到磁盘文件里去了,查询的时候,操作系统会将磁盘文件里的数据自动缓存到 Filesystem Cache 里面去。要让 ES 性能好,最佳的情况下,就是你的机器的内存,至少可以容纳你的总数据量的一半。文件系统缓存不属于jvm的内存
Es的深度分页问题
查询到分页为n的最后一条数据是第m条,每个分片都需要内存中查询到m条然后进行合并出结果,分页太深,需要加载的数据就太多占用内存及查询性能
Es不仅限制了用户在一次查询中最多数据条数是1w条,并且限制了start+size 必须小于1w
即:不能查询第1万条以后的数据。
分页越深则越容易OOM,即便不OOM,也很消耗CPU和内存资源
解决方案:
一、不允许深度分页(默认深度分页性能很差)。跟产品经理说,你系统不允许翻那么深的页,默认翻的越深,性能就越差。
二、类似于 App 里的推荐商品不断下拉出来一页一页的;类似于微博中,下拉刷微博,刷出来一页一页的,你可以用 Scroll API
ES为了避免深分页,不允许使用分页(from&size)查询10000条以后的数据,因此如果要查询第10000条以后的数据,要使用ES提供的 scroll(游标) 来查询
说明
ElasticSearch实际上更适合作为一个搜索的引擎,像这种分页查询遍历查询,实际上不是非常合适的。
游标查询
使用scroll就是一次把要用的数据都排完了放到内存中。查询的时候从内存中取,避免多次读取磁盘。
缺点:不能向前翻页或者直接跳转到某一页。
为了使用scroll-scan,需要执行一个初始化搜索请求,将search_type设置成scan,并且传递一个scroll参数来告诉 Elasticsearch缓存应该持续多长时间,在缓存持续时间内初始化搜索请求后对索引的修改不会反应到快照中。每次搜索请求后都会返回一个scrollId,是一个 64 位的字符串编码,后续会使用此scrollId来获取数据。scroll时间指的是本次数据处理所需要的时间,如果超过此时间,继续使用该scrollId搜索数据则会报错。在使用scroll-scan时可以指定返回结果集大小,在 scan 的时候,size 作用在每个分片上,所以将会在每发现批次中得到最大为 size * 主分片数 个文档。
影响Es性能因素
一:硬件方面
1)内存:
单实例的情况下,尽量分配32G(默认的堆内存是1G),排序和统计都是在内存计算的。
2)硬盘:
在条件允许下,尽量使用一些高性能io的硬盘,SSD
3)CPU:
在高并发的情况下,cpu的计算能力要求就很高了。cpu配置尽量高。
4)网络:
当然这个越大越好。毕竟服务交互通过http 。网络传输是个很重要的因数
- 软件层面
- 查询缓存(打分走query,不打分就filter),Filter查询结果可以缓存,而且不需要计算相关度分数,性能更高
- 减少副本数量,副本越多虽然能提高集群的可用性,但是也增加了搜索的并发数、也会影响索引写入效率,增大合并时间。在索引过程中,需要把更新的文档发到副本节点上,等副本节点生效后在进行返回结束。所以建议副本不要过多,一般1~3个足够了。像内部 ELK 日志系统、分布式跟踪系统中,完全可以将副本数目设置为 1 个。
- 避免深度分页
- 优化查询返回字段,减少内存占用(可以使用_source_include或者_source_exclude参数)
- 增加 Refresh 时间间隔
为了提高索引性能,Elasticsearch 在写入数据时候,采用延迟写入的策略,即数据先写到内存中,当超过默认 1 秒 (index.refresh_interval)会进行一次写入操作,就是将内存中 segment 数据刷新到操作系统中,此时我们才能将数据搜索出来,所以这就是为什么 Elasticsearch 提供的是近实时搜索功能,而不是实时搜索功能。
当然像我们的内部系统对数据延迟要求不高的话,我们可以通过延长 refresh 时间间隔,可以有效的减少 segment 合并压力,提高索引速度。
elasticsearch使用
搭建elasticsearch集群环境,创建index,创建type,向elasticsearch写入数据
java项目中添加elasticsearch的jar包,我们用的5.6版本的,调用api进行操作
es的发现机制
发现机制是用来发现新节点的功能模块,集群启动后默认生效。Zen发现机制默认配置是用单播来寻找其它的节点。
我们可以设置为 Elasticsearch 提供一些它应该去尝试连接的节点列表。当要加入的节点联系到节点列表中的成员时,它就会得到整个集群所有节点的状态,然后它会联系 master 节点,并加入集群。
集群的cluster.name设置一致,在同一网段下,某个节点通过发现机制找到其他节点是使用 Ping 的方式实现的。
es的持久化机制
es的数据会分片存储在集群的数据节点中。主分片先写数据,主分片写成功之后推送给副本分片写。
1)先写数据到内存缓冲区,在内存缓冲区里数据是搜索不到的;同时在内存缓存区写translog日志信息(每隔5秒钟刷到磁盘)
2)如果buffer快满了,或者到一定时间(默认每隔1秒钟),就会将buffer数据refresh到一个新的segment中(如果buffer里面此时没有数据,不会执行refresh操作),但是此时数据不是直接进入segment file的磁盘文件的,而是先进入os cache(操作系统缓存/文件系统缓存)的。
数据进入os cache中,这个数据就可以被搜索到了。只要数据被输入os cache中,buffer就会被清空了。
3)重复1~2步骤,新的数据不断进入os cache和translog,随着这个过程推进,translog会变得越来越大。当translog大到一定值(默认512M)或者每隔30分钟,就会触发commit操作。
4)commit操作发生第一步,就是将buffer中现有数据refresh到os cache中去,清空buffer
5)将一个commit point写入磁盘文件,里面标识着这个commit point对应的所有segment file
6)强行将os cache中目前所有的数据都fsync到磁盘文件中去
7)清空translog日志文件,新建translog文件
如果是删除操作,commit的时候会生成一个记录删除文档信息的.del文件,里面将某个doc标识为deleted状态
磁盘中的segment file会越来越多,此时会定期执行merge,每次merge的时候,会将多个segment file合并成一个,同时这里会将标识为deleted的doc给物理删除掉,然后将新的segment file写入磁盘,这里会写一个commit point,标识所有新的segment file,然后打开segment file供搜索使用,同时删除旧的segment file。
refresh和flush的区别是什么?
refresh,是数据从堆内存进入到文件系统。此时数据从不可见到可以用来被搜索到。
flush是数据从文件系统到磁盘。此时数据不会丢失了,除非硬盘坏了。
es的日志文件
事务日志文件,记录所有对索引分片的事务操作,包括添加、更新和删除操作。用来发生故障时恢复数据。
内存缓冲区写数据时,也会在内存缓冲区写日志信息,默认每5秒会刷新到磁盘上,当磁盘上的日志文件大到一定的阀值(默认512M)或者每隔30分钟会触发数据信息持久化到磁盘,这个时候会删除日志文件,并新建新的日志文件。
Es节点的选主
通常的选主算法:
Bully算法:实现简单,需要考虑主节点假死的问题
Paxos算法:实现复杂,灵活性高
Raft算法:选举时间更短
7.x版本之前elasticsearch的选举算法是基于Bully算法改造的。
Bully是Leader选举的基本算法之一。 它假定所有节点都有一个惟一的ID,该ID对节点进行排序。 任何时候的当前Leader都是参与集群的最高id节点。 该算法的优点是易于实现,但是,当拥有最大 id 的节点处于不稳定状态的场景下会有问题,例如 Master 负载过重而假死,集群拥有第二大id 的节点被选为 新主,这时原来的 Master 恢复,再次被选为新主,然后又假死,但是容易产生脑裂(双主),为此,再通过“法定得票人数过半”解决脑裂问题
假死:Master节点承担的职责负载过重的情况下,可能无法及时对组内成员作出响应,这种便是假死。
脑裂:一个集群,有多个master,各自提供服务;比如发生网络分区就容易出现脑裂
网络分区:正常情况下集群机器间可以双双通信,网络分区后部分机器间无法通信
elasticsearch 通过推迟选举直到当前的 Master 失效来解决上述问题
ES集群中必须 有且只有一个master节点
一、候选节点对所有可以成为master的节点根据nodeId排序,,然后选出id最小的一个(第0位)节点,暂且认为它是master节点。
二、如果对某个节点的投票数达到一定的值(可以成为master节点数n/2+1)并且该节点自己也选举自己,那这个节点就是master。否则重新选举。
(分布式环境下,nodeid是uuid,每次节点重启nodeid会更改)
投票数为n/2+1是为了防止脑裂,
如何进行选主
候选节点根据可以联系上的节点的id进行排序,选出id最小的进行投票,当超过半数的候选节点认为它可以称为主节点,并且自己也认为自己可以为主节点时,就确认它为主节点;否则进行重新选举
什么时候触发选主:
- 集群启动
- Master 失效
非master节点检测到master失效后,发起重新选主,大多数非master节点检测master是否失效,大多数认为失效,就开始重新选主
- X之后的ES,采用一种新的选主算法,基于Raft 的实现
什么节点可以参与选主:
候选节点(node.master:true)具有选举权和被选举权,其他节点没有
脑裂问题
一个集群,有多个master,各自提供服务;比如发生网络分区就容易出现脑裂
脑裂问题的原因:(主节点失去响应)
- 网络问题:集群间的网络延迟或网络分区导致一些节点访问不到Master,认为 Master 挂掉了从而选举出新的 Master,并对 Master 上的分片和副本标红,分配新的主分片。
- 节点负载:主节点的角色既为 Master 又为 Data,访问量较大时可能会导致 ES 停止响应(假死状态)造成大面积延迟,此时其他节点得不到主节点的响应认为主节点挂掉了,会重新选取主节点。
- 内存回收:主节点的角色既为 Master 又为 Data,当 Data 节点上的 ES 进程占用的内存较大,引发 JVM 的大规模内存回收,造成 ES 进程失去响应。
处理脑裂问题:
- 调大响应超时时间:参数 zen.ping_timeout 设置节点状态的响应时间,默认为 3s,可以适当调大。
- 设置参与选举最少候选节点数:配置文件中设置参数zen.munimum_master_nodes 的值,官方建议取值(master_eligibel_nodes/2)+1;即超过一半的候选节点参与选举才能开始选举
- 角色分离。即是上面我们提到的候选主节点和数据节点进行角色分离,这样可以减轻主节点的负担,防止主节点的假死状态发生
es分片的选主
分片决策过程在主节点完成,
选数据最新的副本分片为主分片,每个分片都有一个id,集群的元数据维护了一个活跃的副本id列表,最活跃的且没有挂掉的就会选为主分片
es数据同步:
集群状态数据:
集群状态信息通过主节点进行修改,然后发给其他所有节点
其他节点可能不会保留完整的状态数据,只保留自己要用的。
分片及副本数据:
写入操作(增加、删除)先在主分片上执行,执行成功,之后会把写操作推送到相应的副本分片进行同步。这个同步过程主要是基于主分片与副本分片之间的直接通信和数据复制完成的。不会用到translog文件
es集群扩容:
当你的集群扩容或缩小,Elasticsearch将会自动在你的节点间迁移分片,以使集群保持平衡。
在新增的机器的elasticsearch.yml文件中配置上 集群host列表,集群名字配置成一样即可加入集群。它通过host列表会与一个节点沟通进而找到主节点
document数据写入过程
1)客户端指定索引向一个es节点发出写入请求,这个节点就扮演了协调节点的角色
2)该节点根据路由字段和集群的分片信息确定主分片位置,将请求转发给该主分片;
3)主分片处理请求(没有id的话会分配文档id),处理成功了在从primary shard同时发送给replica shard(异步的)
4)主分片把处理结果反馈给协调节点,协调节点响应结果给客户端
主分片错误的情况:
主分片写入失败,不会再把请求发送给副本分片,整个请求被认为处理失败
主分片或副本分片写入失败都不会重试,会报告给主节点,主节点设置该分片不可用
副本分片错误的情况:
部分副本分片处理失败,整体处理结果还是返回的成功
主分片所在节点将发送一个shardFailed请求给Master。
然后Master会更新集群状态,在新的集群状态中问题分片副本的状态将被更新,不能对外提供搜索服务
segment file:
段(Segment):索引中最小的独立存储单元。一个索引文件由一个或者多个段组成。在 Luence 中的段有不变性,也就是说段一旦生成,在其上只能有读操作,不能有写操作。一个Segment有多个document
commit操作:
默认每隔30分钟会自动执行一次commit,但是如果translog过大,也会触发commit。
整个commit的过程,叫做flush操作。我们也可以通过es api,手动执行flush操作
translog日志文件:
一旦此时机器宕机,再次重启的时候,es会自动读取translog日志文件中的数据,恢复到内存buffer和os cache中去。
translog其实也是先写入os cache的,默认每隔5秒刷一次到磁盘中去,所以默认情况下,可能有5秒的数据会仅仅停留在buffer或者os cache如果此时机器挂了,会丢失5秒钟的数据。但是这样性能比较好,最多丢5秒的数据。也可以将translog设置成每次写操作必须是直接fsync(同步)到磁盘,但是性能会差很多。
对refresh时间的设置:
默认是1秒一次把buffer的数据刷新到segment file放在内存(OSCache),提供搜索
对index_buffer的设置:
可以设置百分比或者具体大小,默认jvm内存的10%
对translog的配置:
可以配置translog刷新到磁盘的频率,默认是5秒
可以配置translog的大小,达到指定大小就会刷新到磁盘
document数据查询过程
一个查询需要进过两个阶段才能完成,分别是query和fetch。
- query(查询阶段)
查询的数据主要有文档id及相关度分数
客户端的请求被分配到集群中的某个节点该节点作为协调节点,协调节点使用分析器分析,向集群中所有其他的分片(主shard或副本shard)发起查询请求,每个shard会在本地执行查询请求将满足条件的数据id排序字段等信息生成一个排好序的from+size大小的队列。这些分片的数据会返回给协调,协调节点再进行合并排序。
(二)fetch(读取阶段)
协调节点根据文档ID构建一个批量请求并发送到相关分片(只会拉取符合要求的from+size的数据),相关分片会根据需要从_source字段里面获取数据,并将结果返回给协调节点
协调节点将结果组装返回给客户端
es数据恢复
主分片从translog中自我恢复:
它首先会根据上一次停止时的commit point文件把所有已知的segments文件给恢复出来,然后再通过translog文件把上一次commit point之后的所有索引变化包括添加,删除,更新等操作给重放出来。
副本分片恢复:
分为文件拷贝和translog回放:与主分片数据对比差异,有差异的进行文件拷贝,主节点把translog发送给副本分片,副本分片对translog回放
索引恢复条件:
快照备份恢复,节点加入,索引的_open操作
es分片自动分配(rebalance)
当集群重启,或者节点的离开和加入都会触发分片的自动分配,可以配置禁止自动分配;
分片的分配是由主节点负责的。
es机器宕机与重启
集群中一个节点宕机或者网络故障无法通信:
如果该机器上有主分片,将其他机器上的丢失的主分片的副本分片升级为主分片
根据es的延时分配时间延时(默认是1分钟,可以动态配置)
如果延时时间内节点未恢复,将丢失的分片数量在其他机器上补齐(如果有足够的节点数量)
如果延时时间内节点恢复了,丢失的副本将重新分配给该节点
如果超时后正在给其他节点分配副本分片,会停止恢复,再把丢失的副本分配给该节点
es和CAP原则
CAP原则:
一致性(C:Consistency)、
可用性(A:Availability)、
分区容错性(P:Partition Tolerance)
一致性:在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
可用性:在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。好的可用性主要是指系统能够很好的为用户服务,不出现用户操作失败或者访问超时等用户体验不好的情况。就是指在客户端想要访问数据的时候,可以得到响应。
分区容错性:指在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务。
base理论:
基本可用:指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。电商大促时,为了应对访问量激增,部分用户可能会被引导到降级页面,服务层也可能只提供降级服务。这就是损失部分可用性的体现。
软状态:软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。mysql replication的异步复制也是一种体现。
最终一致性 :经过一段时间以后,更新的数据会到达系统中的所有相关节点。这段时间就被称之为最终一致性的时间窗口
分布式系统不可能同时满足,最多只能同时满足其中两项。
CAP原则的精髓就是要么AP,要么CP,要么AC,但是不存在CAP。
由于网络硬件肯定会出现延迟丢包或者系统宕机等问题,所以分区容错性是我们必须需要实现的。所以我们只能在一致性和可用性之间进行权衡
实时证明,大多数都是牺牲了一致性。
比如淘宝抢购,看到还有库存,结算的时候没有库存了,就是牺牲了一致性
比如火车票抢票
一致性的分类:
(1)强一致性
对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性。
(2)弱一致性
如果能容忍后续的部分或者全部访问不到,则是弱一致性。
(3)最终一致性
如果经过一段时间后要求能访问到更新后的数据,则是最终一致性。
为什么CAP不能同时满足锁
es的一致性
es保证的是最终一致性
写一致性:
我们发送增删改操作的时候都可以传一个consistency(一致性)指明我们想要的写一致性是什么,它有三个值:
one:要求我们这个写操作,只要有一个primary shard 是active活跃可用的,就可以执行
all:要求我们的写操作,必须所有的primary shard和replica shard都是活跃的,才可以执行这个写操作
quorum(法定人数):默认的值,要求所有的shard中,必须是大部分shard都是活跃的,可用的,才可以执行这个写操作
主分片和副本分片的一致性:写数据先写主分片,主分片写入成功会发给副本分片写请求,副本分片写入结果反馈给主分片,主分片再反馈给协调节点再反馈给客户端。
主分片写入数据,副本分片还没有写完,查询数据会有差别,所以es实现的是最终一致性
es如何保证存储数据可靠性
写数据时会写translog,如果写入过程中服务器宕机可以使用translog恢复数据
分片副本机制,保证服务器宕机副本分片转为主分片提供服务
es还提供单个索引或集群整体数据备份功能
es会丢失数据吗
如果主分片和副本分片在一台机器上,这台机器宕机,这些数据就会丢失
默认情况下,由于translog日志文件是先写入osCache每隔5秒钟刷到磁盘,所以有5秒的数据会仅仅停留在buffer或者os cache,如果此时机器挂了,会丢失5秒钟的数据。(如果es作为方便文本查询的工具或者日志查询工具不在乎这些丢失,主数据放在更加可靠的数据库里如mysql,hbase)
es并发处理
多个线程对同一个文档读数据修改数据会有并发问题:比如并发读库存修改库存
控制方式:使用乐观锁方式,document设置版本号,修改的时候对比版本号是否一样,不一样就表示被修改过了,根据业务场景看是否重新执行逻辑修改数据;
es中添加document会自动给文档添加一个_version字段,默认是1,对文档进行修改和删除都会给_version加1,也可以用自己的逻辑来维护_version的值(指定version_type=external)。
我们修改的时候把version的参数带上来使用es的默认版本号来进行并发控制
副本分片存在的并发问题:
并发的:主分片修改完会把修改请求并发的发送给副本分片
异步的:发给副本分片的请求是乱序的不一定按照在主分片上执行的顺序
es会比较版本号,如果新版本还没有达到比现在的版本大可以执行,如果旧的版本号过来比如跟原来的一样就不会覆盖新的数据
Es相关度评分
在 Elasticsearch 中, 相关性得分由一个浮点数进行表示,并在搜索结果中通过 _score 参数返回, 默认排序是 _score 降序。
搜索的相关性算分,描述了一个文档和查询语句匹配的程度。 ES会对每个匹配查询条件的结果进行算分_score
查询的权重基于三个因素:词频、逆向文档频率和字段长度归一值。
词频:查询词在该文档中出现的频率。频率越高,权重越高。
逆向文档频率:查询词在所有文档中出现的频率。频率越高,权重越低。可以降低日常使用的高频率词的权重。
字段长度归一值:查询字段的长度。字段长度越长,查询词权重越高,反之越低。
查询参数boost
给查询条件设置boost可以影响相关性评分,boost默认是1,这个查询条件的boost越大权重越高,同样条件分数越高
Elastic Stack
Elastic Stack是有ELK(Elasticsearch、Logstash、Kibana)发展过来的,由于有新的成员Beats的加入,所以就形成了Elastic Stack。
日志分析平台
Logstash和Beats负责数据的收集,Kibana负责结果数据的可视化展现,Elasticsearch作为核心部分用于数据的分布式存储以及索引(提供了高效的数据检索和分析能力)。
Logstash:是一个开源的用于收集,分析和存储日志的工具。
Kibana:可以为 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面
Beats:轻量级的数据采集工具的统称,可以直接把数据发送给Elasticsearch或者通过Logstash发送给Elasticsearch,然后进行后续的数据分析活动。
比如:在需要采集日志数据的 server 上安装 Filebeat,并指定日志目录或日志文件后,Filebeat 就能读取数据,迅速发送到 Logstash 进行解析。
功能
日志管理与分析、安全指标监控、应用性能监控、Web抓取舆情分析
es使用调优建议:
内存设置:jvm最大内存和最小内存设置为一样,避免频繁的内存分配;给文件系统留有足够的内存可以进行缓存索引数据,提高查询效率
路由规则:添加和修改数据可以指定routing字段,也就是自定义路由,这样查询时只会在路由到的分片上查询,而不必在所有分片上查询,提高效率
减少字段:
如果我们的表里有很多的字段,而我们只需要往es库里写入我们需要检索的那几个字段就可以了,对于其他的字段我们可以存到mysql或者说其他的比如Hbase中,hbase的特点是适用于海量数据的在线存储
优化查询返回字段:
减少内存占用(可以使用_source_include或者_source_exclude参数)
尽量使用filter查询:
不需要相关度评分的尽量使用filter查询,还会对查询进行缓存,效率比较高
深度翻页:
避免深度翻页,实在要求,提供滚动翻页,使用游标查询
使用静态映射:
提前把索引的每个字段的数据类型定义好,避免动态映射影响效率及有可能映射的类型不是自己想要的
flush命令基本不需要我们手动操作,但当我们要重启节点或者关闭索引时,最好提前执行以下flush命令作为优化,因为es恢复索引或者重新打开索引时,它必须要先把translog里面的所有操作给恢复,所以也就是说translog越小,recovery恢复操作就越快。
说一下solr
solr是开源的采用Java开发,基于Lucene的全文检索引擎。
使用solr可以快速的对已有的数据做数据分析,结构性查询,全文检索
Solr的优缺点:
优点
对于已存在的数据做全文检索效率高效
缺点
建立索引时,搜索效率下降(solr会产生io阻塞),实时索引搜索效率没有Elasticsearch高。
Solr中涉及的概念
Collection
表示逻辑上的一个完整的索引集合
1.在单节点的solr上,一个core等于一个collection。
2.在solrCloud上,一个collection由分布在不同节点的core组成,但是一个collection仍然为一个逻辑索引,但是这个colletion由不同的core包含不同的shards组成。
Core
core是solr的一个索引库,包含相同字段的多个document,一个solr服务器可以创建多个core,就像mysql服务器可以创建多个数据库一样 ,每个Solr Core可以独立提供索引和查询功能
单机的每个Solr Core对应一个索引
每个core包括连个目录conf和data,用于存放配置文件和数据
core的核心配置文件有两个:solrconfig.xml和schema.xml。
document
document(文档)是Solr对信息处理的基本单位
filed
filed(字段)是Solr中的数据单元。Solr所处理的数据(索引、更新等操作)都是存放在相应的Fied中的,如果把Document当做Java中的一个对象,一个Field则是就是一个成员属性。
Shard
Collection的逻辑分片。一个Collection可以分成多个分片存储(每个分片存储的内容不同),每个Shard被化成一个或者多个replicas(同一个分片的副本内容一样,会放到不同的机器上),通过选举确定哪个是Leader。
数据类型说明
Solr中每个字段都有一个对应的字段类型,比如:float、long、double、date、text,Solr提供了丰富字段类型,同时,我们还可以自定义适合自己的数据类型
solr的使用
Solr对外提供标准的http接口来实现对数据的索引的增加、删除、修改、查询。在 Solr 中,用户通过向部署在servlet 容器中的 Solr Web 应用程序发送 HTTP 请求来启动索引和搜索。Solr 接受请求,确定要使用的适当SolrRequestHandler,然后处理请求。通过 HTTP 以同样的方式返回响应。默认配置返回Solr 的标准 XML 响应,也可以配置Solr 的备用响应格式。
处理中文可以使用IK分词器,(导包,在schema.xml中指定使用IK分词器)
solr的内置服务器:jetty(小型的应用服务器)
搭建sor服务器
应用端使用solrj进行操作:
系统导入solrj的依赖
在要添加到solr服务器的实体类上标上@Field注解,利用httpSolrServer进行增删改查
利用SolrQuery对象来构建查询条件
是否建立索引,是否建立存储判断:
需要用做搜索的就做索引,需要作为结果返回的就做存储
重要的两个配置文件:
solrconfig.xml配置文件主要定义了SOLR的一些处理规则,包括索引数据的存放位置,更新,删除,查询的一些规则配置。
schema.xml文件里面主要定义了索引数据类型,索引字段,字段是否索引,是否存储等信息。
这里在schema.xml中配置的索引结构为

solr查询参数说明
sort参数
sort参数安排以升序(搜索结果asc)或降序(desc)命令。不区分大小写,asc和ASC一样。
start参数
start参数指定查询结果集的偏移量,默认值为0
rows参数
rows参数对查询中的结果进行分页。该参数指定Solr应一次返回客户端的完整结果集中的最大文档数。默认是10
q参数
指定查询条件查询,如Name:张三 AND Address:北京;查询所有使用*:*;又被称为主查询,会计算相关度评分进行正序排序。q参数在每次查询时是必传的
fq(过滤查询)参数
在q查询符合结果中同时是fq查询符合的
fq参数可以在查询指定多次
fl(字段列表)参数
该fl参数将查询响应中包含的信息限制为指定的字段列表。字段必须是stored="true"或docValues="true"
可以将字段列表指定为以空格分隔或以逗号分隔的字段名称列表。
debug参数
如果要返回调试信息,可以指定debug参数
timeAllowed参数
此参数指定搜索完成所允许的时间量(以毫秒为单位)。如果此时间在搜索完成之前到期,则将返回任何部分结果
cache参数
Solr默认缓存所有查询和筛选查询的结果。要禁用结果缓存,请设置cache=false参数
查询实例:
select?q=*:*&rows=6&start=0&rows=6&fq=category:2002&fq=namespace:d&fl=productId+category
查询category为2002,并且namespace为d的文档查询6条,返回productId和category的值
fq对评分的影响:
应为fq是在q查询的基础上做出过滤,所以要想按照分数排序,把需要评分的字段放到q中,不需要评分的放在fq中就行
solr修改数据
添加相同的id自然会覆盖原来的id字段
Solr查询流程
用户输入查询字符串,根据用户的请求类型qt(查询为/select)选择请求处理器RequestHandler,根据用户输入的参数defType来选择一个查询解析器解析用户的查询串(默认使用RequestHander中配置的默认查询解析器),查询解析器解析完以后根据用户输入的参数qf指定的字段进行搜索(默认是所有索引字段),查询到结果以后做一些特殊的处理(fq,sort,start,rows,wt)以后使用响应处理器ResponseWriter返回给用户
solr如何实现搜索的
倒排索引,先抽取文档中词,并建立词与文档id的映射关系,然后查询的时候会根据词去查询文档id,根据文档id查询出文档
solr的索引查询为什么比数据库要快
Solr使用的是Lucene API实现的全文检索。全文检索本质上是查询的索引。而数据库中并不是所有的字段都建立的索引,更何况如果使用like查询时很大的可能是不使用索引,所以使用solr查询时要比查数据库快
Solr原理
Solr是基于Lucene开发的全文检索服务器,而Lucene就是一套实现了全文检索的api,其本质就是一个全文检索的过程。全文检索就是把原始文档根据一定的规则拆分成若干个关键词,然后根据关键词创建索引,当查询时先查询索引找到对应的关键词,并根据关键词找到对应的文档,也就是查询结果,最终把查询结果展示给用户的过程
solr索引的增删改
Solr 索引的实现方法很简单,用 POST 方法向 Solr 服务器发送一个描述 Field 及其内容的 XML 文档,Solr根据xml文档添加、删除、更新索引 。
solr如何分词,新增词和禁用词如何解决
schema.xml文件中配置一个IK分词器,然后域指定分词器为IK
新增词添加到词典配置文件中ext.dic,禁用词添加到禁用词典配置文件中stopword.dic,然后在schema.xml文件中配置禁用词典:<filter class="solr.StopFilterFactory" ignore="true" words="/禁止词文件目录"/>
solr怎么设置搜索结果排名靠前
设置文档中域的boost值,值越高相关性越高,排名就靠前
es中也有boost跟相关性分数有关,也是越大,分数越高
Solr配置文件介绍
solr.xml
solr.xml 文件定义了适用于全部或多个内核的全局配置选项。 可以设置集群,设置分片,设置默认的core,core的host地址,端口等
solrconfig.xml
从整体上对当前core进行了配置,可以配置索引数据的目录,日志存放的目录,lucence版本,solr引用包的位置,索引存储方案,更新、删除、查询的一些规则,字段的最大长度,缓存的大小等
schema.xml
是对单个core进行配置的配置文件
schema.xml主要是对索引的配置
可以配置字段名称,字段类型,字段是否唯一,文档唯一字段,字段是否可以有多个值,是否索引(是否作为搜索条件),是否存储(是否要在搜索结果中返回),定义字段类型,字段类型指定的类,配置分析器,默认查询字段,默认查询多次之间的关系(OR或AND)
solr的并发控制
solr中使用乐观锁控制并发
solr的持久化机制
先写入内存的缓存区,写入大小足够多或定时将数据合并,并进行持久话到磁盘。
solr也会写日志,来避免数据丢失
solr中的缓存使用
Solr 中主要有这三种缓存:
过滤器缓存,用于保存过滤器(fq 参数)和层面搜索的结果
当搜索请求参数中带有参数"ids"时,Solr会去filterCache里查,filterCache里Key是query,值是DocSet,也就是无序的Document id
查询缓存:保存的是有序的DocList
文档缓存:存储显示查询结果时请求的文档字段。
操作系统也会利用文件系统缓存对文档数据进行缓存
Solr优化
1.将所有只用于搜索,不作为结果返回的field的stored设置为false;
2.将不需要被用于搜索的,只作为结果返回的field的indexed设置为false;
3.删除所有不必要的copyField声明为了索引字段的最小化和搜索的效率;
4.将所有的text fields的index都设置成false,然后使用copyField将他们都复制到一个总的text Field上,然后进行搜索。
solr关联查询
solr支持对多个core进行关联查询
如:放在q或fq中
{!join fromIndex=brdgConditionSearch toIndex=bridgeBasics from=brdgrecogID to=brdgrecogId} 条件
不适合做关联查询,查询不灵活而且效率低
Leader重新选举
每个shard进入集群后,会在zookeeper上注册一个序列号,先进序列号越小
正常运行的SolrCloud已产生一个leader(Znode编号最小,比如XXX_node1_0000001),后续的Replica后在leader节点上注册Watcher。当Leader下线时候,即短连接断开,那么Zookeeper上的Znode(比如XXX_node1_0000001)就会被删除。
此时,所有Replica在Leader节点上的watcher就会监控到这一变化,所有的Replica就会进行leader选举,选举的原则依然是判断自己是不是目前注册在/collections/collectionTest/leader_select/shard1/election下的Znode编号最小的那位,是的话就是Leader,否则就是Replica。
如果判断自己是Replica,就会继续在leader的Znode上(这个时候的leader是XXX_node1_0000002)注册watcher,等待leader下线再次触发选举leader。
如果这个时候原先下线的leader上线了会怎么样,它就会被当做新的一个Solr节点注册到Zookeeper上,并获取一个比现有Znode更大的编号,在Leader Znode节点上注册watcher,等待它的选举机会。
Solr集群
当一个系统的索引数据量少的时候是不需要使用SolrCloud的,当索引量很大,搜索请求并发很高,这时需要使用SolrCloud来满足这些需求。
SolrCloud是基于Solr和Zookeeper的分布式搜索方案,它的主要思想是使用Zookeeper作为集群的配置信息中心。
solr高可用
solr的分片也有主分片和副本分片,zookeeper进行提供检测服务并进行故障转移
zookeeper在solr集群中的作用
配置管理:使用zookeeper来管理solr的配置信息,配置变动多有节点都可以感知到
负载均衡:使用zookeeper来进行请求转发

浙公网安备 33010602011771号