elasticsearch基础概念和集群概念
前言
思考一个问题:当系统数据量上了10亿、100亿条的时候,我们在做系统架构的时候通常会从以下角度去考虑问题:
- 用什么数据库好?(mysql、sybase、oracle、达梦、神通、mongodb、hbase…)
- 如何解决单点故障;(lvs、F5、A10、Zookeep、MQ)
- 如何保证数据安全性;(热备、冷备、异地多活)
- 如何解决检索难题;(数据库代理中间件:mysql-proxy、Cobar、MaxScale等;)
- 如何解决统计分析问题;(离线、近实时)
- 如何快速进行检索?
安全性问题
可以通过主从备份(关系型数据库)或者副本备份(非关系型数据库)解决数据安全性问题
单点故障
关系型通过数据库代理中间件心跳监测,解决单点故障问题,非关系型数据库通过节点竞选机制解决单点问题;
解决检索和统计难题
关系型通过代理中间件将查询语句分发到各个slave节点进行查询,并汇总结果,非关系型数据库先从配置库检索分片信息,然后将请求分发到各个节点,最后由路由节点合并汇总结果
如何快速进行检索
mysql为了提高检索速度可以采用分表分库,建立索引等操作,但在大数据、文档全文检索环境下,mysql这些花里胡哨的操作根本就收效甚微,redis速度固然快,但redis是内存级的,面对PB级大数据环境下,内存则是显得那么无力。
ES基础概念
ES基础概念和ES的基础命令我们都会对比大家都已经滚瓜烂熟的mysql来进行讲解,但es集群其实和kafka非常相似,我们会参考kafka来进行对比;
是一个基于Lucene构建的开源、分布式、RESTful接口的全文搜索引擎。Elasticsearch还是一个分布式文档数据库,其中每个字段均可被索引,而且每个字段的数据均可被搜索,ES能够横向扩展至数以百计的服务器存储以及处理PB级的数据。可以在极短的时间内存储、搜索和分析大量的数据。通常作为具有复杂搜索场景情况下的核心发动机。
是为高可用和可扩展而生的。可以通过购置性能更强的服务器或者升级硬件来完成系统扩展,称为垂直或向上扩展(Vertical Scale/Scaling Up)。另一方面,增加更多的服务器来完成系统扩展,称为水平扩展或者向外扩展(Horizontal Scale/Scaling Out)。尽管ES能够利用更强劲的硬件,垂直扩展毕竟还是有它的极限。真正的可扩展性来自于水平扩展,通过向集群中添加更多的节点来分担负载,增加可靠性。ES天生就是分布式的:它知道如何管理多个节点来完成扩展和实现高可用性。这也意味你的应用不需要做任何的改动。
Lucene只是一个库。想要使用它,你必须使用Java来作为开发语言并将其直接集成到你的应用中,更糟糕的是,Lucene非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的。Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
Index
index可以类比为mysql数据库里的一张表,是数据组织和存储的最基本单位之一,创建一个索引的命令如下
PUT products
{}
这样只是创建了一个空索引,没有对索引的结构进行约束,这样后续可能会插入一定的脏数据导致数据也会逐渐无序,所以标准场景方式应该如下
PUT products
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"id": { "type": "keyword" },
"title": { "type": "text" },
"brand": { "type": "keyword" },
"category":{ "type": "keyword" },
"desc": { "type": "text" }
}
}
}
也就是限定了索引的字段内容和格式,预防后续数据集逐步被无序数据污染,这样就比较类似于mysql的建表sql语句了
插入数据的过程也是类似于mysql中的insert
PUT products/_doc/1
{
"id": "1",
"title": "苹果 iPhone 15 手机",
"brand": "Apple",
"category": "phone",
"desc": "最新款苹果手机,支持5G,全网通"
}
PUT products/_doc/2
{
"id": "2",
"title": "Nike 跑步鞋",
"brand": "Nike",
"category": "shoes",
"desc": "耐克男士跑步运动鞋,舒适透气"
}
Setting
这个概念类似于mysql表级存储/索引参数的设置,setting中可以设置的是
- number_of_shards(数据水平切分成多少个“逻辑分片”)
- number_of_replicas(每个分片多少副本)
- refresh_interval(相当于“多长时间把内存里的改动刷成可查询”)
- analysis:定义分词器,相当于 TEXT 字段用什么全文索引规则
- filter:定义过滤器,相当于 TEXT 字段用什么全文索引规则
定义过滤器
{
"filter": {
"brand_synonym_filter": {
"type": "synonym",
"synonyms": [
"苹果,apple"
]
}
}
定义分词器
{
"analyzer": {
"product_search_analyzer": {
"type": "custom",
"tokenizer": "ik_max_word",
"filter": [
"lowercase",
"brand_synonym_filter"
]
}
}
然后在mapping中给某个text类型字段绑定,比如title字段,那么这个字段中如果出现苹果或apple,分词的时候都会同时分出来两个词即苹果和apple(详细案例请看下面ES实战部分)
{
"title": {
"type": "text",
"analyzer": "product_search_analyzer",
"search_analyzer": "product_search_analyzer",
"fields": {
"raw": {
"type": "keyword"
}
}
}
}
映射Mapping
mapping就完全类似于mysql中建表sql中对字段名、字段类型、以及主键、外键、唯一性、非空约束,mapping也可以指定字段名、字段类型、绑定分词器
- 字段类型除了通用的int,float,double,long,char以外,还有es特殊的类型text和keywork,keyword和通用类似不会被分词,text类型字段会被分词
- 可以给text类型字段绑定专属分词器,通过分词器和字段类型来做具体的分词工作,然后通过这些分词来完成倒排索引的建立
倒排索引
其性质和mysql创建的正排索引一样,都是为了加速查询,而倒排索引一定程度上也是弥补了正排索引的性能缺陷,比如正派索引非常不擅长的全模糊查询就非常适合用倒排索引来做,深入理解倒排索引推荐参考:终于有人把es原理讲透了
DOC
一个文档是一个可被索引的基础信息单元,也就是一条数据,类似于mysql的一行表数据
比如:你可以拥有某一个客户的文档,某一个产品的一个文档,当然,也可以拥有某个订单的一个文档。文档以 JSON(Javascript Object Notation)格式来表示,而 JSON 是一个到处存在的互联网数据交互格式。在一个 index/里面,你可以存储任意多的文档。类似于mysql的数据行
字段Field
相当于是数据表的字段,对文档数据根据不同属性进行的分类标识
ES实战案例
假如我们要做一个电商网站,商品字段有id、title、brand、category、desc,而且要求某些词汇要中英文通用,比如有如下
Apple iPhone 15 Pro Max 手机 256GB 钛金属
要求无论输入apple还是苹果做查询关键词都要能检索出来这个文本,流程如下
1、索引mapping和setting内容如下
自定义过滤规则,把苹果和apple设为同义词,自定义分词器product_search_analyzer,通过该分词器分词时,会把apple这个单词从文本中分出来,同时对apple和苹果这两个单词做倒排索引,这样你用苹果做查询关键词,也能搜索出这条文本了
PUT products
{
{
"settings": {
"analysis": {
"filter": {
"brand_synonym_filter": {
"type": "synonym",
"synonyms": [
"苹果,apple"
]
}
},
"analyzer": {
"product_search_analyzer": {
"type": "custom",
"tokenizer": "ik_max_word",
"filter": [
"lowercase",
"brand_synonym_filter"
]
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "product_search_analyzer",
"search_analyzer": "product_search_analyzer",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"brand": {
"type": "keyword"
},
"category": {
"type": "keyword"
},
"desc": {
"type": "text",
"analyzer": "product_search_analyzer",
"search_analyzer": "product_search_analyzer"
}
}
}
}
创建索引文本
{
"id": 1,
"title": "Apple iPhone 15 Pro Max 手机 256GB 钛金属",
"brand": "Apple",
"category": "手机",
"desc": "A17 芯片,支持 5G,全新钛金属边框"
}
es中的查询命令
term
精确查询,等同与mysql中的等值查询
terms
多值查询,类似与mysql中的in
match
是针对 text 字段而设计,类似于mysql中的全模糊查询,比如
GET products/_search
{
"query": {
"match": {
"title": {
"query": "苹果 手机",
"operator": "and"
}
}
}
}
等价于mysql中的
SELECT * FROM products WHERE title LIKE '%苹果%' and title LIKE '%手机%';
需要注意match中的operator默认值就是or
搜索词会被 分词,然后和索引中的 terms 做匹配。默认是「OR」逻辑,多词匹配越多评分越高
GET products/_search
{
"query": {
"match": {
"title": "苹果 手机"
}
}
}
假设 title 使用中文分词器(比如 IK),查询串 “苹果 手机” → analyzer → tokens:[“苹果”, “手机”],在倒排索引中查含有「苹果」或「手机」的文档,词越多命中的文档,评分越高(相关度更高)
带参数写法
GET products/_search
{
"query": {
"match": {
"title": {
"query": "苹果 手机 256G 钛金属",
"operator": "or",
"minimum_should_match": "50%"
}
}
}
}
这表示:4 个词里至少要匹配 2 个才算命中;匹配得越多,排序越靠前
wildcard
wildcard 查询 不会分词,是对字段原值做匹配,所以适用于 keyword、wildcard 等未分词字段。
搜索前后加 * 的模式(如 abc)会比较慢,大数据量下要谨慎
GET index/_search
{
"query": {
"wildcard": {
"field_name": {
"value": "he*lo?",
"case_insensitive": true
}
}
}
}
exists
exists 查询用来判断:某个字段是否存在且有值,类似于mysql中的exists和in
WHERE col IS NOT NULL
multi_match
multi_match 是 Elasticsearch 里在「多个字段上一起做全文搜索」的查询,等价于在多个字段上分别做 match,再把结果合并 + 打分,比如同时对title和desc通过关键字苹果和手机做查询,而且title权重*3倍,即title上每命中一次得分✖️3
GET products/_search
{
"query": {
"multi_match": {
"query": "苹果 手机",
"fields": ["title^3", "desc"]
}
}
}
组合查询bool
类似于mysql中的where语句的复杂查询
{
"query": {
"bool": {
"must": [ ... ], // 等价于 AND(有评分)
"filter": [ ... ], // AND(不算分,过滤用)
"should": [ ... ], // OR
"must_not": [ ... ] // NOT
}
}
}
{
"bool": {
"must": [
{ "match": { "title": "苹果 手机" } },
{ "term": { "brand": "Apple" } }
]
}
}
类似于
WHERE title MATCH '苹果 手机' AND brand = 'Apple'
{
"bool": {
"should": [
{ "match": { "title": "苹果 手机" } },
{ "match": { "title": "apple iphone" } }
],
"minimum_should_match": 1
}
}
类似于
WHERE title MATCH '苹果 手机' OR brand = 'Apple'
{
"bool": {
"must": [
{ "match": { "title": "手机" } }
],
"must_not": [
{ "term": { "brand": "Apple" } }
]
}
}
类似于
WHERE title MATCH '手机' AND brand <> 'Apple'
ES评分机制
如果是做模糊查询的情况,需要评分,如果只是过滤,那么不需要参与评分,比如
什么时候需要评分
GET products/_search
{
"query": {
"bool": {
"must": [
{ // 这是全文检索,**要评分**
"multi_match": {
"query": "苹果手机",
"fields": ["title^3", "brand^2", "desc"]
}
}
],
"filter": [
{ "term": { "status": "online" } }, // 上架状态:只过滤,不评分
{ "term": { "category": "phone" } } // 类目过滤:只过滤,不评分
]
}
}
}
multi_match 会触发 BM25,给出每个商品的 _score;
排名基本由 _score 决定(再可能叠加销量、时间等业务因素);
status、category 是“资格条件”,只要满足就行,不用靠它们区分先后。
ES的评分机制
ES的评分机制是BM25,注意有
- TF词频,一个词在文档中出现越多,代表该文档和这个词更相关,得分越高,但不是线性增长,会有“收益递减”:出现 2 次比 1 次相关,但 20 次 vs 21 次差别很小
- IDF(逆文档频率),出现在“很多文档里的词” → 区分度低 → IDF 低,得分贡献小,出现在“少数文档里的词” → 区分度高 → IDF 高,得分贡献大
- 文档长度归一,非常长的文档里,任何词出现的次数都容易多,所以要对长度做归一化:
短文档里出现一次,往往更“核心”;超长文档里出现一次,可能只是顺带提到
评分机制对es倒排索引结构的思考与反推
倒排索引结构能够支持es低成本获取词频吗?答案是可以的,倒排索引天生就非常适合低成本获取词频,而且是多种粒度的“词频”
ES(底层 Lucene)的倒排索引结构可以理解为这种格式,也就是在生成倒排索引的时候会顺便把词语在索引中出现的总次数和在某个文档中出现的次数全都统计出来,这样能更好的支持词频和你文档频率评分算法,结构如下
"苹果" ->
docID: 1, freq: 3, positions: [5, 20, 50]
docID: 7, freq: 1, positions: [10]
docID: 15, freq: 2, positions: [3, 8]
...
向量数据库的评分机制
向量检索的 _score 和文本 BM25 完全不是一套逻辑,本质就是把向量数据库中和向量值的相识度较高的向量进行一个评分,其中在向量空间里,“相似度”通常用 3 种度量之一,余弦相似度(cosine)、点积(dot_product / inner product)、欧式距离(l2_norm / L2),距离越小越相似,评分也越高
ES向量数据库
向量检索可以解决什么问题
向量检索 = 把文本编码成向量(Embedding),然后做“向量之间的相似度搜索”虽然es中可以支持近义词配置,比如iphone和苹果手机,但这只能 针对可以提前预判并提前配置的场景有效,而生产环境必然是更加复杂和多样性的,只有向量搜索可以应对客户的模糊性搜索和说明性搜索,例如夏天通勤的鞋子、便宜一点的苹果手机,学生用性价比高的 5G 手机,这一类问题,显然只靠es中的分词和分词配置的同义词是安全无法实现的
向量检索底层原理
把“东西”(文本、图片、用户、商品等)转成向量,然后在大量向量中,快速找到“和给定向量最相似”的那些,向量可以理解为一个高维特征坐标,实际上常见是 128、384、768、1024 维等,维度越高搜索越精准,相似的东西 → 向量距离近
- 苹果手机”和“iPhone 15”的向量很近
- 跑步鞋”和“运动鞋”的向量很近
我们把数据文本转换为向量,存入向量数据库,向量查询的时候,
向量数据库完成RGA增强检索
用向量数据库做 RAG,流程主要有三步
1、把本地知识库的文档信息变成向量存起来
这涉及到切分文档,每个文本块转换成向量、存到向量数据库,例如比如做一个机场智能客服系统,机场知识库里有订票流程说明、退票 / 改签规则、特殊旅客(儿童、孕妇、军人)政策、常见问题 FAQ 文档、航空公司 / 机场联合发布的公告,其中的文档
【退票规则】
1. 正常退票:
- 起飞前24小时以上退票,收取票面价的5%作为手续费。
- 起飞前24小时内退票,收取票面价的10%作为手续费。
- 起飞后不办理退票。
2. 航班取消:
- 因航空公司原因导致航班取消或延误超过2小时,旅客可免费退票或改签。
3. 特殊旅客(儿童、婴儿票):
- 儿童票退票规则同成人票;
- 婴儿票如未使用,可以免费退票。
我们定义的切片规则是
- 每段 200–500 字;
- 按自然段 / 标题分块
切片的目的是
- 一块内容不要太长(方便模型处理)
- 又要尽量保持语义完整(不要把一条规则拆得七零八落)
# 切片后得出类似这样的知识块
chunk_1:
"正常退票:起飞前24小时以上退票,收取票面价的5%作为手续费;起飞前24小时内退票,收取票面价的10%作为手续费;起飞后不办理退票。"
chunk_2:
"航班取消:因航空公司原因导致航班取消或延误超过2小时,旅客可免费退票或改签。"
chunk_3:
"儿童票退票规则同成人票;婴儿票如未使用,可以免费退票。"
...
chunk_N:
"网上订票成功后,旅客需在航班起飞前45分钟完成值机手续..."
把这些切块后的数据转换为向量存入向量数据库
2、用户问问题时,把提问也变成向量
比如,用户提问航班因为天气原因取消了,我退票要收手续费吗,在向量库里做“语义最近邻搜索,那么搜索结果可能是
chunk_2:
"航班取消:因航空公司原因导致航班取消或延误超过2小时,旅客可免费退票或改签。"
chunk_1:
正常退票:起飞前24小时以上退票,收取票面价的5%作为手续费;起飞前24小时内退票,收取票面价的10%作为手续费;起飞后不办理退票。
实际开发中可能搜到成本上千个语义相近的chunk,所以通常我们会取前topK个语意最接近的词语拼成一个 Prompt,喂给大模型,完整的Prompt如下
系统指令:
你是某机场的智能客服,只能根据给定的“官方规则文本”回答问题。
如果在规则中找不到答案,要明确说明“不确定”或“规则未明确”。
知识库片段:
[1] "航班取消:因航空公司原因导致航班取消或延误超过2小时,旅客可免费退票或改签。"
[2] "正常退票:起飞前24小时以上退票,收取票面价的5%作为手续费;起飞前24小时内退票,收取票面价的10%作为手续费;起飞后不办理退票。"
用户问题:
"航班因为天气原因取消了,我退票要收手续费吗?"
请根据知识库片段,回答用户的问题,并简要说明依据。
3、大模型后基于你提供的Prompt生成答案,并把答案展示给用户
此案例大模型生成的回答如下
根据我们的退票规则:
若航班因航空公司原因导致取消或延误超过2小时,旅客可以免费退票或改签,不收取任何退票手续费。
如果您的航班确实是因为天气等航空公司不可控因素取消,您可以申请免费退票。
建议您在原购票渠道或机场服务柜台办理退票手续。
这样一来回答内容是“从你给它的规则文本中归纳、转述”出来的;而不是模型“凭感觉胡编”,真正达到“检索增强生成”:检索控制内容边界,生成负责自然语言组织
企业级数据切片策略
划分文档类型
- 规则、协议、手册类,按标题或章节切片,遇到一级/二级标题就断开,每个标题下的若干段落合并成一个 chunk,控制每个 chunk 的字数不超过某个上限
- FAQ / 问答类:一问一答切成一个chunk
- 业务流程 / 操作说明:按步骤/小节切片
- 日志 / 工单 / 知识库案例:可能每个工单都是一个chunk
- 技术文档 / API 文档:按标题、接口分节
- 半结构化内容(HTML、PDF、富文本):用解析器提取结构(DOM、书签、样式)再按结构切片
实战案例
一、正常退票
1. 起飞前24小时以上退票:收取...
2. 起飞前24小时内退票:收取...
3. 起飞后不办理退票。
二、航班取消或变更
1. 因航空公司原因取消或延误超过2小时:旅客可免费退票或改签。
切片策略:HeadingChunkStrategy(按标题/小节切):
chunk_1: “一、正常退票 + 下方所有细则(约300字)”
chunk_2: “二、航班取消或变更 + 下方所有细则(约300字)
总之,数据怎么切片,不是有一个固定标准,而是要根据企业自己的知识库格式、文档类型和业务目标来灵活定制,把各种样式的长文档切割成可以直接喂给大模型的chunk
Node节点
节点是单个服务器实例,它是群集的一部分,可以存储数据,并参与群集的索引和搜索功能。就像一个集群,节点的名称默认为一个随机的通用唯一标识符(UUID),确定在启动时分配给该节点。如果不希望默认,可以定义任何节点名。这个名字对管理很重要,目的是要确定你的网络服务器对应于你的ElasticSearch群集节点。
我们可以通过群集名配置节点以连接特定的群集。默认情况下,每个节点设置加入名为“elasticSearch”的集群。这意味着如果你启动多个节点在网络上,假设他们能发现彼此都会自动形成和加入一个名为“elasticsearch”的集群。
在单个群集中,您可以拥有尽可能多的节点。此外,如果“elasticsearch”在同一个网络中,没有其他节点正在运行,从单个节点的默认情况下会形成一个新的单节点名为"elasticsearch"的集群。
Cluster 集群
群集是一个或多个节点(服务器)的集合, 这些节点共同保存整个数据,并在所有节点上提供联合索引和搜索功能。一个集群由一个唯一集群ID确定,并指定一个集群名(默认为“elasticsearch”)。该集群名非常重要,因为节点可以通过这个集群名加入群集,一个节点只能是群集的一部分。
确保在不同的环境中不要使用相同的群集名称,否则可能会导致连接错误的群集节点。例如,你可以使用logging-dev、logging-stage、logging-prod分别为开发、阶段产品、生产集群做记录。
Shared分片
索引可以存储大量的数据,这些数据可能超过单个节点的硬件限制。例如,十亿个文件占用磁盘空间1TB的单指标可能不适合对单个节点的磁盘,基本能存储下来,服务仅从单个节点的搜索请求也会速度很慢。
为了解决这一问题,Elasticsearch提供细分你的指标分成多个块称为分片的能力。当你创建一个索引,你可以简单地定义你想要的分片数量。每个分片本身是一个全功能的、独立的“指数”,可以托管在集群中的任何节点。
Shards分片的重要性主要体现在以下两个特征:
- 分片允许您水平拆分或缩放内容的大小
- 分片允许你分配和并行操作的碎片(可能在多个节点上)从而提高性能/吞吐量,这个机制中的碎片是分布式的以及其文件汇总到搜索请求是完全由ElasticSearch管理,对用户来说是透明的。
分片shareds类似于kafka的partition,这两者同时也可以对标mysql的分表分库技术;
Replic副本
在同一个集群网络或云环境上,故障是任何时候都会出现的,拥有一个故障转移机制以防分片和结点因为某些原因离线或消失是非常有用的,并且被强烈推荐。为此,Elasticsearch允许你创建一个或多个拷贝,你的索引分片进入所谓的副本或称作复制品的分片,简称Replicas。
Replicas的重要性主要体现在以下两个特征:
- 副本为分片或节点失败提供了高可用性。为此,需要注意的是,一个副本的分片不会分配在同一个节点作为原始的或主分片,副本是从主分片那里复制过来的。
- 副本允许用户扩展你的搜索量或吞吐量,因为搜索可以在所有副本上并行执行。ES中的shared分片机制和kafka的分区partition机制市一样的,但他们的副本机制有差异
共同点都是副本为分片或节点失败提供了高可用性。但kafka中副本的作用仅限于此了,但ES操作以在所有副本上并行执行,es中副本地位更高
ES分布式集群概念 vs Kafka
es Index VS kafka topic
Index可以类比为mysql的数据库那么topic也依然可以,比如你经验一家网店,那么你可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引。分布式环境下这些业务会位于不同的模块下,甚至部署到不同的服务器上,kafka中我们同样可与将这些不同的业务抽象为不同的主题,比如constomTopic,productTopic,orderTopic来供消费者消费和生产者写入。
es shared VS kafka partition
- 概念层面:es和kafka都是支持分布式环境的 =>Index和topic不会局限于一台服务器,否则在无限的消息堆积中很容易超出单机性能极限,还有单点故障问题,无法保证高可用,无法负载均衡,es和kafka跨分区的基本单位分别就是shared和parition,而且这两者都是一旦创建好就不能再变化了,因为es中依赖通过分区的数目来计算写入的位置,数目上没有严格限制,数量可以大于集群节点数量,也就是一个节点可以有多个分片or分区。
es replica VS kafka replica
- 首先数据都是写入到主分区/主分片后再把数据同步到副本
- 副本的意义主都是保证分布式环境下的高可用HA,但 kafka中副本单纯是做备份或分区后续,es中的副本功能更强大,不单纯是做备份和候选分片,而且也可以参与到es数据查询业务中,所以后续虽然es分片数量后期无法扩展,但我们可以通过扩展副本的形式来增强并发吞吐量
- 如果主分区/主分片挂断,副本都会站出来竞争产生一个新的主分区/主分片,kafka中通过ISR机制,而es中依赖???????
- 副本的数量不能大于节点的数量,大于节点数相当于一个服务器会有针对同一分片/分区两个及以上的数据,这被认为是没有意义的事;
集群数据问题
主分片才能进线增删改查,副本分片可以进行查询,我们的插入请求首先会落到主结点上,主节点会根据路由公式:hash(id) % 分片数量来计算插入到那个结点的分片上,根据计算由结点服务器node1处理插入请去,数据a插入到node1结点的主分片p1,数据插入成功后,p1对应的两个副本r0和r1也会收到同步数据a的请求,副本插入成功后反馈主结点,主节点再响应客户端数据插入成功
- 响应策略可以优化来减少响应时间,比如consistency三个选项
- 如果当前集群只有三个结点服务器的话,那就意味着集群中每台服务器都会有这个结点,查询数据a时,请求首先会落到主分片p1上,此时es服务器采用轮询的负载均衡策略,也就是三台服务器结点依次处理查询请求,大大提高了并发吞吐量
- 查询操作也类似,客户端查询请求到协调节点,协调节点计算数据所在分配以及全部的副本位置,并且轮询他们所在的全部结点,将请求转发到具体结点,结点返回查询结果,在文档被检索时,已经被索引的文档可能已经存在于主分片上但是还没有复制到副本分片。 在这种情况下,副本分片可能会报告文档不存在,但是主分片可能成功返回文档。 一旦索引请求成功返回给用户,文档在主分片和副本分片都是可用的。
集群安全问题
集群健康值:green( 6 of 6 ):表示所有 6 个分片(包括 2 个主分片和 4个副本分片)都在正常运行
刚创建时:
此时模拟副本分片挂掉的情况(关闭node-1003),此时集群健康值: yellow (4 of 6)
此时模拟副本主分片挂掉的情况,如果有主分片挂掉那么集群健康值就为red,此外需要注意,es集群只至少要启动两个及以上节点
这里有个坑,当集群刚配置完毕我们只启动第一台服务器节点时,如下,所有分片集中到这个节点上,后续就不会再出现这种情况了,而是这个集群至少两个节点启动才能正常运行
但此时其它服务器启动失败or启动后没有被纳入该集群中,此时需要通过情况data数据甚至重新建立集群的方式恢复过来
kafka vs es集群的发现机制???
# 9300 端口为 Elasticsearch 集群间组件的通信端口, 9200 端口为浏览器访问的 http协议 RESTful 端口。
如何在elk中精准查到group=kf_开头的全部连接(ngnix-access里)
transport.tcp.port: 9302
discovery.seed_hosts,第一台节点(1001)的话不需要配置,这个主要是告诉后续启动的服务器,他们集群中有哪些其它节点,比如:
node1002配置:discovery.seed_hosts: ["localhost:9301"]
node1003配置:discovery.seed_hosts: ["localhost:9301", "localhost:9302"]
由此集群的启动顺序也是node1001,node1002,node1003
验证:把node1003的服务配置注释掉那么即使启动该服务器也无法被加入到节点中,而且会报错
master not discovered or elected yet, an election requires at least 2 nodes with ids from [fR3pcma8Q2C78mEC-nFPyA, pAorzSQfQ1aZZAfKDS2WUg, di9Va3EXRkm7E6MaOj5sNg],
因为es为了防止某个结点被意外加入到某个集群,那么这个节点不仅要配置同样的集群名,唯一的节点名,还要配discovery.seed_hosts标明和那些节点一个集群
同理,集群正常运行过程中,node1001挂掉,后续又恢复重启,那么如果想在重启的同时被加入到节点中,则需要如下配置
discovery.seed_hosts: ["localhost:9302", "localhost:9303"]
结点挂掉,后续又重启再次被加入到集群的过程中
副本数量超限案例
kafka写操作 vs es写操作
kafka的producor主要按如下操作:
- 指明 partition 的情况下,直接将指明的值直接作为 partiton 值;
- 没有指明 partition 值但有 key 的情况下,将 key 的 hash 值与 topic 的 partition
数进行取余得到 partition 值; - 既没有 partition 值又没有 key 值的情况下,第一次调用时随机生成一个整数(后
面每次调用在这个整数上自增),将这个值与 topic 可用的 partition 总数取余得到 partition
值,也就是常说的 round-robin 算法
es的写操作:
虽然es的写操作会按照公式
shard = hash(routing) % number_of_primary_shards
来确定将data写入到那个分片上,但在写入的请求到达es集群前,es集群还没从request中获取到数据,那么此时自然不知道请求该发往哪个分区,实时上,请求是随机发往es的,率先接收到请求的服务器节点我们称之为协调节点,协调节点会根据计算发现目标服务器上然后把请求转发给目标主分片节点,然后同步到各个副本,然后反馈用户response
kafka读操作 vs es读操作
kafka(消费者)读操作的三种方式:kafka消费者分区的分配的三种机制
es读操作:
协调节点收到请求数据,这里协调阶段不单单是计算请求的主分片位置,而且连同相关副本的位置一同计算出来,然后负载均衡轮询所有节点,请求转发到主分片服务器,然后再由主分片服务器节点转发给用户
注意:无论es还是kafka写操作都是在主分片上面完成之后才能被复制到相关的副本分片。
两者集群对请求的处理
- kafka中请求的处理非常简单,无论是消费者还是生产者都需要绑定一到多个主题来进行消费,这样请求会围绕着主题进行发送,其中有三种方法决定请求发往那个分区。
- 我们可以发送请求到集群中的任一节点。每个节点都有能力处理任意请求。每个节点都知道集群中任一文档位置,所以可以直接将请求转发到需要的节点上。在下面的例子中,如果将所有的请求发送到Node 1001,我们将其称为协调节点coordinating node。当发送请求的时候, 为了扩展负载,更好的做法是轮询集群中所有的节点。我们把这种操作称为分片控制
数据安全策略
在默认设置下,即使仅仅是在试图执行一个写操作之前,主分片都会要求必须要有规定数量quorum的分片副本处于活跃可用状态,才会去执行写操作(其中分片副本 可以是主分片或者副本分片)。这是为了避免在发生网络分区故障的时候进行写操作,进而导致数据不一致。 规定数量即
consistency 参数的值可以设为:
- one :只要主分片状态 ok 就允许执行写操作。
- all:必须要主分片和所有副本分片的状态没问题才允许执行写操作。
- quorum:默认值为quorum , 即大多数的分片副本状态没问题就允许执行写操作
数据一致性问题,和kafka基本一致,都是按数据安全级别由高到低分为三种策略,只不过kafka中的参数值是ack,es中参数值是consistency
ELK是什么?
ELK=elasticsearch+Logstash+kibana
elasticsearch:后台分布式存储以及全文检索
logstash: 日志加工、“搬运工”
kibana:数据可视化展示。
logstash作为日志收集工具收集相关业务日志,存储到es中,es中的数据通过kibana视图工具展示给运维人员,ELK架构为数据分布式存储、可视化查询和日志解析创建了一个功能强大的管理链。 三者相互配合,取长补短,共同完成分布式大数据处理工作。
分析日志的用处:假如一个分布式系统有 1000 台机器,系统出现故障时,我要看下日志,还得一台一台登录上去查看,是不是非常麻烦?
但是如果日志接入了 ELK 系统就不一样。比如系统运行过程中,突然出现了异常,在日志中就能及时反馈,日志进入 ELK 系统中,我们直接在 Kibana 就能看到日志情况。如果再接入一些实时计算模块,还能做实时报警功能。
这都依赖ES强大的反向索引功能,这样我们根据关键字就能查询到关键的错误日志了。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)