N-gram数据库的查找策略

时间:2011-11-23 17:03:32

标签: database database-design language-agnostic

我有N-grams的大型数据库(原始未压缩文本大约2 TB)。 4克的示例行看起来像:

cat in the cradle 2
cat in the hat 187
cat in the window 32

即。 4个字符串文本,带有一个(可能很大的)整数(w1,w2,w3,w4,c)。我已设法将数据放在数据库中,并在[w1,w2,w3]上建立索引。查找第一个单词与给定查询匹配的内容,最后一个单词是wild:

SELECT * FROM db WHERE (w1="cat" AND w2="in" AND w3="the")

非常快。我对这个查询和第一个单词是狂野的一个感兴趣:

SELECT * FROM db WHERE (w2="in" AND w3="the" AND w4="hat")

无论我如何设计索引或数据库,查询都很慢或数据库大小膨胀到极端。此外,在我的计算机上构建索引需要几天时间,因此实验速度很慢。我正在寻找有关如何管理此类查询的建议。我认为我没有足够的硬盘空间来为[w1,w2,w3][w2,w3,w4]构建索引,因此任何答案都应该尝试适应这些约束。

4 个答案:

答案 0 :(得分:3)

您可以考虑将单词拆分为单独的表格,例如

CREATE TABLE word
  ( id INT PRIMARY KEY
  , text VARCHAR(32) NOT NULL UNIQUE
  )

只存储每个唯一单词字符的单个副本,可以节省磁盘空间(仅“潜在”取决于平均字长)。更重要的是,现在只有一个基于字符串的索引可以用于所有单词,无论它们在N-gram中的位置如何。 N-gram将通过主键标识符而不是文本来引用单词:

CREATE TABLE ngram
   ( id INT PRIMARY KEY
   , w1Id INT FOREIGN KEY REFERENCES word(id)
   , w2Id INT FOREIGN KEY REFERENCES word(id)
   , w3Id INT FOREIGN KEY REFERENCES word(id)
   , w4Id INT FOREIGN KEY REFERENCES word(id)
   , n INT NOT NULL
   )

所有外键索引都是基于整数的,而不是基于字符串的。

查询将表达如下:

SELECT w1.text, w2.text, w3.text, w4.text, ng.n
FROM ngram AS ng
INNER JOIN word AS w1 ON w1.id = ng.w1Id
INNER JOIN word AS w2 ON w2.id = ng.w2Id AND w2.text = 'in'
INNER JOIN word AS w3 ON w3.id = ng.w3Id AND w2.text = 'the'
INNER JOIN word AS w4 ON w4.id = ng.w4Id AND w2.text = 'hat'

答案 1 :(得分:2)

来自MySQL手册:

  

如果表具有多列索引,则优化程序可以使用索引的任何最左前缀来查找行。例如,如果在(col1,col2,col3)上有三列索引,则在(col1),(col1,col2)和(col1,col2,col3)上建立索引搜索功能。

     

如果列不构成索引的最左前缀,则MySQL无法使用索引。假设您有SELECT语句:

因此,您可以尝试使用所有四列(w1,w2,w3,w4)创建索引,然后更改第二个查询,如下所示:

SELECT * FROM db WHERE (w1 IS NOT NULL AND w2="in" AND w3="the" AND w4="hat")

这应该使用索引,但当然只有当你没有将w1设置为NULL的n-gram时它才有效。 (注意像''的空字符串不是空的)

无论如何,我建议尝试使用EXPLAIN命令检查它。

答案 2 :(得分:1)

如果您无法预测访问模式,或者您必须容纳多种任意访问模式,则单列索引可能是更好的选择。测试将告诉;尝试在开发计算机上测试一部分数据。

如果在四列{w1,w2,w3,w4}的组合上构建索引,那么从WHERE子句中省略列w1的任何查询都可能不使用索引。 “帽子里的猫”,“帽子里的男人”和“帽子里的人”这两个值在复合指数中都会被广泛分开。

您的dbms,无论哪一个,都会为您提供一些方法来查看查询优化器正在执行的操作。

答案 3 :(得分:0)

在(w2,w3)上创建复合索引。使用带有WHERE子句的查询,该子句在索引顺序中比较w2w3,然后使用其他非索引比较。

SELECT * FROM db WHERE (w2="in" AND w3="the" AND w1="cat") 
SELECT * FROM db WHERE (w2="in" AND w3="the" AND w4="hat")