我正在开发一个使用SimpleDB存储数据的Facebook应用程序,但我意识到亚马逊没有提供备份数据的方法(至少据我所知)
SimpleDB速度较慢。每秒可以获得大约4个列表,每个列表包含100条记录。这不是备份大量记录的好方法。
我在网上找到了一些服务,可以为你做备份,但我不愿意给他们我的AWS凭据。
所以我考虑使用线程。问题是,如果您对域中的所有密钥进行选择,则需要等待第一页的next_token值才能处理第二页,依此类推
我想的一个解决方案是根据Facebook id的最后两位数字创建一个新属性。因此,我会用一个选择"00"、另一个选择为"01"等来启动一个线程,这样可能会运行100个线程,并更快地进行备份(至少在理论上)。一个相关的解决方案是将该域拆分为100个域(这样我就可以单独备份每个域),但这会打破我需要做的一些选择。另一个解决方案,可能更适合PHP,是使用cron作业备份10000条记录并保存"next_token",然后下一个作业从next_toke开始,等等。
有人对此有更好的解决方案吗?如果它是一个PHP解决方案,那就太好了,但如果它涉及到其他东西,无论如何都是受欢迎的。
PS:在你提到它之前,据我所知,PHP仍然不是线程安全的。我知道,除非我在备份期间停止写入,否则会出现一些一致性问题,但在这种特殊情况下,我并不太担心。
根据我的经验,创建代理碎片属性的方法当然有效。
或者,我们过去所做的是将备份分解为两步过程,以获得尽可能多的多处理潜力(尽管这是在java中,对于对备份文件的写入,我们可以依靠同步来确保写入安全-不确定php方面的处理是什么)。
基本上,我们有一个线程在域内的数据之间进行选择,但它不是"select*FROM…",而是"select itemName FROM……"来获取需要备份的条目的密钥。然后将这些项放入项键的队列中,线程池使用getItem API读取这些项键,并以线程安全的方式将其写入备份文件。
这使我们在单个域上的吞吐量比在单个线程上旋转要好。
然而,最终,由于夜间备份中有许多域,我们最终恢复到在单线程和"SELECT*FROM domain"类型的模型中进行每个域备份,这主要是因为我们已经有大量线程在运行,线程过载开始成为备份处理器上的一个问题,但也因为备份程序开始变得非常复杂。
截至2012年10月,我已经研究过这个问题。三个主要问题似乎支配着选择:
- 没有"本机"方法可以确保与SimpleDB一致的导出或导入。您有责任理解和管理此w.r.t.应用程序代码的含义
- 亚马逊没有提供托管备份解决方案,但许多第三方公司在这一领域提供了一些服务(通常将"备份到S3"作为一种选项)
- 对于一定数量的数据,您需要考虑一种多线程方法,它同样具有重要的含义:一致性
如果你只需要从一个域转储数据,并且你的数据量足够低,因此单线程导出是有意义的,那么下面是我写的一些Python代码,它对我来说非常有用。没有明示或暗示的保证,只有在你理解的情况下才使用它:
#simpledb2json.py
import boto
import simplejson as json
AWS_KEY = "YOUR_KEY"
AWS_SECRET = "YOUR_SECRET"
DOMAIN = "YOUR_DOMAIN"
def fetch_items(boto_dom, dom_name, offset=None, limit=300):
offset_predicate = ""
if offset:
offset_predicate = " and itemName() > '" + offset + "'"
query = "select * from "
+ "`" + dom_name + "`"
+ " where itemName() is not null"
+ offset_predicate
+ " order by itemName() asc limit " + str(limit)
rs = boto_dom.select(query)
# by default, boto does not include the simpledb 'key' or 'name' in the
# dict, it is a separate property. so we add it:
result = []
for r in rs:
r['_itemName'] = r.name
result.append(r)
return result
def _main():
con = boto.connect_sdb(aws_access_key_id=AWS_KEY, aws_secret_access_key=AWS_SECRET)
dom = con.get_domain(DOMAIN)
all_items = []
offset = None
while True:
items = fetch_items(dom, DOMAIN, offset=offset)
if not items:
break
all_items += items
offset = all_items[-1].name
print json.dumps(all_items, sort_keys=True, indent=4)
if __name__ == "__main__":
_main()