7

现在我使用remote_api和appcfg.py download_data来每天晚上拍摄我的数据库的快照。这需要很长时间(6小时),而且很贵。如果没有滚动我自己的基于变更的备份(我会太害怕做这样的事情),确保我的数据从失败中安全的最佳选择是什么? PS:我认识到Google的数据可能比我的更安全。但是如果有一天我不小心编写了一个删除它的程序呢?备份appengine数据存储的推荐策略

+0

关于您最后一个问题,请为此错误加分:[776](http://code.google.com/p/googleappengine/issues/detail?id=776) – 2011-11-10 00:32:57

回答

3

我想你几乎已经确定了你所有的选择。

  1. 请相信Google不会丢失您的数据,并且希望您不会意外地指示它们销毁它。
  2. 使用download_data执行完整备份,如果成本过高,则可能不如每晚一次。
  3. 推出您自己的增量备份解决方案。

选项3实际上是一个有趣的想法。您需要在所有实体上修改时间戳,并且不会捕获已删除的实体,但除此之外,它对于remote_api和游标非常有用。

编辑

下面是使用remote_api使用一个简单的增量下载。此外,注意事项是它不会注意到已删除的实体,并且假定所有实体都将最后一次修改时间存储在名为updated_at的属性中。在你自己的危险中使用它。

import os 
import hashlib 
import gzip 
from google.appengine.api import app_identity 
from google.appengine.ext.db.metadata import Kind 
from google.appengine.api.datastore import Query 
from google.appengine.datastore.datastore_query import Cursor 

INDEX = 'updated_at' 
BATCH = 50 
DEPTH = 3 

path = ['backups', app_identity.get_application_id()] 
for kind in Kind.all(): 
    kind = kind.kind_name 
    if kind.startswith('__'): 
    continue 
    while True: 
    print 'Fetching %d %s entities' % (BATCH, kind) 
    path.extend([kind, 'cursor.txt']) 
    try: 
     cursor = open(os.path.join(*path)).read() 
     cursor = Cursor.from_websafe_string(cursor) 
    except IOError: 
     cursor = None 
    path.pop() 
    query = Query(kind, cursor=cursor) 
    query.Order(INDEX) 
    entities = query.Get(BATCH) 
    for entity in entities: 
     hash = hashlib.sha1(str(entity.key())).hexdigest() 
     for i in range(DEPTH): 
     path.append(hash[i]) 
     try: 
     os.makedirs(os.path.join(*path)) 
     except OSError: 
     pass 
     path.append('%s.xml.gz' % entity.key()) 
     print 'Writing', os.path.join(*path) 
     file = gzip.open(os.path.join(*path), 'wb') 
     file.write(entity.ToXml()) 
     file.close() 
     path = path[:-1-DEPTH] 
    if entities: 
     path.append('cursor.txt') 
     file = open(os.path.join(*path), 'w') 
     file.write(query.GetCursor().to_websafe_string()) 
     file.close() 
     path.pop() 
    path.pop() 
    if len(entities) < BATCH: 
     break