查找速度:状态还是数据库?

时间:2013-05-05 01:03:45

标签: python mongodb flask

我在服务器上有一堆单词列表,我一直计划制作一个简单的开源JSON API,如果密码在列表 1 上,则返回验证方法。我正在使用Flask在Python中执行此操作,并且只要输入存在就会返回。

一个小问题:单词列表总共约有1.5亿个条目和1.1GB的文本。

我的API(最小)如下。将所有行存储在MongoDB中并重复查找,或者使用单例将整个内容存储在内存中,并在调用app.run时在启动时填充它是否更有效?或者主观上的差异是什么? 此外,做后者甚至是好的做法?我认为如果我向公众公开这些查找可能会开始变得很费劲。我也有人建议Trie进行有效搜索。

更新:我已经做了一些测试,如此大量的记录,文档搜索速度非常慢。对于需要高效搜索的单列数据,使用具有适当索引的数据库是否合理?

from flask import Flask
from flask.views import MethodView
from flask.ext.pymongo import PyMongo
import json

app = Flask(__name__)
mongo = PyMongo(app)

class HashCheck(MethodView):

  def post(self):
    return json.dumps({'result' : 
      not mongo.db.passwords.find({'pass' : request.form["password"])})
    # Error-handling + test cases to come. Negate is for bool.

  def get(self):
    return redirect('/')

if __name__ == "__main__":
  app.add_url_rule('/api/', view_func=HashCheck.as_view('api'))
  app.run(host="0.0.0.0", debug=True)

1:我是一个安全坚果。我在登录表单中使用它并拒绝常见输入。其中一个单词列表是UNIQPASS。

4 个答案:

答案 0 :(得分:4)

鉴于您的列表完全是静态的并且适合内存,我没有看到使用数据库的令人信服的理由。

我同意Trie对你的目标有效。哈希表也可以使用。

PS:Python的Global Interpreter Lock太糟糕了。如果您使用的语言具有真正的多线程,您可以利用不变的数据结构,并在具有共享内存的多个核心上运行服务器。

答案 1 :(得分:4)

我建议采用混合方法。在提出请求时,请进行两次检查。第一个在本地缓存中,第二个在MongoDB存储中。如果第一个失败但第二个成功,则将其添加到内存缓存中。随着时间的推移,应用程序将在最常见的“错误密码”/记录中“出错”。

这有两个好处:
1)常用词在记忆中被很快拒绝 2)启动成本接近于零,并在许多查询中摊销。

在MongoDB中存储单词列表时,我会使_id字段保存每个单词。默认情况下,您将获得一个ObjectId,在这种情况下完全是浪费。然后我们也可以利用_id上的自动索引。我怀疑你看到的糟糕表现是因为'pass'字段没有索引。您还可以尝试在“通行证”字段中添加一个:

mongo.db.passwords.create_index("pass")

完成_id场景:插入一个单词:

mongo.db.passwords.insert( { "_id" : "password" } );

查询看起来像:

mongo.db.passwords.find( { "_id" : request.form["password"] } )

正如@Madarco所提到的那样,你也可以通过将返回的字段限制在_id字段({ "_id" : 1})来确保从索引返回结果,从而缩短查询时间。

mongo.db.passwords.find( { "_id" : request.form["password"] }, { "_id" : 1} )

HTH - Rob

P.S。我不是Python / Pymongo专家,所以可能没有100%正确的语法。希望它仍然有用。

答案 2 :(得分:2)

我建议您查看并尝试使用redis作为选项。它快速,非常快,并具有很好的python绑定。我会尝试在单词列表的redis中创建一个集合,然后使用SISMEMBER函数来检查单词是否在集合中。 SISMEMBER是一个O(1)操作,因此它应该比mongo查询更快。

多数民众赞成你假设你想要整个列表记忆,并且你愿意取消mongo。 。 。

以下是有关redis的SISMEMBERpython bindings的更多信息 for redis

答案 3 :(得分:0)

我推荐kyotocabinet,速度非常快。我在类似情况下使用过它:

import kyotocabinet as kyc

from flask import Flask
from flask.views import MethodView
import json

app = Flask(__name__)


dbTree = kyc.DB()

if not dbTree.open('./passwords.kct', DB.OREADER):
    print >>sys.stderr, "open error: " + str(dbTree.error())
    raise SystemExit


app = Flask(__name__)


class HashCheck(MethodView):

  def post(self):
    return json.dumps({'result' : 
      dbTree.check(request.form["password"]) > 0 })
    # Error-handling + test cases to come. Negate is for bool.

  def get(self):
    return redirect('/')

if __name__ == "__main__":
  app.add_url_rule('/api/', view_func=HashCheck.as_view('api'))
  app.run(host="0.0.0.0", debug=True)