0

问题

我正在编写一个应用引擎卡拉 OK 目录应用程序。该应用程序非常简单:在第一个版本中,它提供了将 CSV 歌曲列表导入目录并显示它们的功能。

我在导入 CSV 时遇到问题:在我的开发环境中导入 17,500 条记录需要很长时间(14 小时)。在生产环境中,它导入了大约 1000 条记录,然后以代码 500 崩溃。我正在查看日志,但没有找到任何有用的线索。

编码

class Song(ndb.Model):
    sid     = ndb.IntegerProperty()
    title   = ndb.StringProperty()
    singer  = ndb.StringProperty()
    preview = ndb.StringProperty()

    @classmethod
    def new_from_csv_row(cls, row, parent_key):
        song = Song(
                sid=int(row['sid']),
                title=row['title'],
                singer=row['singer'],
                preview=row['preview'],
                key=ndb.Key(Song, row['sid'], parent=parent_key))
        return song

class CsvUpload(webapp2.RequestHandler):
    def get(self):
        # code omit for brevity 

    def post(self):
        catalog = get_catalog(…) # retrieve old catalog or create new

        # upfile is the contents of the uploaded file, not the filename
        # because the form uses enctype="multipart/form-data"
        upfile = self.request.get('upfile')

        # Create the songs
        csv_reader = csv.DictReader(StringIO(upfile))
        for row in csv_reader:
            song = Song.new_from_csv_row(row, catalog.key)
            song.put()

        self.redirect('/upload')

样本数据

sid,title,singer,preview
19459,Zoom,Commodores,
19460,Zoot Suit Riot,Cherry Poppin Daddy,
19247,You Are Not Alone,Michael Jackson,Another day has gone. I'm still all alone

笔记

  • 在开发环境中,我尝试最多导入 17,500 条记录,没有遇到崩溃
  • 起初,记录的创建和插入速度很快,但随着数据库增长到数千个,创建和插入记录所需的时间增加到每条记录几秒钟。

如何加快导入操作?任何建议、提示或提示将不胜感激。

更新

我听从了 Murph 的建议,并使用 aKeyProperty将歌曲链接回目录。结果对于 17,500 条记录大约需要 4 分 20 秒——这是一个巨大的进步。这意味着,我还没有完全理解 NDB 在 App Engine 中的工作原理,而且我还有很长的路要走。

虽然是一个很大的改进,但 4 分钟以上的时间无疑还是太长了。我现在正在研究 Tim 和 Dave 的建议,以进一步缩短我的应用程序的感知响应时间。

4

3 回答 3

1

在 Google App Engine 的数据存储区中,对实体组的写入限制为每秒 1 次写入。

由于您为每首歌曲指定了一个“父”键,它们最终都在一个实体组中,这非常慢。

仅使用 KeyProperty 来跟踪该关系是否可以接受?这会快得多,尽管数据可能存在更多的一致性问题。

于 2013-10-21T20:43:24.020 回答
1

除了其他答案回复:实体组,如果导入过程需要超过 60 秒,使用任务,那么你有 10 分钟的运行时间。

将 csv 作为 BlobProperty 存储在实体中(如果压缩 <1MB)或 GCS 更大,然后启动从存储中检索 CSV 然后进行处理的任务。

于 2013-10-21T22:48:46.493 回答
1

首先,蒂姆走在了正确的轨道上。如果您无法在 60 秒内完成工作,请推迟执行任务。但是,如果您无法在 10 分钟内完成工作,请使用App Engine MapReduce,它将处理您的 csv 的工作分配给多个任务。请参阅演示程序,其中包含您需要的一些部分。

对于开发时间缓慢,您是否--use_sqlite在启动 dev_appserver 时使用该选项?

Murph 谈到了你问题的另一部分。使用实体组,您可以执行的插入次数(每个实体组)受到速率限制。尝试使用单亲插入 17,500 行根本无法正常工作。这大约需要5个小时。

那么,您真的需要一致的读取吗?如果这是一次性上传,您是否可以进行非祖先插入(以目录作为属性),然后等待数据最终变得一致?这简化了查询。

如果您真的,绝对需要一致的读取,您可能需要将您的写入拆分到多个父键。这将提高您的写入速率,但代价是使您的祖先查询更加复杂。

于 2013-10-22T04:28:36.153 回答