删除Google App Engine中某种类型的所有数据

时间:2020-03-06 14:29:21  来源:igfitidea点击:

我想清除Google App Engine中特定种类的所有数据。是什么
最好的方法吗?
我写了一个删除脚本(hack),但是由于有这么多数据
几百条记录后超时。

解决方案

大概黑客是这样的:

# Deleting all messages older than "earliest_date"
q = db.GqlQuery("SELECT * FROM Message WHERE create_date < :1", earliest_date)
results = q.fetch(1000)

while results:
    db.delete(results)
    results = q.fetch(1000, len(results))

就像我们说的那样,如果有足够的数据,我们将在请求遍历所有记录之前达到请求超时。我们必须从外部多次重新调用此请求,以确保删除所有数据。做起来很容易,但并不理想。

管理控制台似乎没有提供任何帮助,因为(根据我的经验),它似乎只允许列出给定类型的实体,然后逐页删除。

测试时,我必须在启动时清除数据库以摆脱现有数据。

由此推断,谷歌的运行原理是磁盘便宜,因此数据通常是孤立的(替换了冗余数据的索引),而不是删除了。鉴于目前每个应用程序都有固定数量的数据(0.5 GB),这对于非Google App Engine用户而言并没有太大帮助。

不幸的是,没有办法轻松地进行批量删除。最好的选择是编写一个脚本,该脚本在每次调用时删除合理数量的条目,然后反复调用它,例如,只要有更多数据要删除,请让删除脚本返回302重定向,然后使用" wget- max-redirect = 10000"(或者其他一些大数字)。

Google的官方回答是,我们必须删除多个请求中的多个块。我们可以使用AJAX,元刷新或者从脚本请求URL,直到没有剩余实体为止。

尝试使用App Engine控制台,那么我们甚至不必部署任何特殊代码

我已经尝试过db.delete(results)和App Engine控制台,但它们似乎都不适合我。由于我已上传了10000多个条目,因此无法从Data Viewer手动删除条目(上限最多增加200个)也不起作用。我结束了写这个剧本

from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
import wsgiref.handlers
from mainPage import YourData #replace this with your data
class CleanTable(webapp.RequestHandler):
    def get(self, param):
        txt = self.request.get('table')
        q = db.GqlQuery("SELECT * FROM "+txt)
        results = q.fetch(10)
        self.response.headers['Content-Type'] = 'text/plain'
        #replace yourapp and YouData your app info below.
        self.response.out.write("""
          <html>
          <meta HTTP-EQUIV="REFRESH" content="5; url=http://yourapp.appspot.com/cleanTable?table=YourData">
            <body>""")

        try:
            for i in range(10):
                db.delete(results)
                results = q.fetch(10, len(results))
                self.response.out.write("<p>10 removed</p>")
                self.response.out.write("""
                </body>
              </html>""")

        except Exception, ints:
            self.response.out.write(str(inst))

def main():
  application = webapp.WSGIApplication([
    ('/cleanTable(.*)', CleanTable),
  ])

  wsgiref.handlers.CGIHandler().run(application)

诀窍是在HTML中包含重定向,而不是使用self.redirect。我准备整夜等待摆脱表中的所有数据。希望GAE团队将来能够更轻松地删除表格。

我目前正在按其键删除实体,而且似乎更快。

from google.appengine.ext import db

class bulkdelete(webapp.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        try:
            while True:
                q = db.GqlQuery("SELECT __key__ FROM MyModel")
                assert q.count()
                db.delete(q.fetch(200))
                time.sleep(0.5)
        except Exception, e:
            self.response.out.write(repr(e)+'\n')
            pass

从终端,我运行curl -N http:// ...

一个提示。我建议我们了解这些类型的用法(批量删除,修改等)的remote_api。但是,即使使用远程api,批量大小也可以一次限制为几百个。

如果我是一个偏执狂的人,我想说Google App Engine(GAE)并不能使我们轻松删除数据。我将跳过有关索引大小以及它们如何将6 GB的数据转换为35 GB的存储(收费)的讨论。那是另一回事了,但是他们确实有一些方法可以解决该限制数量的属性,以在(自动生成的索引)等等上创建索引。

我决定写这篇文章的原因是,我需要在一个沙箱中"核对"所有种类。我读了一下,最后想出了这段代码:

package com.intillium.formshnuker;

import java.io.IOException;
import java.util.ArrayList;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;

import com.google.appengine.api.labs.taskqueue.QueueFactory;
import com.google.appengine.api.labs.taskqueue.TaskOptions.Method;

import static com.google.appengine.api.labs.taskqueue.TaskOptions.Builder.url;

@SuppressWarnings("serial")
public class FormsnukerServlet extends HttpServlet {

 public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws IOException {

  response.setContentType("text/plain");

  final String kind = request.getParameter("kind");
  final String passcode = request.getParameter("passcode");

  if (kind == null) {
   throw new NullPointerException();
  }

  if (passcode == null) {
   throw new NullPointerException();
  }

  if (!passcode.equals("LONGSECRETCODE")) {
   response.getWriter().println("BAD PASSCODE!");
   return;
  }

  System.err.println("*** deleting entities form " + kind);

  final long start = System.currentTimeMillis();

  int deleted_count = 0;
  boolean is_finished = false;

  final DatastoreService dss = DatastoreServiceFactory.getDatastoreService();

  while (System.currentTimeMillis() - start < 16384) {

   final Query query = new Query(kind);

   query.setKeysOnly();

   final ArrayList<Key> keys = new ArrayList<Key>();

   for (final Entity entity: dss.prepare(query).asIterable(FetchOptions.Builder.withLimit(128))) {
    keys.add(entity.getKey());
   }

   keys.trimToSize();

   if (keys.size() == 0) {
    is_finished = true;
    break;
   }

   while (System.currentTimeMillis() - start < 16384) {

    try {

     dss.delete(keys);

     deleted_count += keys.size();

     break;

    } catch (Throwable ignore) {

     continue;

    }

   }

  }

  System.err.println("*** deleted " + deleted_count + " entities form " + kind);

  if (is_finished) {

   System.err.println("*** deletion job for " + kind + " is completed.");

  } else {

   final int taskcount;

   final String tcs = request.getParameter("taskcount");

   if (tcs == null) {
    taskcount = 0;
   } else {
    taskcount = Integer.parseInt(tcs) + 1;
   }

   QueueFactory.getDefaultQueue().add(
    url("/formsnuker?kind=" + kind + "&passcode=LONGSECRETCODE&taskcount=" + taskcount).method(Method.GET));

   System.err.println("*** deletion task # " + taskcount + " for " + kind + " is queued.");

  }

  response.getWriter().println("OK");

 }

}

我有超过600万条记录。好多啊。我不知道删除记录将花费多少(不删除它们可能更经济)。另一种选择是请求删除整个应用程序(沙盒)。但这在大多数情况下是不现实的。

我决定使用较小的记录组(易于查询)。我知道我可以使用500个实体,但是后来我开始收到很高的失败率(重新删除功能)。

我从GAE小组提出的要求:请添加一项功能,以在一次交易中删除同类的所有实体。

我们可以使用任务队列删除例如100个对象的块。
在GAE中删除对象显示了GAE中管理功能的有限程度。我们必须在1000个或者更少的实体上使用批处理。我们可以使用与csv一起使用的bulkloader工具,但文档不涵盖Java。
我正在使用GAE Java,删除策略涉及2个servlet,一个用于实际删除,另一个用于加载任务队列。当我想删除时,我运行队列加载servlet,它加载队列,然后GAE开始执行队列中的所有任务。

怎么做:
创建一个删除少量对象的servlet。
将servlet添加到任务队列中。
回家或者做其他事情;)
经常检查数据存储区...

我有一个每周大约要清除5000个对象的数据存储,清理大约需要6个小时,所以我在星期五晚上运行任务。
我使用相同的技术来批量加载我的数据,恰巧是大约5000个对象,具有大约十二个属性。

使用django,设置网址:

url(r'^Model/bdelete/$', v.bulk_delete_models, {'model':'ModelKind'}),

设置视图

def bulk_delete_models(request, model):
    import time
    limit = request.GET['limit'] or 200
    start = time.clock()
    set = db.GqlQuery("SELECT __key__ FROM %s" % model).fetch(int(limit))
    count = len(set)
    db.delete(set)
    return HttpResponse("Deleted %s %s in %s" % (count,model,(time.clock() - start)))

然后在powershell中运行:

$client = new-object System.Net.WebClient
$client.DownloadString("http://your-app.com/Model/bdelete/?limit=400")

这为我工作:

class ClearHandler(webapp.RequestHandler):  
    def get(self):  
        self.response.headers['Content-Type'] = 'text/plain'  
        q = db.GqlQuery("SELECT * FROM SomeModel")  
        self.response.out.write("deleting...")  
        db.delete(q)

处理数据存储区上批量删除的最快,最有效的方法是使用最新的Google I / O上宣布的新的mapper API。

如果我们选择的语言是Python,则只需在mapreduce.yaml文件中注册映射器,然后定义如下函数:

from mapreduce import operation as op
def process(entity):
 yield op.db.Delete(entity)

在Java上,我们应该看一下这篇文章,该文章建议这样的功能:

@Override
public void map(Key key, Entity value, Context context) {
    log.info("Adding key to deletion pool: " + key);
    DatastoreMutationPool mutationPool = this.getAppEngineContext(context)
            .getMutationPool();
    mutationPool.delete(value.getKey());
}