用于删除数据存储中的数据的 while 循环
Posted
技术标签:
【中文标题】用于删除数据存储中的数据的 while 循环【英文标题】:while loop for deleting data in datastore 【发布时间】:2012-09-28 01:06:36 【问题描述】:我已尝试清理和修改答案here 中的代码以满足我的需要,我只想从模型Reservations
中删除在get 中表示为yy,mm,dd
的日期之前的数据记录。
如果我正确预期 cleanTable/2012/10/5
对路由 ('/cleanTable/([\d]+)/([\d]+)/([\d]+)', CleanTable)
的操作,那么我的代码最多只会删除 50 (10*nlimit) 个数据记录。
顺便说一句,原始代码的作者(他可能不再订阅 SO)声称他完成此代码的主要技巧是“在 html 中包含重定向而不是使用 self.redirect”。
我不熟悉raise Exception
之类的东西,但我的直觉是在for 循环中添加一个raise Exception
或raise StopIteration
,然后将其制成while 循环。但我不清楚引发 StopIteration 异常是否真的会导致迭代停止,或者是否需要更多。另外,我不知道如何修改,以便 html 在提前退出时顺利结束。
class CleanTable(BaseHandler):
def get(self, yy,mm,dd):
nlimit=5
iyy=int(yy)
imm=int(mm)
idd=int(dd)
param=date(iyy,imm,idd)
q=Reservations.all(keys_only=True)
q.filter("date < ", dt(iyy,imm,idd))
results = q.fetch(nlimit)
self.response.headers['Content-Type'] = 'text/plain'
self.response.out.write("""
<html>
<meta HTTP-EQUIV="REFRESH" content="url=http://yourapp.appspot.com/cleanTable">
<body>""")
try:
for i in range(10):
db.delete(results)
results = q.fetch(nlimit, len(results))
for r in results:
logging.info("r.name: %s" % r.name)
self.response.out.write("<p> "+str(nlimit)+" removed</p>")
self.response.out.write("""
</body>
</html>""")
except Exception, inst:
logging.info("inst: %s" % inst)
self.response.out.write(str(inst))
【问题讨论】:
这看起来像一个非常丑陋的黑客......从您的代码中,我假设您所有实体的键都适合您的应用程序的内存。如果这是真的,更优雅和可靠的方法是使用任务队列 - 对于每个键,使用该键启动一个任务以将其删除。然后工作人员将加载然后执行删除。如果这不起作用,请批处理该过程。如果以后有时间,我会发布示例代码。 您可以批量删除( db.delete(keys) )任务中查询的结果实体。一项任务的时间限制为 10 分钟。如果任务每次运行最多删除 10000 个实体,并且您有更多实体要删除,则重复该任务,直到查询结果少于 10000 个实体。你也可以使用mapreduce:developers.google.com/appengine/docs/python/dataprocessing/… 当我不知道有多少记录时,我对这段代码的最终使用将是在一个 cron 工作中丢弃旧记录,所以我认为某种 while 循环是合适的。 【参考方案1】:这不是清理模型的最佳方法。更好的方法是获取实体的所有密钥并创建Task Queues。每个队列都会得到一批需要修改的实体的键。
另一种方法是创建一个cron job,它将查询 x 个最旧的修改实体,修复它们,然后将它们存储回来。
最后,如果您的实体数量如此庞大,您还可以考虑使用Backends。
希望这会有所帮助。
【讨论】:
【参考方案2】:这是我的更新例程,它已经转换了 500.000 个实体。确保在后端实例上运行它(您可以将队列定位到后端实例)。请注意,我使用的是游标,这是您可以始终如一地遍历数据的唯一方法(永远不要使用偏移量!)。
Queue queue = QueueFactory.getQueue("grinderQueue");
queue.add(TaskOptions.Builder.withPayload(new DeferredTask() //lets generate
private static final long serialVersionUID = 1L;
@Override
public void run()
String cursor = null;
boolean done = false;
Date now = new Date(1346763868L * 1000L); // 09/04/2012
while(!done)
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
Query query = new Query("Venue");
query.setFilter(new FilterPredicate("timeOfLastUpdate", Query.FilterOperator.LESS_THAN,now));
PreparedQuery pq = datastore.prepare(query);
FetchOptions fetchOptions = FetchOptions.Builder.withLimit(1000);
if(cursor != null)
fetchOptions.startCursor(Cursor.fromWebSafeString(cursor));
QueryResultList<Entity> results = pq.asQueryResultList(fetchOptions);
List<Entity> updates = new ArrayList<Entity>();
List<Entity> oldVenueUpdates = new ArrayList<Entity>();
int tuples = 0;
for(Entity en : results)
tuples++;
try
if(en.getProperty(Venue.VENUE_KEY) == null)
continue;
Entity newVenue = new Entity("CPVenue",(String)en.getProperty(Venue.VENUE_KEY));
newVenue.setPropertiesFrom(en);
newVenue.removeProperty("timeOfLastVenueScoreCalculation");
newVenue.removeProperty("actionsSinceLastVenueScoreCalculation");
newVenue.removeProperty("venueImageUrl");
newVenue.removeProperty("foursquareId");
newVenue.setProperty("geoCell", GeoCellCalculator.calcCellId(Double.valueOf((String)en.getProperty("lng")), Double.valueOf((String)en.getProperty("lat")),8));
newVenue.setProperty(Venue.TIME_SINCE_LAST_UPDATE, new Date());
updates.add(newVenue);
Venue v = new Venue(newVenue);
//Set timestamp on Venue
en.setProperty("timeOfLastUpdate", now);
oldVenueUpdates.add(en);
catch(Exception e)
logger.log(Level.WARNING,"",e);
done = tuples == 0;
tuples = 0;
if(results.getCursor() != null)
cursor = results.getCursor().toWebSafeString();
else
done = true;
System.out.println("Venue Conversion LOOP updates.. " + updates.size() + " cursor " + cursor);
datastore.put(updates);
datastore.put(oldVenueUpdates);
System.out.println("Venue Conversion DONE");
));
【讨论】:
我不知道在后端实例上运行它意味着什么。我假设使用的语言是 Java,但我的应用程序是 gae 上的 python。我仍然可以使用你的代码吗? 你需要把它转换成python。但它的所有 API 调用,所以它应该很容易。您可以使用目标(只是一个名称)定义一个任务 QUEUE,该名称应该是一个后端实例,您也需要对其进行配置 我必须好好学习才能做到这一点,这可能需要一些时间。我会在文档中查找backend instance
。我会尽量记住回复你。以上是关于用于删除数据存储中的数据的 while 循环的主要内容,如果未能解决你的问题,请参考以下文章