不要以关系数据库的观点来使用GAE的BigTable

标签:Google App Engine

你或许会对《How I Learned to Stop Worrying and Love Using a Lot of Disk Space to Scale》这篇文章感兴趣,这里有其中文翻译:《优化使用BigTable的原则与方针》

我感觉最重要的一点就是围绕数据的用途来决定数据的结构
在关系数据库里,为了避免数据冗余带来的不良影响,一个实体会被分割成数个相互关联的表。
而在BigTable中,一般来说较好的方式是让其能一次全部读取,而无需join(甚至可以为此使用可选字段)。处理数据应尽可能地让应用服务器去做,数据库服务器只需要简单地过滤和排序即可。

举例来说,假设我想设计一个游戏王卡片的数据库:
通常情况下,我会需要卡片名称、卡片类型、效果描述和卡片图案等信息。
如果卡片属于怪兽卡,我还需要怪兽类型、属性、种族、攻击力和防御力等信息;如果是魔法卡和陷阱卡,我还要其连锁速度(发动时机)等信息。
我没必要让后面的信息用另外的3张表来保存,而只需让它们为可选字段即可。


这种方式可以很好地配合BigTable适合读取的特性,但并不适合更新(写入)。

举例来说,假设我需要设计一个玩家信息的数据结构:
我需要玩家的ID、战斗次数等信息,但也需要一个帮会属性,记录玩家属于哪个团体的。
一般情况下,用字符串类型是很方便的,但倘若我要改帮会名称,我不得不遍历整个玩家信息表,然后把该帮会玩家的所属帮会改掉。这样是很费时间的。
可如果帮会是属于另一张表,我只需让玩家关联到这个帮会,更改名称也只需要改变帮会信息中的名称属性即可(即只改动一条记录)。
然而如果读取占了绝大多数时间(你可能读取几十万次,也不会改动一次帮会名称),或许就最初那样设计也无妨。


同时也要警惕使用列表作为搜索的字段,将其分离出去或许更好。

例如卡片有其日文名或英文名,以及很多种的中文翻译。
当玩家需要查询一张卡片时,只能对一个字段做过滤,此时可能会很想当然地用到列表,直接用IN操作符来进行。
要知道这个操作将替换成很多个==操作,非常费时;而且在更新时,由于索引会有很多条,排序也将占用大量时间,甚至导致索引超出限制。
你可以将其分离出去,用名称表来表示;甚至干脆让其出现冗余,卡片信息表里放一个常见名称,用于显示,而名称表则仅用于搜索。


顺便补充一点BigTable的实现。实际上它是key、value组成的稀疏映射表,内部仅有字符串这一种数据类型,需要外部进行类型转换。
由于冗余数据很多,采用了压缩技术(Zippy),就避免了冗余数据占用大量空间。(不知道计算数据库空间时,是否是计算压缩后的。)
而其底层用的是Google File System(GFS),对文件的附加(append)操作做过优化,有些尺寸甚至比write操作快2个数量级。
更多信息可以参考《大表(Bigtable):结构化数据的分布存储系统》《Google文件系统(Google File System)论文》这2篇文章。


总之,BigTable适合读取,而不适合写入。如果更新非常频繁的话,最好考虑下是否必须要用Google App Engine,或许其他数据库更为适合。(也许memcache也能解决一些性能问题。)
如果你的项目已经在Google App Engine上了,你可以看看AppScale这个项目,数小时前刚发布了1.0.3新版本。

0条评论 你不来一发么↓

    想说点什么呢?