【MIT 6.5840(6.824)学习笔记】GFS

1 分布式存储系统难点

在设计大型分布式系统或存储系统时,初衷通常是为了获得显著的性能提升,通过数百台计算机的资源来并行完成大量工作。因此,性能问题成为最初的关注点。一个自然的想法是将数据分片(Sharding),分布到大量服务器上,从而并行读取数据。

当你在成百上千台服务器上进行分片时,服务器故障将成为常态。如果你有数千台服务器,每天甚至每小时都可能有服务器宕机。因此,需要自动化的方法来修复错误,而不是依赖人工介入。为此,自动容错系统至关重要,这引出了容错(fault tolerance)的概念。

实现容错最有效的方法之一是使用数据复制,只需维护2-3个数据副本,当其中一个故障时,可以使用另一个。因此,要实现容错,必须进行数据复制(replication)。

然而,数据复制带来了不一致性(inconsistency)问题。拥有多个数据副本,如果管理不当,副本之间可能不一致。理想情况下,可以任意使用任一副本进行容错,但如果副本不一致,应用程序将面临麻烦。因此,数据复制不可避免地会引发不一致性问题。

通过精巧的设计,可以减少甚至避免不一致性,使数据表现得符合预期。但要实现这一点,服务器之间需要进行额外的网络交互,这会降低性能。因此,如果追求一致性(consistency),必须付出性能的代价,这与最初的高性能目标相悖。

尽管可以构建高性能系统,但不可避免地会陷入性能与一致性的权衡之中。在实际应用中,为了获得良好的一致性,必须付出相应的代价。如果不愿付出代价,就需要忍受一定程度的不确定性。很多系统中都存在这种权衡,人们往往不愿为高一致性牺牲性能。

image-20240528101751499

2 错误的设计

对于强一致性系统,应用程序或客户端感受到的就像在与一台服务器通信。尽管系统由数百台计算机组成,但理想的强一致模型让它看起来像只有一台服务器,一份数据,并且一次只处理一个请求。这种设计确保了每个请求能看到之前所有请求按顺序执行的结果。

对于存储服务器来说,它通常包含一块磁盘。执行写请求可能意味着向磁盘写入数据或对数据进行自增操作。如果是一次修改操作,并且我们有一个以 key-value 为索引的数据表单,那么我们会更新这个表单。如果是读取操作,只需从表单中取出之前写入的数据即可。为了保证这个简单服务的行为可预期,需要遵循一个规则:每次只执行一个请求。这样,每个请求都能看到之前所有请求按顺序执行后的结果。如果服务器按某种顺序依次处理写请求,当你读取数据时,你就能看到预期的数据。

举例来说,客户端 C1 发起写请求将 X 设置为 1,同时客户端 C2 发起写请求将 X 设置为 2。待 C1 和 C2 的写请求都执行完毕后,客户端 C3 发送读取 X 的请求,得到一个结果;客户端 C4 也发送读取 X 的请求,得到另一个结果。此时,问题是这两个客户端看到的结果会是什么。

即使在一个非常简单的系统中,仍会出现一些模糊场景,使你无法确定系统的执行过程及其输出结果。你只能根据结果判断系统是否保持了一致性。

如果 C3 读取 X 得到 2,那么 C4 也应该读取到 2,因为这表明写 X 为 2 的请求是第二个执行的写请求。当 C4 读取 X 时,写 X 为 2 应该仍然是第二个写请求。

然而,单服务器设计存在容错能力差的问题。如果服务器故障或磁盘损坏,系统将无法使用。因此,现实中我们会构建多副本的分布式系统,这引发了一系列新的问题。

假设我们有两台服务器,每台服务器都有数据的一份完整拷贝,并在磁盘上存储一个 key-value 表单。我们希望这两个表单完全一致,这样当一台服务器故障时,可以切换到另一台服务器继续读写操作。

两个表单完全一致意味着,每一个写请求都必须在两台服务器上执行,而读请求只需要在一台服务器上执行,否则就没有容错性了。因为如果读请求也需要从两台服务器读数据,那么一台服务器故障我们就没法提供服务了。现在问题来了,假设客户端C1和C2都想执行写请求,其中一个要写X为1,另一个写X为2。C1会将写X为1的请求发送个两个服务器,因为我们想要更新两台服务器上的数据。C2也会将写X为2的请求发送给两个服务器。

这里会出现什么错误呢?是的,我们没有做任何事情来保障两台服务器以相同的顺序处理这2个请求。如果服务器1(S1)先处理C1的请求,那么在它的表单里面,X先是1,之后S1看到了来自C2的请求,会将自己表单中的X覆盖成2。但是,如果S2恰好以不同的顺序收到客户端请求,那么S2会先执行C2的请求,将X设置为2,之后收到C1的请求,将X设置为1。

image-20240528104959963

之后,如果另外一些客户端,假设C3从S1读数据,C4从S2读数据,我们就会面临一个可怕的场景:这两个客户端读取的数据不一样。但是从前一个例子中的简单模型可以看出,相连的读请求应该读出相同的数据。

这里的问题可以以另一种方式暴露出来。假设我们尝试修复上面的问题,我们让客户端在S1还在线的时候,只从S1读取数据,S1不在线了再从S2读取数据。这样最开始所有的客户端读X都能得到2。但是突然,如果S1故障了,尽管没有写请求将X改成1,客户端读X得到的数据将会从2变成1。因为S1故障之后,所有的客户端都会切换到S2去读数据。这种数据的神奇变化与任何写操作都没有关联,并且也不可能在前一个例子的简单模型中发生。

当然,这里的问题是可以修复的,修复需要服务器之间更多的通信,并且复杂度也会提升。由于获取强一致会带来不可避免的复杂性的提升,有大量的方法可以在好的一致性和一些小瑕疵行为之间追求一个平衡。

3 GFS设计目标

Google的目标是构建一个大型且快速的文件系统(GFS),以便各种应用程序都能全局访问数据。传统的方法是为每个应用程序构建特定的存储系统,但这会导致重复建设。GFS 作为一个全局通用的存储系统,允许不同应用程序共享和访问数据。例如,存储了大量互联网抓取数据后,其他用户可以通过申请权限查看这些数据,因为大家使用的是同一个存储系统。这样,Google 内部的人员可以根据名字读取 GFS 中可共享的内容。

为了实现大容量和高速性能,GFS 将数据文件自动分割并存储在多台服务器上,这样可以并行读取同一个文件,从而获得更高的聚合吞吐量。文件分割存储还允许存储比单个磁盘更大的文件。由于存储系统分布在数百台服务器上,GFS 具备自动故障修复功能,不需要人工干预来修复服务器或迁移数据。

GFS 的一些特征并非设计目标。例如,GFS 只在一个数据中心内运行,多个副本并未分布在全球各地。理论上,数据副本应该地理分散,但实现起来很难,所以 GFS 局限于单个数据中心内。

此外,GFS 面向 Google 内部使用,供工程师开发应用程序,并不直接面向普通用户。虽然 Google 可能会出售基于 GFS 的服务,但 GFS 本身并不对外提供。

最后,GFS 专注于对大型顺序文件的读写优化。例如,银行账户系统需要能够读写小数据块的数据库,而 GFS 针对 TB 级别的文件进行优化,只支持顺序处理而非随机访问。某种程度上,它更像批处理系统,注重巨大的吞吐量而非低延迟,每次操作都涉及 MB 级别的数据。

4 Master节点

假设我们有上百个客户端和一个Master节点。虽然实际中可以有多台机器作为Master节点,但GFS中Master采用Active-Standby模式(系统包含两个或多个实例,其中一个实例(Active)正在运行并处理所有的请求,而其他实例(Standby)则处于待命状态,准备在Active实例发生故障时立即接管工作。),所以只有一个Master节点在工作。Master节点保存了文件名和存储位置的对应关系。除此之外,还有大量的Chunk服务器,每个Chunk服务器上都有1-2块磁盘。

Master节点管理文件和Chunk的信息,而Chunk服务器存储实际数据。这种设计将管理和存储分开处理,提高了系统效率。在GFS中,Master节点知道每个文件对应的所有Chunk handle,这些Chunk每个是64MB大小,共同构成一个文件。例如,一个1GB的文件会分成多个Chunk,Master节点知道每个Chunk存储在哪。读取文件时,需要先向Master节点查询Chunk位置,然后从对应的Chunk服务器读取数据。

我们需要了解Master节点内保存的数据内容,这里我们关心的主要是两个表单:

  1. 文件名到Chunk handle的对应关系:Master节点有一个表单记录了文件名到Chunk handle数组的对应关系。
  2. Chunk handle到Chunk数据的对应关系:另一个表单记录了Chunk handle和它们的数据的对应关系,包括每个Chunk的服务器列表、当前版本号、主Chunk(Primary Chunk)和租约过期时间。

这些数据都存储在内存中,但为了防止数据丢失,Master节点也将部分数据存储在磁盘上。Master节点的写操作会记录到磁盘的日志(log)中,并定期生成检查点(CheckPoint)。

有些数据需要存在磁盘上,而有些不用。它们分别是:

  • Chunk Handle数组(非易失性,NV):保存到磁盘上。
  • Chunk服务器列表(易失性,V):不用写入磁盘,重启后可重新获取。
  • 版本号(非易失性,NV):写入磁盘,确保数据一致性。
  • 主Chunk的handle(易失性,V):不写入磁盘,重启后可重新分配。
  • 租约过期时间(易失性,V):不写入磁盘。

当文件扩展到新的64MB或主Chunk变更时,Master节点会向磁盘日志中追加记录。这种日志追加方式比数据库高效,因为它只需顺序写入,不涉及磁盘的随机访问。

Master节点故障重启时,会从最近的检查点开始恢复状态,然后通过执行日志中的记录恢复到最新状态。这种方式避免了从日志最开始重建状态的低效问题。

5 读文件

对于读请求来说,应用程序或GFS客户端会提供一个文件名和读取的偏移量(offset)。首先,客户端将这些信息发送给Master节点。Master节点从自己的文件表单中查找文件名,获取对应的Chunk handle数组。每个Chunk大小为64MB,因此可以通过偏移量除以64MB来确定对应的Chunk handle。接着,Master节点从Chunk表单中找到包含该Chunk的服务器列表,并将这个列表返回给客户端。

具体步骤如下:

  1. 客户端(或应用程序)将文件名和偏移量发送给Master节点。
  2. Master节点将Chunk Handle(即Chunk ID)和服务器列表发送给客户端。

客户端接下来可以从服务器列表中选择一个服务器来读取数据。根据GFS论文的描述,客户端会选择一个在网络上最近的服务器(在Google的数据中心中,通过IP地址的差异可以判断网络位置的远近),然后将读请求发送到这个服务器。由于客户端每次可能只读取1MB或64KB的数据,它可能会多次读取同一个Chunk的不同部分。为此,客户端会缓存Chunk和服务器的对应关系,这样在后续读取相同Chunk数据时,不需要每次都向Master请求相同的信息。

接下来,客户端会与选定的Chunk服务器通信,将Chunk Handle和偏移量发送给该服务器。Chunk服务器在本地硬盘上将每个Chunk存储为独立的Linux文件,并通过普通的Linux文件系统进行管理。可以推测,Chunk文件会按照Handle(即ID)命名。因此,Chunk服务器需要做的就是根据文件名找到对应的Chunk文件,从文件中读取相应的数据段,并将数据返回给客户端。

6 写文件

对于应用程序来说,写文件的过程与读文件的接口非常类似,都是通过调用GFS的库函数进行操作。在写文件时,应用程序会告诉库函数,要将缓冲区中的数据追加到指定文件中。为了简化讨论,我们只考虑GFS论文中的记录追加(Record Append)的情况。

所以再次描述一下,对于写文件,客户端会向Master节点发送请求说:我想向这个文件名对应的文件追加数据,请告诉我文件中最后一个Chunk的位置。

当有多个客户端同时写同一个文件时,一个客户端并不能知道文件究竟有多长。因为如果只有一个客户端在写文件,客户端自己可以记录文件长度,而多个客户端时,一个客户端没法知道其他客户端写了多少。例如,不同客户端写同一份日志文件,没有一个客户端会知道文件究竟有多长,因此也就不知道该往什么样的偏移量,或者说向哪个Chunk去追加数据。这个时候,客户端可以向Master节点查询哪个Chunk服务器保存了文件的最后一个Chunk。

对于读操作,可以从任何最新的Chunk副本读取数据,但写操作必须通过Chunk的主副本(Primary Chunk)进行。Master节点需要确保Chunk的主副本存在。如果不存在,Master节点会查找所有存有该Chunk最新副本的Chunk服务器。Master节点确定哪些副本是最新的(副本中保存的版本号与Master中记录的Chunk的版本号一致),并从中选择一个作为Primary,其余作为Secondary。

之后,Master节点增加Chunk的版本号,并将新的版本号写入磁盘。Master节点通知Primary和Secondary服务器新的Chunk版本号,并指定它们的角色。Primary和Secondary服务器将新版本号存储在本地磁盘中,以便在重启时报告给Master。

所以客户端将要追加的数据发送给Primary和Secondary服务器,这些服务器将数据写入临时位置。所以最开始,这些数据不会追加到文件中。当所有服务器确认数据已写入临时位置后,客户端向Primary发送消息,要求将数据追加到文件中。Primary按照顺序处理来自多个客户端的并发请求,确保每次只执行一个请求。Primary将数据写入Chunk的末尾,并通知所有Secondary服务器也将数据写入它们的Chunk末尾。

Secondary服务器将数据写入本地磁盘后,向Primary发送确认消息。如果所有Secondary服务器成功写入数据并回复“yes”,Primary向客户端返回写入成功。如果任何Secondary服务器写入失败,Primary向客户端返回写入失败。

如果客户端接到写入失败的消息,应重新发起整个追加过程。首先,客户端再次与Master节点交互,找到文件末尾的Chunk,然后重新向Primary和Secondary发起追加操作。

7 GFS的一致性

在GFS中,追加数据的过程相对复杂。我们通过一个例子来解释这一过程。

  1. **数据追加请求:**客户端发送一个追加数据的请求,例如数据A,要将数据A追加到文件末尾。Chunk的三个副本(一个Primary和两个Secondary)都成功将数据A追加到了Chunk中,因此Chunk中的第一个记录是A。

  2. **网络问题导致的部分写入:**第二个客户端加入,想要追加数据B。但由于网络问题,消息只被Primary和一个Secondary收到并处理。因此,两个副本追加了数据B,而另一个副本没有。

  3. **后续写入:**第三个客户端想要追加数据C,并且Primary选择了偏移量并通知Secondary。三个副本都成功追加了数据C。

  4. **处理写入失败:**由于网络问题,第二个客户端会收到写入失败的回复,并重新发起追加数据B的请求。假设这次数据B没有丢包,三个副本都成功追加了数据B。现在,三个副本都在线,并且都有最新的版本号。

  5. **读取数据的影响:**客户端读取文件时,读取的内容取决于读取的是哪个副本。例如:

    • 读取第一个副本时,可能会看到数据A、B、C,然后是重复的B。
    • 读取第三个副本时,可能会看到数据A、一个空白数据、然后是C、B。

    所以不同的读请求可能得到不同的结果,具体取决于读取的是哪个副本。

  6. **处理写入失败的复杂情况:**在最坏情况下,某个Secondary未能成功执行数据追加操作,客户端从Primary收到写入失败的回复。在客户端重新发送写文件请求之前,客户端可能故障,导致数据D只存在于某些副本中,而其他副本完全没有。

在GFS的这种工作方式下,如果Primary返回写入成功,一切正常。如果Primary返回写入失败,不同副本的数据可能不同。GFS的设计简单,但可能会暴露一些奇怪的数据顺序问题。应用程序需要容忍数据乱序,或通过在文件中写入序列号来识别顺序。如果应用程序对数据顺序敏感,可以避免并发写入,例如,电影文件的写入应使用一个客户端顺序追加数据。

4f013eecba1d5a096fdac0325605e35f

如果要将GFS升级为强一致系统,需要考虑以下几点:

  1. **检测重复请求:**Primary需要能够检测重复的请求,确保数据不会重复写入。
  2. **Secondary的强制执行:**Secondary必须执行Primary的请求,而不能简单地返回错误。对于永久性故障的Secondary,需要有机制将其移除。
  3. **两阶段提交:**写请求需要两个阶段:首先Primary向Secondary发出请求并等待确认;如果所有Secondary都确认,Primary再指示实际执行操作。
  4. **处理Primary崩溃:**当Primary崩溃时,新Primary需要与Secondary同步,确保操作历史一致。
  5. **Secondary的租约系统:**Secondary需要一个类似Primary的租约系统,确保在合法时间内响应客户端请求。

总体而言,GFS取得了巨大的成功,许多Google的应用都依赖于它。例如,BigTable和MapReduce等关键基础架构都是构建在GFS之上的,因此GFS在Google内部得到了广泛应用。然而,GFS也有其局限性,最严重的问题在于它只有一个Master节点,这带来了以下几个问题:

  1. **内存限制:**Master节点必须为每个文件和每个Chunk维护表单。随着使用量的增加,文件数量不断上升,最终Master节点会耗尽内存来存储这些表单。虽然可以增加内存,但单台计算机的内存总有上限,这成为了早期遇到的一个显著问题。

  2. **处理能力:**单个Master节点需要处理数千个客户端的请求,而其CPU每秒只能处理数百个请求。尤其当Master节点还需要将部分数据写入磁盘时,这个问题变得更加严重,导致客户端数量很快超过了单个Master的处理能力。

  3. **复杂的语义:**应用程序发现很难处理GFS复杂的语义,特别是副本数据同步问题(或不同步问题),这在一定程度上增加了开发难度。

  4. **故障切换:**从GFS论文中可以了解到,Master节点的故障切换不是自动的。当Master节点永久故障时,需要人工干预来更换新的服务器,这可能需要几十分钟甚至更长时间来处理。对于某些应用程序来说,这样的停机时间是不可接受的。

8 FAQ

  1. 应用程序如何知道Chunk的哪些部分由填充和重复记录组成?
    为了检测填充,应用程序可以在有效记录的开头放置一个可预测的幻数,或者包含一个校验和,该校验和可能仅在记录有效时才有效。应用程序可以通过在记录中包含唯一 ID 来检测重复项。然后,如果它读取的记录与之前的记录具有相同的 ID,它就知道它们是彼此的重复项。 GFS 为处理这些情况的应用程序提供了一个库。 GFS 设计的这一方面有效地将复杂性从 GFS 转移到了应用程序,但这可能并不理想。

  2. 怎样知道一个文件存储在哪台机器上?
    根据master中文件到chunk再到chunk位置的映射来定位具体的chunkserver。

  3. 论文提到了引用计数——它们是什么?
    它们是快照写时复制实现的一部分。当GFS创建快照时,它不会复制块,而是增加每个块的引用计数器。这使得创建快照的成本很低。如果客户端写入一个chunk并且主服务器注意到引用计数大于 1,则主服务器首先创建一个副本,以便客户端可以更新该副本(而不是属于快照一部分的块)。您可以将此视为延迟复制,直到绝对必要为止。希望并非所有块都会被修改,并且可以避免制作一些副本。

  4. 什么是租约?
    对于 GFS,租约是master授予 chunkserver 充当特定 chunk 的主chunkserver的能力的一段时间。master保证在租约期间不会分配不同的主chunkserver,并且主服务器同意在租约到期之前停止充当主chunkserver(除非主chunkserver要求master延长租约)。租约是避免主chunkserver必须反复询问master是否仍然是主chunkserver的一种方法—它知道它可以在下一分钟(或无论租约间隔是多少)充当主chunkserver,而无需再次与master通信。

  5. 什么是内部碎片?为什么惰性分配有帮助?
    内部碎片是当系统使用大于所请求分配所需的分配单元时浪费的空间。如果 GFS 以 64MB 为单位分配磁盘空间,那么一个 1 字节的文件将浪费近 64MB 的磁盘空间。 GFS 通过延迟分配磁盘空间来避免这个问题。每个块都是一个Linux文件,Linux文件系统使用的块大小为几十KB;因此,当应用程序创建一字节 GFS 文件时,该文件的块仅消耗 1 个 Linux 磁盘块,而不是 64 MB

  6. Google 还在使用 GFS 吗?
    有传言称 GFS 已被 Colossus 所取代,总体目标相同,但在主性能和容错性方面有所改进。此外,Google内部的许多应用程序已经转向更多类似数据库的存储系统,例如BigTable和Spanner。然而,GFS 的大部分设计仍然存在于 HDFS(Hadoop 开源 MapReduce 的存储系统)中。

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

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

相关文章

《数字图像处理》笔记/期末复习资料

目录 1 简述二值图像、灰度图像与彩色图像间的区别。 2 图像量化时,如果量化级比较小会出现什么现象?为什么? 3 图像增强的目的是什么? 4 什么是中值滤波,有何特点? 5 叙述高通滤波、低通滤波、带通滤…

区块链技术引领:Web3时代的新网络革命

随着区块链技术的快速发展和不断成熟,人们已经开始意识到它所带来的潜在影响,尤其是在构建一个更加去中心化、安全和透明的互联网时。这个新的互联网时代被称为Web3,它将不再受制于传统的中心化平台,而是更多地依赖于去中心化的网…

Rust最新版安装(v1.78.0+)

系统:Windows 11 专业版 23H2rustc:1.78.0 配置环境变量和设置配置文件 新建文件夹“C:\Rust\Rustup”和“C:\Rust\Cargo”。【以管理员身份运行】打开CMD 设置系统环境变量,如下设置RUSTUP_DIST_SERVER,其余同理 C:\Windows\S…

Golang | Leetcode Golang题解之第103题二叉树的锯齿形层序遍历

题目: 题解: func zigzagLevelOrder(root *TreeNode) (ans [][]int) {if root nil {return}queue : []*TreeNode{root}for level : 0; len(queue) > 0; level {vals : []int{}q : queuequeue nilfor _, node : range q {vals append(vals, node.V…

物联网架构实例—Ubuntu 安装MySQL

1.ubuntu安装mysql apt-get upgrade apt-get update 安装mysql apt-get install mysql-server Y执行安装后,会来到软件包设置界面. 再次确认设置的密码. 开启mysql的服务 service mysql start 确认是否成功开启mysql service mysql status 确认是否启动成功&a…

Java Object类方法介绍

Object作为顶级类,所有的类都实现了该类的方法,包括数组。 查询Java文档: 1、object.eauqls(): 其作用与 有些类似。 : 是一个比较运算符,而不是一个方法。 ①可以判断基本类型,也可以判断引用类型。 ②若…

gitLab 使用tortoiseGit 克隆新项目 一直提示tortoiseGitPlink输入密码 输完也不生效

问题描述:准备用TortoiseGit拉取gitlab上一个新项目代码,出现tortoiseGitPlink提示让输入密码,输入后又弹出,反复几次,无法down下来代码。 解决方案: 1.找到PuTTYgen工具,打开 2. 点击load 按钮…

山脉数组的峰顶索引 ---- 二分查找

题目链接 题目: 分析: 我们很明显, 可以从峰值位置将数组分成两段, 具有"二段性", 所以可以用二分查找因为arr是山峰数组, 不存在相等的情况如果arr[mid] > arr[mid 1], 说明mid的位置可能是峰值, 移动right mid如果arr[mid] < arr[mid 1], 说明mid的位置…

神奇的一万

在代码界&#xff0c;有个神奇的存在&#xff0c;它叫一万&#xff1a;eval&#xff08;&#xff09;。 这个神奇的一万&#xff0c;在python和JavaScript中都存在&#xff0c;作用也是基本相同的。 Python中的eval函数能将字符串str当成有效的表达式来求值并返回计算结果。 …

vue contextPath的思考

先说我这边的情况&#xff0c;目前项目都是前后端分离开发的&#xff0c;上线有种部署方式&#xff0c;常见的就是前后端分开部署&#xff0c;这是比较常见的&#xff0c;我这边因客户原因&#xff0c;打包一起进行部署比较简单&#xff0c;交付技术运维部方便后期其他现场部署…

docker ps显示的参数具体是什么意思

1&#xff0c;运行一个容器 docker run -d ubuntu:15.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"这段命令的作用是使用 docker run 命令运行一个基于 ubuntu:15.10 镜像的 Docker 容器&#xff0c;并在容器中执行一个无限循环的命令。 具体解…

10Django项目--用户管理系统--改

对应视频链接点击直达 10Django项目--用户管理系统--改 对应视频链接点击直达改a&#xff0c;本质b&#xff0c;修改(更新) 页面相关a&#xff0c;index页面新增操作按钮b&#xff0c;修改(更新)页面c&#xff0c;路由和函数 OVER&#xff0c;不会有人不会吧不会的加Q139400651…

基于jeecgboot-vue3的Flowable流程-我的任务(一)

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、首先可以用现成生成代码的前端来做这个&#xff0c;只要做一些调整就可以了&#xff0c;这样利用现有的一些模板可以快速构建我的任务&#xff0c;否则vue2与vue3相差太大&#xff0c;移…

质量人,你还在等什么?快来六西格玛培训公司充电吧!——张驰咨询

在竞争激烈的商业环境中&#xff0c;质量成为了企业生存和发展的关键。而六西格玛&#xff0c;作为一种全球公认的质量管理方法论&#xff0c;正在成为越来越多企业追求品质革命的重要工具。而六西格玛培训公司&#xff0c;则成为了这场品质革命中&#xff0c;质量人不可或缺的…

菊花链通信技术整理

目录 一、菊花链简介 二、菊花链与CAN通信的区别 三、常见的菊花链AFE芯片 四、菊花链数据结构 五、菊花链方案介绍 一、菊花链简介 首先简单的说一下菊花链以及菊花链的应用&#xff0c;在目前国内的BMS开发中&#xff0c;我们应用最广泛的目前还还是分布式&#xff0c;…

Linux:线程

文章目录 前言1. 线程概念1.1 什么是线程1.2 线程比进程更加轻量化1.3 虚拟地址到物理地址的转化物理内存的管理页表 1.4 线程的优点1.5 线程的缺点1.6 线程异常1.7 线程用途 2. 进程 vs 线程3. 线程控制3.1 线程创建3.2 线程退出3.3 线程等待3.4 分离线程3.5 线程取消 4. 线程…

机器学习-6-对随机梯度下降算法SGD的理解

参考一文带您了解随机梯度下降(Stochastic Gradient Descent):python代码示例 参考sklearn-SGDClassifier 1 梯度下降 在机器学习领域,梯度下降扮演着至关重要的角色。梯度下降是一种优化算法,通过迭代沿着由梯度定义的最陡下降方向,以最小化函数。类似于图中的场景,可以…

english语法

从句&#xff1a;简单句连词 介词

Android studio sdk 虚拟机无法打开运行

1.确认是否在BIOS开启硬件虚拟化支持,选择Enable 2.win8/win10 Hyper-V冲突。控制面板-》程序与功能-》windows功能-》关闭 Hyper-V 3.sdk 路径非默认路径 复制avd C:\Users\Administrator\.android\avd 到 sdk的安装路径下 D:\Android\sdk 。重启软件重新启动即可

老板:2个亿的销售额,利润只有55万!电商这个生意真的到头了?

近来&#xff0c;一段对话轰动了半个电商圈的老板&#xff0c;干拼多多&#xff0c;2亿的销售额&#xff0c;利润只有55万&#xff01; 其实造成这一现象的原因就是“内卷” 说一句电商行业真实的现状&#xff0c;电商发展了十几年&#xff0c;网友对网购已经完全熟悉&#x…