为什么whoosh提交这么慢

7 投票
1 回答
925 浏览
提问于 2025-04-18 10:03

我在想,为什么使用下面的代码时,whoosh的速度有点慢。尤其是提交操作,花的时间特别长。

我试着把写入器的limitmb设置为2048,而不是默认的128,但几乎没有什么变化。根据建议,我把写入器的procs设置为3,这样索引的速度稍微快了一点,但提交的速度反而更慢了。而且commit(merge=False)在这里也没什么帮助,因为索引是空的。

我得到的结果是这样的:

index_documents 12.41 seconds
commit 22.79 seconds
run 35.34 seconds

对于这样一个小的结构和大约45000个对象来说,这个结果似乎有点多。

我是在whoosh 2.5.7和Python 2.7上测试的。

这是正常现象吗?还是我期待得太高了,或者我做错了什么?

我也做了一些性能分析,发现whoosh似乎在写出和读取很多的pickle文件。这似乎与事务处理的方式有关。

from contextlib import contextmanager
from whoosh import fields
from whoosh.analysis import NgramWordAnalyzer
from whoosh.index import create_in
import functools
import itertools
import tempfile
import shutil
import time


def timecall(f):
    @functools.wraps(f)
    def wrapper(*args, **kw):
        start = time.time()
        result = f(*args, **kw)
        end = time.time()
        print "%s %.2f seconds" % (f.__name__, end - start)
        return result
    return wrapper


def schema():
    return fields.Schema(
        path=fields.ID(stored=True, unique=True),
        text=fields.TEXT(analyzer=NgramWordAnalyzer(2, 4), stored=False, phrase=False))


@contextmanager
def create_index():
    directory = tempfile.mkdtemp()
    try:
        yield create_in(directory, schema())
    finally:
        shutil.rmtree(directory)


def iter_documents():
    for root in ('egg', 'ham', 'spam'):
        for i in range(1000, 16000):
            yield {
                u"path": u"/%s/%s" % (root, i),
                u"text": u"%s %s" % (root, i)}


@timecall
def index_documents(writer):
    start = time.time()
    counter = itertools.count()
    for doc in iter_documents():
        count = counter.next()
        current = time.time()
        if (current - start) > 1:
            print count
            start = current
        writer.add_document(**doc)


@timecall
def commit(writer):
    writer.commit()


@timecall
def run():
    with create_index() as ix:
        writer = ix.writer()
        index_documents(writer)
        commit(writer)


if __name__ == '__main__':
    run()

1 个回答

1

在这个提交过程中,有一些段落正在合并;这也解释了为什么设置 procs=3 会让事情变得更慢(因为要合并更多的段落!)。

对我来说,解决办法是设置 multisegment=True,正如这里所建议的那样。

writer = ix.writer(procs=4, limitmb=256, multisegment=True)

你可以根据需要调整 procslimitmb,但要记住limitmb 是针对每个 procs 的!(也就是说,它们是相乘的)


注意:这会影响搜索速度。例如:

  • 10000 个文档: ~200毫秒(不使用 multisegment)对比 1.1秒(使用 multisegment)

  • 50000 个文档: ~60毫秒(不使用 multisegment)对比 ~100毫秒(使用 multisegment)

在我的系统上,commit 过程大约慢了 40%。我没有测量索引的时间,但 multisegment 也快得多。

这可能是原型设计的解决方案。一旦你确认了所需的 Schema 和参数,就可以把 multisegment 设置回 False,然后再运行一次。

所以只是给你一个大概念……

撰写回答