【问题标题】:Large Sqlite database search大型 Sqlite 数据库搜索
【发布时间】:2010-11-03 10:17:48
【问题描述】:

如何实现高效的大型 Sqlite db 搜索(超过 90000 个条目)?

我正在使用 Python 和 SQLObject ORM:

    import re
    ...

    def search1():
        cr = re.compile(ur'foo')
        for item in Item.select():
            if cr.search(item.name) or cr.search(item.skim):
                print item.name

此函数运行时间超过 30 秒。我应该如何让它运行得更快?

UPD:测试:

    for item in Item.select():
        pass

... 与我的初始函数所用的时间几乎相同(0:00:33.093141 到 0:00:33.322414)。所以正则表达式不会浪费时间。

一个 Sqlite3 shell 查询:

    select '' from item where name like '%foo%';

大约一秒钟后运行。所以主要的时间消耗是由于ORM从db中检索数据效率低下造成的。我猜 SQLObject 在这里抓取整行,而 Sqlite 只涉及必要的字段。

【问题讨论】:

  • @Alex:请使用任何基准信息更新您的问题。

标签: python sql database search performance


【解决方案1】:

最好的方法是重新编写逻辑以在数据库中而不是在 Python 程序中进行选择。

您应该重新编写它以执行 Item.select("""name LIKE ....

,而不是执行 Item.select()

如果您这样做,并确保您已为 name 和 skim 列建立索引,它将很快返回。 90000 个条目并不是一个大型数据库。

【讨论】:

  • 唉,索引对“LIKE '%foo%'”没有帮助,这似乎是@Alex 需要的:-(
  • 啊,真的。它仍然比返回每条记录(构造所有对象)并使用正则表达式进行逻辑搜索要快得多。
  • 如果搜索是一个可以用 strait 值而不是 LIKE 表达式来完成的搜索,索引会有所帮助。鉴于这是使用“foo”,我猜真正的代码略有不同。
【解决方案2】:

30 秒获取 90,000 行可能不是那么糟糕。

您是否对执行以下操作所需的时间进行了基准测试?

    for item in Item.select():
        pass

只看时间是DB时间、网络时间还是应用时间?

如果您的 SQLite 数据库在物理上非常大,您可能会看到——简单地说——大量的物理 I/O 来读取所有数据库内容。

【讨论】:

  • @S.Lott:您建议的测试与我的初始函数花费的时间几乎相同(0:00:33.093141 到 0:00:33.322414)。所以正则表达式没有时间。 “select '' from item where name like '%foo%'”查询在 ssqlite3 shell 中运行大约一秒钟。所以主要的时间消耗是由于ORM从db中检索数据效率低下造成的。我猜 SQLObject 在这里抓取整行,而 Sqlite 只涉及必要的字段。
  • 不足为奇。这是一条黄金法则:“在优化之前,先进行衡量。”由于您的问题没有任何测量值,因此这必须是第一步。请使用此基准信息更新您的问题。
【解决方案3】:

如果您真的需要使用正则表达式,那么您实际上无法做任何事情来极大地加快它的速度。

最好的办法是编写一个 sqlite 函数,在 db 引擎中为您执行比较,而不是 Python。

您还可以切换到支持 SIMILAR 的数据库服务器,例如 postgresql。

http://www.postgresql.org/docs/8.3/static/functions-matching.html

【讨论】:

    【解决方案4】:

    我肯定会接受 Reed 的建议,将过滤器传递给 SQL(尽管忘记索引部分)。

    我不认为只选择指定的字段或所有字段会有所不同(除非您确实有很多大字段)。我敢打赌 SQLObject 创建/实例化 80K 对象并将它们放入 Session/UnitOfWork 进行跟踪。这肯定需要一些时间。

    此外,如果您的会话中不需要对象,则必须有一种方法可以使用自定义查询创建来选择您需要的字段,这样就不会创建 Item 对象,而只会创建元组。

    【讨论】:

      【解决方案5】:

      最初通过 Python 执行正则表达式被考虑用于 y_serial,但是 被放弃,取而代之的是 SQLite 的 GLOB(速度要快得多)。 GLOB 类似于 LIKE 只是它的语法更多 常规:* 而不是 %, ?而不是 _ 。

      更多详情请参阅http://yserial.sourceforge.net/ 的尾注。

      【讨论】:

        【解决方案6】:

        鉴于您的示例并扩展 Reed 的答案,您的代码可能类似于以下内容:

        import re
        import sqlalchemy.sql.expression as expr
        
        ...
        
        def search1():
            searchStr = ur'foo'
            whereClause = expr.or_(itemsTable.c.nameColumn.contains(searchStr), itemsTable.c.skimColumn.contains(searchStr))
            for item in Items.select().where(whereClause):
                print item.name
        

        翻译成

        SELECT * FROM items WHERE name LIKE '%foo%' or skim LIKE '%foo%'
        

        这将使数据库为您完成所有过滤工作,而不是获取所有 90000 条记录并可能对每条记录执行两次正则表达式操作。

        You can find some info here on the .contains() method here.

        还有SQLAlchemy SQL Expression Language Tutorial here

        当然,上面的示例假定您的 itemsTable 及其具有的列(nameColumn 和skimColumn)的变量名称。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-07-28
          • 2012-09-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-23
          • 2011-12-31
          • 2015-12-01
          相关资源
          最近更新 更多