App Engine:13个StringProperty与1个StringListProperty(索引/存储和查询性能)
先简单介绍一下背景:GeoModel是我写的一个库,它为App Engine应用程序增加了一些基本的地理空间索引和查询功能。它的工作方式和地理哈希(geohashing)类似。在GeoModel中,相应的位置哈希被称为“地理单元”(geocell)。
目前,GeoModel库为每个位置感知的实体添加了13个属性(location_geocell__n_,n=1..13)。例如,一个实体可以有这样的属性值:
location_geocell_1 = 'a'
location_geocell_2 = 'a3'
location_geocell_3 = 'a3f'
...
这样做是为了在空间查询时不消耗不等式过滤器。
使用13个属性的方法有个问题,就是每次应用想要运行地理查询时,都必须定义和构建13个新的索引。这确实很麻烦,尤其是我在为这个项目重写演示应用时深有体会。这引出了我的第一个问题:
问题 1:每个索引的存储开销是否很大?也就是说,如果我有13个索引,每个索引有n个实体,和一个索引有13n个实体,前者在存储上是否比后者差很多?
根据这篇文章,答案似乎是否定的,但我还是想看看有没有人有不同的经验。
现在,我在考虑调整GeoModel库,把13个字符串属性改成一个叫做location_geocells的字符串列表属性,也就是:
location_geocells = ['a', 'a3', 'a3f']
这样会让index.yaml
看起来更简洁。但我对性能的影响有些疑问:
问题 2:如果我把13个字符串属性换成1个字符串列表属性,查询性能会受到影响吗?我现在的过滤器看起来是:
query.filter('location_geocell_%d =' % len(search_cell), search_cell)
而新的过滤器看起来是:
query.filter('location_geocells =', search_cell)
注意,第一个查询的搜索空间是_n_个实体,而第二个查询的搜索空间是_13n_个实体。
根据这篇博客文章中的第6条提示,答案似乎是两者的查询性能是相等的,但我还是想看看有没有人有不同的实际经验。
最后,如果有人有其他建议或技巧,可以帮助改善存储利用率、查询性能和/或使用方便性(特别是关于index.yaml的),请告诉我!源代码可以在这里找到geomodel和geomodel.py
3 个回答
看起来你得到了13个索引,因为你使用了十六进制编码(为了人类可读性/地图级别?)。如果你充分利用一个字节的潜力(ByteString),你本可以有256个单元,而不是每个字符(字节)只有16个单元。这样可以大大减少所需的索引数量,同时保持相同的精度。
ByteString 只是字符串(str)的一个子类,如果长度小于500字节,它的索引方式是相似的。
不过,层级的数量可能会更少;对我来说,4或5个层级在大多数情况下已经足够了,尤其是在“地球”上。对于更大的星球,或者在记录每一个沙粒时,可能无论使用什么编码,都需要引入更多的划分。在这两种情况下,ByteString 比十六进制编码要好得多,并且可以大幅减少索引的数量。
- 为了表示40亿个最低级别的单元,我们只需要4个字节,也就是4个索引。(根据基本的计算机架构或内存寻址)
- 如果用十六进制表示同样的内容,我们需要16个十六进制数字,也就是16个索引。
我可能错了。也许与地图缩放级别相匹配的索引层级数量更重要。如果有其他人觉得这个有意义,请纠正我。我打算尝试这个,而不是十六进制 :)
或者一种方案是有更少的大单元(16个),但随着层级的下降,单元数量更多(128、256)。你有什么想法吗?
例如:
- [0-15][0-31][0-63][0-127][0-255] 可以用5个索引表示1G的低级单元,大小以log2递减。
- [0-15][0-63][0-255][0-255][0-255] 可以用5个索引表示16G的低级单元。
最后,如果有人有其他建议或技巧,可以帮助改善存储利用率、查询性能和/或使用方便性
使用StringList属性是个不错的选择,原因如上所述。不过在实际使用中,可能会想把地理单元(geocells)添加到自己之前已有的StringList中,这样就可以对多个属性进行查询。
所以,如果你提供一个更底层的API,它可以与像Bill Katz的全文搜索实现一起工作。
def point2StringList(Point, stub="blah"):
.....
return ["blah_1:a", "blah_2":"a3", "blah_3":"a3f" ....]
def boundingbox2Wheresnippet(Box, stringlist="words", stub="blah"):
.....
return "words='%s_3:a3f' AND words='%s_3:b4g' ..." %(stub)
etc.
你说得对,单个索引的开销并不大。一个索引里有13n条记录,和13个索引里各有n条记录差不多是一样的。不过,索引的总数量限制是100个,所以这会占用你可用的索引数量。
不过,从使用方便性和索引消耗的角度来看,使用ListProperty绝对是更好的选择。正如你所想的,查询一个小索引和查询一个大索引在性能上没有区别,只要这两个查询返回的行数相同。
我能想到使用单独属性的唯一原因是你知道只需要在某些细节级别上建立索引,但其实在插入数据时就可以更好地指定你想要添加到列表中的细节级别。
需要注意的是,无论哪种情况,如果你打算根据排序顺序或不等式过滤器来查询地理单元属性,才需要索引。在其他情况下,自动索引就足够了。