GAE Datastore是Google App Engine提供的(半)结构化数据存储系统,基于Google大名鼎鼎的Bigtable技术构建。
一、数据模型
GAE Datastore的数据模型与关系模型有很大的相似性,但是无模式的。GAE Datastore的接口主要是ORM风格的,一个类,称为kind,与关系数据库中的表类似。一个kind中的数据为多个entity,每个entity有唯一的key标识。每个entity可有多个property,一个property可用多个value。这与关系模型有类似的地方,但GAE Datastore中属于同一个model的不同entity可以拥有完成不同的property,不同entity的同一个property的value的类型也可以不一样。因此GAE Datastore的数据模型更为灵活。
多个entity可组成entity group,一个entity group实际上是以一个entity为根,通过父子关系(在创建entity时指定父亲)构成的子树。这一模型类似于关系模型之前的层次数据库,自然也拥有与层次数据库类似的局限,如很多模型就很难自然的用这种层次模型建模,如学生选课系统Student-Course-Elect,谁是谁的父亲呢。entity group主要用于事务,后面会讲到。
二、查询与索引
GAE Datastore提供类似于SQL的GQL查询,从SQL的观点看,GQL的限制是只有单表查询,有WHERE、ORDER BY和LIMIT/OFFSET,但没有GROUP BY、HAVING、聚集函数等功能,也不支持子查询。WHERE条件可以是基本的"property op value"条件通过and/or任意组合,ORDER BY可指定多个属性。但条件的复杂度有一定限制:
1、如IN (list)条件中list最多只能有30个元素;
2、不等条件只能针对一个属性指定;
3、不等条件属性必须出现到ORDER BY的最前;
这些限制,据估计应该是为了实现方便和保证性能,如不等条件属性在ORDER BY的最前这一限制使得系统可以方便的通过索引扫描直接输出有序的结果,不需要再来排序。
更新的形式相比SQL有很大的限制。UPDATE通过put接口实现,给的参数是一个完整的entity,似乎不能像SQL一样只更新某些属性,为了更新一个属性,似乎需要先取出整个entity(系统可能用lazy load技术,没有用到的属性不会取)。删除时只能指定一个key列表,在关系数据库中的一条DELETE语句要分成两步,先通过查询得到要删除的entity的key,然后再来删除。并且一次操作中删除的entity个数不能超过500。
默认情况下GAE Datastore会建立一些基本的索引,根据文档的描述,我推测GAE应默认为每个属性建了一个索引,并且索引中都包含key (类似于InnoDB中的二级索引中都包含主键)。应用也可以在在配置文件中定义索引,指定索引包含的属性及排序方向。索引的排序方向必须与查询中ORDER BY的方向一致,也就是索引只能正向,不能反向扫描,我不清楚造成这一奇怪限制的原因是什么。
如果一个查询没有合适的索引,则不允许执行,也就是像关系数据库一样的表扫描是不行的。
三、事务
不使用事务时,对每个entity的写操作是原子的。
系统使用乐观的并发控制,其特征是在有并发冲突时,不等待,而是让操作回滚失败。这保证了操作的响应时间,但可能导致由于无法立即完成而失败的操作增多,这就好比基于锁的数据结构会被阻塞,无锁数据结构则可能需要不断的进行CAS循环。系统提供自动的重试机制缓解这一问题。
在同一个entity group中的多个entity操作可组合成一个事务,事务的ACID性质有保障。GAE Datastore应该是通过多版本的技术实现的,因此事务能够获得事务开始时的一致快照,奇怪的是事务本身的更新也看不到。
对不同entity group的操作是无法组合事务的,而entity group必须通过entity间的父子关系才能组织赶来。这使得GAE Datastore的事务会受一些限制,比如经典的银行转账问题是搞不定的,两个银行账户,谁是谁的父亲呢。理论上用一个伪的根entity把所有entity组成一个entity group,可以解决这一问题,但这会影响性能。因此只所以限制事务只能在一个entity group内,是因为系统在决定entity存储位置时,会将同一entity group存在在一台机器上,如果把所有entity都纳到一个group,系统就无法分布与伸缩。
有一个细节问题是事务的提交分两步进行:更新entity和更新索引。因此可能出现根据key找到的是更新后的entity,但根据索引找不到。
四、限制
GAE Datastore的数据或操作有很多限制,比如entity***1M,一次删除的entity最多500个,查询最多返回1000个结果等。这些限制可能会给应用开发带来不便。对于查询最多返回1000个结果这个限制,准确的说是limit + offset不能超过1000,即如果你指定了offset是200,那最多只能返回800个结果了。
五、评价
系统性能的两大要素是Scalabity和Efficiency。Scalable的系统不一定Efficient,Efficient的系统也不一定Scalable。对于海量数据存储系统来说,Scalable是必须的,是行与不行的问题,但Efficient也是非常重要的,是省与不省的问题。
GAE Datastore的Scalabity我估计是不错的,但我不清楚有什么具体的证据表明其Scalabity到底怎么样。GAE Datastore的数据分布对应用来说是透明的,应用不能指定根据某属性的值进行哈希分区之类的显式数据分布策略,这使得精准的查询路由难以实现,Bigtable论文中说的bloom filer的效果是值得怀疑的。如果实现不了精准的查询路由,很多查询都要访问大量存储节点的话,就会影响到Scalabity。 虽然Google内部很多产品也用用GAE Datastore,但我们知道Google是买服务器不眨眼的,不好比。
但其Efficiency怎么样,我持很大的怀疑态度。无模式将使得系统不能进行很多优化,底层基于GFS,通过冗余保证可靠性等都可能会影响到系统效率。有人反映Amazon SimpleDB不快,最简单的查询的响应时间也超过100ms,不知道类似的GAE Datastore会怎么样。
功能上,***的优势是无模式带来的好处,即应用升级时不需要像数据库那样做非常耗时的增加/删除字段操作了。但这是一把双刃剑,也可能会带来混乱。打个比方,这就类似于静态类型与动态类型编程语句的区别。
其次,是ORM风格的接口带来的开发便利,不需要像JDBC编程那样写很多SQL语句。
与数据库相比,GAE Datastore的功能局限性也是明显的,主要体现在查询处理和事务处理两个方面。查询处理方面,查询只能是单表,没有GROUP BY和聚集函数,查询的条件复杂度和查询返回的记录数都存在限制;DELETE不能指定WHERE,只能指定key的列表等。对事务的支持是受限的,只能在entity group中进行。这些局限,给应用开发会带来多大的困难,我不清楚。我想,只有将我们的常见的应用如用户与好友关系、日志、相册、消息等,用关系数据库和GAE Datastore对照着实现一遍,那么哪个系统好用,哪个不好用才能一清二楚。
【编辑推荐】