删除Google App Engine中的所有数据

时间:2008-09-20 17:34:24

标签: python google-app-engine

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

19 个答案:

答案 0 :(得分:27)

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

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:// ...

答案 1 :(得分:23)

答案 2 :(得分:10)

如果我是一个偏执狂的人,我会说谷歌应用引擎(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团队请求:请添加一项功能,以便在一次交易中删除所有类型的实体。

答案 3 :(得分:9)

尝试使用App Engine Console,您甚至不必部署任何特殊代码

答案 4 :(得分:9)

据推测,你的黑客是这样的:

# 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用户没什么帮助。

答案 5 :(得分:7)

我已经尝试过db.delete(结果)和App Engine Console,但似乎没有一个对我有用。手动删除数据查看器中的条目(增加限制高达200)也不起作用,因为我上传了超过10000个条目。我结束了写这个剧本

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团队能够在将来更容易丢弃表格。

答案 6 :(得分:6)

来自Google的official answer是您必须在分布在多个请求中的块中删除。您可以使用AJAX,meta refresh或从脚本请求您的URL,直到没有实体为止。

答案 7 :(得分:5)

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

如果您选择的语言为Python,则只需在 mapreduce.yaml 文件中注册映射器并定义如下函数:

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

Java上,你应该看看this article建议这样的函数:

@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());
}

答案 8 :(得分:4)

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

答案 9 :(得分:3)

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

答案 10 :(得分:1)

使用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")

答案 11 :(得分:1)

dev server上,可以cd到他的应用程序目录,然后像这样运行:

dev_appserver.py --clear_datastore=yes .

这样做会启动应用并清除数据存储区。如果您已经有另一个实例在运行,那么该应用程序将无法绑定到所需的IP,因此无法启动...并清除您的数据存储区。

答案 12 :(得分:1)

是的,您可以: 转到“数据存储管理”,然后选择要删除的实体类型,然后单击“删除”。 Mapreduce将负责删除!

答案 13 :(得分:1)

如果您使用的是Java / JPA,可以执行以下操作:

    em = EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory)
    Query q = em.createQuery("delete from Table t");
    int number = q.executeUpdate();

可在此处找到Java / JDO信息:http://code.google.com/appengine/docs/java/datastore/queriesandindexes.html#Delete_By_Query

答案 14 :(得分:0)

谢谢大家,我得到了我需要的东西。 :d
如果您要删除许多数据库模型,这可能很有用,您可以在终端中分发它。而且,您可以自己管理DB_MODEL_LIST中的删除列表 删除DB_1:

python bulkdel.py 10 DB_1

删除所有数据库:

python bulkdel.py 11

这是bulkdel.py文件:

import sys, os

URL = 'http://localhost:8080'
DB_MODEL_LIST = ['DB_1', 'DB_2', 'DB_3']

# Delete Model
if sys.argv[1] == '10' :
    command = 'curl %s/clear_db?model=%s' % ( URL, sys.argv[2] )
    os.system( command )

# Delete All DB Models
if sys.argv[1] == '11' :
    for model in DB_MODEL_LIST :
        command = 'curl %s/clear_db?model=%s' % ( URL, model )
        os.system( command )

以下是alexandre fiori代码的修改版本。

from google.appengine.ext import db
class DBDelete( webapp.RequestHandler ):
    def get( self ):
        self.response.headers['Content-Type'] = 'text/plain'
        db_model = self.request.get('model')
        sql = 'SELECT __key__ FROM %s' % db_model

        try:
            while True:
                q = db.GqlQuery( sql )
                assert q.count()
                db.delete( q.fetch(200) )
                time.sleep(0.5)
        except Exception, e:
            self.response.out.write( repr(e)+'\n' )
            pass

当然,您应该将链接映射到文件中的模型(例如GAE中的main.py);;)
如果像我这样的人有详细需要它,这里是main.py的一部分:

from google.appengine.ext import webapp
import utility # DBDelete was defined in utility.py
application = webapp.WSGIApplication([('/clear_db',utility.DBDelete ),('/',views.MainPage )],debug = True)

答案 15 :(得分:0)

这对我有用:

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)

答案 16 :(得分:0)

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

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

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

答案 17 :(得分:0)

要删除Google App Engine中给定种类的所有实体,您只需执行以下操作:

from google.cloud import datastore

query = datastore.Client().query(kind = <KIND>)
results = query.fetch()
for result in results:
    datastore.Client().delete(result.key)

答案 18 :(得分:-2)

在javascript中,以下内容将删除页面上的所有条目:

document.getElementById("allkeys").checked=true;
checkAllEntities();
document.getElementById("delete_button").setAttribute("onclick","");
document.getElementById("delete_button").click();

鉴于您在管理页面(... / _ ah / admin)中有您要删除的实体。