Google App Engine에서 특정 종류의 모든 데이터 삭제
-
01-07-2019 - |
문제
Google App Engine에서 특정 종류에 대한 모든 데이터를 삭제하고 싶습니다.이것을하는 가장 좋은 방법은 무엇입니까?삭제 스크립트 (Hack)를 썼지 만 수백 레코드 후에 데이터가 너무 많아서 시간 초과가 나옵니다.
다른 팁
현재 키로 항목을 삭제하는 중인데 더 빠른 것 같습니다.
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
터미널에서 컬 -N http://...를 실행합니다.
이제 Datastore 관리자를 사용하여 다음을 수행할 수 있습니다. https://developers.google.com/appengine/docs/adminconsole/datastoreadmin#Deleting_Entities_in_Bulk
내가 편집증적인 사람이라면 Google App Engine(GAE)을 사용하면 원하는 경우 데이터를 쉽게 삭제할 수 없다고 말할 것입니다.인덱스 크기와 6GB 데이터를 35GB 스토리지(요금 청구)로 변환하는 방법에 대한 논의는 건너뛰겠습니다.그것은 또 다른 이야기이지만 이를 해결할 수 있는 방법이 있습니다. 즉, 인덱스(자동 생성 인덱스)를 생성할 속성 수를 제한하는 등의 방법이 있습니다.
내가 이 글을 쓰기로 결정한 이유는 샌드박스에 있는 모든 종류를 "핵화"해야 하기 때문입니다.나는 그것에 대해 읽고 마침내 다음 코드를 생각해 냈습니다.
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");
}
}
6백만 개가 넘는 레코드를 보유하고 있습니다.그것은 많은 것입니다.기록을 삭제하는 데 드는 비용이 얼마인지 모르겠습니다(삭제하지 않는 것이 더 경제적일 수도 있습니다).또 다른 대안은 전체 애플리케이션(샌드박스)에 대한 삭제를 요청하는 것입니다.그러나 그것은 대부분의 경우 현실적이지 않습니다.
나는 (쉬운 쿼리로) 더 작은 레코드 그룹을 사용하기로 결정했습니다.500개의 엔터티를 사용할 수 있다는 것을 알고 있지만 매우 높은 실패율을 받기 시작했습니다(재삭제 기능).
GAE 팀의 요청:단일 거래에서 같은 종류의 항목을 모두 삭제하는 기능을 추가하세요.
사용해 보세요 App Engine 콘솔 그러면 특별한 코드를 배포할 필요도 없습니다.
아마도 해킹은 다음과 같았을 것입니다.
# 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))
말씀하신 대로 데이터가 충분하면 모든 레코드를 통과하기 전에 요청 시간 초과가 발생하게 됩니다.모든 데이터가 지워졌는지 확인하려면 외부에서 이 요청을 여러 번 다시 호출해야 합니다.충분히 쉽지만 이상적이지는 않습니다.
관리 콘솔은 어떤 도움도 제공하지 않는 것 같습니다. (내 경험에 따르면) 특정 유형의 엔터티만 나열되고 페이지별로 삭제되도록 허용하는 것 같습니다.
테스트할 때 기존 데이터를 제거하기 위해 시작 시 데이터베이스를 제거해야 했습니다.
이를 통해 Google은 디스크 가격이 저렴하다는 원칙에 따라 운영되므로 데이터는 일반적으로 삭제되지 않고 고아(중복 데이터에 대한 인덱스 교체)로 유지된다는 점을 추론합니다.현재 각 앱에서 사용할 수 있는 데이터 양은 고정되어 있으므로(0.5GB) Google App Engine을 사용하지 않는 사용자에게는 그다지 도움이 되지 않습니다.
db.delete(results) 및 App Engine Console을 사용해 보았지만 그 중 아무 것도 작동하지 않는 것 같습니다.10000개가 넘는 항목을 업로드했기 때문에 데이터 뷰어(최대 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)
비결은 self.redirect를 사용하는 대신 html에 리디렉션을 포함하는 것이었습니다.테이블의 모든 데이터를 제거하기 위해 밤새 기다릴 준비가 되었습니다.앞으로 GAE 팀이 테이블 삭제를 더 쉽게 만들 수 있기를 바랍니다.
Datastore에서 일괄 삭제를 처리하는 가장 빠르고 효율적인 방법은 새로운 매퍼 API 최근에 발표됨 구글 I/O.
선택한 언어가 다음과 같은 경우 파이썬, 매퍼를 mapreduce.yaml 파일을 작성하고 다음과 같은 함수를 정의하십시오.
from mapreduce import operation as op
def process(entity):
yield op.db.Delete(entity)
~에 자바 너 좀 봐야 해 이 기사 이는 다음과 같은 기능을 제안합니다.
@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());
}
팁 하나.나는 당신이 알고있는 것이 좋습니다 원격_API 이러한 유형의 사용(대량 삭제, 수정 등)에 사용됩니다.그러나 원격 API를 사용하더라도 배치 크기는 한 번에 수백 개로 제한될 수 있습니다.
안타깝게도 대량 삭제를 쉽게 수행할 수 있는 방법은 없습니다.가장 좋은 방법은 호출당 합리적인 수의 항목을 삭제하는 스크립트를 작성한 다음 반복적으로 호출하는 것입니다. 예를 들어 삭제할 데이터가 더 많을 때마다 삭제 스크립트가 302 리디렉션을 반환하도록 한 다음 "wget - -max-redirect=10000"(또는 다른 큰 숫자).
django를 사용하여 설정 URL:
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")
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
그래 넌 할수있어:데이터 저장소 관리로 이동한 다음 삭제하려는 엔터티 유형을 선택하고 삭제를 클릭합니다.Mapreduce가 삭제를 처리해 드립니다!
에 개발 서버, 앱 디렉토리로 CD를 이동한 후 다음과 같이 실행할 수 있습니다.
dev_appserver.py --clear_datastore=yes .
그러면 앱이 시작되고 데이터 저장소가 지워집니다.이미 다른 인스턴스가 실행 중인 경우 앱이 필요한 IP에 바인딩할 수 없으므로 시작에 실패하고 데이터 저장소가 지워집니다.
작업 대기열을 사용하여 100개의 개체 청크를 삭제할 수 있습니다.GAE에서 개체를 삭제하면 GAE의 관리 기능이 얼마나 제한되어 있는지 보여줍니다.1000개 이하의 엔터티에 대한 일괄 작업을 수행해야 합니다.csv와 함께 작동하는 벌크로더 도구를 사용할 수 있지만 설명서에서는 Java를 다루지 않습니다.나는 GAE Java를 사용하고 있으며 삭제 전략에는 2개의 서블릿이 포함됩니다. 하나는 실제로 삭제를 수행하고 다른 하나는 작업 대기열을 로드하기 위한 것입니다.삭제를 원할 때 큐 로딩 서블릿을 실행하면 큐가 로드되고 GAE는 큐에 있는 모든 작업을 실행하게 됩니다.
방법:소수의 객체를 삭제하는 서블릿을 만듭니다.작업 대기열에 서블릿을 추가합니다.집에 가거나 다른 일을하십시오.) 데이터 스토어를 자주 확인하십시오 ...
매주 제거하는 개체가 약 5000개 있는 데이터 저장소가 있고 정리하는 데 약 6시간이 걸리므로 금요일 밤에 작업을 실행합니다.나는 동일한 기술을 사용하여 약 12개의 속성이 포함된 약 5000개의 개체로 구성된 데이터를 대량 로드합니다.
이것은 나에게 효과적이었습니다.
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)
여러분 감사합니다. 필요한 것을 얻었습니다.:디
삭제할 DB 모델이 많은 경우 유용할 수 있으며 터미널에서 이를 전달할 수 있습니다.또한, DB_MODEL_LIST에서 삭제 목록을 직접 관리할 수도 있습니다.
DB_1 삭제:
python bulkdel.py 10 DB_1
모든 DB 삭제:
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)
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)
자바스크립트에서 다음은 페이지의 모든 항목을 삭제합니다.
document.getElementById("allkeys").checked=true;
checkAllEntities();
document.getElementById("delete_button").setAttribute("onclick","");
document.getElementById("delete_button").click();
삭제하려는 엔터티가 있는 관리 페이지(.../_ah/admin)에 있다고 가정합니다.