一文带你深入了解分布式数据的复制原理!!

news2024/12/28 5:55:58

在分布式数据系统中,复制是一种重要的能力。简单来说,复制就是将数据的副本存储在多个位置,通常是在不同的服务器或节点上。这样做有几个关键的优点:

  • 使得数据与用户在地理上接近(从而减少延迟),例如,在一个全球分布式的系统中,如果用户在美国请求数据,而数据只存储在欧洲的服务器上,那么这个请求可能需要跨越大洋,导致延迟。但是,如果数据的复制品也存储在美国的服务器上,那么这个请求就可以更快地得到响应。
  • 即使系统的一部分出现故障,系统也能继续工作(从而提高可用性)
  • 扩展可以接受读请求的机器数量(从而提高读取吞吐量)

你可以将这个过程想象成是一家连锁餐厅。每家分店(分布式系统的节点)都有一份菜单(数据)。这样,无论客户(用户)去哪家分店,都能得到相同的菜单。即使某家分店因为一些原因(例如装修)暂时关门,客户仍然可以去其他分店。同时,如果菜单有更新,每家分店都会收到新的菜单,以保证信息的同步。

复制的困难之处在于处理复制数据的变更。三种流行的变更复制算法:单领导者(single leader),多领导者(multi leader)和无领导者(leaderless)

我们在复制时要考虑很多问题,使用同步复制还是异步复制?如何处理失败的副本?

当存在多个副本时,如果多个副本包含不一样的数据,就会导致读取的时候产生数据不一致情况,如何确保所有数据都落在了所有的副本上?

单领导者

最常见的解决方案被称为基于领导者的复制,也称为主从复制

  1. 副本之一被指定为领导者(leader),也称为 主库(master) ,首要(primary)。当客户端要向数据库写入时,它必须将请求发送给领导者,领导者会将新数据写入其本地存储。
  2. 其他副本被称为追随者(followers),亦称为只读副本(read replicas),从库slaves),次要( sencondaries),热备(hot-standby) 。每当领导者将新数据写入本地存储时,它也会将数据变更发送给所有的追随者,称之为复制日志(replication log)记录或变更流(change stream)。每个跟随者从领导者拉取日志,并相应更新其本地数据库副本,方法是按照领导者处理的相同顺序应用所有写入。
  3. 当客户想要从数据库中读取数据时,它可以向领导者或追随者查询。 但只有领导者才能接受写操作(从客户端的角度来看从库都是只读的)。
    在这里插入图片描述
    如果对于数据一致性有比较高的要求,领导者需要等所有的追随者返回成功状态,当然,这种操作显而易见,会导致性能的降低

同步复制与异步复制

在这里插入图片描述
同步复制的优点是,从库保证有与主库一致的最新数据副本。

缺点是,如果同步从库没有响应(比如它已经崩溃,或者出现网络故障,或其它任何原因),主库就无法处理写入操作。主库必须阻止所有写入,并等待同步副本再次可用。

实际上还有一种半同步配置(其中一个跟随者是同步的,而其他的则是异步的。如果同步从库变得不可用或缓慢,则使一个异步从库同步),通常情况下,基于领导者的复制都配置为完全异步。 在这种情况下,如果主库失效且不可恢复,则任何尚未复制给从库的写入都会丢失。 这意味着即使已经向客户端确认成功,写入也 不能保证持久。 然而,一个完全异步的配置也有优点:即使所有的从库都落后 了,主库也可以继续处理写入。

我们如何处理新增的从节点呢

设置新从库

有时候需要设置一个新的从库:也许是为了增加副本的数量,或替换失败的节点。核心在于要保证新从节点有主库数据的精确副本,简单的全量主从同步是不满足的,还要考虑如何进行增量数据同步。我们可以简单想一下,通过锁定主库,保证数据静态化,完成同步,但这样会造成集群不可用,显然与实际生产目标违背。

如何确保新的从库拥有主库数据的精确副本?

1、锁定数据库(使其不可用于写入)来使磁盘上的文件保持一致,但是这会违背高可用的目标。

2、不停机更新

  1. 在某个时刻获取主库的一致性快照(如果可能),而不必锁定整个数据库。大多数数据库都具有这个功能,因为它是备份必需的。对于某些场景,可能需要第三方工具,例如MySQL的innobackupex。
  2. 将快照复制到新的从库节点。
  3. 从库连接到主库,并拉取快照之后发生的所有数据变更。这要求快照与主库复制日志中的位置精确关联。该位置有不同的名称:例如,PostgreSQL将其称为日志序列号(log sequence number, LSN),MySQL将其称为二进制日志坐标(binlog coordinates)。
  4. 当从库处理完快照之后积压的数据变更,我们说它赶上了主库,就可以继续处理主库产生的数据变化了。

有些系统中,这种过程是完全自动化的,但有些系统可能还需要管理员手动执行

处理节点宕机

分布式系统中,任何节点都可能宕机,我们肯定是期望,及时偶尔有机器宕机,整个集群仍然能够对外提供稳定服务。那么要如何保证呢

从库失效:追赶恢复

在其本地磁盘上,每个从库记录从主库收到的数据变更。如果从库崩溃并重新启动,或者,如果主库和从库之间的网络暂时中断,则比较容易恢复:从库可以从日志中知道,在发生故障之前处理的最后一个事务。因此,从库可以连接到主库,并请求在从库断开连接时发生的所有数据变更。当应用完所有这些变化后,它就赶上了主库

主库失效:故障转移

主库失效就麻烦很多,需要先将一个从库变为主库,然后告诉客户端换老大了,其他从库需要开始拉取来自新主库的数据变更

  1. 确认主库失效。有很多事情可能会出错:崩溃,停电,网络问题等等。没有万无一失的方法来检测出现了什么问题,所以大多数系统只是简单使用超时(Timeout):节点频繁地相互来回传递消息,并且如果一个节点在一段时间内(例如30秒)没有响应,就认为它挂了。
  2. 选择一个新的主库。这可以通过选举过程(主库由剩余副本以多数选举产生)来完成,或者可以由之前选定的控制器节点(controller node)来指定新的主库。主库的最佳人选通常是拥有旧主库最新数据副本的从库(最小化数据损失)。让所有的节点同意一个新的领导者,是一个共识问题,关注我,后面内容也会讲解。
  3. 重新配置系统以启用新的主库。客户端现在需要将它们的写请求发送给新主库(。如果老领导回来,可能仍然认为自己是主库,没有意识到其他副本已经让它下台了。系统需要确保老领导认可新领导,成为一个从库。

故障转移带来的问题

  • 如果使用异步复制,则新主库可能没有收到老主库宕机前最后的写入操作。在选出新主库后,如果老主库重新加入集群,新主库在此期间可能会收到冲突的写入,那这些写入该如何处理?最常见的解决方案是简单丢弃老主库未复制的写入,这很可能打破客户对于数据持久性的期望。
  • 脑裂(split brain):如果两个主库都可以接受写操作,却没有冲突解决机制,那么数据就可能丢失或损坏。一些系统采取了安全防范措施:当检测到两个主库节点同时存在时会关闭其中一个节点 ,但设计粗糙的机制可能最后会导致两个节点都被关闭
  • 正确设置超时时间,太长数据丢失多,太短容易误判

复制日志的实现

复制日志是支持数据同步的基础,你可能想的很简单,只需要告诉从库,主库执行了什么不就行了。但是思考一下这几个场景

  1. 执行了一个Now()函数,目的是为了获取当前时间,但是主从机器时间不一致

  2. 执行update … where <条件>,依赖于数据库中的现有数据,但数据库在主从出现了使用不同索引情况,导致更新内容不一致

当然,对于上面的问题,我们可以用另一种方法,主库可以用固定的返回值替换任何不确定的函数调用,以便从库获得相同的值。但是由于边缘情况实在太多了,现在通常会选择 其他的复制方法。

基于语句的复制

主库记录下它执行的每个写入请求(语句(statement))并将该语句日志发送给其从库。对于关系数据库来说,这意味着每个 INSERT , UPDATE 或 DELETE 语句都被转发给每个从库,每个从库解析并执行该SQL语句,就像从客户端收到一样。

问题:

  • 执行了一个Now()函数,目的是为了获取当前时间,但是主从机器时间不一致
  • 执行update … where <条件>,依赖于数据库中的现有数据,但数据库在主从出现了使用不同索引情况,导致更新内容不一致
  • 有副作用的语句(例如,触发器,存储过程,用户定义的函数)可能会在每个副本上产生不同的副作用,除非副作用是绝对确定的。

当然,对于上面的问题,我们可以用另一种方法,主库可以用固定的返回值替换任何不确定的函数调用,以便从库获得相同的值。但是由于边缘情况实在太多了,现在通常会选择 其他的复制方法。

传输预写式日志(WAL)

  • 对于日志结构存储引擎(“SSTables和LSM树”),日志是主要的存储位置。日志段在后台压缩,并进行垃圾回收。
  • 对于覆写单个磁盘块的B树,每次修改都会先写入预写式日志(Write Ahead Log,WAL),以便崩溃后索引可以恢复到一个一致的状态。

这种主要缺点是日志记录的数据非常底层:WAL包含哪些磁盘块中的哪些字节发生了更改。这使复制与存储引擎紧密耦合。如果数据库将其存储格式从一个版本更改为另一个版本,通常不可能在主库和从库上运行不同版本的数据库软件。这对运维产生巨大的影响。如果复制协议允许从库使用比主库更新的软件版本,则可以先升级从库,然后执行故障转移,使升级后的节点之一成为新的主库,从而执行数据库软件的零停机升级。如果复制协议不允许版本不匹配(传输WAL经常出现这种情况),则此类升级需要停机。

逻辑日志复制(基于行)

复制和存储引擎使用不同的日志格式,这样可以使复制日志从存储引擎内部分离出来。这种复制日志被称为逻辑日志,以将其与存储引擎的(物理)数据表示区分开来。

由于逻辑日志与存储引擎内部分离,因此可以更容易地保持向后兼容,从而使领导者和跟随者能够运行不同版本的数据库软件甚至不同的存储引擎。

对于外部应用程序来说,逻辑日志格式也更容易解析。如果要将数据库的内容发送到外部系统(如数据仓库),这一点很有用,例如复制到数据仓库进行离线分析,或建立自定义索引和缓存。 这种技术被称为捕获数据变更

基于触发器的复制

在某些情况下需要更多的灵活性。例如,如果只想复制数据的一个子集,或者想从一种数据库复制到另一种数据库,或者如果您需要冲突解决逻辑(“处理写入冲突”),则可能需要将复制移动到应用程序层。一些工具,如Oracle Golden Gate ,可以通过读取数据库日志,使得其他应用程序可以使用数据。另一种方法是使用许多关系数据库自带的功能:触发器和存储过程。基于触发器的复制通常比其他复制方法具有更高的开销,并且比数据库的内置复制更容易出错,也有很多限制,然而由于其灵活性,仍然是很有用的。

复制延迟问题

当应用程序从异步从库读取时,如果从库落后,比如可能由于网络延迟、从库压力大更新数据缓慢等原因,它可能会看到过时的信息。

读己之写
在这里插入图片描述
这种方式的思想一个是记录写操作的用户,用户访问自己修改的数据,就读主库,一个场景就是修改个人主页的信息时,用户修改后,再进行查看自己的个人主页时,采用读主库,这样就不会有读到旧数据的问题

但是假如我们修改的是共享文档,那这种方式就没用了,所以另外一种方式是记录修改时间戳,在写入后比如1s的时间范围内,读主库。但这样也不是很精确,万一从库1s还没有完成同步呢,并且这种方式只对单台机器有效,现在服务都是多台机器,那需要将时间戳存储到共享的元数据中心(比如redis),但这无疑又增加了复杂度

单调读

还有一种问题,因为从库机器同步的效率可能会有差异,所以就可能导致读用户可能已经修改过的内容。先读新从库再读就从库,会以为数据突然“消失”
在这里插入图片描述
实现单调读取的一种方式是确保每个用户总是从同一个副本进行读取(不同的用户可以从不同的副本读取)。例如,可以基于用户ID的散列来选择副本,而不是随机选择副本。但是,如果该副本失败,用户的查询将需要重新路由到另一个副本

多主复制

基于领导者的复制有一个主要的缺点:只有一个主库,而所有的写入都必须通过它。如果出于任何原因(例如和主库之间的网络连接中断)无法连接到主库, 就无法向数据库写入。
在这里插入图片描述
多主复制的优势

1、提升写性能,避免异地多活写延迟

2、容忍数据中心停机

3、容忍网络问题

多主复制有这些优势,但也有一个很大的缺点:两个不同的数据中心可能会同时修改相同的数据,写冲突是必须解决的。需要更大的代价去维护数据一致性

我们下面就要讨论如何尽量去解决写冲突问题

处理写入冲突

在这里插入图片描述
如上图,用户1与用户2分别期望将A改为B,C,此时出现了冲突

同步与异步冲突检测

在多活配置中,两个写入都是成功的,并且在稍后的时间点仅仅异步地检测到冲突。那时要求用户解决冲突可能为时已晚。理论上,可以使冲突检测同步, 即等待写入被复制到所有副本,然后再告诉用户写入成功。但是,将失去多主复制的主要优点:允许每个副本独立接受写入。++如果想要同步冲突检测,那么可以使用单主程序复制。++

避免冲突

处理冲突的最简单的策略就是避免它们:如果应用程序可以++确保特定记录的所有写入都通过同一个领导者,那么冲突就不会发生++。比如在用户可以编辑自己的数据的应用程序中,可以确保来自特定用户的请求始终路由到同一数据中心(最简单的方式比如用户id经过hash计算后取模),并使用该数据中心的领导者进行读写。(局限性也很大,有些情况数据更新并不是针对用户级别,比如统计数据)

保证最终一致

多主库执行语句顺序不一致,导致最终结果状态不同。我们可以寻找一个途径使得所有副本必须在所有变更复制完成时收敛至一个相同的最终值。

现冲突合并解决有多种途径:

  • 给每个写入一个唯一的ID(例如一个时间戳,一个长的随机数,一个UUID或者一个键和值的哈希),挑选最高ID的写入作为胜利者,并丢弃其他写入。如果使用时间戳,这种技术被称为最后写入胜利(LWW, last write wins)。虽然这种方法很流行,但是很容易造成数据丢失
  • 为每个副本分配一个唯一的ID,ID编号更高的写入具有更高的优先级。这种方法也意味着数据丢失。
  • 以某种方式将这些值合并在一起 - 例如,按字母顺序排序,然后连接它们(合并的标题可能类似于“B/C”)。
  • 在保留所有信息的显式数据结构中记录冲突,并编写解决冲突的应用程序代码(也许通过提示用户的方式)。

总结下,无非就是选取最新值、记录操作顺序、提醒用户手动处理

多主复制拓扑结构

在这里插入图片描述
数据循环问题:在圆形和星形拓扑中,写入可能需要在到达所有副本之前通过多个节点。因此,节点需要转发从其他节点收到的数据更改。为了防止无限复制循环,每个节点被赋予一个唯一的标识符,并且在复制日志中,每个写入都被标记了所有已经通过的节点的标识符。当一个节点收到用自己的标识符标记的数据更改时,该数据更改将被忽略,因为节点知道它已经被处理。

循环和星型拓扑的问题是,如果只有一个节点发生故障,则可能会中断其他节点之间的复制消息流,导致它们无法通信,直到节点修复。拓扑结构可以重新配置为在发生故障的节点上工作,但在大多数部署中,这种重新配置必须手动完成。更密集连接的拓扑结构(例如全部到全部)的容错性更好,因为它允许消息沿着不同的路径传播,避免单点故障。

全能拓扑也可能有问题。特别是,一些网络链接可能比其他网络链接更快(例如,由于网络拥塞),结果是一些复制消息可能“超过”其他复制消息(顺序不一致带来问题)
在这里插入图片描述
要正确排序这些事件,可以使用一种称为**版本向量(version vectors)**的技术

无主复制

在一些无领导者的实现中,客户端直接将写入发送到到几个副本中,而另一些情况下,一个协调者(coordinator)节点代表客户端进行写入。但与主库数据库不同,协调员不执行特定的写入顺序。无主复制,我觉得其核心在于保证所配置的“多数节点”返回成功状态响应

当节点故障时写入数据库
在这里插入图片描述
当一个客户端从数据库中读取数据时,它不仅仅发送它的请求到一个副本:读请求也被并行地发送到多个节点。客户可能会从不同的节点获得不同的响应。即来自一个节点的最新值和来自另一个节点的陈旧值。

那么思考一下,如何保证读取的新值呢?

读修复和反熵

读修复:当客户端并行读取多个节点时,它可以检测到任何陈旧的响应。客户端发现副本3具有陈旧值,并将新值写回复制品。这种方法适用于频繁阅读的值。

反熵:一些数据存储具有后台进程,该进程不断查找副本之间的数据差异,并将任何缺少的数据从一个副本复制到另一个副本。与基于领导者的复制中的复制日志不同,此反熵过程不会以任何特定的顺序复制写入,并且在复制数据之前可能会有显着的延迟。就好比业务上,我们常常会遇见一些比如统计数出现错误的问题,常见的做法就是通过一个定时器定期修复嘛,维护最终一致性

Quorum NWR算法

上面的读修复和反熵保证了底层数据的最终一致性,那么我们读的时候如何保证一定读到新值呢?
在这里插入图片描述
如果有n个副本,每个写入必须由w节点确认才能被认为是成功的,并且我们必须至少为每个读取查询r个节点。 (在我们的例子中,$n = 3,w = 2,r = 2$)。只要$w + r> n$,我们期望在读取时获得最新的值,因为r个读取中至少有一个节点是最新的。遵循这些r值,w值的读写称为法定人数(quorum) 的读和写。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

渗透测试--3.1嗅探欺骗攻击

目录 1.中间人攻击 2. 社会工程学攻击 SET使用实例——建立克隆钓鱼网站收集目标凭证 SET工具集之木马欺骗实战反弹链接 后渗透阶段 钓鱼邮件 总结 1.中间人攻击 中间人攻击&#xff08;Man-in-the-middle attack&#xff0c;简称MITM&#xff09;是一种常见的网络攻击…

一文带你完整了解数据的编码!!

数据编码是将数据转换为计算机可读取和处理的二进制格式的过程。在数据存储中&#xff0c;正确的数据编码非常重要&#xff0c;因为它能够保证数据的完整性、可靠性和可读性。 数据编码可以确保数据在存储过程中不会发生错误。通过使用适当的数据编码规则&#xff0c;可以防止…

三十二、自定义镜像

1 、Docker镜像的原理 Docker镜像本质是什么? Docker中一个centos镜像为什么只有200MB&#xff0c;而一个centos操作系统的iso文件要几个G? Docker中一个tomcat镜像为什么有500MB&#xff0c;而一个tomcat安装包只有10多MB? 操作系统组成部分: 计算机组成原理 进程调度子…

OPPO 关停“造芯”业务 ZEKU:一场大胆的尝试的结束

前言 近期&#xff0c;OPPO 关停了其“造芯”业务 ZEKU&#xff0c;结束了其自主研发处理器的尝试。本文将对这一事件进行探讨&#xff0c;分析其背后的原因及其对整个行业的影响。 1. ZEKU的成立与使命 在2019年&#xff0c;OPPO旗下的ZEKU研究所成立&#xff0c;标志着OPP…

Redis缓存穿透、缓存雪崩和缓存击穿

缓存穿透 缓存穿透&#xff0c;是指查询一个缓存和数据库中都没有的数据。正常的使用缓存流程大致是&#xff0c;数据查询先进行缓存查询&#xff0c;如果key不存在或者key已经过期&#xff0c;再对数据库进行查询&#xff0c;并把查询到的对象&#xff0c;放进缓存。如果数据库…

Centos和Windows之间通过主机名实现相互访问

一、业务需求 在内网环境中,我们想直接通过特定的主机名称去访问我们的服务器,而不用去记忆服务器的IP地址,且不想通过nginx等代理工具或域名配置内容来操作。 二、分析 通常我们可以直接在浏览器中输入【localhost】后按下回车键后就可以访问我们的主机80端口的内容了;那…

计算机网络大作业(Wireshark抓包分析)

实验要求 wireshark的深入学习与掌握&#xff0c;如过滤器的使用&#xff0c;归纳方法通过实验阐述ARP的工作原理利用实验结果分析 ICMP 协议的报文结构字段定义基于实验数据深入分析 TCP 协议的连接过程原理&#xff0c;报文的分片等功能从校园网发起向外网中某 Web 服务器的…

基于正点原子电机实验的pid调试助手代码解析(速度环控制)

这里写目录标题 下位机与PID调试助手传输的原理代码讲解(基于正点原子)解析数据接受和数据发送的底层函数数据接受数据帧格式环形数组以及怎么找到它的帧头位置crc校验 数据发送数据上传函数 通过前两节文章&#xff0c;我已经了解了基本的pid算法&#xff0c;现在在完成了电机…

MatrixGate 5.0 性能再升级,加载速度提升三倍!

前言 数据的加载速度在数据处理和分析领域一直是一个挑战&#xff0c;为应对这一挑战&#xff0c;YMatrix 数据库开发了 mxgate 高速数据加载工具。最近&#xff0c;随着 YMatrix 5.0 的 GA&#xff0c;新版 mxgate 与上一版本&#xff08;4.8.1&#xff09;相比&#xff0c;速…

Linux 虚拟机 磁盘扩容

概述 在单台虚拟机上部署了过多服务&#xff0c;导致磁盘使用过度达到98%。 现在扩容提高磁盘容量&#xff0c;增加10G。 现象 df -h df -ih du -s具体步骤 VMware 扩容 关闭虚拟机的情况下执行&#xff0c;类似于生产环境下需要关闭服务器&#xff0c;从而添加硬盘等相关操作…

Liunx基础命令 - rm删除命令

rm命令 – 删除文件或目录 rm命令来自英文单词“remove”的缩写&#xff0c;中文译为“消除”&#xff0c;其功能是用于删除文件或目录&#xff0c;一次可以删除多个文件&#xff0c;或递归删除目录及其内的所有子文件。 rm也是一个很危险的命令&#xff0c;使用的时候要特别…

从BinDiff到0day 在IE中利用CVE-2019-1208

前言 如上所述&#xff0c;CVE-2019-1208是UAF漏洞&#xff0c;这类安全漏洞可以破坏有效数据、引发进程crash、并且可以精心利用最终导致任意代码执行。而对于本文介绍的CVE-2019-1208而言&#xff0c;成功利用此漏洞的攻击者可以获得系统当前用户权限。如果当前用户具有admi…

理解Java虚拟机——JVM

目录 一、初识JVM 二、JVM执行流程 三、内存区域划分&#xff08;JVM运行时数据区&#xff09; 3.1 本地方法栈&#xff08;线程私有&#xff09; 3.2 程序计数器&#xff08;线程私有&#xff0c;无并发问题&#xff09; 3.3 JVM虚拟机栈&#xff08;线程私有&#xff0…

最全的分布式事务详情,它来啰~

我们首先得理解什么是分布式事务呢&#xff1f;分布式事务是指在分布式系统中&#xff0c;涉及多个计算机或服务器的操作序列&#xff0c;这些操作需要满足一致性和可靠性的要求。每个操作要么全部成功执行&#xff0c;要么全部回滚&#xff0c;以保持数据的一致性和完整性。 …

嵌入式通信协议【Modbus】Modbus TCP的帧格式

一、请求帧格式 Client request:例&#xff1a; 19 B2 00 00 00 06 06 03 00 27 00 02 1、头字节 上面是modbus客户端发出的报文内容&#xff0c;为modbus tcp/ip协议格式&#xff0c;其前面的六个字节为头字节( header handle)&#xff1b; 19 B2 00 00 00 06 19 B2 00 00…

3D点云之语义分割(相关官方示例介绍)

之前在博客中提到&#xff0c;会考虑用深度学习来对3D点云进行处理&#xff0c;接下来迈出脚步&#xff0c;先整几个例子来熟悉它。例子原型来源于官网&#xff0c;博主在其基础上做了一些代码修改。 一. 例子参考 1. Keras中的资源 Code examples 2.openvinotoolkit open_…

20230515在亚马逊Amazon扣费之后的申诉

20230515在亚马逊Amazon扣费之后的申诉 2023/5/15 22:56 缘起&#xff1a;使用Amazon的12个月的免费存储桶&#xff0c;然后调用S3功能翻译&#xff01; 但是&#xff0c;被扣费了&#xff01; 由于绑定的信用卡是工行的&#xff0c;要求和亚马逊解除绑定&#xff0c;工行给了一…

第十章创建模式—外观模式

文章目录 外观模式解决的问题概念结构 案例优缺点使用场景Tomcat 源码 结构型模式描述如何将类或对象按某种布局组成更大的结构&#xff0c;有以下两种&#xff1a; 类结构型模式&#xff1a;采用继承机制来组织接口和类。对象结构型模式&#xff1a;釆用组合或聚合来组合对象。…

GEE:无监督聚类算法(wekaKMeans)

作者:CSDN @ _养乐多_ 本文将介绍如何使用 Google Earth Engine(GEE)进行卫星图像 K-means 聚类分析的基本步骤,并提供相应的示例代码。 结果如下图所示, 文章目录 一、K-means 原理二、代码详解三、代码链接一、K-means 原理 二、代码详解 var roi = table; Map.cent…

【前后端异常】axios post请求 返回415错误状态码的解决方法

错误描述&#xff1a; 进行有文件的表单提交时出现415错误&#xff0c;以前没遇到过记录一下 415错误的解释是说&#xff0c;服务器无法处理请求附带的媒体格式。以下是HTTP的状态码关于415返回码的说明&#xff1a; 415Unsupported Media Type服务器无法处理请求附带的媒体格…