Google app engine 备份appengine数据存储的推荐策略

Google app engine 备份appengine数据存储的推荐策略,google-app-engine,google-cloud-datastore,database-backups,Google App Engine,Google Cloud Datastore,Database Backups,现在我每天晚上都使用remote_api和appcfg.py download_data拍摄数据库快照。这需要很长时间(6小时)而且很昂贵。如果不滚动我自己的基于更改的备份(我会害怕做类似的事情),那么确保我的数据不会发生故障的最佳选择是什么 附言:我知道谷歌的数据可能比我的安全得多。但是如果有一天我不小心写了一个程序,把它全部删除了呢?我想你几乎已经确定了你所有的选择 相信谷歌不会丢失你的数据,希望你不会意外地指示他们销毁数据 使用下载\u数据执行完整备份,如果成本过高,则备份频率可能低于每晚

现在我每天晚上都使用remote_api和
appcfg.py download_data
拍摄数据库快照。这需要很长时间(6小时)而且很昂贵。如果不滚动我自己的基于更改的备份(我会害怕做类似的事情),那么确保我的数据不会发生故障的最佳选择是什么


附言:我知道谷歌的数据可能比我的安全得多。但是如果有一天我不小心写了一个程序,把它全部删除了呢?

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

  • 相信谷歌不会丢失你的数据,希望你不会意外地指示他们销毁数据
  • 使用
    下载\u数据执行完整备份
    ,如果成本过高,则备份频率可能低于每晚一次
  • 推出您自己的增量备份解决方案
  • 选项3实际上是一个有趣的想法。在所有实体上都需要修改时间戳,并且不会捕获已删除的实体,但在远程api和游标上,这是非常可行的

    编辑

    下面是一个用于远程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
    
    导入操作系统
    导入hashlib
    导入gzip
    从google.appengine.api导入应用程序_identity
    从google.appengine.ext.db.metadata导入种类
    从google.appengine.api.datastore导入查询
    从google.appengine.datastore.datastore\u查询导入光标
    索引='updated_at'
    批次=50
    深度=3
    path=['backups',app_identity.get_application_id()]
    以实物表示实物。all():
    种类=种类。种类\名称
    如果种类不同,则开始使用(“”“”):
    持续
    尽管如此:
    打印“正在提取%d%s个实体”%(批,种类)
    extend([kind,'cursor.txt'])
    尝试:
    游标=打开(os.path.join(*path)).read()
    游标=游标。来自Web安全字符串(游标)
    除IOError外:
    游标=无
    path.pop()
    查询=查询(种类,光标=光标)
    查询.订单(索引)
    实体=query.Get(批处理)
    对于实体中的实体:
    hash=hashlib.sha1(str(entity.key()).hexdigest()
    对于范围内的i(深度):
    append(散列[i])
    尝试:
    os.makedirs(os.path.join(*path))
    除操作错误外:
    通过
    append(“%s.xml.gz”%entity.key())
    打印“写入”,os.path.join(*path)
    file=gzip.open(os.path.join(*path),“wb”)
    file.write(entity.ToXml())
    file.close()文件
    路径=路径[:-1-深度]
    如果实体:
    append('cursor.txt')
    文件=打开(os.path.join(*path),“w”)
    file.write(query.GetCursor().to_websafe_string())
    file.close()文件
    path.pop()
    path.pop()
    如果len(实体)<批次:
    打破
    
    关于您的最后一个问题,请勾选此错误: