文章来自于:http://www.cnblogs.com/geekma/archive/2013/05/30/3108391.html
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/guoyongrong/p/3700974.html
你可能感兴趣的:(转:Google论文之一----Bigtable学习翻译)
情绪觉察日记第37天
露露_e800
今天是家庭关系规划师的第二阶最后一天,慧萍老师帮我做了个案,帮我处理了埋在心底好多年的一份恐惧,并给了我深深的力量!这几天出来学习,爸妈过来婆家帮我带小孩,妈妈出于爱帮我收拾东西,并跟我先生和婆婆产生矛盾,妈妈觉得他们没有照顾好我…。今晚回家见到妈妈,我很欣赏她并赞扬她,妈妈说今晚要跟我睡我说好,当我们俩躺在床上准备睡觉的时候,我握着妈妈的手对她说:妈妈这几天辛苦你了,你看你多利害把我们的家收拾得
机器学习与深度学习间关系与区别
ℒℴѵℯ心·动ꦿ໊ོ꫞
人工智能 学习 深度学习 python
一、机器学习概述定义机器学习(MachineLearning,ML)是一种通过数据驱动的方法,利用统计学和计算算法来训练模型,使计算机能够从数据中学习并自动进行预测或决策。机器学习通过分析大量数据样本,识别其中的模式和规律,从而对新的数据进行判断。其核心在于通过训练过程,让模型不断优化和提升其预测准确性。主要类型1.监督学习(SupervisedLearning)监督学习是指在训练数据集中包含输入
铭刻于星(四十二)
随风至
69夜晚,绍敏同学做完功课后,看了眼房外,没听到动静才敢从书包的夹层里拿出那个心形纸团。折痕压得很深,都有些旧了,想来是已经写好很久了。绍敏同学慢慢地、轻轻地捏开折叠处,待到全部拆开后,又反复抚平纸张,然后仔细地一字字默看。只是开头的三个字是第一次看到,让她心漏跳了几拍。“亲爱的绍敏:从四年级的时候,我就喜欢你了,但是我一直不敢说,怕影响你学习。六年级的时候听说有人跟你表白,你接受了,我很难过,但
UI学习——cell的复用和自定义cell
Magnetic_h
ui 学习
目录cell的复用手动(非注册)自动(注册)自定义cellcell的复用在iOS开发中,单元格复用是一种提高表格(UITableView)和集合视图(UICollectionView)滚动性能的技术。当一个UITableViewCell或UICollectionViewCell首次需要显示时,如果没有可复用的单元格,则视图会创建一个新的单元格。一旦这个单元格滚动出屏幕,它就不会被销毁。相反,它被添
学点心理知识,呵护孩子健康
静候花开_7090
昨天听了华中师范大学教育管理学系副教授张玲老师的《哪里才是学生心理健康的最后庇护所,超越教育与技术的思考》的讲座。今天又重新学习了一遍,收获匪浅。张玲博士也注意到了当今社会上的孩子由于心理问题导致的自残、自杀及伤害他人等恶性事件。她向我们普及了一个重要的命题,她说心理健康的一些基本命题,我们与我们通常的一些教育命题是不同的,她还举了几个例子,让我们明白我们原来以为的健康并非心理学上的健康。比如如果
《策划经理回忆录之二》
路基雅虎
话说三年变六年,飘了,飘了……眨眼,2013年5月,老吴回到了他的家乡——油城从新开启他的工作幻想症生涯。很庆幸,这是一家很有追求,同时敢于尝试的,且实力不容低调的新星房企——金源置业(前身泰源置业)更值得庆幸的是第一个盘就是油城十路的标杆之一:金源盛世。2013年5月,到2015年11月,两年的陪伴,迎来了一场大爆发。2000个筹,5万/筹,直接回笼1个亿!!!这……让我开始认真审视这座看似五线
LocalDateTime 转 String
igotyback
java 开发语言
importjava.time.LocalDateTime;importjava.time.format.DateTimeFormatter;publicclassMain{publicstaticvoidmain(String[]args){//获取当前时间LocalDateTimenow=LocalDateTime.now();//定义日期格式化器DateTimeFormatterformat
向内而求
陈陈_19b4
10月27日,阴。阅读书目:《次第花开》。作者:希阿荣博堪布,是当今藏传佛家宁玛派最伟大的上师法王,如意宝晋美彭措仁波切颇具影响力的弟子之一。多年以来,赴海内外各地弘扬佛法,以正式授课、现场开示、发表文章等多种方法指导佛学弟子修行佛法。代表作《寂静之道》、《生命这出戏》、《透过佛法看世界》自出版以来一直是佛教类书籍中的畅销书。图片发自App金句:1.佛陀说,一切痛苦的根源在于我们长期以来对自身及外
ArcGIS栅格计算器常见公式(赋值、0和空值的转换、补充栅格空值)
研学随笔
arcgis 经验分享
我们在使用ArcGIS时通常经常用到栅格计算器,今天主要给大家介绍我日常中经常用到的几个公式,供大家参考学习。将特定值(-9999)赋值为0,例如-9999.Con("raster"==-9999,0,"raster")2.给空值赋予特定的值(如0)Con(IsNull("raster"),0,"raster")3.将特定的栅格值(如1)赋值为空值,其他保留原值SetNull("raster"==
三大师传
beca酱
巴尔扎克的作品被誉为“法国社会的一面镜子”。文学大师维克多·雨果对巴尔扎克的评价是:“在最伟大的人物中间,巴尔扎克是名列前茅者;在最优秀的人物中间,巴尔扎克是佼佼者之一。”一个原本寂寂无名的小人物,从地中海的某个海岛上,只身一人来到巴黎,没有朋友,也没有名望。作为一个一文不名的外乡人,凭着赤手空拳赢得了巴黎,征服了整个法兰西,并且赢得了世界。这个人就是十九世纪法国伟大的军事家、政治家,法兰西第一帝
【一起学Rust | 设计模式】习惯语法——使用借用类型作为参数、格式化拼接字符串、构造函数
广龙宇
一起学Rust # Rust设计模式 rust 设计模式 开发语言
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录前言一、使用借用类型作为参数二、格式化拼接字符串三、使用构造函数总结前言Rust不是传统的面向对象编程语言,它的所有特性,使其独一无二。因此,学习特定于Rust的设计模式是必要的。本系列文章为作者学习《Rust设计模式》的学习笔记以及自己的见解。因此,本系列文章的结构也与此书的结构相同(后续可能会调成结构),基本上分为三个部分
回溯 Leetcode 332 重新安排行程
mmaerd
Leetcode刷题学习记录 leetcode 算法 职场和发展
重新安排行程Leetcode332学习记录自代码随想录给你一份航线列表tickets,其中tickets[i]=[fromi,toi]表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。所有这些机票都属于一个从JFK(肯尼迪国际机场)出发的先生,所以该行程必须从JFK开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。例如,行程[“JFK”,“LGA”]与[“JFK”,“LGB
Python数据分析与可视化实战指南
William数据分析
python python 数据
在数据驱动的时代,Python因其简洁的语法、强大的库生态系统以及活跃的社区,成为了数据分析与可视化的首选语言。本文将通过一个详细的案例,带领大家学习如何使用Python进行数据分析,并通过可视化来直观呈现分析结果。一、环境准备1.1安装必要库在开始数据分析和可视化之前,我们需要安装一些常用的库。主要包括pandas、numpy、matplotlib和seaborn等。这些库分别用于数据处理、数学
2019-12-22-22:30
涓涓1016
今天是冬至,写下我的日更,是因为这两天的学习真的是能量的满满,让我看到了自己,未来另外一种可能性,也让我看到了这两年这几年的过程中我所接受那些痛苦的来源。一切的根源和痛苦都来自于人生,家庭,而你的原生家庭,你的爸爸和妈妈,是因为你这个灵魂在那一刻选择他们作为你的爸爸和妈妈来的,所以你得接受他,你得接纳他,他就是因为他的存在而给你的学习和成长带来这些痛苦,那其实是你必然要经历的这个过程,当你去接纳的
第四天旅游线路预览——从换乘中心到喀纳斯湖
陟彼高冈yu
基于Google earth studio 的旅游规划和预览 旅游
第四天:从贾登峪到喀纳斯风景区入口,晚上住宿贾登峪;换乘中心有4路车,喀纳斯①号车,去喀纳斯湖,路程时长约5分钟;将上面的的行程安排进行动态展示,具体步骤见”Googleearthstudio进行动态轨迹显示制作过程“、“Googleearthstudio入门教程”和“Googleearthstudio进阶教程“相关内容,得到行程如下所示:Day4-2-480p
直抒《紫罗兰永恒花园外传》
雷姆的黑色童话
没看过《紫罗兰永恒花园》的我莫名的看完了《紫罗兰永恒花园外传》,又莫名的被故事中的姐妹之情狠狠地感动了的一把。感动何在:困苦中相依为命的姐妹二人被迫分离,用一个人的自由换取另一个人的幸福。之后,虽相隔不知几许依旧心心念念彼此牵挂。这种深深的姐妹情谊就是令我为之动容的所在。贝拉和泰勒分别影片开始,海天之间一个孩童凭栏眺望,手中拿着折旧的信纸。镜头一转,挑灯伏案的薇尔莉特正在打字机前奋笔疾书。这些片段
Google earth studio 简介
陟彼高冈yu
旅游
GoogleEarthStudio是一个基于Web的动画工具,专为创作使用GoogleEarth数据的动画和视频而设计。它利用了GoogleEarth强大的三维地图和卫星影像数据库,使用户能够轻松地创建逼真的地球动画、航拍视频和动态地图可视化。网址为https://www.google.com/earth/studio/。GoogleEarthStudio是一个基于Web的动画工具,专为创作使用G
将cmd中命令输出保存为txt文本文件
落难Coder
Windows cmd window
最近深度学习本地的训练中我们常常要在命令行中运行自己的代码,无可厚非,我们有必要保存我们的炼丹结果,但是复制命令行输出到txt是非常麻烦的,其实Windows下的命令行为我们提供了相应的操作。其基本的调用格式就是:运行指令>输出到的文件名称或者具体保存路径测试下,我打开cmd并且ping一下百度:pingwww.baidu.com>./data.txt看下相同目录下data.txt的输出:如果你再
四章-32-点要素的聚合
彩云飘过
本文基于腾讯课堂老胡的课《跟我学Openlayers--基础实例详解》做的学习笔记,使用的openlayers5.3.xapi。源码见1032.html,对应的官网示例https://openlayers.org/en/latest/examples/cluster.htmlhttps://openlayers.org/en/latest/examples/earthquake-clusters.
利用LangChain的StackExchange组件实现智能问答系统
nseejrukjhad
langchain microsoft 数据库 python
利用LangChain的StackExchange组件实现智能问答系统引言在当今的软件开发世界中,StackOverflow已经成为程序员解决问题的首选平台之一。而LangChain作为一个强大的AI应用开发框架,提供了StackExchange组件,使我们能够轻松地将StackOverflow的海量知识库集成到我们的应用中。本文将详细介绍如何使用LangChain的StackExchange组件
GitHub上克隆项目
bigbig猩猩
github
从GitHub上克隆项目是一个简单且直接的过程,它允许你将远程仓库中的项目复制到你的本地计算机上,以便进行进一步的开发、测试或学习。以下是一个详细的步骤指南,帮助你从GitHub上克隆项目。一、准备工作1.安装Git在克隆GitHub项目之前,你需要在你的计算机上安装Git工具。Git是一个开源的分布式版本控制系统,用于跟踪和管理代码变更。你可以从Git的官方网站(https://git-scm.
HTML网页设计制作大作业(div+css) 云南我的家乡旅游景点 带文字滚动
二挡起步
web前端期末大作业 web设计网页规划与设计 html css javascript dreamweaver 前端
Web前端开发技术描述网页设计题材,DIV+CSS布局制作,HTML+CSS网页设计期末课程大作业游景点介绍|旅游风景区|家乡介绍|等网站的设计与制作HTML期末大学生网页设计作业HTML:结构CSS:样式在操作方面上运用了html5和css3,采用了div+css结构、表单、超链接、浮动、绝对定位、相对定位、字体样式、引用视频等基础知识JavaScript:做与用户的交互行为文章目录前端学习路线
Day1笔记-Python简介&标识符和关键字&输入输出
~在杰难逃~
Python python 开发语言 大数据 数据分析 数据挖掘
大家好,从今天开始呢,杰哥开展一个新的专栏,当然,数据分析部分也会不定时更新的,这个新的专栏主要是讲解一些Python的基础语法和知识,帮助0基础的小伙伴入门和学习Python,感兴趣的小伙伴可以开始认真学习啦!一、Python简介【了解】1.计算机工作原理编程语言就是用来定义计算机程序的形式语言。我们通过编程语言来编写程序代码,再通过语言处理程序执行向计算机发送指令,让计算机完成对应的工作,编程
人工智能时代,程序员如何保持核心竞争力?
jmoych
人工智能
随着AIGC(如chatgpt、midjourney、claude等)大语言模型接二连三的涌现,AI辅助编程工具日益普及,程序员的工作方式正在发生深刻变革。有人担心AI可能取代部分编程工作,也有人认为AI是提高效率的得力助手。面对这一趋势,程序员应该如何应对?是专注于某个领域深耕细作,还是广泛学习以适应快速变化的技术环境?又或者,我们是否应该将重点转向AI无法轻易替代的软技能?让我们一起探讨程序员
node.js学习
小猿L
node.js node.js 学习 vim
node.js学习实操及笔记温故node.js,node.js学习实操过程及笔记~node.js学习视频node.js官网node.js中文网实操笔记githubcsdn笔记为什么学node.js可以让别人访问我们编写的网页为后续的框架学习打下基础,三大框架vuereactangular离不开node.jsnode.js是什么官网:node.js是一个开源的、跨平台的运行JavaScript的运行
阶段总结反思
轻争
马上就要进入10月份了,今天做一下前段时间的总结和反思。前段时间,日更、英语、健身、护肤坚持的比较好。阅读、书法坚持的不好。1.中间被迫停更半个多月,其余时间一直在坚持日更挑战。偶尔也有不想写的时候,就做一下摘抄。因为阅读(输入)没跟上来,所以写作(输出)质量有待进一步加强。2.英语做到了一周至少学习5天,每次不少于30分钟,但是小班课没有跟上更新速度,下一步要争取利用零碎时间补听小班课。3.减肥
ARM驱动学习之基础小知识
JT灬新一
ARM 嵌入式 arm开发 学习
ARM驱动学习之基础小知识•sch原理图工程师工作内容–方案–元器件选型–采购(能不能买到,价格)–原理图(涉及到稳定性)•layout画板工程师–layout(封装、布局,布线,log)(涉及到稳定性)–焊接的一部分工作(调试阶段板子的焊接)•驱动工程师–驱动,原理图,layout三部分的交集容易发生矛盾•PCB研发流程介绍–方案,原理图(网表)–layout工程师(gerber文件)–PCB板
ARM驱动学习之5 LEDS驱动
JT灬新一
嵌入式 C 底层 arm开发 学习 单片机
ARM驱动学习之5LEDS驱动知识点:•linuxGPIO申请函数和赋值函数–gpio_request–gpio_set_value•三星平台配置GPIO函数–s3c_gpio_cfgpin•GPIO配置输出模式的宏变量–S3C_GPIO_OUTPUT注意点:DRIVER_NAME和DEVICE_NAME匹配。实现步骤:1.加入需要的头文件://Linux平台的gpio头文件#include//三
ARM驱动学习之4小结
JT灬新一
嵌入式 C++ arm开发 学习 linux
ARM驱动学习之4小结#include#include#include#include#include#defineDEVICE_NAME"hello_ctl123"MODULE_LICENSE("DualBSD/GPL");MODULE_AUTHOR("TOPEET");staticlonghello_ioctl(structfile*file,unsignedintcmd,unsignedlo
展现思维导图魅力,不断挖掘人生宝藏
思维导图讲师Mandy
第13期最强思维导图训练营已经结束一周了,但是我依旧是感觉所有学员还在努力的学习,这些学员中有教师、学生、白领、公务员、宝妈等等,只要你努力,只要你想改变自己,任何行业,任何岗位都可以参与进来,28天足以让你见成效,在这28天中,我们的学员不仅仅是收获了一枚毕业证,最重要的是让自己的思维方式得到升级,今天的你为自己投资,明天的你就会感谢你今天的付出,我们来听一听来自13期最强思维导图训练营优秀学员
windows下源码安装golang
616050468
golang安装 golang环境 windows
系统: 64位win7, 开发环境:sublime text 2, go版本: 1.4.1
1. 安装前准备(gcc, gdb, git)
golang在64位系
redis批量删除带空格的key
bylijinnan
redis
redis批量删除的通常做法:
redis-cli keys "blacklist*" | xargs redis-cli del
上面的命令在key的前后没有空格时是可以的,但有空格就不行了:
$redis-cli keys "blacklist*"
1) "blacklist:12:
[email protected]
oracle正则表达式的用法
0624chenhong
oracle 正则表达式
方括号表达示
方括号表达式
描述
[[:alnum:]]
字母和数字混合的字符
[[:alpha:]]
字母字符
[[:cntrl:]]
控制字符
[[:digit:]]
数字字符
[[:graph:]]
图像字符
[[:lower:]]
小写字母字符
[[:print:]]
打印字符
[[:punct:]]
标点符号字符
[[:space:]]
2048源码(核心算法有,缺少几个anctionbar,以后补上)
不懂事的小屁孩
2048
2048游戏基本上有四部分组成,
1:主activity,包含游戏块的16个方格,上面统计分数的模块
2:底下的gridview,监听上下左右的滑动,进行事件处理,
3:每一个卡片,里面的内容很简单,只有一个text,记录显示的数字
4:Actionbar,是游戏用重新开始,设置等功能(这个在底下可以下载的代码里面还没有实现)
写代码的流程
1:设计游戏的布局,基本是两块,上面是分
jquery内部链式调用机理
换个号韩国红果果
JavaScript jquery
只需要在调用该对象合适(比如下列的setStyles)的方法后让该方法返回该对象(通过this 因为一旦一个函数称为一个对象方法的话那么在这个方法内部this(结合下面的setStyles)指向这个对象)
function create(type){
var element=document.createElement(type);
//this=element;
你订酒店时的每一次点击 背后都是NoSQL和云计算
蓝儿唯美
NoSQL
全球最大的在线旅游公司Expedia旗下的酒店预订公司,它运营着89个网站,跨越68个国家,三年前开始实验公有云,以求让客户在预订网站上查询假期酒店时得到更快的信息获取体验。
云端本身是用于驱动网站的部分小功能的,如搜索框的自动推荐功能,还能保证处理Hotels.com服务的季节性需求高峰整体储能。
Hotels.com的首席技术官Thierry Bedos上个月在伦敦参加“2015 Clou
java笔记1
a-john
java
1,面向对象程序设计(Object-oriented Propramming,OOP):java就是一种面向对象程序设计。
2,对象:我们将问题空间中的元素及其在解空间中的表示称为“对象”。简单来说,对象是某个类型的实例。比如狗是一个类型,哈士奇可以是狗的一个实例,也就是对象。
3,面向对象程序设计方式的特性:
3.1 万物皆为对象。
C语言 sizeof和strlen之间的那些事 C/C++软件开发求职面试题 必备考点(一)
aijuans
C/C++求职面试必备考点
找工作在即,以后决定每天至少写一个知识点,主要是记录,逼迫自己动手、总结加深印象。当然如果能有一言半语让他人收益,后学幸运之至也。如有错误,还希望大家帮忙指出来。感激不尽。
后学保证每个写出来的结果都是自己在电脑上亲自跑过的,咱人笨,以前学的也半吊子。很多时候只能靠运行出来的结果再反过来
程序员写代码时就不要管需求了吗?
asia007
程序员不能一味跟需求走
编程也有2年了,刚开始不懂的什么都跟需求走,需求是怎样就用代码实现就行,也不管这个需求是否合理,是否为较好的用户体验。当然刚开始编程都会这样,但是如果有了2年以上的工作经验的程序员只知道一味写代码,而不在写的过程中思考一下这个需求是否合理,那么,我想这个程序员就只能一辈写敲敲代码了。
我的技术不是很好,但是就不代
Activity的四种启动模式
百合不是茶
android 栈模式启动 Activity的标准模式启动 栈顶模式启动 单例模式启动
android界面的操作就是很多个activity之间的切换,启动模式决定启动的activity的生命周期 ;
启动模式xml中配置
<activity android:name=".MainActivity" android:launchMode="standard&quo
Spring中@Autowired标签与@Resource标签的区别
bijian1013
java spring @Resource @Autowired @Qualifier
Spring不但支持自己定义的@Autowired注解,还支持由JSR-250规范定义的几个注解,如:@Resource、 @PostConstruct及@PreDestroy。
1. @Autowired @Autowired是Spring 提供的,需导入 Package:org.springframewo
Changes Between SOAP 1.1 and SOAP 1.2
sunjing
Changes Enable SOAP 1.1 SOAP 1.2
JAX-WS
SOAP Version 1.2 Part 0: Primer (Second Edition)
SOAP Version 1.2 Part 1: Messaging Framework (Second Edition)
SOAP Version 1.2 Part 2: Adjuncts (Second Edition)
Which style of WSDL
【Hadoop二】Hadoop常用命令
bit1129
hadoop
以Hadoop运行Hadoop自带的wordcount为例,
hadoop脚本位于/home/hadoop/hadoop-2.5.2/bin/hadoop,需要说明的是,这些命令的使用必须在Hadoop已经运行的情况下才能执行
Hadoop HDFS相关命令
hadoop fs -ls
列出HDFS文件系统的第一级文件和第一级
java异常处理(初级)
白糖_
java DAO spring 虚拟机 Ajax
从学习到现在从事java开发一年多了,个人觉得对java只了解皮毛,很多东西都是用到再去慢慢学习,编程真的是一项艺术,要完成一段好的代码,需要懂得很多。
最近项目经理让我负责一个组件开发,框架都由自己搭建,最让我头疼的是异常处理,我看了一些网上的源码,发现他们对异常的处理不是很重视,研究了很久都没有找到很好的解决方案。后来有幸看到一个200W美元的项目部分源码,通过他们对异常处理的解决方案,我终
记录整理-工作问题
braveCS
工作
1)那位同学还是CSV文件默认Excel打开看不到全部结果。以为是没写进去。同学甲说文件应该不分大小。后来log一下原来是有写进去。只是Excel有行数限制。那位同学进步好快啊。
2)今天同学说写文件的时候提示jvm的内存溢出。我马上反应说那就改一下jvm的内存大小。同学说改用分批处理了。果然想问题还是有局限性。改jvm内存大小只能暂时地解决问题,以后要是写更大的文件还是得改内存。想问题要长远啊
org.apache.tools.zip实现文件的压缩和解压,支持中文
bylijinnan
apache
刚开始用java.util.Zip,发现不支持中文(网上有修改的方法,但比较麻烦)
后改用org.apache.tools.zip
org.apache.tools.zip的使用网上有更简单的例子
下面的程序根据实际需求,实现了压缩指定目录下指定文件的方法
import java.io.BufferedReader;
import java.io.BufferedWrit
读书笔记-4
chengxuyuancsdn
读书笔记
1、JSTL 核心标签库标签
2、避免SQL注入
3、字符串逆转方法
4、字符串比较compareTo
5、字符串替换replace
6、分拆字符串
1、JSTL 核心标签库标签共有13个,
学习资料:http://www.cnblogs.com/lihuiyy/archive/2012/02/24/2366806.html
功能上分为4类:
(1)表达式控制标签:out
[物理与电子]半导体教材的一个小问题
comsci
问题
各种模拟电子和数字电子教材中都有这个词汇-空穴
书中对这个词汇的解释是; 当电子脱离共价键的束缚成为自由电子之后,共价键中就留下一个空位,这个空位叫做空穴
我现在回过头翻大学时候的教材,觉得这个
Flashback Database --闪回数据库
daizj
oracle 闪回数据库
Flashback 技术是以Undo segment中的内容为基础的, 因此受限于UNDO_RETENTON参数。要使用flashback 的特性,必须启用自动撤销管理表空间。
在Oracle 10g中, Flash back家族分为以下成员: Flashback Database, Flashback Drop,Flashback Query(分Flashback Query,Flashbac
简单排序:插入排序
dieslrae
插入排序
public void insertSort(int[] array){
int temp;
for(int i=1;i<array.length;i++){
temp = array[i];
for(int k=i-1;k>=0;k--)
C语言学习六指针小示例、一维数组名含义,定义一个函数输出数组的内容
dcj3sjt126com
c
# include <stdio.h>
int main(void)
{
int * p; //等价于 int *p 也等价于 int* p;
int i = 5;
char ch = 'A';
//p = 5; //error
//p = &ch; //error
//p = ch; //error
p = &i; //
centos下php redis扩展的安装配置3种方法
dcj3sjt126com
redis
方法一
1.下载php redis扩展包 代码如下 复制代码
#wget http://redis.googlecode.com/files/redis-2.4.4.tar.gz
2 tar -zxvf 解压压缩包,cd /扩展包 (进入扩展包然后 运行phpize 一下是我环境中phpize的目录,/usr/local/php/bin/phpize (一定要
线程池(Executors)
shuizhaosi888
线程池
在java类库中,任务执行的主要抽象不是Thread,而是Executor,将任务的提交过程和执行过程解耦
public interface Executor {
void execute(Runnable command);
}
public class RunMain implements Executor{
@Override
pub
openstack 快速安装笔记
haoningabc
openstack
前提是要配置好yum源
版本icehouse,操作系统redhat6.5
最简化安装,不要cinder和swift
三个节点
172 control节点keystone glance horizon
173 compute节点nova
173 network节点neutron
control
/etc/sysctl.conf
net.ipv4.ip_forward =
从c面向对象的实现理解c++的对象(二)
jimmee
C++ 面向对象 虚函数
1. 类就可以看作一个struct,类的方法,可以理解为通过函数指针的方式实现的,类对象分配内存时,只分配成员变量的,函数指针并不需要分配额外的内存保存地址。
2. c++中类的构造函数,就是进行内存分配(malloc),调用构造函数
3. c++中类的析构函数,就时回收内存(free)
4. c++是基于栈和全局数据分配内存的,如果是一个方法内创建的对象,就直接在栈上分配内存了。
专门在
如何让那个一个div可以拖动
lingfeng520240
html
<!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
第10章 高级事件(中)
onestopweb
事件
index.html
<!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/
计算两个经纬度之间的距离
roadrunners
计算 纬度 LBS 经度 距离
要解决这个问题的时候,到网上查了很多方案,最后计算出来的都与百度计算出来的有出入。下面这个公式计算出来的距离和百度计算出来的距离是一致的。
/**
*
* @param longitudeA
* 经度A点
* @param latitudeA
* 纬度A点
* @param longitudeB
*
最具争议的10个Java话题
tomcat_oracle
java
1、Java8已经到来。什么!? Java8 支持lambda。哇哦,RIP Scala! 随着Java8 的发布,出现很多关于新发布的Java8是否有潜力干掉Scala的争论,最终的结论是远远没有那么简单。Java8可能已经在Scala的lambda的包围中突围,但Java并非是函数式编程王位的真正觊觎者。
2、Java 9 即将到来
Oracle早在8月份就发布
zoj 3826 Hierarchical Notation(模拟)
阿尔萨斯
rar
题目链接:zoj 3826 Hierarchical Notation
题目大意:给定一些结构体,结构体有value值和key值,Q次询问,输出每个key值对应的value值。
解题思路:思路很简单,写个类词法的递归函数,每次将key值映射成一个hash值,用map映射每个key的value起始终止位置,预处理完了查询就很简单了。 这题是最后10分钟出的,因为没有考虑value为{}的情