Bigtable研究
摘要
Bigtable是一个用于管理结构型数据的分布式存储系统,被设计为可扩展到很大规模:通过数以千台的机器存储PB级数据。很多Google的工程都将数据存储在Bigtable中,包括网页索引、Google Earth和Google金融。这些应用在数据量和延迟方面对Bigtable的需求很不相同。尽管这些不尽相同的需求,Bigtable能够成功的为这些Google的产品提供一个弹性的、高性能的解决方案。在这篇文章中,我们描述Bigtable提供的简单的数据模型,它能给客户端在数据布局和格式上提供动态控制,而且我们会描述Bigtable的设计和实现。
一、 介绍
经过过去两年半的时间,我们设计、实现并部署了一个分布式存储系统,用来管理Google的结构化数据,称为Bigtable。
Bigtable被设计为一个可靠扩展到PB数据和数千台机器。Bigtable实现了几个目标:广泛应用、可扩展、高性能和高可用。目前Bigtable已被用于超过60个的Google产品和工程,包括Google分析、Google金融、Orkut、个人搜索、Writely和Google Earth。这些系统针对于各种不同的需求使用了Bigtable,范围从面向吞吐量的批处理进程到时延敏感的面向终端用户的数据服务。这些产品使用的Bigtable集群跨越了很多的结构,从少数到数以千计的服务器,存储多至几百TB的数据。
在很多方面,Bigtable很像一个数据库:它实现了很多数据库的策略。并行数据库和内存数据库已经实现了可扩展和高性能,但是Bigtable与这些系统相比提供了不同的接口。Bigtable不支持全关系型的数据模型;作为代替,它提供了一种简单的数据模型,在数据布局和格式上提供了动态控制,并且允许客户端推出在底层存储中数据的位置属性。数据通过行和列名进行索引,这些名字可以是任意的字符串。Bigtable也将数据看成是无解释的字符串,尽管客户端经常将结构化和半结构化的数据序列化成不同的格式。客户端能够通过在它们的模式中精心的选择来控制他们的数据位置。最后,Bigtable模式参数可以使客户端动态的控制是从内存中获取数据还是从硬盘中。
第二章描述了数据模型的更多细节,并且在第三章提供了客户端API进行了概述。第四章简要的描述了Bigtable使用到的Google基础项目。第五章描述了Bigtable实现的基本原理。第六章描述了几个用于提高Bigtable性能的优化。第七章测试了Bigtable的性能。我们在第八章描述了几个如何使用Bigtable的例子,并在第九章讨论一些在设计和实现Bigtable中的教训。最后,在第十章描述相关工作,在第十一章进行了总结。
二、 数据模型
一个Bigtable是一个稀疏的、分布式的、持久的多维排序映射(MAP)。这个映射(MAP)由行key、列key和时间戳进行索引,每个映射值都是一个连续的byte数组。
(row:string, column:string,time:int64) à string
图1:存储Web页面的样例表中的一部分。行名是一个翻转了的URL。Contents列族包含了页面内容,anchor列族包含了涉及页面中的所有anchor的文本。CNN主页被Sports Illustrated和My-look主页引用,所以,本行包含了名为anchor:cnnsi.com和anchor:my.look.ca的列。每个anchor单元都有一个版本;contents列在根据时间戳t3,t5和t6有三个对应的版本。
在调查了一个类似Bigtable系统的各种潜在用途之后,我们选定了这个数据模型。作为驱动我们对设计做出一些决定的一个具体的例子,假设我们想要保留一个包含大量网页的集合和用于很多不同项目相关信息的拷贝,我们将这个特殊的表称为Webtable。在Webtable中,我们使用URL作为row key,页面的不同属性作为column names,并将页面的内容存储在contents:如图1所示,它们被获取时的时间戳将作为存放它们的列的列关键字。
2.1 行(Rows)
在表中,行的key是任意字符串(目前最大为64KB,尽管用户大多数只使用10-100字节)。每次在一行中读或写数据都是一个原子操作(尽管一行中不同列正在进行读或写),一个设计决定使客户端更加方便的推导出在并发更新相同行的系统行为。
Bigtable以row key的字典序保存数据。一个表的行范围是动态分配的。每个行范围被称为一个tablet,它是分布式和负载平衡的单位。因此,小范围的读取是高效的,只需要少量机器的通信。客户端可以通过选择合适的row keys来利用这个属性,这样可以为他们的数据访问提供良好的局域性。例如,在Webtable中,相同域名下的页面通过反转URL中的hostname,被集中存放到连续的行中。例如,我们将maps.google.com/index.html存放在关键字com.google.maps/index.html下。将相同域名的网页存储在一起可以更加高效对一些主机和域名进行分析。
2.2 列族(Column Families)
列关键字(Column keys)被聚合到一个名为列族的集合中,它形成了访问控制的基础单元。存储在一个列族中的所有数据通常有相同的类型(我们将在一个列族下的数据压缩到一起)。必须先创建列族,然后才能将数据存储到其下面的列;在列族创建好之后,列族中的任意一个列关键字(column key)都可用。我们的目的是每个表中不同的列族数量较小(最多不会多于几百个),并且列族很少在操作中变化。相反的,一个表可以有无限数量的列。
一个列关键字(column key)使用下面的语法进行命名:family:qualify。列族名必须是可显示的,但是qualify可以是任意字符。例如,Webtable的列族名是language,用于存储页面所用到的语言。我们在language列族中只使用一个列关键字(column key),它存储每个页面的languageID。另外一个有用的列族是anchor;这个族中的每个列关键字(column key)代表一个单独的anchor,如图1所示。qualify(限定词)是引用站点;该单元的内容是链接文本。
访问控制,以及磁盘和内存的统计信息都是在列族层面上进行的。在我们的Webtable例子中,这些控制允许我们管理几个不同的应用类型:一些添加新的基础数据,一些读取基础数据并创建派生列族,一些只允许查看已存在的数据(甚至出于隐私考虑也不能查看所有已存在的列族)。
2.3 时间戳(Timestamps)
Bigtable中的每个单元都能够包含相同数据的多个版本;这些版本由时间戳进行索引。Bigtable时间戳是一个64bit的整数。它们能够由Bigtable分配,在这种情况下,它们表现成以毫秒为单位的当前时间,或者显式的由客户端应用指定。应用必须保证时间戳的唯一性。不同版本的单元以时间戳的降序进行排列,这样可以使最近的版本最早被读取。
为了减少管理不同版本数据的工作量,我们支持两个列族设置,它们可以使Bigtable自动的垃圾回收单元中的版本。客户端可以指定保留最近的n个版本,也可以指定只保留new-enouge内的版本(如:只保留最近7天写入的数据)。
在我们的Webtable例子中,我们将抓取页面的时间戳存放在contents中:页面被抓取的时间就是所对应的时间戳。上面所提到的垃圾回收机制使我们只保留一个页面最近的三个版本。
三、 API
Bigtable API 提供了创建、删除表和族列的方法。它也提供了更改集群、表和列族元数据(如访问控制权限)的方法。
图2:向Bigtable写数据
客户端应用能够写或删除Bigtable中的值,从单独的行中查询值,或者循环访问一个表中的一部分数据。图2显示了使用一个RowMutation抽象执行的一系列更新操作的C++代码(为了使例子较短,去掉了不相干的代码)。Apply对Webtable执行了一个原子操作:它增加了另一个链接到www.cnn.com的anchor,并删除了一个不同的anchor。
图3:从Bigtable中读数据
图3显示了使用Scanner抽象方法遍历一个指定行的所有anchors的C++代码。客户端可以遍历多个列族,并且有一些对行、列和时间戳的浏览的限制机制。比如,我们能限制只浏览列可以正则匹配anchor:*.cnn.com列的anchors,或者是时间戳在距离当前时间10天之内的anchors。
Bigtable支持另外的一些功能,可以使用户通过更加复杂的方法操作数据。第一,Bigtable提供了单行事务,它能够用于在一个行关键字(row key)的数据上执行原子的读、改、写操作。Bigtable目前不支持一般的跨行关键字的事务处理,尽管在客户端提供了一个跨行关键字批写入的接口。第二,Bigtable允许单元用于整数计数。最后,Bigtable支持在服务器地址空间中执行客户端提供的脚本。这些脚本使用Google用于数据处理的语言Sawzall编写。同时,我们基于Sawzall的API不允许客户端脚本写回Bigtable,但是它允许不同形式的数据转换,基于任意表达式的过滤,以及使用各种操作的进行数据汇总。
Bigtable能够与MapReduce一起使用,后者是一个Google用于运行大规模并行计算的框架。我们已经写了一系列包装器,使Bigtable既能用于MapReduce的输入源,也能用于它的输出目标。
四、 构件
Bigtable建立在几个其它的Google基础构件之上。Bigtable使用Google文件系统GFS存储log和数据文件。一个Bigtable集群通常工作在一个共享的机器池中,运行各种的其它分布式应用,Bigtable进程与其它进程共享同一台机器。Bigtable依靠集群管理系统来分配任务、管理共享系统的资源、处理机器故障和监视机器状态。
Google的SSTable文件格式用于内部存储Bigtable数据。SSTable提供了一个持久的、排序不变的key-value映射,其中key和value都可以是任意字符串。提供了根据一个指定的key查找value的操作,以及遍历指定key的一个范围内所有的key-value对。内部地,每个SSTable包含了一系列的块(一般情况下,每个块的大小为64KB,但是这是可配置的)。一个块索引(存储在SSTable的末尾)用来定位块,这个索引会在SSTable打开时载入到内存中。一个查询可以执行一次独立的磁盘查询:我们首先通过在内存索引中的二叉查找找到适当的块,然后从磁盘中将适当的块读取出来。同样地,一个SSTable能够完整的被映射到内存中,这样可以让我们在不接触磁盘的情况下来执行查询和浏览。
Bigtable依赖一个高可用的、持久的称为Chubby的分布式锁服务。一个Chubby服务由5个活跃的副本组成,其中一个被选举为master,并处理请求。当大多数副本正常运行,并且能够相互通信时,这个服务是正常的。Chubby使用Paxos算法来保证它的副本在面对故障时的一致性。Chubby提供一个由目录和少量文件组成的命名空间。每个目录或者文件都能被用做一个锁,并且读和写文件都是原子的。Chubby客户端库提供了一致的Chubby文件缓存。每个Chubby客户端维护一个Chubby服务的会话。如果一个客户端会话在租约到期后不能更新它的会话租约时,则就会过期。当一个客户端会话过期时,它将释放所有的锁和打开的句柄。Chubby客户端能够在Chubby文件和目录下注册回调函数,用于处理变化通知或会话过期。
Bigtable将Chubby用于各种工作:确保在任何时刻最多存在一个活跃的master;存储Bigtable数据的引导位置;发现tablet服务器和处理失败的tablet服务器;存储Bigtable概要信息(每个表的列族信息);存储访问控制列表。如果Chubby超过一段时间不可用,则Bigtable会变为不可用。我们最近在一个有11个Chubby实例的14个Bigtable集群上测试了效果。由于Chubby不可用(由Chubby中断或者是网络问题造成)而造成存储在Bigtable中的数据不可用的时间占Bigtable服务器时间的平均百分比为0.0047%。在单独的集群中受到Chubby不可用影响的最大百分比为0.0326%。
五、 实现
Bigtable实现由三个主要的组成部分:一个连接到每个客户端的库、一个master服务器和许多tablet服务器。Tablet服务器能够从集群中动态的增加(或删除)以适应工作量的变化。
Master负责将tablet分配到tablet服务器上,探测增加和超时的tablet服务器,平衡tablet服务器的负载,以及在GFS上回收垃圾文件。此外,它会处理概要变化,如表和列族的创建。
每个tablet服务器管理一个tablet集合(一般情况下在一个tablet服务器上我们有10到1000个tablets)。Tablet服务器处理它负载的tablet相关的读和写请求,并在tablets过大后进行分片。
像许多单master分布式存储系统一样,客户端数据不能通过master进行传输:客户端直接与tablet服务器进行读和写的通信。由于Bigtable客户端不依赖master获取tablet位置信息,大多数客户端不需要跟master进行通信。因此,在实际中master的负载很轻。
一个Bigtable集群存储了大量的表。每个表有一系列tablet组成,每个tablet包含了一个行范围内的所有数据。最初,每个表都仅仅由一个tablet组成。随着表的增长,它会自动的分片成多个tablets,默认情况下,每个tablet的大小大约在100-200MB。
5.1 Tablet定位
我们使用三层的类似于B+树的结构存储tablet位置信息(图4)。
图4:Tablet位置等级
第一层是存储在Chubby的一个文件,它包含了root tablet的位置。Root tablet将所有tablet的位置包含在一个特殊的METADATA表中。每个METADATA tablet包含一系列的用户tablet位置。Root tablet只是METADATA表的第一个tablet,但是特殊之处在于其永远不会分裂,以此确保tablet位置层级不会超过3层。
在METADATA表中,每个tablet的位置信息都存放在一个行关键字下面,而这个关键字是由tablet所在的表的标示符和它的最后一行编码形成的。每个METADATA行在内存中存储了大约1KB的数据。通过限制tablets的大小为128MB,三层定位方案可以满足定位2^34个tablets(或者是2^61字节,按着每个tablet有128MB数据)。
客户端库会缓存tablet位置。如果客户端不知道一个tablet的位置,或者它发现缓存的位置信息不正确,则它会递归查询tablet的位置信息。如果客户端的缓存是空的,定位算法需要3次网络交互更新数据,包括一次Chubby文件的读取。如果客户端缓存过时,则定位算法需要6次网络交互才能更新数据,因为过时的客户端缓存条目只有在没有查到数据的时候才能发现数据过期(假设METADATA tablets移动的不频繁)。尽管tablet的位置信息存在内存中,不需要访问GFS,但是我们会通过客户端库预取tablet位置的方式来减少这种消耗:无论何时读取METADATA表都读取不止一个的METADATA tablets。
我们在METADATA表中还存储了次要的信息,包含与每个tablet有关的所有事件(如:什么时候一个服务器开始为该tablet提供服务)。这些信息对debugging和性能分析很有帮助。
5.2 Tablet分配
每个tablet只能分配给一个tablet服务器。Master记录了正常运行的tablet服务器、tablet服务器当前的tablet任务和没有被分配的tablet。当一个tablet没有被分配,并且有tablet服务器对于这个tablet有足够的空间可用时,master会通过向这个tablet服务器发送一个tablet载入请求分配这个tablet。
Bigtable使用Chubby跟踪tablet服务器的状态。当一个tablet服务器启动时,它在指定的Chubby目录下创建一个命名唯一的文件并获取一个互斥锁。Master监控这个目录(服务器目录)来检测tablet服务器。如果一个tablet服务器丢失了它的互斥锁,则停止它的tablet服务:例如,由于网络终端,造成了服务器丢失了它的Chubby会话。(Chubby提供了一个高效的机制使tablet服务器在不引入网络流量的情况下,能够检测它是否仍然持有它的锁。)只要文件依然存在,一个tablet服务器试图再次在这个文件上获取一个互斥锁。如果文件不存在了,则tablet服务器将不能再次提供服务,进而会kill掉自己。一个tablet服务器无论何时结束,它都会试图释放它的锁,以使master能够更加快速的重新分配它上面的tablets。
Master负责检测一个tablet服务器何时不能继续为它的tablets服务,并尽快将这些tablets重新分配。Master通过周期性的询问每个tablet服务器的状态来检测一个tablet服务器何时不能继续工作。如果一个tablet服务器报告它失去了它的锁,或者如果master在最近的几次尝试都不能到达一个服务器,则master会尝试获取这个服务器文件的互斥锁。如果master能够获取这个锁,则Chubby运行正常, tablet要么是宕机了,要么就是不能与Chubby正常通信了,因此master通过删除这个tablet服务器的服务器文件来确保这个服务器不能再次进行服务。一旦一个服务器文件被删除,master将之前分配到这个tablet服务器上的所有tablets移动到未被分配的tablets集合里面。为了确保Bigtable集群不易受到master和Chubby之间的网络问题的影响,master将会在它的Chubby会话超时后kill掉自己。然而,如上所说,master失败不会改变tablet服务器上的tablet分布。
当一个master被集群管理系统启动时,它需要在改变tablet分布之前先发现当前的分布。Master在启动时执行下面的步骤。
(1) master从Chubby中抢占一个唯一的master锁,用来阻止其它的master实例化。
(2) master扫描Chubby中的服务器目录,来查找哪些服务器正在运行。
(3) master与每个正常运行的tablet服务器通信,获取每个tablet服务器上tablet的分配信息。
(4) master扫描METADATA表获取所有的tablets的集合。在扫描的过程中,如果遇到一个tablet没有被分配,则将其放入到未被分配的tablets集合中,并可以进行分配。
一种复杂的情况是,在METADATA tablets被分配之前,不能扫描METADATA表。因此在开始扫描之前(第4步),如果在第三步发现root tablet没有分配,则master将root tablet加入到未被分配的tablet集合中。这个附加的操作确保了root tablet将会被分配。因为root tablet包含了所有的METADATA tablets的名字,所以在扫描完root tablet之后,master会得到所有METADATA tablet的名字。
已存在的tablet集合只有在创建或删除表、两个已存在的tablet合并成一个更大的tablet,或者一个已存在的tablet分裂成两个较小的tablet时才会改变。Master会记录所有的这些变化,因为上面几种情况除了最后一个都是它发起的。tablet分裂是比较特殊的,因为它是由tablet服务器发起的。Tablet服务器为METADATA表中记录新tablet的信息提交这次分裂操作。当分裂操作提交后,它会通知master。如果分裂通知丢失(因为tablet服务器或者master宕机),master在询问一个tablet器载入那个分裂的tablet时会检测到新的tablet。
5.3 Tablet服务
图5:Tablet表述
一个tablet的持久状态存储在GFS中,如图5中的描述。更新操作提交到REDO日志中。在这些更新中,最近提交的那些操作存储在一块名为memtable的有序缓存中;较老的更新存放在一系列的SSTable中。为了恢复这个tablet,一个tablet服务器会从METADATA表中读取它的元数据(metadata),这个元数据包含了组成这个tablet的SSTable的列表,以及一系列redo点,这些点指向可能含有该Tablet数据已提交的日志记录。服务器将SSTable的索引读入到内存,并通过执行从redo点开始的所有已提交的更新操作重构memtable。
当一个写操作到达一个tablet服务器时,服务器检查其是否符合语法要求,并验证发送者是否有权限执行这个操作。验证是通过读取一个Chubby文件(这个文件几乎会存在客户端的缓存中)中的可写用户的列表完成的。一个有效的写操作会写入到操作日志中。批处理方式可以提高大量细小操作的吞吐量。在写操作提交后,它的内容被写入到memtable中。
当一个读操作到达一个tablet服务器时,同样会检查是否符合语法要求和本身的权限。一个有效的读操作会在一系列SSTable和memtable合并视图上执行。由于SSTable和memtable是按字典序排序的数据,所以能够高效的生成合并视图。
当tables进行分裂和合并时,进来的读和写操作能够继续执行。
5.4 合并压缩
随着写操作的执行,memtable的大小会增加。当memtable的大小达到一个门限值时,这个memtable会被冻结,创建一个新的memtable,并将冻结的memtable转换成一个SSTable写入到GFS中。这里的次压缩(minor compaction)过程有两个目标:减少tablet服务器的内存使用,减少操作日志中在恢复tablet服务器时需要读取的数据总量。当压缩发生时,进来的读和写操作能够继续执行。
每次次压缩(minor compaction)都会创建一个新的SSTable。如果这种行为不停的进行下去,则读操作可能需要合并来自任意数量的SSTable的更新。否则,我们通过在后台周期性的执行合并压缩来限制这些文件的数量。一个合并压缩读取一些SSTable和memtable中的内容,并写入到一个新的SSTable中。输入SSTable和memtable可以在压缩完成后立即丢弃。
一个将所有SSTables写入到一个SSTable中的合并压缩称为主压缩(major compaction)。非主压缩产生的SSTable能够包含特定的删除条目,它阻止在仍然活着的旧SSTable中删除数据。另一方面,主压缩产生的SSTable不会包含删除信息或已删除的数据。Bigtable循环扫描所有的tablets,并定期的对它们执行主压缩。这些主压缩可以回收删除数据所使用的资源,并尽快的确保删除的数据在系统内彻底消失,对于存储的敏感数据,这是十分重要的。
六、 优化
在前面章节所描述的实现需要一些优化来满足我们用户的高性能、高可用和高可靠性需求。这一章通过对这些实现更加细节的描述来强调这些优化。
局域性群组
客户端能将多个列族聚集成一个局域性群组。对Tablet中每个局域性群组都会产生一个单独的SSTable。将通常不会被一起访问的列族分隔成不同的局域性群组能够提高读取效率。例如,Webtable中的页面元数据(如语言和校验和)作为一个局域性群组,而页面的内容作为另外一个局域性群组:一个想要读取元数据的应用不需要读取所有的页面内容。
此外,可以针对于每个局域性群组设定一些有用的调整参数。例如,一个局域性群组可以声明为存储在内存中。Tablet服务器采用惰性加载的策略对设定为内存中存储的局域性群组的SSTable进行内存加载。一旦加载过,则属于这些局域性群组的列族能够直接被读取,而不需要访问硬盘。这个功能对一些经常访问的小片数据很有用:在内部,我们使用它存放METADATA表中的位置信息列族。
压缩
客户端能够控制作为局域性群组的SSTable是否被压缩,如果压缩,选定什么样的压缩格式。每个SSTable块(它的大小能够通过局域性群组特定的调整参数来控制)都会按着用户指定的格式进行压缩。尽管由于对每个块分开压缩而浪费了一些空间,但是我们能够受益于在不需要解压整个文件的情况下能够访问部分SSTable。许多客户端使用两遍定制的压缩方式。第一遍使用了Bentley和Mcllroy方式,它通过在一个很大的窗口中对常见的长字符串进行压缩;第二遍使用了一种快速压缩算法,它在16KB大小的窗口内查找重复的数据。两种压缩都很快,在当前的机器上,它们压缩速度为100MB-200MB/s,解压速度为400-1000MB/s。
虽然在选择压缩算法时我们更看重速度而不是减少的空间,但是这种两遍的压缩方式对空间上的压缩也出奇的好。例如,在Webtable中,我们使用了这种压缩方式存储web页面内容。在一个实验中,我们在一个局域性群组中存储大量的文档。针对这个实验的目的,我们没有存储文档的所有版本,而是只存了一份,这种方式实现了10:1的空间压缩率。这比对HTML页面的压缩率为3:1或4:1的常用的Gzip压缩更好,原因在于Webtable存放行的方式:一个域名下的所有页面会存储在临近的地方。这使Bentley-Mcllroy算法能够将相同域名下的大量页面的共同部分识别出来。很多应用,不只是Webtable,选择他们行名以至于所有相似的数据聚集到一起,因此实现了很好的压缩率。当我们在Bigtable中存储相同值的多个版本时,压缩率会更好。
读操作的缓存
为了读操作的性能,tablet服务器使用双层缓存。扫描缓存是高层缓存,它缓存了tablet服务器代码使用SSTable接口获取的key-value对;块缓存是底层缓存,它缓存了从GFS上读取的SSTables块。扫描缓存主要用于倾向重复访问相同数据的应用。块缓存主要用于倾向读取近期数据附近数据的应用(如:顺序读取或随机读取同一个局域性群组的一个频繁访问行的不同列)。
Bloom 过滤器
如5.3中描述的,一个读操作必须从所有的组成tablet的SSTable中读取数据。如果这些SSTable没有在内存中,则我们最终会多次访问硬盘。我们通过允许客户端对特定局域性群组的SSTable指定Bloom过滤器来降低访问次数。一个Bloom过滤器允许我们查询一个SSTable是否含有特定的行/列对的数据。对于某些特定应用,虽然存储Bloom过滤器占用了tablet服务器少量的内存,但能够彻底的减少读操作对磁盘的查询次数。我们使用Bloom过滤器也可以隐式的达到了当查询的行和列不存在时,不需要访问磁盘。
操作日志实现
如果我们为每个tablet保存一份单独的日志,那么我将会在GFS中并发的写大量的文件。取决于每个GFS服务器的底层系统实现,这些写操作会引起大量的磁盘查找,用来将数据写入到不同的物理日志文件中。此外,由于批操作通常较小,每个tablet分开保存日志文件会影响批操作所带来的优化。针对这些问题,对于每个tablet服务器我们将修改追加到一份操作日志中,不同的tablet修改混合存储在一个物理日志文件中。
在进行一般的操作时,使用一个日志可以提供很好的性能,但是会使恢复复杂化。当一个tablet服务器宕机,其上的tablets会被移动到大量其它的tablet服务器上:每个tablet服务器通常只负载原始tablet服务器上的一小部分tablets。为了恢复一个tablet的状态,新的tablet服务器需要重新执行原始tablet服务器上操作日志中针对这个tablet的修改。然而,这些tablets的修改混合存在同一个物理日志文件中。一种方式是每个新tablet的服务器会读取整个操作日志文件,然后只执行对于需要恢复的tablet的修改。然而,在这种方式下,如果100台机器每个都从失败的tablet服务器上分配了一个tablet,那么日志文件将被读取100次。
我们通过对日志文件条目以key
进行排序,来避免这种重复的文件读取。在排序输出中,对于一个指定的tablet的所有修改将会是连续的,并因此能够通过一次硬盘查询和顺序读取进行高效的操作。为了并行排序,我们先将日志文件分成64MB大小的片段,在不同的tablet服务器上对每个片段并行的进行排序。这个排序过程由master协同处理,并在当一个tablet服务器声明它需要从一些操作日志中恢复修改时启动。
向GFS中写日志文件有时会由于各种原因引起性能波动(如:写操作进行时,一个GFS服务器宕机了,或者连接三个GFS副本服务器所在的网络发生拥塞或过载)。为了确保在GFS高负载时修改能够正常进行,每个tablet服务器实际有两个写日志线程,每个线程写自己的日志文件,同一时刻,两个线程中只有一个是工作的。如果一个写日志的线程效率很差,则会切换到另一个线程,修改操作的日志就会写在这个线程下的日志文件中。每个日志记录都有一个序列号,以此使tablet服务器在恢复时忽略掉线程切换所产生的重复的条目。
Tablet 恢复提速
如果master将一个tablet从一个tablet服务器移动到另一个服务器,源tablet服务器会在本地先进行一个次压缩。这个压缩通过减少了tablet服务器日志中没有归并的记录的数量来缩短恢复时间。压缩完成后,tablet服务器停止对该tablet的服务。在卸载tablet之前,源服务器还会再做一次次压缩(通常很快),以消除第一次次压缩过程中新进入的未归并到SSTable中的修改。在这次次压缩完成后,tablet能够被载入到另一个tablet服务器上,而无需通过任何的日志条目恢复。
利用不变性
除了SSTable缓存,实际中我们产生的其它部分的SSTable都是不变的,我们可以通过这个事实来简化Bigtable系统。例如,当从SSTable读取数据时,我们不需要对文件系统访问操作进行任何同步。这样,就可以非常高效的实现对行的并行操作。Memtable是唯一一个能被读和写操作同时访问的可变数据结构。为了减少在读操作时的竞争,我们对内存采用了copy-on-write机制,这样就允许读写操作同时进行了。
因为SSTable是不可修改的,所以我们将永久移除要删除的数据问题转换为对废弃的SSTable进行垃圾回收的问题。Tablet的每个SSTable都在METADATA表中注册过。Master通过标记-删除的方式移除SSTable集合中废弃的SSTable,METADATA表中包含了ROOT集合。
最后,SSTable的不可变性使我们能够快速的分裂tablet。我们让子tablet(分裂后的tablet)共享父tablet(分裂前的tablet)的SSTables,而不是为每个子tablet创建新的SSTable集合。
七、 性能评估
我们建立一个有N个tablet服务器的Bigtable集群,用于测试Bigtable的性能和可扩展性,其中N是可以变化的。每个Tablet服务器配置为1G的RAM,并向一个由1786台有两块400GB IDE硬盘的机器组成的一个GFS单元上写数据。N个客户端机器为这个测试生成Bigtable负载。(我们使用与tablet服务器相同数量的客户端,以保证客户端不会造成瓶颈。)每个服务器配置双核Opteron 2GHz处理器,足够容纳所有进程工作数据集所需的内存,一个Gb以太网卡。这些机器排列在两层树结构的交互网络中,在根节点出的带宽大约有100-200Gbps。所有的机器都在同一个虚拟环境中,这样可以使任意两台机器间的往返时间都在1ms以内。
Tablet服务器和master服务器、测试客户端,以及GFS服务器都运行在同一个机器集合中。所有的机器都运行一个GFS服务,其中一些机器同时还可以再运行一个GFS服务,或者是客户端进程,或者是其它的工作进程。
R是测试中Bigtable不同行关键字的具体数量。R的选取保证了每次对每个tablet服务器的基准读或写数据量都大概在1GB左右。
在序列写的基准测试中,使用了0 ~ R-1作为行关键字。这些行关键字可以被分成10N个大小相同的区间。这些区间通过一个中心调度者被分配到N个客户端,这个中心调度者能够快速的将下一个可用的区间分配到一个处理完之前分配过来的区间的客户端上。动态分配有助于缓解由于其他进程引起的性能变化所造成的影响。我们在每个行关键字下写一个单独的字符串。每个字符串都是随机生成的,因此不可被压缩。此外,不同行关键字下的字符串是不同的,所以也就没有可能跨越行进行压缩。随机写基准测试是类似的,除了行关键字是在写入前通过模R散列出来的。这样保证了在整个基准测试过程中,写操作的负载均匀的分布在整个行空间中。
序列读基准测试使用的生成行关键字的方法与序列写的基准测试的基本相同,但它是从行关键字下读取字符串(之前在序列写基准测试中已经写入),而不是写入。类似的,随机读基准测试与随机写基准测试相似。
扫描基准测试与序列读基准测试类似,但是使用了Bigtable API所提供的接口来扫描一个行区间的所有数据。由于一次RPC就能够从tablet获得大量的数据,所以使用扫描操作减少了基准测试RPCs的执行次数。
随机读(内存)基准测试与随机读基准测试类似,但包含基准测试数据的局域性群组被标识为in-memory,因此,这些读操作能够直接从内存中进行读取,而不需要访问GFS。对于这个基准测试,我们将每个tablet服务器的数据量从1GB减少到100MB,以保证这些tablet服务器能够将所有数据加载到内存中。
图6:每秒读/写1000字节的数据。上表显示了每个tablet服务器的速率;上图显示了所有tablet服务器的总速率。
图6显示了当我们向Bigtable读写1000字节的基准测试性能的两个视图。表中显示了每个tablet服务器的操作数量,图中显示了每秒中操作的数量总和。
单 Tablet 服务器性能
让我们先考虑单个tablet服务器的性能。随机读比其它的操作要慢一个数量级以上。每个随机读包含了从GFS到一个tablet服务器的一个64KB大小的SSTable块的网络传输,其中只有1000字节是被用到的。一个tablet服务器每秒执行大约1200次的读操作,也就是从GFS上每秒读取75MB的数据。由于网络栈的开销、SSTable的解析和Bigtable代码的执行,这个带宽使tablet服务器的CPUs接近饱和,并且我们系统中使用的网络链路也几乎被占满。大多数采用这种访问方式的Bigtable应用会把块大小设置为一个较小的值,通常情况下为8KB。
从内存中随机读会快很多,因为每个1000字节的数据都是从tablet服务器本地内存中获取的,而不需要去从GFS上获取64KB的大数据块。
随机写和序列写比随机读表现的好一些,因为每个tablet服务器将所有进来的写操作追加到一个单独的操作日志中,并且使用批提交的方式,将数据以流的形式高效的写入到GFS。随机写和序列写在性能上没有明显的区别,所有的tablet服务器写操作都是将记录追加到同一个操作日志中。
序列读比随机读的性能要好,因为每个从GFS读取到的64KB的SSTable块都放入了块缓存中,我们可以使用它完成接下来的64个读请求。
扫描的速度更快,因为tablet服务器对于一个单独的客户端RPC可以返回大量的数据,因此RPC的消耗基本可以抵消了。
缩放规模
随着我们将系统中的tablet服务器的数量从1增加到500,总吞吐量有着戏剧般的增长,有超过100倍的增长。举个例子,随着机器数量增加了500倍,从内存中随机读的性能同时提高了300倍。之所以是这种现象,是因为这个基准测试中,单个服务器的CPU达到了瓶颈。
然而,性能不会线性增长。对于大多数基准测试,随着tablet服务器数量从1增加到50,每台tablet服务器的吞吐量有着明显的下降。这是由于多台服务器配置负载不均衡造成的,主要是因为其他进程抢占CPU和网络。我们使用均衡算法来尽量解决这个问题,但是由于两个原因,这个算法不能完美的工作:一个是为了减少tablet的移动会压制重新均衡(一个tablet在移动时会短暂的不可用,通常会小于1秒);另一个是基准测试进程产生的负载会有波动。
随机读基准测试显示了最坏的比例(随着服务器数量增加了500倍,总吞吐量只增加了100倍)。造成这种情况的原因(如上说解释的)就是为了1000字节的数据我们需要在网络上传输64KB的大块数据,这种传输会使1G的链路饱和,因此每个服务器吞吐量随着机器的增加下降的很明显。
八、 真实应用
表1:Bigtable机群中的tablet服务器分布
截止到2006年8月,已经有388个未经测试的Bigtable集群运行在各种Google的机群上了,总共大概有24500个tablet服务器。表1中显示了每个集群中tablet服务器的粗略分布。这些集群中的很多都用于开发产品,因此会有一段时间比较空闲。观察一组由14个繁忙的集群、8069个tablet服务器集群组,我们看到整体的流量是大约每秒1200万个请求,其中包括接收到的大约741MB/s的RPC流量,以及发送出去的大约1GB/s的RPC流量。
表2:一些产品使用的表的特点。表大小(压缩前)和#Cell指出近似的大小。在表中禁用压缩的产品对应的没有给出压缩率。
表2提供了当期正在使用的一些表的数据。一些表存储了与用户相关的数据,而另一些存储了用于批处理的数据;表的大小、平均的单元大小、从内存中读取数据的比例和表概要的复杂度都有很大的区别。在本章下面的步骤中,我们简要的描述三个产品如何使用Bigtable。
8.1 Google分析
Google分析是用来帮助Web站点管理者分析他们网站的流量模式的服务。它提供了汇总统计,如每天独立访问的用户量和每个URL每天的访问量,它还提供了用户使用网站行为的报告,如根据之前访问的特殊页面,统计出有几成用户购买了东西。
为了开启这个服务,web站点管理员需要在他们的web页面中嵌入一小段Javascript程序。这个程序在页面被访问时调用。它记录了Google分析需要的各种信息,如一个用户的标识和获取的页面的信息。Google分析总结这些数据,提供给站点管理员有用的信息。
我们简单描述Google分析使用的两个表。Raw click表(大约200TB)每一行保存着一个终端用户的会话信息。行名是一个由网站的名字和会话信息建立的时间组成的元祖。这种模式保证了访问同一个网站的会话信息是相邻的,并且是按着时间顺序排列的。这个表压缩成原始大小的14%。
Summary表(大约20TB)保存着每个站点的预定义的各种汇总信息。这个表的数据是由周期性的调用MapReduce进程处理raw click表中的数据产生的,每个MapReduce进程从raw click表中获取最新的数据。整个系统的吞吐量是受限于GFS的吞吐量的。这个表能压缩到原始大小的29%。
8.2 Google Earth
Google提供了一组的服务,用于为用户提供高分辨率的地球表面卫星图像,可以通过网页版的Google地图接口进行访问,也可以通过Google Earth的客户端进行访问。这些产品允许用户浏览地球表面:他们能在不同的分辨率下平移、查看和标注卫星图像。这个系统使用一个表存储预处理的数据,用另外的一些表存储用户的数据。
预处理管道使用了一个表存储原始图像。在预处理过程中,图像被清除并被固化到最终的服务数据中。这个表包含了大约70TB的数据,因此存储在磁盘中。图像数据已经是高效压缩过的了,所以禁用了Bigtable的压缩功能。
图像表中的每一行都与一个地理分片相关。行名需要命名为可以使相邻地理分片能够存放在相连的位置。表包含了一个列族用于记录每个分片的原始数据。这个列族有很多列:基本上每列对应一个原始图片的数据。由于每个分片只是由一些图片组成的,所以这个列族是十分稀疏的。
预处理严重依赖于运行在Bigtable上MapReduce的数据传输。在这些MapReduce工作期间,整个系统中每个tablet服务器每秒处理1MB的数据。
这个服务系统使用了一个表来索引存在GFS中的数据。这个表相对较小(大约500GB)但是对于每个数据中心,它必须低时延的每秒处理数以万计的请求。因此这个表保存在数百个tablet服务器上,并且存储在in-memory的列族中。
8.3 个性化搜索
个性化搜索是一个双向的服务,它记录用户的请求和点击,涉及到各种Google的服务,如网页搜索、图片和新闻。用户能够浏览他们的搜索记录,来查看他们之前的请求和点击,并且他们能够定制基于Google历史使用习惯模式的个性化搜索。
个性化搜索将每个用户的数据存储在Bigtable中。每个用户有一个唯一的用户ID,并基于用户ID分配一个行名。所有用户行为都会存储在这个表中。一个单独的列族保存每个类型的行为(如,这有一个列族专门用来保存所有的web查询)。每个数据元素使用用户行为发生时的时间作为Bigtable的时间戳。个性化搜索通过运行在Bigtable上的MapReduce产生用户的行为概要。这些用户的行为概要用于个性化搜索结果。
个性化搜索数据会复制到几个Bigtable集群中,用于提高可用性和降低由于到客户端的距离所造成的延迟。个性化搜索组最初在Bigtable之上建立了一个客户端的副本机制,以确保所有副本数据的一致性。目前的系统使用了一个建立在服务器中的副本子系统。
个性化搜索存储系统的设计允许其他团队向他们自己的列中增加新的用户信息,并且目前则个系统被很多其他的Google服务使用,需要存放用户配置选项和设置。在很多团队中共享一个表的结果就是产生大量的列族。为了支持共享,我们为Bigtable增加一个简单的配额机制,来限制用户在共享表中的存储空间。这个机制也为使用这个系统进行用户信息存储的各种产品提供了隔离机制。
九、 经验教训
在设计、实现、维护和支持Bigtable过程中,我们得到了有用的经验和一些有趣的教训。
我们得到的一个教训就是,大的分布式系统很容易受到各种故障的影响,不仅仅是标准的网络中断和许多分布式协议中定义的“失败-停止”故障。举个例子,我们遇到了下列原因引起的问题:内存和网络问题、时钟偏差过大、机器挂起、扩展的和不对称的网络分区、我们使用的其它系统的bug(比如Chubby)、GFS配额溢出、计划内和计划外的硬件维护。我们通过这些问题收获了很多经验,我们通过修改各种协议解决了它们。举个例子,我们为我们的RPC机制增加了校验和。我们还通过删除对系统其它部分的假设解决了一些问题。比如,我们不再假设一个给定的Chubby操作只返回一个指定集合内的错误。
另一个教训就是推迟增加一个新功能,直到我们弄清楚这个新功能如何使用。比如,我们起初计划提供支持多用途的API。因为我们不是马上需要使用它们,所以我们就先不实现它们。现在,我们有很多现实的应用运行在Bigtable上,我们能调查它们的真正需求,并且发现大多数应用只需要单行上的事务功能。人们需要分布式事务功能之处,大多数都是用来存储二级索引的,我们计划增加一个特定的机制来实现这个需求。这个新机制的通用性比分布式事务要差,但是效率更高(特别是对于几百行以上的更新操作),并且更符合我们的跨数据中心复制的优化方案。
我们从支持Bigtable中还学到了一个有实践意义的教训,适当的系统级监控相当重要(如:监控Bigtable本身和监控使用Bigtable的客户端)。比如,我们扩展了我们的PRC系统,因此对于一个RPC的例子,它可以为这个RPC详细记录重要的动作。这个功能允许我们检测和修正很多问题,如在tablet上数据结构的锁竞争。当提交Bigtable修改时GFS写入慢,当METADATA tablet不可用时访问METADATA操作被挂起。关于监控的另外一个例子是,每个Bigtable集群都在Chubby中进行注册。这允许我们追踪多有的集群,发现他们有多大,查看它们运行的软件的版本,它们收到了多少流量以及是否存在任何问题。
我们学到的最重要的一课就是简单设计的价值。考虑到我们的系统的大小(大约100000行的非测试代码),随着时间的推移,新的代码以各种意想不到的方式加入进来,我们发现简洁的设计和编码给维护带来了巨大的好处。这方面的一个例子就是我们的tablet服务器的成员协议。我们第一版的协议很简单:master周期性的和tablet服务器签订租约,它们的租约过期后会kill掉自己。不幸的是,这个协议的可用性在网络问题出现时明显的降低了,并且对于master的恢复时间十分敏感。我们又重新几次设计了这个协议,直到这个协议表现良好。然而,最终的协议过于复杂,而且依赖了Chubby很少被其它应用使用的功能特性。我们发现浪费了大量的时间去调试各种古怪的用例,不仅有Bigtable的问题,也有Chubby本身的问题。最后,我们只好放弃这个协议,重新设计了一个新的更为简单的协议,只使用了Chubby广为使用的功能特性。
十、 相关工作
Boxwood项目的组件在一些方面与Chubby、GFS和Bigtable相重叠,因为它也提供了分布式协议、锁、分布式块存储和分布式B树存储。在这些方面,Boxwood组件相对于其它Google服务来说,更倾向于提供更为底层的服务。Boxwood项目的目标是给类似于文件系统或数据库的高层服务提供基础构件,而Bigtable的目的则是为了给客户端应用直接提供数据存储服务的。
许多现有的项目已经攻克了很多问题,实现了广域网上的分布式存储或高级服务。这包括了分布式的Hash表,这个项目最初是由CAN、Chord、Tapestry和Pastry项目发起的。这些系统的主要关注点与Bigtable不同,比如各种不同的带宽、不可信的协作者或者频繁的配置更改等。此外,去中心化和Byzantine错误容忍也不是Bigtable的目的。
依照应用开发者提供的分布式数据存储的模型,我们相信,分布式B树或分布式Hash表提供的key-value对模型有很大的局限性。Key-velue是很有用的组件,但是它们不应该是唯一的提供给开发者的组件。我们选择的模型要不限于简单的key-value对,支持稀疏的半结构化的数据。另外,它依旧足够简单,以能高效的处理平面文件;它也是透明的(通过局域性群组),以允许用户对系统重要的行为进行调整。
有些数据库厂商已经开发出并行数据库,它能够存储海量的数据。Oracle的实时应用集群数据库使用了共享磁盘存储数据(Bigtable使用了GFS),并且有一个分布式锁管理系统(Bigtable使用了Chubby)。IBM的DB2平行版本基于一种类似于Bigtable的无共享架构。每个DB2服务器都复制处理存储在一个关系型数据库中的表的一个子表。两个产品都提供了带有事务功能的完整的关系模型。
Bigtable局域性群组基于列存储的方式实现了类似压缩和磁盘读性能的提升,而不是基于行进行存储,这样的系统主要有C-Stroe和一些商业产品如Sybase IQSenSage、KDB+和MonetDB/X100的ColumnDM存储层。另外的一些系统对平面文件使用了垂直和水平分区,可以实现很好的数据压缩率,如AT&T的Daytona数据库。局域性群组不支持CPU级缓存优化,如Ailamaki中所描述的。
Bigtable使用了memtables和SSTables存储tablet更新的方法,与Log-Structured Merge Tree存储更新索引数据的方法类似。这两个系统中,排序好的数据在写入磁盘前会存在内存中,读操作必须对来自内存和磁盘的数据进行融合。
C-Store和Bigtable有很多共同的特点:两个系统都是用了无共享架构,并且都有两种不同的数据结构,一个用于近期的写操作,另一个用于长时间存储数据,并有一个将数据从一种形式转换到另一种形式的机制。他们的系统API有很大的不同:C-Store操作像一个关系型数据库,而Bigtable则提供了一个更底层的读和写接口,用于支持每台服务器每秒中的数千次操作。C-Store也是一个读优先的关系型DBMS,而Bigtable则在密集读和密集写的应用上都有很好的性能。
Bigtable也必须解决一些所有无共享数据库锁需要面对的、相同类型的负载和内存均衡方面的问题。我们的问题再某种程度上简单一些:(1)我们不需要考虑同一份数据有多个拷贝的问题,同一份数据可能由于视图或索引的原因以不同的形式表现出来;(2)我们让用户决定哪些数据应该放在内存中,哪些应该放在磁盘中,而不是由系统动态的判断;(3)我们的系统中没有复杂的查询操作或优化工作。
十一、 总结
我们已经描述了Bigtable,一个分布式存储系统,用来存储Google的结构化数据。Bigtable集群已经从2005年4月开始用于生产环境,在这之前,我们在设计和实现上大概花费了7人年的时间。截止到2006年8月,已经有超过60个项目使用了Bigtable。我们的用户喜欢Bigtable实现提供的性能和高可用性,并且他们能简单的通过向系统中增加更多的机器来进行扩容,因为他们的资源需求一直在变。
由于Bigtable提供的接口并不常见,一个有趣的问题时我们的用户要去适应它有多困难。新的用户有时不确定如何最好的使用Bigtable的接口,特别是他们已经习惯使用支持通用事务功能的关系型数据库。但是,事实是,Google的很多产品都成功的使用了Bigtable,这证明了我们的设计在实际中工作的很好。
我们在实现Bigtable的几个其它的功能,如支持二级索引,以及支持多Master节点的跨数据中心的复制的Bigtable基础构件。我们已经开始部署Bigtable作为其它产品的服务,这样就可以使不同的团队不需要维护他们自己的Bigtable集群了。随着我们的服务集群的扩容,我们需要通过Bigtable自己处理更多的资源分配问题。
最后,我们发现建设Google自己的存储解决方案带来了明显的优势。通过为Bigtable设计自己的数据模型,使我们的系统有很好的灵活性。此外,由于我们能够控制Bigtable的实现,以及Bigtable使用到的其它的Google的基础构件,这就意味着我们在系统出现瓶颈或效率低下时能够及时解决问题。
十二、 一些个人的理解
12.1 关于table、tablet和SSTable的关系
图7:table、tablet和SSTable的关系
12.2 METADATA表中的数据
这是本人根据这篇论文推测出来的,不一定正确,只是个人的一个理解。
图8:METADATA表中一行的内容
12.3 次压缩和主压缩对删除数据的处理
图9:次压缩和主压缩对删除数据的处理
转载于:https://www.cnblogs.com/geekma/archive/2013/05/30/3108391.html
你可能感兴趣的:(Google论文之一----Bigtable学习翻译)
【OpenCV入门学习--python】绘图函数
喜欢星星的田螺姑娘
OpenCV opencv python 学习
源代码:(查看教材《OpenCV-Python中文教程》段力辉译)importnumpyasnpimportcv2#Createablackimageimg=np.zeros((512,512,3),np.uint8)#将所有像素点的各通道数值赋0#其中“3”是三个通道的意思#np.zeros函数用于创建一个数值全为0的矩阵,np.ones用于创建一个数值全为1的矩阵#Drawadiagonalb
Mac pnpm安装
是二牙
前端 vue.js
安装pnpm的时候一定要把npm更新到最新版不然pnpm下载不成功。(更新npm):sudonpminstall-gnpm(安装pnpm:)sudonpminstall-gpnpm检验安装是否成功:pnpm--version项目内安装依赖:pnpminstall/运行项目:pnpmdev最近在开发vue3的项目后续应该会更新一些关于v3的笔记以往都是开发的v2现在开始学习v3如果写的不对的地方可以
行为识别的方法
人工智能专属驿站
深度学习
行为识别主要有以下几大类方法,每类方法各有特点及典型算法:传统方法特点:利用手工设计特征对行为进行表征,再用统计学习的分类方法进行识别。需一定专业知识设计特征,耗费人力物力,对复杂场景、遮挡等适应性差,但对简单背景、规则动作识别效果尚可。典型算法:时空关键点(Space-TimeInterestPoints):基于视频图像中的关键点在时空维度上的变化来提取动作特征,但可能忽略视频细节,泛化能力较弱
机器学习和深度学习的概念
你好呀我是裤裤
深度学习笔记 机器学习 深度学习 人工智能
MachineLearning机器学习,可以看作是找一个函数。这个函数是人类找不到的,所以交给机器来找。DifferenttypesofFunctions**Regression:**函数的输出是一个数值forexample:**Classification:**给出选项,让机器去选择。forexample:检测一个邮件是不是垃圾文件,就可以通过这个来做。选项是两个:垃圾文件or非垃圾文件。下面,
Pytorch实现:LSTM-火灾温度预测
骑猪玩狗
pytorch lstm 人工智能
本文为365天深度学习训练营中的学习记录博客原作者:K同学啊前期工作语言环境:Python3.9.18编译器:JupyterLab深度学习环境:Pytorch1.12.11.设置GPUimporttorchimporttorch.nnasnnimporttorchvisionfromtorchvisionimporttransforms,datasetsimportos,PIL,pathlibde
Python学习路线
Python_JC
python
Python是一门易学易用的编程语言,广泛应用于数据处理、Web开发、人工智能、自动化运维、游戏开发等领域。本篇文章将介绍Python的学习路线以及一些值得参考的书籍。Python学习路线Python的学习路线主要包括以下几个方面:掌握Python基础知识:了解变量、数据类型、表达式、流程控制、函数、模块等基础概念。学习Python面向对象编程:学习类、对象、继承、多态等面向对象编程的概念和技术。
【2025最新计算机毕业设计】基于SSM的旅游与自然保护平台【提供源码+答辩PPT+文档+项目部署】
万码堂源码
计算机毕设精品实战案例 实战项目源码 课程设计 vue.js 前端 计算机毕业设计 毕设项目 spring boot
作者简介:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流。✌主要内容:Java项目、Python项目、前端项目、PHP、ASP.NET、人工智能与大数据、单片机开发、物联网设计与开发设计、简历模板、学习资料、面试题库、技术互助、就业指导等。业务范围:免费功能设计、开题报告、任务书
【源码+文档】基于SpringBoot+Vue旅游网站系统【提供源码+答辩PPT+参考文档+项目部署】
万码堂源码
实战项目源码 计算机毕设精品实战案例 spring boot vue.js 旅游
作者简介:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流。✌主要内容:Java项目、Python项目、前端项目、PHP、ASP.NET、人工智能与大数据、单片机开发、物联网设计与开发设计、简历模板、学习资料、面试题库、技术互助、就业指导等。业务范围:免费功能设计、开题报告、任务书
【大数据学习 | kafka】kafka的组件架构
Vez'nan的幸福生活
大数据 mysql oracle json sql kafka
broker:每个kafka的机器节点都会运行一个进程,这个进程叫做broker,负责管理自身的topic和partition,以及数据的存储和处理,因为kafka是集群形式的,所以一个集群中会存在多个broker,但是kafka的整体又不是一个主从集群,需要选举出来一个broker节点为主节点,管理整个集群中所有的数据和操作,以及所有节点的协同工作。每个broker上面都存在一个controll
深度学习项目--基于LSTM的火灾预测研究(pytorch实现)
羊小猪~~
RNN LSTM神经网络案例 机器学习/数据分析案例 深度学习 lstm pytorch 人工智能 机器学习 rnn gru
本文为365天深度学习训练营中的学习记录博客原作者:K同学啊前言LSTM模型一直是一个很经典的模型,这个模型当然也很复杂,一般需要先学习RNN、GRU模型之后再学,GRU、LSTM的模型讲解将在这两天发布更新,其中:深度学习基础–一文搞懂RNN深度学习基础–GRU学习笔记(李沐《动手学习深度学习》)这一篇:是基于LSTM模型火灾预测研究,讲述了如何构建时间数据、模型如何构建、pytorch中LST
Oracle 学习全攻略
来恩1003
Oracle oracle 学习 数据库
Oracle学习资料Oracle学习资料Oracle学习资料在当今信息技术蓬勃发展的时代,数据库管理系统起着举足轻重的作用,而Oracle作为行业内的领军者,以其强大、稳定、高效的特性,广泛应用于金融、电信、政府等诸多关键领域。若你渴望踏入数据库领域的高阶殿堂,开启Oracle的学习之旅无疑是明智之举。以下将为你详细阐述Oracle的学习路径。一、入门奠基:环境搭建与初步认知了解Oracle体系架
opencv基础学习
yzx991013
OpenCV基础全集 opencv 学习 人工智能
3.3OTSU阈值代码:importcv2importnumpyasnpfrommatplotlibimportpyplotaspltimg=cv2.imread('./image/otus.png',0)#简单阈值ret1,th1=cv2.threshold(img,127,255,cv2.THRESH_BINARY)#二值化阈值ret2,th2=cv2.threshold(img,0,255,
PLUTO:突破基于模仿学习的自动驾驶规划极限
硅谷秋水
机器学习 自动驾驶 人工智能 自动驾驶 人工智能 机器学习 计算机视觉
24年4月来自香港科技大学的论文“PLUTO:PushingtheLimitofImitationLearning-basedPlanningforAutonomousDriving”。PLUTO,突破基于模仿学习的自动驾驶规划极限。改进来自三个关键方面:一种纵向横向感知模型架构,可实现灵活多样的驾驶行为;一种创新的辅助损失计算方法,可广泛应用且可高效地进行批量计算;一种利用对比学习的训练框架,采
LargeAD:用于自动驾驶的大规模跨传感器数据预训练
硅谷秋水
自动驾驶 计算机视觉 机器学习 自动驾驶 人工智能 机器学习 计算机视觉
25年1月来自新加坡国立大学、南京航空航天、德国Bremerhaven技术大学、上海AI实验室、香港科技大学和香港大学的论文“LargeAD:Large-ScaleCross-SensorDataPretrainingforAutonomousDriving”。视觉基础模型(VFM)的最新进展彻底改变2D视觉感知,但它们在3D场景理解方面的潜力,特别是在自动驾驶应用中的潜力仍未得到充分探索。Lar
Python AI教程之二十一:监督学习之支持向量机(SVM)算法
潜洋
人工智能 Python中级 支持向量机 算法 机器学习 python
支持向量机(SVM)算法支持向量机(SVM)是一种功能强大的机器学习算法,广泛用于线性和非线性分类以及回归和异常值检测任务。SVM具有很强的适应性,适用于各种应用,例如文本分类、图像分类、垃圾邮件检测、笔迹识别、基因表达分析、人脸检测和异常检测。SVM特别有效,因为它们专注于寻找目标特征中不同类别之间的最大分离超平面,从而使其对二分类和多分类都具有鲁棒性。在本大纲中,我们将探讨支持向量机(SVM)
架构师反向代理Haproxy+压力测试 - 学习笔记
无影V随风
学习笔记 linux相关
一.Apache的反向代理(生产不建议使用)1.Apache的编译安装:yuminstallapr-develapr-util-develpcre-developenssl-develcd/usr/local/src/wgethttp://archive.apache.org/dist/httpd/httpd-2.4.18.tar.gztar-zxvfhttpd-2.4.18.tar.gzcdht
SSM框架从入门到精通
布朗克168
# SSM Java开发相关后端框架 经验分享 spring mybatis java
文章目录一、SSM框架概述二、Spring基础(一)配置与依赖注入(二)Bean的生命周期(三)事务管理三、SpringMVC入门(一)控制器实现(二)请求映射(三)模型数据绑定四、MyBatis学习(一)SQL映射文件(二)动态SQL(三)实体类与数据映射五、整合SSM(一)基础集成(二)异常处理六、实战应用(一)项目构建(二)调试与优化(三)常见问题解决七、总结一、SSM框架概述SSM框架是由
Oracle从入门到精通
布朗克168
# Oracle学习 数据库 经验分享 数据库 oracle
文章目录一、Oracle基础认知二、安装与配置(一)下载与准备(二)安装步骤(三)基本操作三、数据库查询与管理(一)查询数据(二)备份与恢复四、高级功能探索(一)数据库优化(二)存储过程与触发器五、Java与Oracle集成(一)连接Oracle数据库(二)使用JPA六、进阶学习(一)分页(二)视图(三)存储过程(四)约束(五)序列(六)同义词(七)索引一、Oracle基础认知Oracle数据库是
odoo 学习
卫玠_juncheng
python
环境问题psycopg2-binary==2.9.9python-ldap==3.4.0gevent==22.10.2环境问题。最后使用的是conda环境pythonodoo-bin-codoo.conf-ibase使用了conda环境执行命令:pythonodoo-bin-codoo.conf-ibase开发顺序打开视图添加菜单自定义表单自定义树自定义脚手架安装开发者模式进入开发者模式:?deb
Cortex-M3(转)
oldbalck
嵌入式 操作系统 系统架构
原来一直在Cortex-A8上做相关算法的开发和移植,最近要在Cortex-M3上实现一小功能,所以要了解一下Cortex-M3架构,在网上看到这篇blog不错,特转载一下。http://blog.mcuol.com/User/share_119/Article/39534_1.htm首先,在学习Cortex-M3时,我们必须要知道必要的缩略语。整理如下:AMBA:先进单片机总线架构ADK:AMB
Python globals 函数 - Python零基础入门教程
苹果酱0567
面试题汇总与解析 课程设计 spring boot layui 毕业设计 java
目录一.Pythonglobals函数语法二.Pythonglobals函数使用三.猜你喜欢零基础Python学习路线推荐:Python学习目录>>Python基础入门在前面我们讲解了Python内置函数locals,内置函数locals直接以字典的形式返回当前位置的所有局部变量,今天需要介绍的是另外一个Python内置函数globals,该函数直接以字典dict的形式返回当前位置的所有全局变量;
【学术会议论文投稿】Spring Boot实战:零基础打造你的Web应用新纪元
m0_54804970
spring boot 前端 后端
第七届人文教育与社会科学国际学术会议(ICHESS2024)_艾思科蓝_学术一站式服务平台更多学术会议请看:https://ais.cn/u/nuyAF3目录一、SpringBoot简介1.1SpringBoot的诞生背景1.2SpringBoot的核心特性二、搭建开发环境2.1安装Java环境2.2安装IDE2.3安装Maven或Gradle三、创建SpringBoot项目3.1使用Spring
Cortex-M3(1)
iteye_14721
网络 c/c++ 嵌入式
大家听说过Cortex-M3吗?在嵌入式处理器的世界,cortex-M3是一位人见人爱的后生。它的成本和功耗低,可配置性很高。如今,很多ARM的工程师加入了cortex-M3的学习与开发中,WIZnet一直都是行业的领先者,即将上市的新产品W7200正是加入了cortex-M3处理器的全硬件TCP/IP协议栈芯片,通过利用它的优势,相信会得到更多客户的青睐。下面,广大的嵌入式爱好者可以跟随我们一起
每天五分钟深度学习框架pytorch:基于vgg块搭建VGG卷积神经网络
每天五分钟玩转人工智能
深度学习框架pytorch 深度学习 pytorch cnn VGG 卷积神经网络
本文重点前面我们使用pytorch搭建了vgg块,本文我们使用vgg块搭建卷积神经网络VGG16,我们先来看一下vgg16的模型结构是什么样的:搭建vgg16importtorchfromtorchimportnndefvgg_block(num_convs,in_channels,out_channels):net=[nn.Conv2d(in_channels,out_channels,kern
Go语言之路————func
一颗知足的心
golang 开发语言 后端
Go语言之路————func前言1.最基本的方法定义2.方法中参数的传递3.方法中的返回值4.匿名方法5.闭包的概念6.defer关键词前言我是一名多年Java开发人员,因为工作需要现在要学习go语言,Go语言之路是一个系列,记录着我从0开始接触Go,到后面能正常完成工作上的业务开发的过程,如果你也是个小白或者转Go语言的,希望我这篇文章对你有所帮助。有关go其他基础的内容的文章大家可以查看我的主
Go语言之路————条件控制:if、for、switch
一颗知足的心
golang 开发语言
Go语言之路————if、for、switch前言ifforswitchgoto和label前言我是一名多年Java开发人员,因为工作需要现在要学习go语言,Go语言之路是一个系列,记录着我从0开始接触Go,到后面能正常完成工作上的业务开发的过程,如果你也是个小白或者转Go语言的,希望我这篇文章对你有所帮助。有关go其他基础的内容的文章大家可以查看我的主页,接下来主要就是把这个系列更完,更完之后我
深度学习 Pytorch 张量(Tensor)的创建和常用方法
白白糖
深度学习pytorch python 深度学习 pytorch 人工智能
1张量的基本创建及其类型和Numpy中的array一样,张量的本质也是结构化地组织了大量的数据。并且在实际操作中,张量的创建和基本功能也与其非常类似。1.1张量(Tensor)函数创建方法张量的最基本创建方法和Numpy中创建Array的格式一致。#Numpy创建数组importnumpyasnp#导入numpya=np.array([1,2,3])importtorch#首次使用,导入torch
PyTorch 神经协同过滤 (NCF) 推荐系统教程
陌北v1
pytorch python NCF 神经协同过滤
目录教程概述1.神经协同过滤模型概述NCF模型的主要组成部分:2.数据加载与预处理3.定义神经协同过滤模型4.训练模型5.模型评估6.推荐物品7.完整示例8.总结在本教程中,我们将使用PyTorch实现一个神经协同过滤(NeuralCollaborativeFiltering,简称NCF)推荐系统。神经协同过滤是一种基于深度学习的推荐系统模型,通过学习用户和物品的嵌入表示来预测用户对物品的评分,进
大厂架构之极致缓存策略实战与原理剖析
喜欢猪猪
spring java 后端
一、背景知识随着互联网的快速发展,数据量呈爆炸式增长,高并发、低延迟成为现代应用系统的基本要求。在这样的背景下,缓存策略成为提升系统性能、降低数据库压力的关键技术之一。无论是CPU中的多级缓存,还是分布式系统中的Redis、Memcached等缓存组件,都在各自的领域内发挥着重要作用。本文将深入探讨大厂架构中的极致缓存策略,从背景知识、概念、功能点、业务场景、底层原理等方面进行详细剖析,并通过JA
高通CamX框架学习系列:Bring Up Actuator
zxlhc
linux
这篇文章主要介绍在高通平台的CamX框架里面,如何去点亮摄像头马达,需要修改哪些相关的文件。一、大体浏览先来个整体的浏览,看下涉及到需要修改哪些目录下的文件。可以看下图,要让马达运转起来,我们需要修改的基本就是下面4个目录下的相关文件。1、各种型号的马达配置文件目录vendor/qcom/proprietary/chi-cdk/oem/qcom/actuator2、Camerasensor配置文件
对于规范和实现,你会混淆吗?
yangshangchuan
HotSpot
昨晚和朋友聊天,喝了点咖啡,由于我经常喝茶,很长时间没喝咖啡了,所以失眠了,于是起床读JVM规范,读完后在朋友圈发了一条信息:
JVM Run-Time Data Areas:The Java Virtual Machine defines various run-time data areas that are used during execution of a program. So
android 网络
百合不是茶
网络
android的网络编程和java的一样没什么好分析的都是一些死的照着写就可以了,所以记录下来 方便查找 , 服务器使用的是TomCat
服务器代码; servlet的使用需要在xml中注册
package servlet;
import java.io.IOException;
import java.util.Arr
[读书笔记]读法拉第传
comsci
读书笔记
1831年的时候,一年可以赚到1000英镑的人..应该很少的...
要成为一个科学家,没有足够的资金支持,很多实验都无法完成
但是当钱赚够了以后....就不能够一直在商业和市场中徘徊......
随机数的产生
沐刃青蛟
随机数
c++中阐述随机数的方法有两种:
一是产生假随机数(不管操作多少次,所产生的数都不会改变)
这类随机数是使用了默认的种子值产生的,所以每次都是一样的。
//默认种子
for (int i = 0; i < 5; i++)
{
cout<<
PHP检测函数所在的文件名
IT独行者
PHP 函数
很简单的功能,用到PHP中的反射机制,具体使用的是ReflectionFunction类,可以获取指定函数所在PHP脚本中的具体位置。 创建引用脚本。
代码:
[php]
view plain
copy
// Filename: functions.php
<?php&nbs
银行各系统功能简介
文强chu
金融
银行各系统功能简介 业务系统 核心业务系统 业务功能包括:总账管理、卡系统管理、客户信息管理、额度控管、存款、贷款、资金业务、国际结算、支付结算、对外接口等 清分清算系统 以清算日期为准,将账务类交易、非账务类交易的手续费、代理费、网络服务费等相关费用,按费用类型计算应收、应付金额,经过清算人员确认后上送核心系统完成结算的过程 国际结算系
Python学习1(pip django 安装以及第一个project)
小桔子
python django pip
最近开始学习python,要安装个pip的工具。听说这个工具很强大,安装了它,在安装第三方工具的话so easy!然后也下载了,按照别人给的教程开始安装,奶奶的怎么也安装不上!
第一步:官方下载pip-1.5.6.tar.gz, https://pypi.python.org/pypi/pip easy!
第二部:解压这个压缩文件,会看到一个setup.p
php 数组
aichenglong
PHP 排序 数组 循环 多维数组
1 php中的创建数组
$product = array('tires','oil','spark');//array()实际上是语言结构而不 是函数
2 如果需要创建一个升序的排列的数字保存在一个数组中,可以使用range()函数来自动创建数组
$numbers=range(1,10)//1 2 3 4 5 6 7 8 9 10
$numbers=range(1,10,
安装python2.7
AILIKES
python
安装python2.7
1、下载可从 http://www.python.org/进行下载#wget https://www.python.org/ftp/python/2.7.10/Python-2.7.10.tgz
2、复制解压
#mkdir -p /opt/usr/python
#cp /opt/soft/Python-2
java异常的处理探讨
百合不是茶
JAVA异常
//java异常
/*
1,了解java 中的异常处理机制,有三种操作
a,声明异常
b,抛出异常
c,捕获异常
2,学会使用try-catch-finally来处理异常
3,学会如何声明异常和抛出异常
4,学会创建自己的异常
*/
//2,学会使用try-catch-finally来处理异常
getElementsByName实例
bijian1013
element
实例1:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/x
探索JUnit4扩展:Runner
bijian1013
java 单元测试 JUnit
参加敏捷培训时,教练提到Junit4的Runner和Rule,于是特上网查一下,发现很多都讲的太理论,或者是举的例子实在是太牵强。多搜索了几下,搜索到两篇我觉得写的非常好的文章。
文章地址:http://www.blogjava.net/jiangshachina/archive/20
[MongoDB学习笔记二]MongoDB副本集
bit1129
mongodb
1. 副本集的特性
1)一台主服务器(Primary),多台从服务器(Secondary)
2)Primary挂了之后,从服务器自动完成从它们之中选举一台服务器作为主服务器,继续工作,这就解决了单点故障,因此,在这种情况下,MongoDB集群能够继续工作
3)挂了的主服务器恢复到集群中只能以Secondary服务器的角色加入进来
2
【Spark八十一】Hive in the spark assembly
bit1129
assembly
Spark SQL supports most commonly used features of HiveQL. However, different HiveQL statements are executed in different manners:
1. DDL statements (e.g. CREATE TABLE, DROP TABLE, etc.)
Nginx问题定位之监控进程异常退出
ronin47
nginx在运行过程中是否稳定,是否有异常退出过?这里总结几项平时会用到的小技巧。
1. 在error.log中查看是否有signal项,如果有,看看signal是多少。
比如,这是一个异常退出的情况:
$grep signal error.log
2012/12/24 16:39:56 [alert] 13661#0: worker process 13666 exited on s
No grammar constraints (DTD or XML schema).....两种解决方法
byalias
xml
方法一:常用方法 关闭XML验证
工具栏:windows => preferences => xml => xml files => validation => Indicate when no grammar is specified:选择Ignore即可。
方法二:(个人推荐)
添加 内容如下
<?xml version=
Netty源码学习-DefaultChannelPipeline
bylijinnan
netty
package com.ljn.channel;
/**
* ChannelPipeline采用的是Intercepting Filter 模式
* 但由于用到两个双向链表和内部类,这个模式看起来不是那么明显,需要仔细查看调用过程才发现
*
* 下面对ChannelPipeline作一个模拟,只模拟关键代码:
*/
public class Pipeline {
MYSQL数据库常用备份及恢复语句
chicony
mysql
备份MySQL数据库的命令,可以加选不同的参数选项来实现不同格式的要求。
mysqldump -h主机 -u用户名 -p密码 数据库名 > 文件
备份MySQL数据库为带删除表的格式,能够让该备份覆盖已有数据库而不需要手动删除原有数据库。
mysqldump -–add-drop-table -uusername -ppassword databasename > ba
小白谈谈云计算--基于Google三大论文
CrazyMizzz
Google 云计算 GFS
之前在没有接触到云计算之前,只是对云计算有一点点模糊的概念,觉得这是一个很高大上的东西,似乎离我们大一的还很远。后来有机会上了一节云计算的普及课程吧,并且在之前的一周里拜读了谷歌三大论文。不敢说理解,至少囫囵吞枣啃下了一大堆看不明白的理论。现在就简单聊聊我对于云计算的了解。
我先说说GFS
&n
hadoop 平衡空间设置方法
daizj
hadoop balancer
在hdfs-site.xml中增加设置balance的带宽,默认只有1M:
<property>
<name>dfs.balance.bandwidthPerSec</name>
<value>10485760</value>
<description&g
Eclipse程序员要掌握的常用快捷键
dcj3sjt126com
编程
判断一个人的编程水平,就看他用键盘多,还是鼠标多。用键盘一是为了输入代码(当然了,也包括注释),再有就是熟练使用快捷键。 曾有人在豆瓣评
《卓有成效的程序员》:“人有多大懒,才有多大闲”。之前我整理了一个
程序员图书列表,目的也就是通过读书,让程序员变懒。 程序员作为特殊的群体,有的人可以这么懒,懒到事情都交给机器去做,而有的人又可以那么勤奋,每天都孜孜不倦得
Android学习之路
dcj3sjt126com
Android学习
转自:http://blog.csdn.net/ryantang03/article/details/6901459
以前有J2EE基础,接触JAVA也有两三年的时间了,上手Android并不困难,思维上稍微转变一下就可以很快适应。以前做的都是WEB项目,现今体验移动终端项目,让我越来越觉得移动互联网应用是未来的主宰。
下面说说我学习Android的感受,我学Android首先是看MARS的视
java 遍历Map的四种方法
eksliang
java HashMap java 遍历Map的四种方法
转载请出自出处:
http://eksliang.iteye.com/blog/2059996
package com.ickes;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
/**
* 遍历Map的四种方式
【精典】数据库相关相关
gengzg
数据库
package C3P0;
import java.sql.Connection;
import java.sql.SQLException;
import java.beans.PropertyVetoException;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class DBPool{
自动补全
huyana_town
自动补全
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml&quo
jquery在线预览PDF文件,打开PDF文件
天梯梦
jquery
最主要的是使用到了一个jquery的插件jquery.media.js,使用这个插件就很容易实现了。
核心代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.
ViewPager刷新单个页面的方法
lovelease
android viewpager tag 刷新
使用ViewPager做滑动切换图片的效果时,如果图片是从网络下载的,那么再子线程中下载完图片时我们会使用handler通知UI线程,然后UI线程就可以调用mViewPager.getAdapter().notifyDataSetChanged()进行页面的刷新,但是viewpager不同于listview,你会发现单纯的调用notifyDataSetChanged()并不能刷新页面
利用按位取反(~)从复合枚举值里清除枚举值
草料场
enum
以 C# 中的 System.Drawing.FontStyle 为例。
如果需要同时有多种效果,
如:“粗体”和“下划线”的效果,可以用按位或(|)
FontStyle style = FontStyle.Bold | FontStyle.Underline;
如果需要去除 style 里的某一种效果,
Linux系统新手学习的11点建议
刘星宇
编程 工作 linux 脚本
随着Linux应用的扩展许多朋友开始接触Linux,根据学习Windwos的经验往往有一些茫然的感觉:不知从何处开始学起。这里介绍学习Linux的一些建议。
一、从基础开始:常常有些朋友在Linux论坛问一些问题,不过,其中大多数的问题都是很基础的。例如:为什么我使用一个命令的时候,系统告诉我找不到该目录,我要如何限制使用者的权限等问题,这些问题其实都不是很难的,只要了解了 Linu
hibernate dao层应用之HibernateDaoSupport二次封装
wangzhezichuan
DAO Hibernate
/**
* <p>方法描述:sql语句查询 返回List<Class> </p>
* <p>方法备注: Class 只能是自定义类 </p>
* @param calzz
* @param sql
* @return
* <p>创建人:王川</p>
* <p>创建时间:Jul