通常,在Web数据管理应用中的请求都相对简单。这导致数据服务系统需要在查询和事务功能间(传统数据库系统中的功能)做出权衡,从而有效地支持可扩展性、灵活性和高可用性等特性。
本文所讨论的观点源自于我在Yahoo的PNUTS(灵活通用表存储平台——Platform for Nimble Universal Table Storage)数据服务平台的经验,该平台从2008年开始投入使用1 。截止2011年,在PNUTS上已托管了超过100个应用以支持运行在全球范围内18个数据中心数千台服务器上的大多数Yahoo! Properties服务,而其使用量和使用方式也在快速成长。2
PNUTS的设计是对地域复制(geo-replication)的一个实现。横跨大陆访问数据的副本会比访问本地数据慢得多,所以我们面临着如何权衡在现存分区中数据访问的可用性和一致性问题。值得注意的是,即使在没有分区的情况下,超慢访问速度的现实使得程序员们更偏向于数据的本地副本。因此,在使用分区时,即便是常规操作(特别是读取数据时),CAP原理也会限制程序员提供的一致性保证,所以他们常常会做变通,只提供较弱的保证。
背景:acid和一致性
数据库系统支持事务的概念,非正式的说事务是某程序的一次执行。为了获得较高的性能,有时系统会以交错方式,并发的执行多个程序去访问数据。这种执行方式下,就需要保证数据库最后结果的状态,必须与该事务中的各个程序串行执行后得到的状态一致。
术语ACID表示事务是:原子的(atomic)是指在系统中要么是完全执行,要么就全部不执行;一致性的(consis¬tent )是指数据库保持完整性;隔离性的(isolated)是指执行未完成前,其效果对外是不可见的;持久性(durable)是指事务完成后的结果是不会受后续失败的影响的。
事务概念是数据库管理系统最伟大的成就之一,它解放了程序的并发执行和失败带给程序员的束缚:他们仅需要确认他们编写的程序在运行完成后能够保证数据库的一致性。数据库系统往往在事务读取或写入同个共享对象时,通过锁的获取来实现事务的概念。通常,根据两阶段锁的制度,可以确保并发执行的结果可以与该事务中的所有操作串行化执行时得到的结果是相同的。数据库系统首先会将数据的所有变更记录到预写入的日志里,通过该日志可以撤销未完成的事务。如果需要,也可以在出现故障后用来恢复已经完成的事务。
在分布式数据库中,如果某个事务修改了存储在多个服务器上的对象时,它必须获取并持有跨越多个服务器的锁。即使这些服务器是并列的,该操作也是相当昂贵的,若这些服务器所处不同的数据中心,那就更加不用说了。当数据被复制时,任何事物都变得更加复杂。我们必须确保所有存在的节点在遇到故障场景时都能够对不同的情况作出正确的决策,比如已经完成的事务(需要恢复)和未完成的事务(需要撤销)。通常,系统可以通过使用大部分的协议(写操作会被作用于大多数副本,或者进行仲裁(quorum),而仲裁成员之一会为读操作进行服务)来实现。除了增加了在正常执行时的成本之外,如CAP原理所描述的3,4,这些包括网络分区,妥协的可用性等措施,可以在故障发生期间对故障进行有效的阻断。
数据库和分布式系统相关的文献中都有提供很多关于并发语义操作的可选方案。虽然数据库的并发观念适用于分布式环境(尽管他们可以以更昂贵的方式来实施并可能引入对可用性的权衡),他们原本是被设计来应对多个程序交错的使用一个集中式数据库的。因此,目标是为了提供一个简单的编程抽象来处理并发操作,而不只是为了处理分布式环境带来的挑战。
这些环境的差异影响了(数据库和分布式系统)两个社区如何处理这个问题的方式,但是下面列举的两个视角的差异是值得强调的:
一致性的单元。作为ACID事务概念的例证,数据库的视角集中在整个数据库的更改,涉及到多个对象(代表性的是关系数据库中的记录)。而分布式系统的文献通常专注于单个对象的更改。
以客户端与以数据为中心的语义。数据库社区定义语义的方式通常是通过规范化(formalizing)数据库上并发访问操作产生的效果;ACID事务的定义再次例证了这个方式——在数据库上交错执行的效果必须与相同事务中顺序执行的效果是一致的。但是分布式系统社区往往使用的是以客户端为中心的方式,它根据一个客户端在与并发执行的其他客户端共同读写一个分布式数据存储时,可能潜在的问题来定义并发级别的。
在分布式系统文献中提出的一致性概念专注于单个对象,并且是以客户端为中心的定义。强一致性表示,一旦一个写请求成功返回到客户端,所有随后而来的任意客户端对该对象的读操作,都能看到本次写的效果,不管是复制,故障,分区等等。请注意,强一致性并不确保ACID事务。举个例子,客户端A可以读取对象x一次,然后稍后再次读取时可以看到另一个客户端介入并进行写操作所产生的效果。因为这不同于两个客户端程序的串行化执行,这只说明,实现ACID事务可以确保强一致性。
弱一致性一词描述了针对个别对象的更改进行非强一致性保证的任意可选方式。弱一致性众多方式1,5 中最有名的一个例子是最终一致性,Amazon的Dynamo系统6就支持这种方式。直观地说,如果一个对象在不同的服务器上具有多份副本,那么更新会首先作用在本地副本上,然后才传播开去;该形式能够保证的是每次更新都最终会作用到全部的副本上。不管怎样,都无法保证在实际更新过程中,系统的更新会以怎样的顺序生效。对不同的副本来说,更新的生效顺序也会不同。除非自然更新可以隐藏顺序的细节,否则会带来一些麻烦。举个例子,对同一个对象的两个交替和关联的更新会产生两个副本,这会使程序员难以进行识别。
研究人员提出了几个弱一致性(weak consistency)5的版本,包括
读己之所写(read-your-writes)——客户端看到的永远是自己的写操作产生的效果
单调读(monotonic read)——如果客户端读到一个数据对象的特定值后,那么在后续的访问中它将不会再访问到该值之前的版本
单调写(monotonic write)——同一客户端的所有写操作将以发起该操作的顺序串行化生效。
以上的每个版本都可以帮助加强向客户端提供的最终一致性。
云数据管理
Web应用作为推动云系统发展的主力军,正快速的普及成熟起来,它需要能够按需求进行扩展。系统必须以低延迟的响应速度(几十毫秒)为世界范围内的用户请求提供服务,高吞吐量(每秒上万的读写),并且应用必须具备高可用性,并长期的维持最小的运维成本。幸运的是我们已不再需要提供完整的事务支持,单独的系统也可以执行复杂的分析任务,比如像Hadoop这样的map-reduce平台。
对于许多应用程序来说,它们处理的请求相比传统数据管理设置更为简单。数据可能是用户的会话数据,用户在网页上所有的行为只会涉及到单条记录的读写。应用也有可能是社交化的,对于社交活动可能会被写入单条的用户记录,而用户的朋友们的活动需要从少量的其他用户记录中读取。
这些挑战推动了新一代分析服务系统的发展,该系统将基于大型分布式架构,包括上千台机器的集群。为了容错,所有数据会一如既往的在数据中心内被复制;甚至有些数据为了保证低延迟读取,需要横跨多个数据中心进行地域性复制。大型分布式架构能够根据自身需求逐步扩大容量,这将为构建多租户(multi-tenanted)模式打开了方便之门,多个应用的托管系统可以共享底层的资源。这些云系统是无需大型分布式的,但是很多目前的产品,比如Amazon,6 Google,7,8 Microsoft,9Yahoo,1 和 Cassandra 以及 HBase 开源系统却都是大型分布式的。
尽管Web数据管理提供了大型分布式云架构的原始动力,这些系统也在快速影响着企业数据管理。此外,快速增长的具备大量存储和计算能力的移动设备,催生了节点数以亿计的系统,节点的断连(disconnectiv¬ity)已不再是罕见的事情。这种新型的大型分布式系统,有可能推动当前的云系统逐步去超越CAP原理所指出的挑战极限。
有人认为应用不再需要传统数据库系统的更强大的功能了,这种想法是错误的。即使对于开发Web应用来说,更强大的功能也可以简化开发者的任务,更好的支持数据一致性,这是有价值的。最终一致性有时是不适合的,而有的时候也并不比ACID差,这取决于应用本身。作为云数据服务系统的根本思想,可以以某种方式融入面向企业的数据管理系统。一部分应用将(确实也需要)受益于更高级别的一致性,并且功能性也急剧增强。尽管很可能还是会存在一些基本的权衡,但是我们也见证着第一代云数据服务系统正在迅速进化为更加完整的系统。
Bigtable和HBase这样的系统会将变更同步写入到所有副本,他们会确保所有副本都一直是最新的。Dynamo和Cassandra强制在将写操作的成功结果返回客户端之前,必须在一台仲裁者服务器上(a quorum of servers)写入成功。他们会在网络分区间维持记录可用性,但是付出的是一致性的代价,因为他们并不支持只从写入的仲裁服务器读取数据。Mega-store更接近传统DBMS(数据库管理系统)的一致性,它支持ACID事务(这意味着会作用在同一组上的记录;它使用Paxos算法来实现跨区域的同步复制)(注意,因为有新系统宣称在这个领域正在快速发展,所以上述的的讨论并不全面)。
Pnuts:案例学习
Yahoo具有6.8亿的用户和众多内部平台,并且对延迟有较严苛的需求(通常在10毫秒以内)。服务器可能故障,个别的数据中心也可能会遭受网络分区或由于灾难造成的常规性停机,但是数据必须在各种故障情况下保持可用,这是通过数据中心间的复制来实现的。我们开发了PNUTS来支持在这种环境下的CRUD——创建、检索、更新、删除等操作所带来的工作负载。
很多的应用已经从纯LAMP(Linux, Apache, MySQL, PHP)栈或其他的遗留key-value存储迁移到了PNUTS。上述的应用包括Yahoo的用户定位系统,用户产生的内容以及社区目录平台(social directory platforms);Yahoo邮件地址簿;Yahoo问答、电影、旅行、天气和地图应用;广告和内容个性化的用户配置等。采用PNUTS的原因包括:灵活的记录和模式演变;能够有效地检索小范围的有序记录(举个例子:每篇文章按时间排序的评论);表被修改时产生的通知;托管在多数据中心的存储上;尤其是可靠、全局、低延迟的访问。
在Yahoo,对于PNUTS的经验带来了以下的一些发现:
对于云模型来说,可托管,低延迟访问且能按需存储以及高可用的多数据中心复制,被证明是非常受欢迎的。
对于多数应用,用户愿意在复杂查询和ACID事务等功能方面做出妥协。
通过使用PNUTS系统作为数据管理,可以容易地进行应用开发,而一些附加的功能极大的提升了这些应用的范围,毫无疑问,这些应用都使用了这些功能。特别要指出的是,提供对有序表的支持使你可根据复合键对数据表进行排列,并且进行有效的范围扫描,这吸引了大量的采用,并且我们也期望通过支持选择性复制和二级索引来达到相同的效果。
用户急切地希望得到更多一致性级别的可选项。
基于本文的上下文,最相关的功能是那些涉及一致性的部分。
松弛一致性
PNUTS是最早原生支持地域复制的系统之一,它使用异步复制来避免长时间的写入延迟。系统可以选择同步复制来在同一个数据中心中生成副本,并以此来确保强一致性。而对跨数据中心的复制来说这并不可行,所以需要多种形式的弱一致性来支持。
然而,最终一致性并不总是满足应用中对于应用本身的语义的支持。举个例子,有一个登录Yahoo想要聊天的用户,假设我们想要维护这个用户的状态,这个状态的多个副本数据可能会被维护在多个地域,并且必须在用户决定聊天或下线时进行更新。考虑到两个地区间因为连接失败而造成断开通信时已发生了一些事情:当连接恢复时,已不足以促使两个副本的数据最终能整合到统一状态;当然,这些副本数据必须整合到用户最近活跃区域最近一次声明的状态。
也许可以通过ACID事务来更新副本,但是在这样的环境中支持ACID事务是一项艰巨的挑战。幸运的是,大多数Web应用往往在同一时间只写入单条记录——例如在用户资料的记录中改变一个用户的聊天状态或者起始位置——并且可以接受后续对于该记录的读操作(举个例子,被一个朋友或用户)可以不立即读到该次写的效果。该结论是我们采用PNUTS作为解决方案的关键,我们称之为时间轴一致性。
时间轴一致性
在时间轴一致性中,对象和它的副本不需要同步维护,但是所有的副本必须遵守相同状态的时间轴(可能会有一些副本跳过部分状态)。PNUTS不允许对象出现在过去的时间或没出现在与该对象相关的时间轴上。这种方式本质上是主副本同步。在任何给定的时间,每个对象只有一个主副本(根据这种观念,在PNUTS中,表中的每条记录都是一个对象),并且更新会作用在这个主副本上,然后传播到其他副本,因此可以确保一条记录的所有更新按照唯一的顺序执行。协议会自动识别主副本故障,并将主副本的身份转移到一个幸存可用的副本上,以确保高可用性,并且该协议也支持自动负载均衡策略,将主副本的身份转移到某条最常更新的记录所在的副本位置。
想要了解该设计背后的动机,就要分析延迟。要做全局数据复制,而同步复制造成的延迟让人无法接受。数据库系统支持例如二级索引和物化视图等辅助数据结构,但是在大型分布式环境中同步地维护这样的结构会造成更大的延迟。
因此,低延迟访问的需求导致了异步复制,这种方式拥有天然的妥协一致性。10 不管怎样,对象时间轴提供了支持多种读操作方式的基础。如果应用在一定程度上可以忍受数据的时效性,则可以通过一致性来换取性能,而那些需要一致性数据的应用可以依赖对象时间轴。
时间轴一致性在可用性方面进行了妥协,但是这种情况出现的概率很小。比如主副本故障时,消息系统同时发生了网络中断或产生了故障导致主副本身份的自动转移协议被阻塞。此外,时间轴一致性削弱了一致性概念,客户端可以在常规操作中选择读取对象的较老版本,这再次反映了对最小化延迟的根本担忧。这也是Daniel Abadi观点的基本精神,CAP原理忽略了大规模分布式系统的一个重要方面——即延迟(L)。 根据Abadi的提出的重构建议,11 CAP实际上应该是PACELC:
如果存在一个分区(P),系统该如何权衡可用性和一致性(A和C);另外,当系统在没有分区的情况下正常运行时,如何权衡延迟(L)和一致性(C)?
选择性记录复制
很多的Yahoo应用都实实在在的拥有着全球性用户基础数据,并且需要复制到远多于实际需要的区域以支持故障容错。然而一个应用可能是全球性的,可它的记录实际上却可以是局部的。一条包含了某个用户配置的PNUTS记录很可能仅仅只是在用户和他(或她)的朋友们生活的一个或几个地域中被读写。一些关于记录可复制限制的法律问题也经常出现在Yahoo;这种模式很好的遵守了用户所在地的相关制度。
为了处理这一关注点,我们为PNUTS增加了对记录的逐条选择性复制。12 各个分区即使没有存储某条记录的完整副本,但仍然会持有该记录的一个存根,该存根包含了足够的元数据来识别哪些分区包含了该条记录数据副本的信息,以支持对于请求的转发。该存根仅在记录创建、删除,以及记录副本位置改变时进行更新。通常,数据更新只会发送到包含该记录完整副本的分区,这样可以节省带宽和磁盘。
一致性范围(consistency spectrum)的案例
云数据管理系统被设计用于实时数据的服务和工作负载更新,其充分阐述了CAP原理的实现:这样的系统无法为在使用分区且保证可用性的情况下提供强一致性的支持。确实,这样的大型分布式系统也许可以通过满足弱一致性保证来减少延迟,尤其是在数据被跨地域复制的情况下。事实上,如果一个程序员使用了这样一个系统,在面对各种故障包括分区时,必须能够明确的在一致性、延迟和可用性中做出权衡。
幸运的是,有很多一致性模型支持这样的权衡,建议程序员们应该混合使用它们来满足应用的需求。
我们组织了讨论,强调两个独立的规格:在界定一致性时需要考虑的数据单元,而一致性范围(spectrum)是对于已选择单元的弱一致性保证的强度。
一致性的单位
通常在数据库文献中,一致性是根据整个数据库的变更来定义的;而在分布式系统的著作中,通常是针对每个对象的变更来考虑的,每个对象的变更是独立的。这些并不是唯一的选择,直观地说,任意我们可能要确保原子访问的对象集合并且系统会作为一个单元进行复制的都可以作为一致性的单元。举个例子,在故障面前,甚至任意在单台服务器上的对象集合都可能被作为一致性的单元(从确保较好性能的观点来看)。
一个在多记录事务中众所周知的例子是实体组(entity group),实体组是由一个实体和所有它相关记录组成的。举个实体组的例子,我们可以认为一个用户(即“实体”)和所有该用户发布的评论和照片,以及用户计数器,例如评论数等一起就是一个实体组。对于更新一个实体组中的所有记录来说这通常都会有用。举个例子,插入一条评论并同时更新评论计数器。通常,一个实体组的规模是适度的,单台服务器可以容纳整个记录集的一份副本。
Google的App Engine提供了定义实体组的方式并且可以对它们进行事务性操作;Microsoft的 Azure具有相似的功能来允许通过分区键进行记录分组,并且可以在同一分区上对这些记录进行事务性更新。在实体组上实现事务的基本方式很直接,但依赖于如何控制记录跨节点的进行分区,我们要确保所有在一个实体组中的记录必须属于单个节点。如此,系统就可以调用传统数据库事务管理器,而不需要使用跨服务器锁或其他昂贵的机制。
这个模型具有两个基本的约束条件:第一,实体组必须足够的小,以适合在单个节点存储;甚至,为了有效的负载均衡,该尺寸必须允许多个组可以在单个节点存储。第二,对于实体组的定义是静态的,典型的做法是在记录的属性中指定一个复合键。最近的一项提议是考虑如何放宽第二条约束,允许更加普遍和动态地定义实体组。13
一致性范围
我们以讨论横跨单个对象的多个副本的一致性范围作为开始,然后讨论如何归纳这些思想来处理其他一致性单元。
为单独的对象建立一致性模型。时间轴一致性提供了简单的编程模型:一条记录的副本可能会延迟于主副本,但是系统会以相同的顺序将主副本的更新应用到每个副本。请注意,这是一个以数据为中心的保证措施。从客户端的视角来看,单调写可以得到保证,所以对象时间轴——由主对象生成的时间戳,标识了对象在时间轴上的每个状态以及相应位置——这样可以对多种不同的读操作提供支持,每种都提供了不同的保证。
任意读(Read-any)。每次读操作可能会返回对象的任意副本,所以如果客户端发起两次请求,第二次实际上可能会看到该对象的一个较老的版本,即使主副本可用,时间轴一致性也会被强制实施。直观地说,客户端读取的本地副本如果稍后变成了不可用,则第二次读会由另一个更加陈旧的(非主)副本提供服务。
关键读(Critical-read)。也称为单调读,关键读可以确保读取的副本会较该客户端曾经读取的任意版本都更新。通过记忆上次客户端发出的写操作,关键读操作可以扩展以支持读己所写(read-your-writes)。虽然这样可以获得更好的效率,但这同样也需要额外做好缓存客户端的本地写操作。
读最新(Read-up-to-date)。读操作将获取该对象的当前最新版本,读最新会直接访问主副本的数据。
测试并设置(Test-and-set.)。测试并设置在PNUTS中被广泛使用,它是一种条件式写操作。该写操作只有当主副本的版本相对于该客户端上次读取的版本没有变化时才会生效。这对于实现单对象的ACID事务来说已经足够了。
实体组的时间轴一致性。时间轴一致性和实体组的天然概括,就是考虑实体组的时间轴而不是独立的记录。该时间轴拥有每个实体组的主副本,而不是每条记录。而且事务性的更新作用于实体组(可能会影响到多条记录)的,且是实体组的主副本,就像以时间轴一致性独立更新单条记录一样。此时事务序列会被日志记录下来,并异步的传递至该实体组所含副本的对应站点,并在每个对应的站点重新应用这些事务。
虽然,这个概括应该可以用“相比于时间轴一致性和实体组一致性的性能及可用性特征”来作为依据来支撑,但是我并不觉得任何的系统(已经)这样做了。这看上去是一致性范围中一个诱人的选项,并且可以覆盖通常应用,不然的话将需要完整的ACID事务。
提供一致性的选择
地域复制使得所有记录对来自任何地方的请求都保证可用性。然而,任何时候一个分布式系统都可能因为故障而分区,这将不能同时保证写的一致性和可用性。一个可选的方式是支持多个一致性模型,并让应用的程序员自己来决定在故障发生时是否以及如何进行降级。
Eric Brewer的建议14中计划设计一个分区模式——客户端如何进入或离开这个模式,并且在分区模式下以及离开时如何进行工作。直观地说,一旦客户端不能以期望的一致性级别完成一次读或写操作时,它将进入分区模式(这取决于某些类型的故障,并由比如超时之类的机制来触发)。客户端必须在操作时接受一个认识:看到的是数据库的某个版本,而且不是强一致性的。当用户从分区模式退出时(当系统解决了底层的故障并且会以某种方式发出信号),它必须能调和它曾经访问过的对象间的不一致性。
在PNUTS的时间轴一致性实现中,客户端在尝试写某个对象,而该对象因为主副本不可达而造成阻塞时,客户端将进入分区模式。同时主副本间的身份转移协议同样也会被阻塞,而发生这样问题的典型情况可能是一个分区或站点发生了故障。这时,客户端应该要能够选择从时间轴一致性降级到最终一致性,并继续向其他的副本进行写入。然而,系统现在已经处于指定对象没有唯一主副本的模式下。进一步说,当最终一致性的弱保障不能满足该对象时,客户端必须明确一致化对象的不同版本——也许可以使用系统提供的针对对象的版本容器。
PNUTS并非如此灵活——它在单个表的记录级别上需要程序员在时间轴一致性和最终一致性中做出选择。它将表中的每条记录作为一个关联一致性语义的对象来对待。一旦选择了最终一致性,插入和更新可以在任意区域和任意时间执行。对于理解并可以接受最终一致性模型的程序员来说,性能收益是非常大的:系统可以根据客户端局部地执行所有写操作,这极大的改进了写操作带来的延迟。考虑到服务器节点如果发生了故障,另一个(远程的)节点将会一直可用并接受写操作。
PNUTS同样可以在某个读请求需要访问主副本时进入分区模式;再次强调,客户端可以选择等待系统恢复访问或进行某个可用版本的读取。此外,客户端如果在常规操作中能够接受的话,可以选择读取任意一个比主副本略有延迟的可用副本。
虽然大型分布式系统提供了多种抽象来处理一致性,程序员们需要能够混合搭配这些抽象。对于单个对象的时间轴一致性的讨论,强调了以数据为中心和以客户端为中心的方法对于一致性的定义是如何进行互补的。对于如何建立单个对象时间轴的一致性来支持不同客户端一致性的讨论,保证了整个实体组上时间轴一致性的存在。事实上,对于分布式系统的一致性来说,这通常是一种有用的方法:
选定系统支持的一致性单元;
从以数据为中心的视角选定系统支持的一致性保证
从以客户端为中心的视角选定系统支持的一致性保证
将这些可用的选项通过创建-读-写等各种操作的变体暴露给程序员们,以便于他们可以根据他们的应用在可用性,一致性以及延迟方面进行权衡。
举个例子,我们可以考虑
将需要的一致性类型——时间轴一致性或最终一致性——作为对象集的一个属性;
多种读取对象的可选方式且支持各种以客户端为中心的一致性语义;
对象组的灵活定义,并可以以一致性为目的,将该对象组作为单个对象处理;
关于在遇到故障时,如何进行优雅降级到弱一致性方式的规范。
为了明确这些选项,我们需要确定正确的抽象和正确的粒度,这需要更加深入的研究和评估。
于广域网潜在的实际延迟和CAP原理,是时候考虑编程抽象了,这可以简单的规范化大型分布式事务,并以一种方式更有效地来反应系统可以实现什么。
在一致性方面和可用性或性能方面的权衡已经成为设计大规模数据管理系统的关键因素。尽管面向Web的系统引领并打破了对传统关系型数据库系统依赖的陈规,而这些思想也已经开始进入数据库主流。在未来的几年内,面向企业的云数据管理系统将为数据库管理员提供更多相同的设计选项。
关键的关注点是我们拥有很多选择,而不仅仅是在以下这两个方面:一方面是ACID事务和完整的RDBMS功能,以及另一方面是NoSQL系统提供了非一致性保证的最小化查询和更新功能。我们将看到系统正处在这个范围中间的某处,并尽可能努力的提供功能来满足不同应用环境的可用性和性能需求。如何通过设计抽象来干净利落的打包这些选项,如何通过开发架构来坚定的支持它们,如何对这些系统进行优化并使其自动调整,这些都将为未来十年的研究带来挑战。
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
相关推荐
-
十大最有用的云数据库
随着商业交易内所蕴含数据量的不断增加,服务提供商正在想办法让公有云的数据管理变得更加轻松,涉足企业数据库领域,那么目前有哪些可用的云数据库?