文章来自于: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学习翻译)
【Python】一文详细介绍 py格式 文件
高斯小哥
Python基础【高质量合集】 python 新手入门 学习
【Python】一文详细介绍py格式文件个人主页:高斯小哥高质量专栏:Matplotlib之旅:零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程希望得到您的订阅和支持~创作高质量博文(平均质量分92+),分享更多关于深度学习、PyTorch、Python领域的优质内容!(希望得到您的关注~)文章目录一、py格式文件简介二、如何创建和编辑py格式文件三、如何运行py
大学播音主持都学什么内容?播音主持专业学什么?
配音新手圈
有些喜欢播音主持并且犹豫要不要报考这个大学专业的小伙伴们就会想要了解大学播音主持都学什么内容吧,毕竟如果不够了解就直接选择这个专业真的等选择完进去学习以后才知道这个专业并不是自己想要学习的东西那就来不及了。下面是小编为大家整理出来的一些播音主持专业学习的内容,请往下看吧。大学播音主持专业主要学习的课程有:播音发声、播音创作基础、广播播音主持、电视播音主持、文艺作品演播学概论、新闻学概论、新闻采编、
【OpenModelica】4命令行大全
Wumbuk
python 开发语言 modelica
命令行大全文章目录命令行大全一、SummaryofCommandsfortheInteractiveSessionHandler二、Runningthecompilerfromcommandline一、SummaryofCommandsfortheInteractiveSessionHandler以下是交互式会话处理器中当前可用命令的完整列表。•simulate(modelname):翻译一个名为
拼多多纸巾推荐:品质与性价比的完美结合
氧惠帮朋友一起省
拼多多纸巾推荐拼多多纸巾返现怎么做在我们的日常生活中,纸巾已经成为不可或缺的用品。无论是在家庭、办公室还是旅途中,纸巾都是我们随时随地需要的物品。随着电商平台的兴起,越来越多的人选择在网上购买纸巾。其中,拼多多作为国内知名的电商平台之一,以其独特的社交电商模式和实惠的价格吸引了大量用户。今天,我们就来探讨如何在拼多多上选择品质优良、性价比高的纸巾,以及如何通过一些小技巧来获取更多的优惠。一、品质与
新网师的精神肤色(幕布笔记)
悦读书香
王子老师的《极简100小妙招》收到已经几天了,之前大概的浏览了全书,今天起给自己定了一个计划,必须每天学习极简小妙招里面的一个妙招,并加以运用。一、今天要打卡什么内容因有完成每天学习极简小妙招的计划,所以今天晚饭吃的比较简单,草草吃完以后带着小宝到广场溜达一圈,急忙赶回来学习极简小妙招。再重看的时候不知道自己要学点什么,打卡哪一招,感觉哪个都简单,就看这一环节像王子老师说的“一看就会”,但做这一环
学习JavaEE的日子 Day32 线程池
A 北枝
学习JavaEE 学习 java-ee java 线程池
Day32线程池1.引入一个线程完成一项任务所需时间为:创建线程时间-Time1线程中执行任务的时间-Time2销毁线程时间-Time32.为什么需要线程池(重要)线程池技术正是关注如何缩短或调整Time1和Time3的时间,从而提高程序的性能。项目中可以把Time1,T3分别安排在项目的启动和结束的时间段或者一些空闲的时间段线程池不仅调整Time1,Time3产生的时间段,而且它还显著减少了创建
没有如释重负
君远近
虽然只有短短的一个多月的努力复习时间,但今天的整个考试经过,还是发现了效果的,题目做的比较自如,没有慌里慌张,而且提前五分钟完成。至于考试成绩,没有实足的把握,60分都不敢保证。但绝对相信自己,比去年肯定要好!今天早早的赶到考场,见到了刘老师,谈起来学习情况,坦率的说,真的是自己不够重视。总以为会很难,没有信心。其实不是的,只要认真对待,树立足够的信心,绝对可以通过考试的。还向老师询问了,后续再报
C++学习笔记(lambda函数)
__TAT__
C&C++ c++ 学习 笔记
C++learningnote1、lambda函数的语法2、lambda函数的几种用法1、lambda函数的语法lambda函数的一般语法如下:[capture_clause](parameters)->return_type{function_body}capture_clause:需要捕获的变量,但要求该变量必须在这个作用域中。通常的捕获方式有以下几种:[]:不捕获任何变量[&]:按引用捕获变
心赏(2018.10.8)
六一节_3928
1.上班第一天,同事彤休完产假,回来上班,给我带了酸奶和水果。她生小孩时,我给她发了一个小红包贺喜,哪知她就记在心里了。心赏这个有心的90后。2.女儿放学回来,说自己当了小组长。一边说不想当,一边得意的样子。心赏老师给了孩子这个锻炼的机会。3.老妈今天做了"蚂蚁上树"的菜,得到女儿的高度肯定。心赏老妈还在不断学习。
keras.optimizers优化器中文文档
地上悬河
python 开发语言 后端
优化器optimizers优化器是编译Keras模型必要的两个参数之一model=Sequential()model.add(Dense(64,init='uniform',input_dim=10))model.add(Activation('tanh'))model.add(Activation('softmax'))sgd=SGD(lr=0.01,decay=1e-6,momentum=0.
2022-2-13晨间日记
越亮也打烊
今天是什么日子起床:7:00就寝:12:08天气:晴心情:糟糕纪念日:无任务清单昨日完成的任务,最重要的三件事:寒假作业,网课,画画改进:作业时间剪短习惯养成:网课不逃~周目标·完成进度数学卷子100%学习·信息·阅读《傅雷家书》《钢铁是怎样炼成的》健康·饮食·锻炼我终于不喝饮料啦,喝茶~人际·家人·朋友邝姐姐带我吃火锅工作·思考啥时候开学,我还有几天赶完作业最美好的三件事1.卷子写完了2.我有冰
中原焦点团队38期王芳芳坚持分享第236天,20230630总约练134次,来访113次,咨8次,观察员13次
芳芳王
学习焦点的初心是想拯救孩子,孩子由于沉迷游戏,成绩下滑,在学习的过程中发现是自己的教育方式出了状况。经过半年的学习,一些焦点的基本技巧,如接纳、欣赏、倾听、同理心、尊重等都有了一定的了解。但在实际应用时仍然存在很多问题,感觉自己仍然没有放下对孩子成绩的期望,仍然把握不住对孩子管理的度。我该如何去陪伴好孩子?多用心去听课,并加强反思,多约练。去思考如何让自己快乐起来?
中国最美的100个地方,待“软禁”结束后,满血复活去一次旅行!
是北河二1995
【阅读原文,请点击:原文,硬核100个】离成为一个倾国倾城、走路带风的女子还有365天!大家好,我是小草们的北河二~一转眼,2020年已过1/4,突如其来的病毒,搅得人心惶惶。原本的3月里,景区爆满、街道拥堵、小吃铺做无缺席。然而,地球妈妈不会一直宠着我们,给了我们好几个警告。如今,连CCTV-1播出的《人与自然》,我都可以看上好一会儿......高山、流水、湖泊、森林、大海.....它们也在这初
【转载】SSD测试第一神器——FIO
running_sheep
转自:[http://www.ssdfans.com]对于SSD性能测试来说,最好的工具莫过于FIO了。FIO是Jens开发的一个开源测试工具,功能非常强大,本文就只介绍其中一些基本功能。线程,队列深度,Offset,同步异步,DirectIO,BIO使用FIO之前,首先要有一些SSD性能测试的基础知识。线程指的是同时有多少个读或写任务在并行执行,一般来说,CPU里面的一个核心同一时间只能运行一个
谷歌浏览器驱动Chromedriver(114-120版本)文件以及驱动下载教程
pigerr杨
Python python chrome drivers
ChromeDriver官方网站GitHub||GoogleChromeLabs/chrome-for-testingChromeDriver113-125_JSONChromeforTestingavailability123-125zip白月黑羽Python基础|进阶|Qt图形界面|Django|自动化测试|性能测试|JS语言|JS前端|原理与安装
虚拟 DOM 的优缺点有哪些
咕噜签名分发
前端 javascript 开发语言
虚拟DOM(VirtualDOM)技术作为现代前端开发中的重要组成部分,已经成为了众多流行前端框架的核心特性。它的引入为前端开发带来了诸多优势,同时也需要我们认真思考其潜在的考量。下面简单的介绍一下虚拟DOM技术的优势与缺点,深入探讨其在实际应用中的影响。提升性能虚拟DOM的最大优势之一是提升页面性能。通过比较前后两次虚拟DOM树的差异,最小化实际DOM操作,从而减少页面重渲染时的性能消耗。这种优
大创项目推荐 深度学习 opencv python 公式识别(图像识别 机器视觉)
laafeer
python
文章目录0前言1课题说明2效果展示3具体实现4关键代码实现5算法综合效果6最后0前言优质竞赛项目系列,今天要分享的是基于深度学习的数学公式识别算法实现该项目较为新颖,适合作为竞赛课题方向,学长非常推荐!学长这里给一个题目综合评分(每项满分5分)难度系数:3分工作量:4分创新点:4分更多资料,项目分享:https://gitee.com/dancheng-senior/postgraduate1课题
#D174-读书会作业-《财务自由之路》3
白洲笔记
最近沉迷于写作营,一直就没时间去弄读书会的作业,书的第二遍也就看了个开头,趁着日更的时间,赶紧把作业做了,这次是15到21课。【1.印象最深刻的部分】(本周所读内容中印象最深刻的部分)*活在未来,最正确的方法是什么?用正确的方法做正确的事情,判断什么是正确的?逻辑。学会思考。"作对事情"永远比“把事情作对“重要的多。”长远思考,耐心验证,小心总结提炼“证明自己正确并不是学习的任务和目标,时刻成长,
读书笔记《穿越寒冬》
如雪般飞舞
各位好,我们今天来讲一本书,名字叫作《穿越寒冬》。看起来特别应景,大家觉得现在创业的状况不景气,大家都在忍受着寒冬的煎熬。但实际上,这本书的英文名字并不是这个意思,它的英文名叫作“如何创立一家新公司,并且能够活下来”。我在整个读完了以后,我发现这本书真正要翻译得好,它的名字应该叫作《创业生存手册》。这个书的作者,来自硅谷的霍夫曼船长。霍夫曼船长写过一本让创业者觉得特别贴心的书,叫作《让大象飞》它和
Flutter运行flutter doctor 命令长时间未响应如何解决
咕噜签名分发-淼淼
flutter
Hello大家好!我是咕噜铁蛋!在移动应用开发领域,Flutter以其高效、跨平台的特性吸引了众多开发者的关注。然而,在使用Flutter进行项目开发时,开发者可能会遇到各种问题,其中之一就是运行flutterdoctor命令时长时间未响应。今天铁蛋将深入探讨这一问题的成因、解决方案以及相关的Flutter环境配置知识。一、Flutter与flutterdoctor命令简介Flutter是Goog
账务处理又出错?资深会计来教你,学会效率翻倍!共同学习
小橘子要努力吖
作为一名会计,在实际工作中会遇到各种麻烦的账务处理问题。那么,最常用的会计处理方法都有哪些呢?今天小编为大家带来了从业二十六年的资深老会计分享的十四中会计常用的账务处理问题的解决方案,快来看看吧!一、促销品的账务处理在促销时公司经常会把一些商品按进价赠送给消费者使用二、款已付清但发票未到的账务处理三、购买材料发生不合理损耗的账务处理问题公司在购买材料时,常常会发生一些不合理的损耗,那么这种问题该怎
【真诚子】通晓鬼谷第七篇读书日记。
真诚子l通晓鬼谷
今天把个人品牌,从193读到208页,书的内容质量出奇的高,尤其是这一段。对标学习法,找一个比自己强,或者你期望成为的人进行模仿性学习,对标学习,不是到处,去找人对标兵学习很多人的优点,或是学习自己认为好的方面,而是找准一个对标高手,然后全方位的学习这个人。我在做品牌咨询时就对标,学习了一个在国内很有名的行业顶尖大咖。我先找到他公司的方案,进行完全模仿,连PPT的排版都一样,而且我只参照他一个人的
唯品会优惠券与代金券:如何免费领取并巧妙使用?
氧惠全网优惠
在互联网购物日益盛行的今天,各类电商平台纷纷推出各种优惠活动,以吸引消费者。其中,优惠券和代金券是消费者最为喜爱的促销方式之一。唯品会,作为国内知名的折扣电商平台,也经常推出各种优惠券和代金券,让消费者在享受购物乐趣的同时,还能节省不少钱。那么,唯品会优惠券和代金券该如何免费领取,又该如何巧妙使用呢?一、唯品会优惠券与代金券的区别在探讨如何领取和使用之前,我们首先需要了解唯品会优惠券和代金券的区别
ES-LTR粗排模块
poins
jenkins 运维
ES-LTR粗排模块官方资源:https://github.com/HeiBoWang/elasticsearch-learning-to-rankElasticsearch学习排名插件使用机器学习提高搜索相关性排名。它为维基媒体基金会和Snagajob等地方的搜索提供了动力!这个插件有什么功能此插件:允许您在Elasticsearch中存储特征(Elasticsearch查询模板)记录特征得分(
2018-11-18成长小组学习笔记
实验中学45
因为嗓子“罢工”,我面对众人只能借“微笑”代言。在开始授课前,绣霞老师先反馈上次作业的情况,提到“接纳”需是真正发自内心的完全接纳,而不是口头上的接纳,内心却是排斥的。提到一个“问题”孩子恰恰对家爱的更加“深沉”,夫妻间的问题不能影响到孩子,对孩子更好的爱不是你为他做的更多,而是给他自由、健康成长的空间。图片发自App一、孩子:家庭的一面镜子夫妻成了彼此的“投射”,婚姻便“吵的不可开交”,婚姻便成
地缚少年花子君,一个小小的甜甜圈,源光看见了源辉内心的小秘密
ACGN安乐
大家好我是小安,很高兴又和大家见面了,话不多说我们直接进入正题,不知大家对日本动画《地缚少年花子君》中海鸥学院高等部二年级生,学生会的会长源辉了解多少呢?源辉从第一集中就已经登场,他是源光的哥哥,也是八寻宁宁的理想对象之一,源辉除了有学生这层身份之外和他的弟弟源光一样都是拔除师,也就是我们统称的除妖师,从表面来看源辉是个高颜值帅哥,脾气很好同时也有很多女孩子想做他的女朋友,但金无足赤人无完人大家也
过去一年,这16本好书不容错过
m0_54050778
perl
编者按:2023年在动荡与希望中收尾,2023年注定会被载入史册。疫情寒冬结束,ChatGPT横空出世,带动了人工智能技术的飞速发展;淄博烧烤、天津大爷、尔滨之旅等充满感动与幸福。但与此同时,2023年又是动荡与不安的一年,俄乌冲突的延宕,新一轮的巴以冲突,极端天气频发。在这个大环境下,有一些经典的书籍著作诞生。本文将分享2023年最值得一读的16本书籍,文章来自翻译,希望对你有所启示。关于202
2019-07-16
振华老凤祥店长崔宁宁
大爱的李老师,智慧的教授,亲爱的跃友们:大家好!我是莱州鑫和金店李总的人~崔宁宁今天是我的日精进行动第56天,我分享一下今天的改变,我们相互勉励,每天进步一点点,离成功便不远。1、比学习:人这一生最主要的就是信念,坚定不移的信念是成功路上的重要基石!2、比改变:我是一切的根源,我变了世界就变了!改变自己的心态!3、比付出:承担才能成长,付出才会杰出!4、比谦卑:学习每位优秀店长身上的优点!5、比感
python清华大学出版社答案_Python机器学习及实践
weixin_39805119
python清华大学出版社答案
第1章机器学习的基础知识1.1何谓机器学习1.1.1传感器和海量数据1.1.2机器学习的重要性1.1.3机器学习的表现1.1.4机器学习的主要任务1.1.5选择合适的算法1.1.6机器学习程序的步骤1.2综合分类1.3推荐系统和深度学习1.3.1推荐系统1.3.2深度学习1.4何为Python1.4.1使用Python软件的由来1.4.2为什么使用Python1.4.3Python设计定位1.4.
2018-12-02
子分小
姓名:张颖公司:菲尔德国际英语【反省总结第146天,始于20180709今天是20181202】【知~学习】六项精进大纲背诵3遍每天十个单词坚持第181天每天学习一篇英文文章第94天英语流利说课程第71天学习30分钟【行~实践】一、修身:(对自己个人)步行5000步二、齐家:(对家庭和家人)无三、建功:(对工作)完成与Arti活动课和两节Demo准备开班事宜{积善}:发愿从2018年7月9日起1年
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为{}的情