http://www.crifan.com/python_mmseg_error_unable_to_find_vcvarsall_bat/

【问题】

折腾:

【original】折腾Python中的mmseg中文分词模块

期间,安装出错:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 `E:Dev_Toolspythonmodulesmmsegmmseg-1.3.0>setup.``install``all` `runni``install``all` `running bdist_egg` `running egg_info` `writing mmseg.egg-infoPKG-INFO` `writi``top``top-level names to mmseg.egg-infotop_level.txt` `writing dependency_links to mmseg.egg-infodependency_links.txt` `reading manife``file``i``'mmseg.egg-infoSOURCES.txt'``xt'` `writing manife``file``i``'mmseg.egg-infoSOURCES.txt'``xt'` `installing library code to buildbdist.win-amd64egg` `running install_lib` `running build_py` `creating build` `creating buildlib.win-amd64-2.7` `creating buildlib.win-amd64-2.7mmseg` `copying mmsegsearch.py -> buildlib.win-amd64-2.7mmseg` `copying mmsegword2.py -> buildlib.win-amd64-2.7mmseg` `copying mmseg_mmseg.py -> buildlib.win-amd64-2.7mmseg` `copying mmseg__init__.py -> buildlib.win-amd64-2.7mmseg` `creating buildlib.win-amd64-2.7mmsegdata` `copy``test``mmsegdatatest.py -> buildlib.win-amd64-2.7mmsegdata` `copying mmsegdataword2_gen.py -> buildlib.win-amd64-2.7mmsegdata` `copying mmsegdataword_in_word_rm.py -> buildlib.win-amd64-2.7mmsegdata` `copying mmsegdata__init__.py -> buildlib.win-amd64-2.7mmsegdata` `creating buildlib.win-amd64-2.7mmsegmmseg_cpp` `copying mmsegmmseg_cpp__init__.py -> buildlib.win-amd64-2.7mmsegmmseg_cpp` `copying mmsegdatachars.dic -> buildlib.win-amd64-2.7mmsegdata` `copying mmsegdatawords.dic -> buildlib.win-amd64-2.7mm``'mmseg'``a` `running build_ext` `building``find``seg' extension` `error: Unable to find vcvarsall.bat`

【解决过程】

1.参考了:

python—解决“Unable to find vcvarsall.bat”错误

error: Unable to find vcvarsall.bat

都是让安装mingw32.

此处懒得装。

2。打算去试试,看看能否利用我已有的cygwin去实现编译。

暂时放弃此复杂的方法。

3.参考上面那个:

error: Unable to find vcvarsall.bat

中别人的回答,去:

执行:

SET VS90COMNTOOLS=%VS100COMNTOOLS%

然后再去编译,貌似至少可以消除此处的问题了。

【总结】

当使用

setup.py install

去安装Python模块出现:

error: Unable to find vcvarsall.bat

的错误时,

对于像我这里:

  • Python 2.7

    • python2.7会去查找已安装的Visual Studio 2008,即VS90(其使用VS90COMNTOOLS这个环境变量)
  • 已经安装了VS2010

    • 对应的:C:Program Files (x86)Microsoft Visual Studio 10.0Common7Tools中就有了vsvars32.bat

的,不想安装mingw32的人来说,可以:

1.设置环境变量

执行:

1 `SET VS90COMNTOOLS=%VS100COMNTOOLS%`

2.再去安装:

1 `setup.py ``install`

就可以正常,编译,安装了。

注:

不过,我这里,好像是mmseg比较特殊,所有又出现了其他错误:

LINK : error LNK2001: 无法解析的外部符号 initmmseg

详细折腾过程参见:

【未解决】Python中安装mmseg时编译出错:LINK : error LNK2001: 无法解析的外部符号 initmmseg

**本文来自吴朱华博客,博客链接[http://blog.csdn.net/ikewu83/archive/2010/06/03/5643914.aspx](http://blog.csdn.net/ikewu83/archive/2010/06/03/5643914.aspx)** 本系列是基于公开资料对Google App Engine是如何实现的这个话题进行深度探讨。而且在切入Google App Engine之前,首先会对Google的核心技术和其整体架构进行分析,以帮助大家之后更好地理解Google App Engine的实现。 本篇将主要介绍Google的十个核心技术,而且可以分为四大类:

**分布式基础设施:GFS,Chubby和Protocol Buffer。 ** **分布式大规模数据处理:MapReduce和Sawzall。** **分布式数据库技术:BigTable和数据库Sharding。** **数据中心优化技术:数据中心高温化,12V电池和服务器整合。**

**分布式基础设施** GFS 由于搜索引擎需要处理海量的数据,所以Google的两位创始人Larry Page和Sergey Brin在创业初期设计一套名为“BigFiles”的文件系统,而GFS(全称为“Google File System”)这套分布式文件系统则是“BigFiles”的延续。 首先,介绍它的架构,GFS主要分为两类节点: 1. Master节点:主要存储与数据文件相关的元数据,而不是Chunk(数据块)。元数据包括一个能将64位标签映射到数据块的位置及其组成文件的表格,数据块副本位置和哪个进程正在读写特定的数据块等。还有Master节点会周期性地接收从每个Chunk节点来的更新(”Heart-beat”)来让元数据保持最新状态。 2. Chunk节点:顾名思义,肯定用来存储Chunk,数据文件通过被分割为每个默认大小为64MB的Chunk的方式存储,而且每个Chunk有唯一一个64位标签,并且每个Chunk都会在整个分布式系统被复制多次,默认为3次。 下图就是GFS的架构图: ![GFS Architecture](http://cms.csdnimg.cn/articlev1/uploads/allimg/100607/11015GR4-0.png "GFS Architecture")**图1\. GFS的架构图** 接着,在设计上,GFS主要有八个特点: 1. **大文件和大数据块**:数据文件的大小普遍在GB级别,而且其每个数据块默认大小为64MB,这样做的好处是减少了元数据的大小,能使Master节点能够非常方便地将元数据放置在内存中以提升访问效率。 2. **操作以添加为主**:因为文件很少被删减或者覆盖,通常只是进行添加或者读取操作,这样能充分考虑到硬盘线性吞吐量大和随机读写慢的特点。 3. **支持容错**:首先,虽然当时为了设计方便,采用了单Master的方案,但是整个系统会保证每个Master都会有其相对应的复制品,以便于在Master节点出现问题时进行切换。其次,在Chunk层,GFS已经在设计上将节点失败视为常态,所以能非常好地处理Chunk节点失效的问题。 4. **高吞吐量**:虽然其单个节点的性能无论是从吞吐量还是延迟都很普通,但因为其支持上千的节点,所以总的数据吞吐量是非常惊人的。 5. **保护数据**:首先,文件被分割成固定尺寸的数据块以便于保存,而且每个数据块都会被系统复制三份。 6. **扩展能力强:**因为元数据偏小,使得一个Master节点能控制上千个存数据的Chunk节点。 7. **支持压缩**:对于那些稍旧的文件,可以通过对它进行压缩,来节省硬盘空间,并且压缩率非常惊人,有时甚至接近90%。 8. **用户空间:**虽然在用户空间运行在运行效率方面稍差,但是更便于开发和测试,还有能更好利用Linux的自带的一些POSIX API。

**现在Google内部至少运行着200多个GFS集 群,最大的集群有几千台服务器,并且服务于多个Google服务,比如Google搜索。但由于GFS主要为搜索而设计,所以不是很适合新的一些 Google产品,比YouTube、Gmail和更强调大规模索引和实时性的Caffeine搜索引擎等,所以Google已经在开发下一代GFS,代 号为“Colossus”,并且在设计方面有许多不同,比如:支持分布式Master节点来提升高可用性并能支撑更多文件,chunk节点能支持1MB大 小的chunk以支撑低延迟应用的需要。**

**Chubby** 简单的来说,Chubby属于分布式锁服务,通过Chubby,一个分布式系统中的上千个client都能够对于某项资源进行“加锁”或者“解锁”,常用于BigTable的协作工作,在实现方面是通过对文件的创建操作来实现“加锁”,并基于著名科学家Leslie Lamport的Paxos算法。 **Protocol Buffer** Protocol Buffer,是Google内部使用一种语言中立,平台中立和可扩展的序列化结构化数据的方式,并提供java、c++ 和python这三种语言的实现,每一种实现都包含了相应语言的编译器以及库文件,而且它是一种二进制的格式,所以其速度是使用xml进行数据交换的10倍左右。它主要用于两个方面:其一是RPC通信,它可用于分布式应用之间或者异构环境下的通信。其二是数据存储方面,因为它自描述,而且压缩很方便,所以可用于对数据进行持久化,比如存储日志信息,并可被Map Reduce程序处理。与Protocol Buffer比较类似的产品还有Facebook的Thrift,而且Facebook号称Thrift在速度上还有一定的优势。 **分布式大规模数据处理** **MapReduce**

**首先,在Google数据中心会有大规模数据需要处 理,比如被网络爬虫(Web Crawler)抓取的大量网页等。由于这些数据很多都是PB级别,导致处理工作不得不尽可能的并行化,而Google为了解决这个问题,引入了 MapReduce这个编程模型,MapReduce是源自函数式语言,主要通过"Map(映射)"和"Reduce(化简)"这两个步骤来并行处理大规 模的数据集。Map会先对由很多独立元素组成的逻辑列表中的每一个元素进行指定的操作,且原始列表不会被更改,会创建多个新的列表来保存Map的处理结 果。也就意味着,Map操作是高度并行的。当Map工作完成之后,系统会先对新生成的多个列表进行清理(Shuffle)和排序,之后会这些新创建的列表 进行Reduce操作,也就是对一个列表中的元素根据Key值进行适当的合并。**

下图为MapReduce的运行机制: ![Map Reduce](http://cms.csdnimg.cn/articlev1/uploads/allimg/100607/11015J0A-1.PNG "Map Reduce") **图2\. MapReduce的运行机制**

**接下来,将根据上图来举一个MapReduce的例 子:比如,通过搜索Spider将海量的Web页面抓取到本地的GFS集群中,然后Index系统将会对这个GFS集群中多个数据Chunk进行平行的 Map处理,生成多个Key为URL,value为html页面的键值对(Key-Value Map),接着系统会对这些刚生成的键值对进行Shuffle(清理),之后系统会通过Reduce操作来根据相同的key值(也就是URL)合并这些键 值对。**

最后,通过MapReduce这么简单的编程模型,不仅能用于处理大规模数据,而且能将很多繁琐的细节隐藏起来,比如自动并行化,负载均衡和机器宕机处理等,这样将极大地简化程序员的开发工作。MapReduce可用于包括“分布grep,分布排序,web访问日志分析,反向索引构建,文档聚类,机器学习,基于统计的机器翻译,生成Google的整个搜索的索引“等大规模数据处理工作。Yahoo也推出MapReduce的开源版本Hadoop,而且Hadoop在业界也已经被大规模使用。 **Sawzall** Sawzall可以被认为是构建在MapReduce之上的采用类似Java语法的DSL(Domain-Specific Language),也可以认为它是分布式的AWK。它主要用于对大规模分布式数据进行筛选和聚合等高级数据处理操作,在实现方面,是通过解释器将其转化为相对应的MapReduce任务。除了Google的Sawzall之外,yahoo推出了相似的Pig语言,但其语法类似于SQL。 **分布式数据库技术** **BigTable** 由于在Google的数据中心存储PB级以上的非关系型数据时候,比如网页和地理数据等,为了更好地存储和利用这些数据,Google开发了一套数据库系统,名为“BigTable”。BigTable不是一个关系型的数据库,它也不支持关联(join)等高级SQL操作,取而代之的是多级映射的数据结构,并是一种面向大规模处理、容错性强的自我管理系统,拥有TB级的内存和PB级的存储能力,使用结构化的文件来存储数据,并每秒可以处理数百万的读写操作。 什么是多级映射的数据结构呢?就是一个稀疏的,多维的,排序的Map,每个Cell由行关键字,列关键字和时间戳三维定位.Cell的内容是一个不解释的字符串,比如下表存储每个网站的内容与被其他网站的反向连接的文本。 反向的URL com.cnn.www是这行的关键字;contents列存储网页内容,每个内容有一个时间戳,因为有两个反向连接,所以archor的Column Family有两列:anchor: cnnsi.com和anchhor:my.look.ca。Column Family这个概念,使得表可以轻松地横向扩展。下面是它具体的数据模型图: [![](http://cms.csdnimg.cn/articlev1/uploads/allimg/100607/10_1103336921.jpg)](http://cms.csdnimg.cn/articlev1) **图3\. BigTable数据模型图** 在结构上,首先,BigTable基于GFS分布式文件系统和Chubby分布式锁服务。其次BigTable也分为两部分:其一是Master节点,用来处理元数据相关的操作并支持负载均衡。其二是tablet节点,主要用于存储数据库的分片tablet,并提供相应的数据访问,同时tablet是基于名为SSTable的格式,对压缩有很好的支持。 ![Big Table Architecture](http://cms.csdnimg.cn/articlev1/uploads/allimg/100607/11015K931-2.PNG "Big Table Architecture") **图4\. BigTable架构图** BigTable正在为Google六十多种产品和项目提供存储和获取结构化数据的支撑平台,其中包括有Google Print, Orkut,Google Maps,Google Earth和Blogger等,而且Google至少运行着500个BigTable集群。

**随着Google内部服务对需求的不断提高和技术的不断地发展,导致原先的BigTable已经无法满足用户的需求,而Google也正在开发下一代BigTable,名为“Spanner(扳手)”,它主要有下面这些BigTable所无法支持的特性:**
  1. 支持多种数据结构,比如table,familie,group和coprocessor等。
  2. 基于分层目录和行的细粒度的复制和权限管理。
  3. 支持跨数据中心的强一致性和弱一致性控制。
  4. 基于Paxos算法的强一致性副本同步,并支持分布式事务。
  5. 提供许多自动化操作。
  6. 强大的扩展能力,能支持百万台服务器级别的集群。
  7. 用户可以自定义诸如延迟和复制次数等重要参数以适应不同的需求。

**数据库Sharding**

**Sharding就是分片的意思,虽然非关系型数据 库比如BigTable在Google的世界中占有非常重要的地位,但是面对传统OLTP应用,比如广告系统,Google还是采用传统的关系型数据库技 术,也就是MySQL,同时由于Google所需要面对流量非常巨大,所以Google在数据库层采用了分片(Sharding)的水平扩展(Scale Out)解决方案,分片是在传统垂直扩展(Scale Up)的分区模式上的一种提升,主要通过时间,范围和面向服务等方式来将一个大型的数据库分成多片,并且这些数据片可以跨越多个数据库和服务器来实现水平 扩展。 Google整套数据库分片技术主要有下面这些优点:**
  1. 扩展性强:在Google生产环境中,已经有支持上千台服务器的MySQL分片集群。
  2. 吞吐量惊人:通过巨大的MySQL分片集群能满足巨量的查询请求。
  3. 全球备份:不仅在一个数据中心还是在全球的范围,Google都会对MySQL的分片数据进行备份,这样不仅能保护数据,而且方便扩展。

在实现方面,主要可分为两块:其一是在MySQL InnoDB基础上添加了数据库分片的技术。其二是在ORM层的Hibernate的基础上也添加了相关的分片技术,并支持虚拟分片(Virtual Shard)来便于开发和管理。同时Google也已经将这两方面的代码提交给相关组织。 **数据中心优化技术 数据中心高温化** 大中型数据中心的PUE(Power Usage Effectiveness)普遍在2左右,也就是在服务器等计算设备上耗1度电,在空调等辅助设备上也要消耗一度电。对一些非常出色的数据中心,最多也就能达到1.7,但是Google通过一些有效的设计使部分数据中心到达了业界领先的1.2,在这些设计当中,其中最有特色的莫过于数据中心高温化,也就是让数据中心内的计算设备运行在偏高的温度下,Google的能源方面的总监Erik Teetzel在谈到这点的时候说:“普通的数据中心在70华氏度(21摄氏度)下面工作,而我们则推荐80华氏度(27摄氏度)“。但是在提高数据中心的温度方面会有两个常见的限制条件:其一是服务器设备的崩溃点,其二是精确的温度控制。如果做好这两点,数据中心就能够在高温下工作,因为假设数据中心的管理员能对数据中心的温度进行正负1/2度的调节,这将使服务器设备能在崩溃点5度之内工作,而不是常见的20度之内,这样既经济,又安全。还有,业界传言Intel为Google提供抗高温设计的定制芯片,但云计算界的顶级专家James Hamilton认为不太可能,因为虽然处理器也非常惧怕热量,但是与内存和硬盘相比还是强很多,所以处理器在抗高温设计中并不是一个核心因素。同时他也非常支持使数据中心高温化这个想法,而且期望将来数据中心甚至能运行在40摄氏度下,这样不仅能节省空调方面的成本,而且对环境也很有利。 **12V电池** 由于传统的UPS在资源方面比较浪费,所以Google在这方面另辟蹊径,采用了给每台服务器配一个专用的12V电池的做法来替换了常用的UPS,如果主电源系统出现故障,将由该电池负责对服务器供电。虽然大型UPS可以达到92%到95%的效率,但是比起内置电池的99.99%而言是非常捉襟见肘的,而且由于能量守恒的原因,导致那么未被UPS充分利用的电力会被转化成热能,这将导致用于空调的能耗相应地攀升,从而走入一个恶性循环。同时在电源方面也有类似的“神来之笔”,普通的服务器电源会同时提供5V和12V的直流电。但是Google设计的服务器电源只输出12V直流电,必要的转换在主板上进行,虽然这种设计会使主板的成本增加1美元到2美元,但是它不仅能使电源能在接近其峰值容量的情况下运行,而且在铜线上传输电流时效率更高。 **服务器整合** 谈到虚拟化的杀手锏时,第一个让人想到肯定是服务器整合,而且普遍能实现1:8的整合率来降低各方面的成本。有趣的是,Google在硬件方面也引入类似服务器整合的想法,它的做法是在一个机箱大小的空间内放置两台服务器,这些做的好处有很多,首先,减小了占地面积。其次,通过让两台服务器共享诸如电源等设备,来降低设备和能源等方面的投入。

EMC中国研究院的研究员颜开又在自己博客上分析了Google的全球级分布式数据库Spanner,他重点分析了Spanner的背景、设计和并发控制。

转自:

http://www.yankay.com/google-spanner%E5%8E%9F%E7%90%86-%E5%85%A8%E7%90%83%E7%BA%A7%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E6%95%B0%E6%8D%AE%E5%BA%93/

 

Google Spanner简介

Spanner 是Google的全球级的分布式数据库 (Globally-Distributed Database) 。Spanner的扩展性达到了令人咋舌的全球级,可以扩展到数百万的机器,数已百计的数据中心,上万亿的行。更给力的是,除了夸张的扩展性之外,他还能同时通过同步复制和多版本来满足外部一致性,可用性也是很好的。冲破CAP的枷锁,在三者之间完美平衡。 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13479630216652_f.jpg)](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13479630216652_f.jpg) Spanner是个可扩展,多版本,全球分布式还支持同步复制的数据库。他是Google的第一个可以全球扩展并且支持外部一致的事务。Spanner能做到这些,离不开一个用GPS和原子钟实现的时间API。这个API能将数据中心之间的时间同步精确到10ms以内。因此有几个给力的功能:无锁读事务,原子schema修改,读历史数据无block。 [EMC中国研究院](http://qing.weibo.com/2294942122/88ca09aa3300221n.html)实时紧盯业界动态,Google最近发布的一篇论文《[Spanner: Google's Globally-Distributed Database](http://research.google.com/archive/spanner.html)》, 笔者非常感兴趣,对Spanner进行了一些调研,并在这里分享。由于Spanner并不是开源产品,笔者的知识主要来源于Google的公开资料,通过现有公开资料仅仅只能窥得Spanner的沧海一粟,Spanner背后还依赖有大量Google的专有技术。研究院[原文](http://qing.weibo.com/2294942122/88ca09aa3300221n.html)。 下文主要是Spanner的背景,设计和并发控制。 ##### Spanner背景 要搞清楚Spanner原理,先得了解Spanner在Google的定位。 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13479632808586_f.jpg)](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13479632808586_f.jpg) 从上图可以看到。Spanner位于F1和GFS之间,承上启下。所以先提一提F1和GFS。 ###### F1 和众多互联网公司一样,在早期Google大量使用了Mysql。Mysql是单机的,可以用Master-Slave来容错,分区来扩展。但是需要大量的手工运维工作,有很多的限制。因此Google开发了一个可容错可扩展的RDBMS——F1。和一般的分布式数据库不同,F1对应RDMS应有的功能,毫不妥协。起初F1是基于Mysql的,不过会逐渐迁移到Spannerr。 F1有如下特点: * 7×24高可用。哪怕某一个数据中心停止运转,仍然可用。 * 可以同时提供强一致性和弱一致。 * 可扩展 * 支持SQL * 事务提交延迟50-100ms,读延迟5-10ms,高吞吐 众所周知Google BigTable是重要的Nosql产品,提供很好的扩展性,开源世界有HBase与之对应。为什么Google还需要F1,而不是都使用BigTable呢?因为BigTable提供的最终一致性,一些需要事务级别的应用无法使用。同时BigTable还是NoSql,而大量的应用场景需要有关系模型。就像现在大量的互联网企业都使用Mysql而不愿意使用HBase,因此Google才有这个可扩展数据库的F1。而Spanner就是F1的至关重要的底层存储技术。 ###### Colossus(GFS II) Colossus也是一个不得不提起的技术。他是第二代GFS,对应开源世界的新HDFS。GFS是著名的分布式文件系统。 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13479636073852_f.jpg)](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13479636073852_f.jpg) 初代GFS是为批处理设计的。对于大文件很友好,吞吐量很大,但是延迟较高。所以使用他的系统不得不对GFS做各种优化,才能获得良好的性能。那为什么Google没有考虑到这些问题,设计出更完美的GFS ? 因为那个时候是2001年,Hadoop出生是在2007年。如果Hadoop是世界领先水平的话,GFS比世界领先水平还领先了6年。同样的Spanner出生大概是2009年,现在我们看到了论文,估计Spanner在Google已经很完善,同时Google内部已经有更先进的替代技术在酝酿了。笔者预测,最早在2015年才会出现Spanner和F1的山寨开源产品。 Colossus是第二代GFS。Colossus是Google重要的基础设施,因为他可以满足主流应用对FS的要求。Colossus的重要改进有: * 优雅Master容错处理 (不再有2s的停止服务时间) * Chunk大小只有1MB (对小文件很友好) * Master可以存储更多的Metadata(当Chunk从64MB变为1MB后,Metadata会扩大64倍,但是Google也解决了) Colossus可以自动分区Metadata。使用Reed-Solomon算法来复制,可以将原先的3份减小到1.5份,提高写的性能,降低延迟。客户端来复制数据。具体细节笔者也猜不出。 ###### 与BigTable, Megastore对比 Spanner主要致力于跨数据中心的数据复制上,同时也能提供数据库功能。在Google类似的系统有BigTable和Megastore。和这两者相比,Spanner又有什么优势呢。 BigTable在Google得到了广泛的使用,但是他不能提供较为复杂的Schema,还有在跨数据中心环境下的强一致性。Megastore有类RDBMS的数据模型,同时也支持同步复制,但是他的吞吐量太差,不能适应应用要求。Spanner不再是类似BigTable的版本化 key-value存储,而是一个“临时多版本”的数据库。何为“临时多版本”,数据是存储在一个版本化的关系表里面,存储的时间数据会根据其提交的时间打上时间戳,应用可以访问到较老的版本,另外老的版本也会被垃圾回收掉。 Google官方认为 Spanner是下一代BigTable,也是Megastore的继任者。 ##### Google Spanner设计 ###### 功能 从高层看Spanner是通过Paxos状态机将分区好的数据分布在全球的。数据复制全球化的,用户可以指定数据复制的份数和存储的地点。Spanner可以在集群或者数据发生变化的时候将数据迁移到合适的地点,做负载均衡。用户可以指定将数据分布在多个数据中心,不过更多的数据中心将造成更多的延迟。用户需要在可靠性和延迟之间做权衡,一般来说复制1,2个数据中心足以保证可靠性。 作为一个全球化分布式系统,Spanner提供一些有趣的特性。 * 应用可以细粒度的指定数据分布的位置。精确的指定数据离用户有多远,可以有效的控制读延迟(读延迟取决于最近的拷贝)。指定数据拷贝之间有多远,可以控制写的延迟(写延迟取决于最远的拷贝)。还要数据的复制份数,可以控制数据的可靠性和读性能。(多写几份,可以抵御更大的事故) * Spanner还有两个一般分布式数据库不具备的特性:读写的外部一致性,基于时间戳的全局的读一致。这两个特性可以让Spanner支持一致的备份,一致的MapReduce,还有原子的Schema修改。 这写特性都得益有Spanner有一个全球时间同步机制,可以在数据提交的时候给出一个时间戳。因为时间是系列化的,所以才有外部一致性。这个很容易理解,如果有两个提交,一个在T1,一个在T2。那有更晚的时间戳那个提交是正确的。 这个全球时间同步机制是用一个具有GPS和原子钟的TrueTime API提供了。这个TrueTime API能够将不同数据中心的时间偏差缩短在10ms内。这个API可以提供一个精确的时间,同时给出误差范围。Google已经有了一个TrueTime API的实现。笔者觉得这个TrueTime API 非常有意义,如果能单独开源这部分的话,很多数据库如MongoDB都可以从中受益。 ###### 体系结构 Spanner由于是全球化的,所以有两个其他分布式数据库没有的概念。 * Universe。一个Spanner部署实例称之为一个Universe。目前全世界有3个。一个开发,一个测试,一个线上。因为一个Universe就能覆盖全球,不需要多个。 * Zones. 每个Zone相当于一个数据中心,一个Zone内部物理上必须在一起。而一个数据中心可能有多个Zone。可以在运行时添加移除Zone。一个Zone可以理解为一个BigTable部署实例 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13479636669121_f.jpg)](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13479636669121_f.jpg) 如图所示。一个Spanner有上面一些组件。实际的组件肯定不止这些,比如TrueTime API Server。如果仅仅知道这些知识,来构建Spanner是远远不够的。但Google都略去了。那笔者就简要介绍一下。 * Universemaster: 监控这个universe里zone级别的状态信息 * Placement driver:提供跨区数据迁移时管理功能 * Zonemaster:相当于BigTable的Master。管理Spanserver上的数据。 * Location proxy:存储数据的Location信息。客户端要先访问他才知道数据在那个Spanserver上。 * Spanserver:相当于BigTable的ThunkServer。用于存储数据。   ?可以看出来这里每个组件都很有料,但是Google的论文里只具体介绍了Spanserver的设计,笔者也只能介绍到这里。下面详细阐述Spanserver的设计。 ###### Spanserver 本章详细介绍Spanserver的设计实现。Spanserver的设计和BigTable非常的相似。参照下图 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13479637559889_f.jpg)](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13479637559889_f.jpg) 从下往上看。每个数据中心会运行一套Colossus (GFS II) 。每个机器有100-1000个tablet。Tablet概念上将相当于数据库一张表里的一些行,物理上是数据文件。打个比方,一张1000行的表,有10个tablet,第1-100行是一个tablet,第101-200是一个tablet。但和BigTable不同的是BigTable里面的tablet存储的是Key-Value都是string,Spanner存储的Key多了一个时间戳: (Key: string, timestamp: int64) -> string。 因此spanner天生就支持多版本,tablet在文件系统中是一个B-tree-like的文件和一个write-ahead日志。 每个Tablet上会有一个Paxos状态机。Paxos是一个分布式一致性协议。Table的元数据和log都存储在上面。Paxos会选出一个replica做leader,这个leader的寿命默认是10s,10s后重选。Leader就相当于复制数据的master,其他replica的数据都是从他那里复制的。读请求可以走任意的replica,但是写请求只有去leader。这些replica统称为一个paxos group。 每个leader replica的spanserver上会实现一个lock table还管理并发。Lock tableoriginal了两阶段提交需要的锁信息。但是不论是在Spanner还是在BigTable上,但遇到冲突的时候长时间事务会将性能很差。所以有一些操作,如事务读可以走lock table,其他的操作可以绕开lock table。 每个leader replica的spanserver上还有一个transaction manager。如果事务在一个paxos group里面,可以绕过transaction manager。但是一旦事务跨多个paxos group,就需要transaction manager来协调。其中一个Transaction manager被选为leader,其他的是slave听他指挥。这样可以保证事务。 ###### Directories and Placement 之所以Spanner比BigTable有更强的扩展性,在于Spanner还有一层抽象的概念directory, directory是一些key-value的集合,一个directory里面的key有一样的前缀。更妥当的叫法是bucketing。Directory是应用控制数据位置的最小单元,可以通过谨慎的选择Key的前缀来控制。据此笔者可以猜出,在设计初期,Spanner是作为F1的存储系统而设立,甚至还设计有类似directory的层次结构,这样的层次有很多好处,但是实现太复杂被摒弃了。 Directory作为数据放置的最小单元,可以在paxos group里面移来移去。Spanner移动一个directory一般出于如下几个原因: * 一个paxos group的负载太大,需要切分 * 将数据移动到access更近的地方 * 将经常同时访问的directory放到一个paxos group里面 Directory可以在不影响client的前提下,在后台移动。移动一个50MB的directory大概需要的几秒钟。 那么directory和tablet又是什么关系呢。可以理解为Directory是一个抽象的概念,管理数据的单元;而tablet是物理的东西,数据文件。由于一个Paxos group可能会有多个directory,所以spanner的tablet实现和BigTable的tablet实现有些不同。BigTable的tablet是单个顺序文件。Google有个项目,名为Level DB,是BigTable的底层,可以看到其实现细节。而Spanner的tablet可以理解是一些基于行的分区的容器。这样就可以将一些经常同时访问的directory放在一个tablet里面,而不用太在意顺序关系。 在paxos group之间移动directory是后台任务。这个操作还被用来移动replicas。移动操作设计的时候不是事务的,因为这样会造成大量的读写block。操作的时候是先将实际数据移动到指定位置,然后再用一个原子的操作更新元数据,完成整个移动过程。 Directory还是original地理位置的最小单元。数据的地理位置是由应用决定的,配置的时候需要指定复制数目和类型,还有地理的位置。比如(上海,复制2份;南京复制1分) 。这样应用就可以根据用户指定终端用户实际情况决定的数据存储位置。比如中国队的数据在亚洲有3份拷贝, 日本队的数据全球都有拷贝。 前面对directory还是被简化过的,还有很多无法详述。 ###### 数据模型 Spanner的数据模型来自于Google内部的实践。在设计之初,Spanner就决心有以下的特性: * 支持类似关系数据库的schema * Query语句 * 支持广义上的事务 为何会这样决定呢?在Google内部还有一个Megastore,尽管要忍受性能不够的折磨,但是在Google有300多个应用在用它,因为Megastore支持一个类似关系数据库的schema,而且支持同步复制 (BigTable只支持最终一致的复制) 。使用Megastore的应用有大名鼎鼎的Gmail, Picasa, Calendar, Android Market和AppEngine。 而必须对Query语句的支持,来自于广受欢迎的Dremel,笔者不久前写了篇文章来介绍他。 最后对事务的支持是比不可少了,BigTable在Google内部被抱怨的最多的就是其只能支持行事务,再大粒度的事务就无能为力了。Spanner的开发者认为,过度使用事务造成的性能下降的恶果,应该由应用的开发者承担。应用开发者在使用事务的时候,必须考虑到性能问题。而数据库必须提供事务机制,而不是因为性能问题,就干脆不提供事务支持。 数据模型是建立在directory和key-value模型的抽象之上的。一个应用可以在一个universe中建立一个或多个database,在每个database中建立任意的table。Table看起来就像关系型数据库的表。有行,有列,还有版本。Query语句看起来是多了一些扩展的SQL语句。 Spanner的数据模型也不是纯正的关系模型,每一行都必须有一列或多列组件。看起来还是Key-value。主键组成Key,其他的列是Value。但这样的设计对应用也是很有裨益的,应用可以通过主键来定位到某一行。 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13479638417129_f.jpg)](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13479638417129_f.jpg) 上图是一个例子。对于一个典型的相册应用,需要存储其用户和相册。可以用上面的两个SQL来创建表。Spanner的表是层次化的,最顶层的表是directory table。其他的表创建的时候,可以用 interleave in parent来什么层次关系。这样的结构,在实现的时候,Spanner可以将嵌套的数据放在一起,这样在分区的时候性能会提升很多。否则Spanner无法获知最重要的表之间的关系。 ###### TrueTime [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13479643368266_f.jpg)](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13479643368266_f.jpg) TrueTime API 是一个非常有创意的东西,可以同步全球的时间。上表就是TrueTime API。TT.now()可以获得一个绝对时间TTinterval,这个值和UnixTime是相同的,同时还能够得到一个误差e。TT.after(t)和TT.before(t)是基于TT.now()实现的。 那这个TrueTime API实现靠的是GFS和原子钟。之所以要用两种技术来处理,是因为导致这两个技术的失败的原因是不同的。GPS会有一个天线,电波干扰会导致其失灵。原子钟很稳定。当GPS失灵的时候,原子钟仍然能保证在相当长的时间内,不会出现偏差。 实际部署的时候。每个数据中心需要部署一些Master机器,其他机器上需要有一个slave进程来从Master同步。有的Master用GPS,有的Master用原子钟。这些Master物理上分布的比较远,怕出现物理上的干扰。比如如果放在一个机架上,机架被人碰倒了,就全宕了。另外原子钟不是并很贵。Master自己还会不断比对,新的时间信息还会和Master自身时钟的比对,会排除掉偏差比较大的,并获得一个保守的结果。最终GPS master提供时间精确度很高,误差接近于0。   每个Slave后台进程会每个30秒从若干个Master更新自己的时钟。为了降低误差,使用Marzullo算法。每个slave还会计算出自己的误差。这里的误差包括的通信的延迟,机器的负载。如果不能访问Master,误差就会越走越大,知道重新可以访问。 ##### Google Spanner并发控制 Spanner使用TrueTime来控制并发,实现外部一致性。支持以下几种事务。 * 读写事务 * 只读事务 * 快照读,客户端提供时间戳 * 快照读,客户端提供时间范围 例如一个读写事务发生在时间t,那么在全世界任何一个地方,指定t快照读都可以读到写入的值。

**Operation** **Concurrency Control** **Replica Required**
**Read-Write Transaction** pessimistic leader
**Read-Only Transaction** lock-free leader for timestamp; any for read
**Snapshot Read, client-provided timestamp** lock-free any
**Snapshot Read, client-provided bound** lock-free any

上表是Spanner现在支持的事务。单独的写操作都被实现为读写事务 ; 单独的非快照被实现为只读事务。事务总有失败的时候,如果失败,对于这两种操作会自己重试,无需应用自己实现重试循环。 时间戳的设计大大提高了只读事务的性能。事务开始的时候,要声明这个事务里没有写操作,只读事务可不是一个简单的没有写操作的读写事务。它会用一个系统时间戳去读,所以对于同时的其他的写操作是没有Block的。而且只读事务可以在任意一台已经更新过的replica上面读。 对于快照读操作,可以读取以前的数据,需要客户端指定一个时间戳或者一个时间范围。Spanner会找到一个已经充分更新好的replica上读取。 还有一个有趣的特性的是,对于只读事务,如果执行到一半,该replica出现了错误。客户端没有必要在本地缓存刚刚读过的时间,因为是根据时间戳读取的。只要再用刚刚的时间戳读取,就可以获得一样的结果。 ###### 读写事务 正如BigTable一样,Spanner的事务是会将所有的写操作先缓存起来,在Commit的时候一次提交。这样的话,就读不出在同一个事务中写的数据了。不过这没有关系,因为Spanner的数据都是有版本的。 在读写事务中使用wound-wait算法来避免死锁。当客户端发起一个读写事务的时候,首先是读操作,他先找到相关数据的leader replica,然后加上读锁,读取最近的数据。在客户端事务存活的时候会不断的向leader发心跳,防止超时。当客户端完成了所有的读操作,并且缓存了所有的写操作,就开始了两阶段提交。客户端闲置一个coordinator group,并给每一个leader发送coordinator的id和缓存的写数据。 leader首先会上一个写锁,他要找一个比现有事务晚的时间戳。通过Paxosoriginal。每一个相关的都要给coordinator发送他自己准备的那个时间戳。 Coordinator leader一开始也会上个写锁,当大家发送时间戳给他之后,他就选择一个提交时间戳。这个提交的时间戳,必须比刚刚的所有时间戳晚,而且还要比TT.now()+误差时间 还有晚。这个Coordinator将这个信息original到Paxos。 在让replica写入数据生效之前,coordinator还有再等一会。需要等两倍时间误差。这段时间也刚好让Paxos来同步。因为等待之后,在任意机器上发起的下一个事务的开始时间,都比如不会比这个事务的结束时间早了。然后coordinator将提交时间戳发送给客户端还有其他的replica。他们original日志,写入生效,释放锁。 ###### 只读事务 对于只读事务,Spanner首先要指定一个读事务时间戳。还需要了解在这个读操作中,需要访问的所有的读的Key。Spanner可以自动确定Key的范围。 如果Key的范围在一个Paxos group内。客户端可以发起一个只读请求给group leader。leader选一个时间戳,这个时间戳要比上一个事务的结束时间要大。然后读取相应的数据。这个事务可以满足外部一致性,读出的结果是最后一次写的结果,并且不会有不一致的数据。 如果Key的范围在多个Paxos group内,就相对复杂一些。其中一个比较复杂的例子是,可以遍历所有的group leaders,寻找最近的事务发生的时间,并读取。客户端只要时间戳在TT.now().latest之后就可以满足要求了。 最后的话 本文介绍了Google Spanner的背景,设计和并发控制。希望不久的将来,会有开源产品出现。

简介

Dremel 是Google 的“交互式”数据分析系统。可以组建成规模上千的集群,处理PB级别的数据。MapReduce处理一个数据,需要分钟级的时间。作为MapReduce的发起人,Google开发了Dremel将处理时间缩短到秒级,作为MapReduce的有力补充。Dremel作为Google BigQuery的report引擎,获得了很大的成功。最近Apache计划推出Dremel的开源实现Drill,将Dremel的技术又推到了浪尖上。 ##### Google Dremel设计 根据Google公开的论文《[Dremel: Interactive Analysis of WebScaleDatasets](http://research.google.com/pubs/pub36632.html)》可以看到Dremel的设计原理。还有一些测试报告。论文写于2006年,公开于2010年,Google在处理大数据方面,果真有得天独厚的优势。下面的内容,很大部分来自这篇论文。 随着Hadoop的流行,大规模的数据分析系统已经越来越普及。数据分析师需要一个能将数据“玩转”的交互式系统。如此,就可以非常方便快捷的浏览数据,建立分析模型。Dremel系统有下面几个主要的特点: * **Dremel****是一个大规模系统。**在一个PB级别的数据集上面,将任务缩短到秒级,无疑需要大量的并发。磁盘的顺序读速度在100MB/S上下,那么在1S内处理1TB数据,意味着至少需要有1万个磁盘的并发读! Google一向是用廉价机器办大事的好手。但是机器越多,出问题概率越大,如此大的集群规模,需要有足够的容错考虑,保证整个分析的速度不被集群中的个别慢(坏)节点影响。 * **Dremel****是MR****交互式查询能力不足的补充。**和MapReduce一样,Dremel也需要和数据运行在一起,将计算移动到数据上面。所以它需要GFS这样的文件系统作为存储层。在设计之初,Dremel并非是MapReduce的替代品,它只是可以执行非常快的分析,在使用的时候,常常用它来处理MapReduce的结果集或者用来建立分析原型。 * **Dremel****的数据模型是嵌套(nested)****的。**互联网数据常常是非关系型的。Dremel还需要有一个灵活的数据模型,这个数据模型至关重要。Dremel支持一个嵌套(nested)的数据模型,类似于Json。而传统的关系模型,由于不可避免的有大量的Join操作,在处理如此大规模的数据的时候,往往是有心无力的。 * **Dremel****中的数据是用列式存储的。**使用列式存储,分析的时候,可以只扫描需要的那部分数据的时候,减少CPU和磁盘的访问量。同时列式存储是压缩友好的,使用压缩,可以综合CPU和磁盘,发挥最大的效能。对于关系型数据,如果使用列式存储,我们都很有经验。但是对于嵌套(nested)的结构,Dremel也可以用列存储,非常值得我们学习。 * **Dremel****结合了Web****搜索 ****和并行DBMS****的技术。**首先,他借鉴了Web搜索中的“查询树”的概念,将一个相对巨大复杂的查询,分割成较小较简单的查询。大事化小,小事化了,能并发的在大量节点上跑。其次,和并行DBMS类似,Dremel可以提供了一个SQL-like的接口,就像Hive和Pig那样。 #### Google Dremel应用场景 设想一个使用场景。我们的美女数据分析师,她有一个新的想法要验证。要验证她的想法,需要在一个上亿条数据上面,跑一个查询,看看结果和她的想法是不是一样,她可不希望等太长时间,最好几秒钟结果就出来。当然她的想法不一定完善,还需要不断调整语句。然后她验证了想法,发现了数据中的价值。最后,她可以将这个语句完善成一个长期运行的任务。 对于Google,数据一开始是放在GFS上的。可以通过MapReduce将数据导入到Dremel中去,在这些MapReduce中还可以做一些处理。然后分析师使用Dremel,轻松愉悦的分析数据,建立模型。最后可以编制成一个长期运行的MapReduce任务。 这种处理方式,让笔者联想到Greenplum的[Chorus](http://www.greenplum.com/products/chorus). Chorus也可以为分析师提供快速的数据查询,不过解决方案是通过预处理,导入部分数据,减少数据集的大小。用的是三十六计,走为上计,避开的瞬时分析大数据的难题。Chorus最近即将开源,可以关注下。 还有一点特别的就是按列存储的嵌套数据格式。如图所示,在按original存储的模式中,一个original的多列是连续的写在一起的。在按列存储中,可以将数据按列分开。也就是说,可以仅仅扫描A.B.C而不去读A.E或者A.B.C。难点在于,我们如何能同时高效地扫描若干列,并做一些分析。 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458644104566_f.jpg "Record-wise vs. columnar representation of nested data ")](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458644104566_f.jpg) #### Google Dremel数据模型 在Google, 用Protocol Buffer常常作为序列化的方案。其数据模型可以用数学方法严格的表示如下:     t=d om|<A 1:t[∗|?],...,An:t[∗|?]>     其中t可以是一个基本类型或者组合类型。其中基本类型可以是integer,float和string。组合类型可以是若干个基本类型拼凑。星号(*)指的是任何类型都可以重复,就是数组一样。问号(?)指的是任意类型都是可以是可选的。简单来说,除了没有Map外,和一个Json几乎没有区别。 下图是例子,Schema定义了一个组合类型Document.有一个必选列DocId,可选列Links,还有一个数组列Name。可以用Name.Language.Code来表示Code列。 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458646314107_f.jpg "Two sample nested records and their schema")](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458646314107_f.jpg) 这种数据格式是语言无关,平台无关的。可以使用Java来写MR程序来生成这个格式,然后用C++来读取。在这种列式存储中,能够快速通用处理也是非常的重要的。 上图,是一个示例数据的抽象的模型;下图是这份数据在Dremel实际的存储的格式。 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458643358445_f.jpg "Column-striped representation of the sample data in Figure")](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458643358445_f.jpg) 如果是关系型数据,而不是嵌套的结构。存储的时候,我们可以将每一列的值直接排列下来,不用引入其他的概念,也不会丢失数据。对于嵌套的结构,我们还需要两个变量R (Repetition Level) ,D (Definition Level) 才能存储其完整的信息。 **Repetition Level**是original该列的值是在哪一个级别上重复的。举个例子说明:对于Name.Language.Code? 我们一共有三条非Null的original。 1. 第一个是”en-us”,出现在第一个Name的第一个Lanuage的第一个Code里面。在此之前,这三个元素是没有重复过的,都是第一个。所以其R为0。 2. 第二个是”en”,出现在下一个Lanuage里面。也就是说Lanague是重复的元素。Name.Language.Code中Lanague排第二个,所以其R为2. 3. 第三个是”en-gb”,出现在下一个Name中,Name是重复元素,排第一个,所以其R为1。 我们可以想象,将所有的没有值的列,设值为NULL。如果是数组列,我们也想象有一个NULL值。有了Repetition Level,我们就可以很好的用列表示嵌套的结构了。但是还有一点不足。就是还需要表示一个数组是不是我们想象出来的。 **Definition Level** 是定义的深度,用来original该列是否是”想象”出来的。所以对于非NULL的original,是没有意义的,其值必然为相同。同样举个例子。例如Name.Language.Country, * 第一个”us”是在R1里面,其中Name,Language,Country是有定义的。所以D为3。 * 第二个”NULL”也是在R1的里面,其中Name,Language是有定义的,其他是想象的。所以D为2。 * 第三个”NULL”还是在R1的里面,其中Name是有定义的,其他是想象的。所以D为1。 * 第四个”gb”是在R1里面,其中Name,Language,Country是有定义的。所以D为3。 就是这样,如果路径中有required,可以将其减去,因为required必然会define,original其数量没有意义。 理解了如何存储这种嵌套结构。写没有难度。读的时候,我们只读其中部分字段,来构建部分的数据模型。例如,只读取DocID和Name.Language.Country。我们可以同时扫描两个字段,先扫描DocID。original下第一个,然后发现下一个DocID的R是0;于是该读Name.Language.Country,如果下一个R是1或者2就继续读,如果是0就开始读下一个DocID。 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458640099204_f.jpg "Automaton for assembling records from two fields, and")](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458640099204_f.jpg) 下图展示了一个更为复杂的读取的状态机示例。在读取过程中使用了Definition Level来快速Jump,提升性能。 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13460300363599_f.jpg "Complete record assembly automaton. Edges are labeled<br /> with repetition levels")](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13460300363599_f.jpg) 到此为止,我们已经知道了Dremel的数据结构。就像其他数据分析系统一样,数据结构确定下来,功能就决定了一大半。对于Dremel的数据查询,必然是“全表扫描”,但由于其巧妙的列存储设计,良好的数据模型设计可以回避掉大部分Join需求和扫描最少的列。 #### Google Dremel查询方式 Dremel可以使用一种SQL-like的语法查询嵌套数据。由于Dremel的数据是只读的,并且会密集的发起多次类似的请求。所以可以保留上次请求的信息,还优化下次请求的explain过程。那又是如何explain的呢? [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458646380664_f.jpg "System architecture and execution inside a server node")](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458646380664_f.jpg) 这是一个树状架构。当Client发其一个请求,根节点受到请求,根据metadata,将其分解到枝叶,直到到位于数据上面的叶子Server。他们扫描处理数据,又不断汇总到根节点。 举个例子:对于请求:

1 SELECT A, COUNT(B) FROM T GROUP BY A

根节点收到请求,会根据数据的分区请求,将请求变成可以拆分的样子。原来的请求会变为。

1 SELECT A, SUM(c) FROM (R1 UNION ALL ... Rn) GROUP BY A

R1,…RN是T的分区计算出的结果集。越大的表有越多的分区,越多的分区可以越好的支持并发。 然后再将请求切分,发送到每个分区的叶子Server上面去,对于每个Server

1 ?Ri = SELECT A, COUNT(B) AS c FROM Ti GROUP BY A

结构集一定会比原始数据小很多,处理起来也更快。根服务器可以很快的将数据汇总。具体的聚合方式,可以使用现有的并行数据库技术。 Dremel是一个多用户的系统。切割分配任务的时候,还需要考虑用户优先级和负载均衡。对于大型系统,还需要考虑容错,如果一个叶子Server出现故障或变慢,不能让整个查询也受到明显影响。 通常情况下,每个计算节点,执行多个任务。例如,技巧中有3000个叶子Server,每个Server使用8个线程,有可以有24000个计算单元。如果一张表可以划分为100000个区,就意味着大约每个计算单元需要计算5个区。这执行的过程中,如果某一个计算单元太忙,就会另外启一个来计算。这个过程是动态分配的。 对于GFS这样的存储,一份数据一般有3份拷贝,计算单元很容易就能分配到数据所在的节点上,典型的情况可以到达95%的命中率。 Dremel还有一个配置,就是在执行查询的时候,可以指定扫描部分分区,比如可以扫描30%的分区,在使用的时候,相当于随机抽样,加快查询。 ##### Google Dremel测试实验 实验的数据源如下表示。大部分数据复制了3次,也有一个两次。每个表会有若干分区,每个分区的大小在100K到800K之间。如果压缩率是25%,并且计入复制3份的事实的话。T1的大小已经达到PB级别。这么小且巨量的分区,对于GFS的要求很高,现在的Hdfs稳定版恐怕受不了。接下来的测试会逐步揭示其是如何超过MR,并对性能作出分析。

**表名** **original数** **大小(****已压缩)** **列数** **数据中心** **复制数量**
T1 85 billion 87 TB 270 A
T2 24 billion 13 TB 530 A
T3 4 billion 70 TB 1200 A
T4 1+ trillion 105 TB 50 B
T5 1+ trillion 20 TB 30 B

列存测试

首先,我们测试看看列存的效果。对于T1表,1GB的数据大约有300K行,使用列存的话压缩后大约在375MB。这台机器磁盘的吞吐在70MB/s左右。这1GB的数据,就是我们的现在的测试数据源,测试环境是单机。 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458638268923_f.jpg)](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458638268923_f.jpg) 见上图。 * 曲线A,是用列存读取数据并解压的耗时。 * 曲线B是一条一条original挨个读的时间。 * 曲线C是在B的基础上,加上了反序列化的时间。 * 曲线d,是按行存读并解压的耗时。 * 曲线e加上了反序列化的时间。因为列很多,反序列化耗时超过了读并解压的50%。 从图上可以看出。如果需要读的列很少的话,列存的优势就会特别的明显。对于列的增加,产生的耗时也几乎是线性的。而一条一条该个读和反序列化的开销是很大的,几乎都在原来基础上增加了一倍。而按行读,列数的增加没有影响,因为一次性读了全部列。 #### Dremel和MapReduce的对比测试 MR和Dremel最大的区别在于行存和列存。如果不能击败MapReduce,Remel就没有意义了。使用最常见的WordCount测试,计算这个数据中Word的个数。

1 Q1: SELECT SUM(CountWords(txtField)) / COUNT(*) FROM T1

[![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458638073453_f.jpg "MR and Dremel execution on columnar vs. recordoriented")](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458638073453_f.jpg)   上图是测试的结果。使用了两个MR任务。这两个任务和Dremel一样都运行在3000个节点上面。如果使用列存,Dremel的按列读的MR只需要读0.5TB的数据,而按行存需要读87TB。 MR提供了一个方便有效的途经来讲按行数据转换成按列的数据。Dremel可以方便的导入MapReduce的处理结果。 #### 树状计算Server测试 接下来我们要对比在T2表示使用两个不同的Group BY查询。T2表有24 billion 行的original。每个original有一个 item列表,每一item有一个amount 字段。总共有40 billion个item.amount。这两个Query分别是。

1 2 3 Q2: SELECT country, SUM(item.amount) FROM T2 GROUP BY country   Q3: SELECT domain, SUM(item.amount) FROM T2 WHERE domain CONTAINS ’.net’ GROUP BY domain

Q2需要扫描60GB的压缩数据,Q3需要扫描180GB,同时还要过滤一个条件。 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458647595741_f.jpg "Execution time as a function of serving tree levels for")](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458647595741_f.jpg) 上图是这两个Query在不同的server拓扑下的性能。每个测试都是有2900个叶子Server。在2级拓扑中,根server直接和叶子Server通信。在3级拓扑中,各个级别的比例是1:100:2900,增加了100个中间Server。在4级拓扑中,比例为1:10:100:2900. Q2可以在3级拓扑下3秒内执行完毕,但是为他提供更高的拓扑级别,对性能提升没有裨益。相比之下,为Q3提供更高的拓扑级别,性能可以有效提升。这个测试体现了树状拓扑对性能提升的作用。 #### 每个分区的执行情况 对于刚刚的两个查询,具体的每个分区的执行情况是这样的。 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458637868939_f.jpg "Histograms of processing times")](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458637868939_f.jpg) 可以看到99%的分区都在1s内完成了。Dremel会自动调度,使用新的Server计算拖后腿的任务。 #### original内聚合 由于Demel支持List的数据类型,有的时候,我们需要计算每个original里面的各个List的聚合。如

1 2 3 4 5 6 7 Q4 : SELECT COUNT(c1 &gt; c2) FROM   (SELECT SUM(a.b.c.d) WITHIN RECORD AS c1,   SUM(a.b.p.q.r) WITHIN RECORD AS c2   FROM T3)

我们需要count所有sum(a.b.c.d)比sum(a.b.p.q.r),执行这条语句实际只需要扫描13GB的数据,耗时15s,而整张表有70TB。如果没有这样的嵌套数据结构,这样的查询会很复杂。 #### 扩展性测试 Dremel有良好的扩展性,可以通过增加机器来缩短查询的时间。并且可以处理数以万亿计的original。 对于查询:

1 Q5: SELECT TOP(aid, 20), COUNT(*) FROM T4?WHERE bid = fvalue1g AND cid = fvalue2g

使用不同的叶子Server数目来进行测试。 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458644318827_f.jpg "Scaling the system from 1000 to 4000 nodes using a")](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458644318827_f.jpg) 可以发现CPU的耗时总数是基本不变的,在30万秒左右。但是随着节点数的增加,执行时间也会相应缩短。几乎呈线性递减。如果我们使用通过CPU时间计费的“云计算”机器,每个租户的查询都可以很快,成本也会非常低廉。 ##### 容错测试 一个大团队里面,总有几个拖油瓶。对于有万亿条original的T5,我们执行下面的语句。

1 Q6: SELECT COUNT(DISTINCT a) FROM T5

值得注意的是T5的数据只有两份拷贝,所以有更高的概率出现坏节点和拖油瓶。这个查询需要扫描大约1TB的压缩数据,使用2500个节点。 [![](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458642912961_f.jpg "Query Q5 on T5 illustrating stragglers")](http://yankaycom-wordpress.stor.sinaapp.com/uploads/2012/12/13458642912961_f.jpg)   可以看到99%的分区都在5S内完成的。不幸的是,有一些分区需要较长的时间来处理。尽管通过动态调度可以加快一些,但在如此大规模的计算上面,很难完全不出问题。如果不在意太精确的结果,完全可以小小减少覆盖的比例,大大提升相应速度。 ##### Google Dremel 的影响 Google Dremel的能在如此短的时间内处理这么大的数据,的确是十分惊艳的。有个伯克利分校的教授Armando Fox说过一句话“如果你曾事先告诉我Dremel声称其将可做些什么,那么我不会相信你能开发出这种工具”。这么给力的技术,必然对业界造成巨大的影响。第一个被波及到的必然是Hadoop。 #### Dremel与Hadoop Dremel的公开论文里面已经说的很明白,Dremel不是用来替代MapReduce,而是和其更好的结合。Hadoop的Hive,Pig无法提供及时的查询,而Dremel的快速查询技术可以给Hadoop提供有力的补充。同时Dremel可以用来分析MapReduce的结果集,只需要将MapReduce的OutputFormat修改为Dremel的格式,就可以几乎不引入额外开销,将数据导入Dremel。使用Dremel来开发数据分析模型,MapReduce来执行数据分析模型。 Hadoop的Hive,Pig现在也有了列存的模式,架构上和Dremel也接近。但是无论存储结构还是计算方式都没有Dremel精致。对Hadoop实时性的改进也一直是个热点话题。要想在Hadoop中山寨一个Dremel,并且相对现有解决方案有突破,笔者觉得Hadoop自身需要一些改进。一个是HDFS需要对并发细碎的数据读性能有大的改进,HDFS需要更加的低延迟。再者是Hadoop需要不仅仅支持MapReduce这一种计算框架。其他部分,Hadoop都有对应的开源组件,万事俱备只欠东风。 #### Dremel的开源实现 Dremel现在还没有一个可以运行的开源实现,不过我们看到很多努力。一个是Apache的Drill,一个是OpenDremel/Dazo。 **OpenDremel/Dazo** OpenDremel是一个开源项目,最近改名为Dazo。可以在GoogleCode上找到[http://code.google.com/p/dremel/](http://code.google.com/p/dremel/)。目前还没有发布。作者声称他已经完成了一个通用执行引擎和OpenStack Swift的集成。笔者感觉其越走越歪,离Dremel越来越远了。 **Apache Drill** [Drill ](http://wiki.apache.org/incubator/DrillProposal)是Hadoop的赞助商之一MapR发起的。Drill作为一个Dremel的山寨项目,有和Dremel相似的架构和能力。他们希望Drill最终会想Hive,Pig一样成为Hadoop上的重要组成部分。为Hadoop提供快速查询的能力。和Dremel有一点不同,在数据模型上,开源的项目需要支持更标准的数据结构。比如CSV和JSON。同时Drill还有更大的灵活性,支持多重查询语言,多种接口。 现在Drill的目标是完成初始的需求,架构。完成一个初始的实现。这个实现包括一个执行引擎和DrQL。DrQL是一个基于列的格式,类似于Dremel。目前,Drill已经完成的需求和架构设计。总共分为了四个组件 * Query language:类似Google BigQuery的查询语言,支持嵌套模型,名为DrQL. * Low-lantency distribute execution engine:执行引擎,可以支持大规模扩展和容错。可以运行在上万台机器上计算数以PB的数据。 * Nested data format:嵌套数据模型,和Dremel类似。也支持CSV,JSON,YAML类似的模型。这样执行引擎就可以支持更多的数据类型。 * Scalable data source: 支持多种数据源,现阶段以Hadoop为数据源。 目前这四个组件在分别积极的推进,Drill也非常希望有社区其他公司来加入。Drill希望加入到Hadoop生态系统中去。 ##### 最后的话 本文介绍了Google Dremel的使用场景,设计实现,测试实验,和对开源世界的影响。相信不久的将来,Dremel的技术会得到广泛的应用。

今天看再要你命3000,list2后面附的一段话,感觉写的很好,抄在这激励自己好好复习,早点出国

Today is the first day of the rest of my life, I wake as a child to see the world begin. On monarch wings and birthday wonderings, want to put on faces, walk in the wet and cold. And look forward to my growing old, to grow is to change, to change is to be new, to be new is to be young again, I barely remember when.