ElasticSearch-第五天

目录

es中脑裂问题

脑裂定义

脑裂过程分析

解决方案

数据建模

前言

nested object

父子关系数据建模

父子关系 

设置 Mapping

索引父文档

索引子文档

Parent / Child 所支持的查询

使用 has_child 查询

使用 has_parent 查询

使用 parent_id 查询

访问子文档

更新子文档

嵌套对象 v.s 父子文档

文件系统数据建模

根据关键字分页搜索

使用from和size来进行分页

使用scroll方式进行分页


es中脑裂问题

脑裂定义

所谓脑裂问题,就是同一个集群中的不同节点,对于集群的状态有了不一样的理解, 比如集群中存在两个master

如果因为网络的故障,导致一个集群被划分成了两片,每片都有多个node,以及一个 master,那么集群中就出现了两个master了。

但是因为master是集群中非常重要的一个角色,主宰了集群状态的维护,以及shard的 分配, 因此如果有两个master,可能会导致破坏数据。

脑裂过程分析

如:

节点1在启动时被选举为主节点并保存主分片标记为0P,而节点2保存复制分片标记为0R

现在,如果在两个节点之间的通讯中断了,会发生什么?由于网络问题或只是因为其中一个节点无响应,这是有可能发生的。

两个节点都相信对方已经挂了。节点1不需要做什么,因为它本来就被选举为主节点。但是节点2会自动选举它自己为主节点,因为它相信集群的一部分没有主节点了。

在elasticsearch集群,是有主节点来决定将分片平均的分布到节点上的。节点2保存的是复制分片,但它相信主节点不可用了。所以它会自动提升Node2节点为主节点。

现在我们的集群在一个不一致的状态了。打在节点1上的索引请求会将索引数据分配在主节点,同时打在节点2的请求会将索引数据放在分片上。在这种情况下,分片的两份数据分开了,如果不做一个全量的重索引很难对它们进行重排序。在更坏的情况下,一个对集群无感知的索引客户端(例如,使用REST接口的),这个问题非常透明难以发现,无论哪个节点被命中索引请求仍然在每次都会成功完成。问题只有在搜索数据时才会被隐约发现:取决于搜索请求命中了哪个节点,结果都会不同。

那么那个参数的作用,就是告诉es直到有足够的master候选节点时,才可以选举出一个master,否则就不要选举出一个master。这个参数必须被设置为集群中master候选节点的quorum数量,也就是大多数。至于quorum的算法,就是:master候选节点数量 / 2 + 1。

比如我们有10个节点,都能维护数据,也可以是master候选节点,那么quorum就是10 / 2 + 1 = 6。

如果我们有三个master候选节点,还有100个数据节点,那么quorum就是3 / 2 + 1 = 2

如果我们有2个节点,都可以是master候选节点,那么quorum是2 / 2 + 1 = 2。此时就有问题了,因为如果一个node挂掉了,那么剩下一个master候选节点,是无法满足quorum数量的,也就无法选举出新的master,集群就彻底挂掉了。此时就只能将这个参数设置为1,但是这就无法阻止脑裂的发生了。

2个节点,discovery.zen.minimum_master_nodes分别设置成2和1会怎么样

综上所述,一个生产环境的es集群,至少要有3个节点,同时将这个参数设置为quorum,也就是2。discovery.zen.minimum_master_nodes设置为2,如何避免脑裂呢?

解决方案

(1)如果master是单独的那个节点,另外2个节点是master候选节点,那么此时那个单独的master节点因为没有指定数量的候选master node在自己当前所在的集群内,因此就会取消当前master的角色,尝试重新选举,但是无法选举成功。然后另外一个网络区域内的node因为无法连接到master,就会发起重新选举,因为有两个master候选节点,满足了quorum,因此可以成功选举出一个master。此时集群中就会还是只有一个master。

(2)如果master和另外一个node在一个网络区域内,然后一个node单独在一个网络区域内。那么此时那个单独的node因为连接不上master,会尝试发起选举,但是因为master候选节点数量不到quorum,因此无法选举出master。而另外一个网络区域内,原先的那个master还会继续工作。这也可以保证集群内只有一个master节点。

综上所述,集群中master节点的数量至少3台,三台主节点通过在elasticsearch.yml中配置discovery.zen.minimum_master_nodes: 2,就可以避免脑裂问题的产生。

数据建模

前言

案例:设计一个用户document数据类型,其中包含一个地址数据的数组,这种设计方式相对复杂,但是在管理数据时,更加的灵活。

就是数据字段包括省,城市,街道信息

PUT /user_index
{
"mappings": {
"properties": {
"login_name" : {
"type" : "keyword"
},
"age " : {
"type" : "short"
},
"address" : {
"properties": {
"province" : {
"type" : "keyword"
},
"city" : {
"type" : "keyword"
},
"street" : {
"type" : "keyword"
}
}
}
}
}
}

但是上述的数据建模有其明显的缺陷,就是针对地址数据做数据搜索的时候,经常会搜索出不必要的数据,如:在下述数据环境中,搜索一个province为北京,city为天津的用户。

PUT /user_index/_doc/1
{
"login_name" : "jack",
"age" : 25,
"address" : [
{
"province" : "北京",
"city" : "北京",
"street" : "枫林三路"
},
{
"province" : "天津",
"city" : "天津",
"street" : "华夏路"
}
]
}
PUT /user_index/_doc/2
{
"login_name" : "rose",
"age" : 21,
"address" : [
{
"province" : "河北",
"city" : "廊坊",
"street" : "燕郊经济开发区"
},
{
"province" : "天津",
"city" : "天津",
"street" : "华夏路"
}
]
}

执行的搜索应该如下:

GET /user_index/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"address.province": "北京"
}
},
{
"match": {
"address.city": "天津"
}
}
]
}
}
} 

但是得到的结果并不准确,这个时候就需要使用nested object来定义数据建模。

他这里为什么认为不准确呢:因为你查询的数据不在一个集合里面,北京在第一个集合,天津在第二个集合里面,其实不大符合搜索要求

nested object

使用nested object作为地址数组的集体类型,可以解决上述问题,document模型如下:

PUT / user_index {
	"mappings": {
		"properties": {
			"login_name": {
				"type": "keyword"
			},
			"age": {
				"type": "short"
			},
			"address": {
				"type": "nested",
				"properties": {
					"province": {
						"type": "keyword"
					},
					"city": {
						"type": "keyword"
					},
					"street": {
						"type": "keyword"
					}
				}
			}
		}
	}
}

这个时候就需要使用nested对应的搜索语法来执行搜索了,语法如下:

GET / user_index / _search {
	"query": {
		"bool": {
			"must": [{
				"nested": {
					"path": "address",
					"query": {
						"bool": {
							"must": [{
									"match": {
										"address.province": "北京"
									}
								},
								{
									"match": {
										"address.city": "天津"
									}
								}
							]
						}
					}
				}
			}]
		}
	}
}

虽然语法变的复杂了,但是在数据的读写操作上都不会有错误发生,是推荐的设计方式。

其原因是:

普通的数组数据在ES中会被扁平化处理,处理方式如下:(如果字段需要分词,会将分词数据保存在对应的字段位置,当然应该是一个倒排索引,这里只是一个直观的案例)

{
  "login_name" : "jack",
  "address.province" : [ "北京", "天津" ],
  "address.city" : [ "北京", "天津" ]
  "address.street" : [ "枫林三路", "华夏路" ]
}

那么nested object数据类型ES在保存的时候不会有扁平化处理,保存方式如下:所以在搜索的时候一定会有需要的搜索结果。

{
  "login_name" : "jack"
}
{
  "address.province" : "北京",
  "address.city" : "北京",
  "address.street" : "枫林三路"
}
{
  "address.province" : "天津",
  "address.city" : "天津",
  "address.street" : "华夏路",
}

父子关系数据建模

nested object的建模,有个不好的地方,就是采取的是类似冗余数据的方式,将多个数据都放在一起了,维护成本就比较高

每次更新,需要重新索引整个对象(包括跟对象和嵌套对象)

ES 提供了类似关系型数据库中 Join 的实现。使用 Join 数据类型实现,可以通过 Parent / Child 的关系,从而分离两个对象

父文档和子文档是两个独立的文档

更新父文档无需重新索引整个子文档。子文档被新增,更改和删除也不会影响到父文档和其他子文档。

要点:父子关系元数据映射,用于确保查询时候的高性能,但是有一个限制,就是父子数据必须存在于一个shard中

父子关系数据存在一个shard中,而且还有映射其关联关系的元数据,那么搜索父子关系数据的时候,不用跨分片,一个分片本地自己就搞定了,性能当然高

父子关系 

  • 定义父子关系的几个步骤
    • 设置索引的 Mapping
    • 索引父文档
    • 索引子文档
    • 按需查询文档

设置 Mapping

DELETE my_blogs# 设定 Parent / Child Mapping
PUT my_blogs {

	"mappings": {
		"properties": {
			"blog_comments_relation": {
				"type": "join",
				"relations": {
					"blog": "comment"
				}
			},
			"content": {
				"type": "text"
			},
			"title": {
				"type": "keyword"
			}
		}
	}
}

索引父文档

PUT my_blogs / _doc / blog1 {
	"title": "Learning Elasticsearch",
	"content": "learning ELK is happy",
	"blog_comments_relation": {
		"name": "blog"
	}
}

PUT my_blogs / _doc / blog2 {
	"title": "Learning Hadoop",
	"content": "learning Hadoop",
	"blog_comments_relation": {
		"name": "blog"
	}
}

索引子文档

  • 父文档和子文档必须存在相同的分片上
    • 确保查询 join 的性能
  • 当指定文档时候,必须指定它的父文档 ID
    • 使用 route 参数来保证,分配到相同的分片

#索引子文档

PUT my_blogs / _doc / comment1 ? routing = blog1 {
	"comment": "I am learning ELK",
	"username": "Jack",
	"blog_comments_relation": {
		"name": "comment",
		"parent": "blog1"
	}
}

PUT my_blogs / _doc / comment2 ? routing = blog2 {
	"comment": "I like Hadoop!!!!!",
	"username": "Jack",
	"blog_comments_relation": {
		"name": "comment",
		"parent": "blog2"
	}
}

PUT my_blogs / _doc / comment3 ? routing = blog2 {
	"comment": "Hello Hadoop",
	"username": "Bob",
	"blog_comments_relation": {
		"name": "comment",
		"parent": "blog2"
	}
}

Parent / Child 所支持的查询

  • 查询所有文档
  • Parent Id 查询
  • Has Child 查询
  • Has Parent 查询
#
查询所有文档
POST my_blogs / _search {}

#
根据父文档ID查看
GET my_blogs / _doc / blog2

# Parent Id 查询
POST my_blogs / _search {
	"query": {
		"parent_id": {
			"type": "comment",
			"id": "blog2"
		}
	}
}

#
Has Child 查询, 返回父文档
POST my_blogs / _search {
	"query": {
		"has_child": {
			"type": "comment",
			"query": {
				"match": {
					"username": "Jack"
				}
			}
		}
	}
}

#
Has Parent 查询, 返回相关的子文档
POST my_blogs / _search {
	"query": {
		"has_parent": {
			"parent_type": "blog",
			"query": {
				"match": {
					"title": "Learning Hadoop"
				}
			}
		}
	}
}

使用 has_child 查询

  • 回父文档
  • 通过对子文档进行查询
    • 返回具体相关子文档的父文档
    • 父子文档在相同的分片上,因此 Join 效率高

使用 has_parent 查询

  • 返回相关性的子文档
  • 通过对父文档进行查询
    • 返回相关的子文档

使用 parent_id 查询

  • 返回所有相关子文档
  • 通过对付文档 Id 进行查询
    • 返回所有相关的子文档

访问子文档

  • 需指定父文档 routing 参数

#通过ID ,访问子文档 GET my_blogs/_doc/comment2 #通过ID和routing ,访问子文档 GET my_blogs/_doc/comment3?routing=blog2

更新子文档

  • 更新子文档不会影响到父文档

#更新子文档

PUT my_blogs / _doc / comment3 ? routing = blog2 {
	"comment": "Hello Hadoop??",
	"blog_comments_relation": {
		"name": "comment",
		"parent": "blog2"
	}
}

嵌套对象 v.s 父子文档

Nested Object Parent / Child

优点:文档存储在一起,读取性能高、父子文档可以独立更新

缺点:更新嵌套的子文档时,需要更新整个文档、需要额外的内存去维护关系。读取性能相对差

适用场景子文档偶尔更新,以查询为主、子文档更新频繁

文件系统数据建模

思考一下,github中可以使用代码片段来实现数据搜索。这是如何实现的?

在github中也使用了ES来实现数据的全文搜索。其ES中有一个记录代码内容的索引,大致数据内容如下:

{
  "fileName" : "HelloWorld.java",
  "authName" : "baiqi",
  "authID" : 110,
  "productName" : "first-java",
  "path" : "/com/baiqi/first",
  "content" : "package com.baiqi.first; public class HelloWorld { //code... }"
}

我们可以在github中通过代码的片段来实现数据的搜索。也可以使用其他条件实现数据搜索。但是,如果需要使用文件路径搜索内容应该如何实现?这个时候需要为其中的字段path定义一个特殊的分词器。具体如下:

PUT / codes {
	"settings": {
		"analysis": {
			"analyzer": {
				"path_analyzer": {
					"tokenizer": "path_hierarchy"
				}
			}
		}
	},
	"mappings": {
		"properties": {
			"fileName": {
				"type": "keyword"
			},
			"authName": {
				"type": "text",
				"analyzer": "standard",
				"fields": {
					"keyword": {
						"type": "keyword"
					}
				}
			},
			"authID": {
				"type": "long"
			},
			"productName": {
				"type": "text",
				"analyzer": "standard",
				"fields": {
					"keyword": {
						"type": "keyword"
					}
				}
			},
			"path": {
				"type": "text",
				"analyzer": "path_analyzer",
				"fields": {
					"keyword": {
						"type": "keyword"
					}
				}
			},
			"content": {
				"type": "text",
				"analyzer": "standard"
			}
		}
	}
}

PUT / codes / _doc / 1 {
	"fileName": "HelloWorld.java",
	"authName": "baiqi",
	"authID": 110,
	"productName": "first-java",
	"path": "/com/baiqi/first",
	"content": "package com.baiqi.first; public class HelloWorld { // some code... }"
}

GET / codes / _search {
	"query": {
		"match": {
			"path": "/com"
		}
	}
}

GET / codes / _analyze {
	"text": "/a/b/c/d",
	"field": "path"
}

############################################################################################################
PUT / codes {
	"settings": {
		"analysis": {
			"analyzer": {
				"path_analyzer": {
					"tokenizer": "path_hierarchy"
				}
			}
		}
	},
	"mappings": {
		"properties": {
			"fileName": {
				"type": "keyword"
			},
			"authName": {
				"type": "text",
				"analyzer": "standard",
				"fields": {
					"keyword": {
						"type": "keyword"
					}
				}
			},
			"authID": {
				"type": "long"
			},
			"productName": {
				"type": "text",
				"analyzer": "standard",
				"fields": {
					"keyword": {
						"type": "keyword"
					}
				}
			},
			"path": {
				"type": "text",
				"analyzer": "path_analyzer",
				"fields": {
					"keyword": {
						"type": "text",
						"analyzer": "standard"
					}
				}
			},
			"content": {
				"type": "text",
				"analyzer": "standard"
			}
		}
	}
}

GET / codes / _search {
	"query": {
		"match": {
			"path.keyword": "/com"
		}
	}
}

GET / codes / _search {
	"query": {
		"bool": {
			"should": [{
					"match": {
						"path": "/com"
					}
				},
				{
					"match": {
						"path.keyword": "/com/baiqi"
					}
				}
			]
		}
	}
}

参考文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-pathhierarchy-tokenizer.html

根据关键字分页搜索

在存在大量数据时,一般我们进行查询都需要进行分页查询。例如:我们指定页码、并指定每页显示多少条数据,然后Elasticsearch返回对应页码的数据。

使用from和size来进行分页

在执行查询时,可以指定from(从第几条数据开始查起)和size(每页返回多少条)数据,就可以轻松完成分页。

l from = (page – 1) * size

POST / es_db / _doc / _search {
	"from": 0,
	"size": 2,
	"query": {
		"match": {
			"address": "广州天河"
		}
	}
}

使用scroll方式进行分页

前面使用from和size方式,查询在1W条数据以内都是OK的,但如果数据比较多的时候,会出现性能问题。Elasticsearch做了一个限制,不允许查询的是10000条以后的数据。如果要查询1W条以后的数据,需要使用Elasticsearch中提供的scroll游标来查询。

在进行大量分页时,每次分页都需要将要查询的数据进行重新排序,这样非常浪费性能。使用scroll是将要用的数据一次性排序好,然后分批取出。性能要比from + size好得多。使用scroll查询后,排序后的数据会保持一定的时间,后续的分页查询都从该快照取数据即可。

第一次使用scroll分页查询

此处,我们让排序的数据保持1分钟,所以设置scroll为1m

GET / es_db / _search ? scroll = 1 m {
	"query": {
		"multi_match": {
			"query": "广州长沙张三",
			"fields": ["address", "name"]
		}
	},
	"size": 100
}

执行后,我们注意到,在响应结果中有一项:

"_scroll_id": "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAZEWY2VQZXBia1JTVkdhTWkwSl9GaUYtQQ=="

后续,我们需要根据这个_scroll_id来进行查询

第二次直接使用scroll id进行查询

GET _search/scroll?scroll=1m
{
"scroll_id":"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAZoWY2VQZXBia1JTVkdhTWkwSl9GaUYtQQ=="
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/1587.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

学习 Python 之 Pygame 开发魂斗罗(一)

学习 Python 之 Pygame 开发魂斗罗(一)Pygame回忆Pygame1. 使用pygame创建窗口2. 设置窗口背景颜色3. 获取窗口中的事件4. 在窗口中展示图片(1). pygame中的直角坐标系(2). 展示图片(3). 给部分区域设置颜色5. 在窗口中显示文字6. 播放音乐7. 图片翻转与…

人脸活体检测系统(Python+YOLOv5深度学习模型+清新界面)

摘要:人脸活体检测系统利用视觉方法检测人脸活体对象,区分常见虚假人脸,以便后续人脸识别,提供系统界面记录活体与虚假人脸检测结果。本文详细介绍基于YOLOv5深度学习技术的人脸活体检测系统,在介绍算法原理的同时&…

【C++】用手搓的红黑树手搓set和map

目录 一、set/map的底层结构 1、set/map的源码 2、利用模板区分set/map 3、利用仿函数控制比较大小 二、set/map的迭代器(红黑树的迭代器) 1、红黑树的begin、end迭代器 2、红黑树迭代器的operator 3、红黑树迭代器的operator-- 三、set的const…

人工智能大模型之ChatGPT原理解析

前言 近几个月ChatGPT爆火出圈,一路狂飙;它功能十分强大,不仅能回答各种各样的问题,还可以信写作,给程序找bug…我经过一段时间的深度使用后,十分汗颜,"智障对话"体验相比&#xff0c…

Spring-Data-Redis 和 Redisson TLS/SSL 连接

先决条件已经部署好redis tls环境。如未部署好,可参考:Redis 6.0 Docker容器使用SSL/TLS已知redis tls环境使用的证书:其中:ca.crt :服务器证书ca.key:服务器私钥redis.crt:客户端证书redis.key:客户端私钥证书处理生成证书p12文件…

Linux环境C语言开发基础

C语言是一门面向过程的计算机编程语言,与C、C#、Java等面向对象编程语言有所不同。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、仅产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。C语言诞生于美国的贝尔实验室,由丹…

信创办公–基于WPS的PPT最佳实践系列(表格和图标常用动画)

信创办公–基于WPS的PPT最佳实践系列(表格和图标常用动画) 目录应用背景操作步骤图表常用动画效果:擦除效果表格常用动画效果:轮子效果应用背景 文不如表,表不如图。在平时用ppt做总结时,我们会经常用到图…

手撕数据结构与算法——树(三指针描述一棵树)

🏆作者主页:king&南星 🎄专栏链接:数据结构 🏅文章目录🌱树一、🌲概念与定义二、🌳定义与预备三、🌴创建结点函数四、🍀查找五、🍁插入六、&a…

SpringBoot接口 - 如何生成接口文档之Swagger技术栈

SpringBoot开发Restful接口,有什么API规范吗?如何快速生成API文档呢?Swagger 是一个用于生成、描述和调用 RESTful 接口的 Web 服务。通俗的来讲,Swagger 就是将项目中所有(想要暴露的)接口展现在页面上&am…

我的创作纪念日——一年的时间可以改变很多

机缘 不知不觉来到CSDN已经创作一年了。打心底讲,对于在CSDN开始坚持创作的原因我用一句话来概括最合适不过了——“无心插柳柳成荫” 为什么这么说呢? 这要从我的一篇博客说起——《输入命令Javac报错详解》: 那也是我第一次接触到Java这…

PostMan工具的使用

PostMan工具的使用 1 PostMan简介 代码编写完后,我们要想测试,只需要打开浏览器直接输入地址发送请求即可。发送的是GET请求可以直接使用浏览器,但是如果要发送的是POST请求呢? 如果要求发送的是post请求,我们就得准备页面在页…

基于OpenCV的传统视觉应用 -- OpenCV图像处理 图像模糊处理 图像锐化处理

图像处理 图像处理是用计算机对图像进行分析,以获取所需结果的过程,又称为影像处理。图像处理一般是指数字图像的处理。数字图像是用工业相机、摄像机、扫描仪等设备经过拍摄得到的一个大的二维数组,该数组的元素称为像素,其值称…

C++造轮子飙车现场之无锁、有锁环形队列实现

先看带锁的实现。 带锁版本 circular_queue.h // 头文件防卫 #ifndef CIRCULAR_QUEUE_H #define CIRCULAR_QUEUE_H#include <mutex> // 互斥量 #include <condition_variable> // 条件变量template <typename T> class CircularQueue { public:// 构造函数…

公司测试员用例写得乱七八糟,测试总监制定了这份《测试用例编写规范》

统一测试用例编写的规范&#xff0c;为测试设计人员提供测试用例编写的指导&#xff0c;提高编写的测试用例的可读性&#xff0c;可执行性、合理性。为测试执行人员更好执行测试&#xff0c;提高测试效率&#xff0c;最终提高公司整个产品的质量。 一、范围 适用于集成测试用…

vue3 项目搭建教程(基于create-vue,vite,Vite + Vue)

vue3 项目搭建教程&#xff08;基于create-vue&#xff0c;vite&#xff0c;Vite Vue&#xff09; 目录 一、搭建vue3 项目前提条件 二、通过create-vue搭建vue3 项目 三、搭建一个 Vite 项目 四、构建一个 Vite Vue 项目 五、打开Vue 项目管理器 六、Vite Vue 项目目…

云开发--实现发送邮件+短信+链接跳转小程序功能

目录 1、小程序实现发送邮件 准备一个qq邮箱&#xff0c;并启动SMTP服务 确定小程序云开发环境&#xff0c;并新建云函数 2、小程序实现发送短信 确定应用 确定签名 确定模板 编写云函数-发送短信 3、链接跳转小程序 H5 配置 生成 URL Link 学习记录&#xff1a; …

【洛谷刷题】蓝桥杯专题突破-深度优先搜索-dfs(4)

目录 写在前面&#xff1a; 题目&#xff1a;P1149 [NOIP2008 提高组] 火柴棒等式 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述&#xff1a; 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 解题思路&#xff1a; …

Java进阶2 排序查找与Lambda、正则表达式

排序查找与Lambda、正则表达式● 导图一、 基础算法1.排序1.1 冒泡排序1.2 选择排序2. 查找2.1 基础查找2.2 二分查找二、Lambda表达式1&#xff09;初识Lambda2&#xff09;函数式编程3&#xff09;.Lambda表达式的标准格式4&#xff09;Lambda的注意事项5&#xff09;Lambda表…

k8s 1.18.20版本部署

身为k8s初学者&#xff0c;在掌握k8s理论知识的同时&#xff0c;也需要掌握一下实际部署k8s的过程&#xff0c;对于理论的学习起到一定的帮助作用。罗列了一下相关步骤&#xff0c;请各位参考&#xff1a; 一、环境准备 三台虚机&#xff1a; 操作系统&#xff1a; CentOS L…

【计算机组成原理 - 第二章】系统总线(完结)

本章参考王道考研相关课程&#xff1a; 【2019版】6.1.1 总线的概念与分类_哔哩哔哩_bilibili 【2019版】6.1.2 总线的性能指标_哔哩哔哩_bilibili 【2019版】6.2 总线仲裁_哔哩哔哩_bilibili 【2019版】6.3 总线操作和定时_哔哩哔哩_bilibili 【2019版】6.4 总线标准_哔哩哔哩…