如果您对前缀范围内的重叠数量做出一个温和的假设,则可以使用 MongoDB 或 MySQL 以最佳方式执行您想要的操作。在下面的答案中,我将使用 MongoDB 进行说明,但是将这个答案移植到 MySQL 应该很容易。
首先,让我们重新表述一下这个问题。当您谈论匹配“前缀范围”时,我相信您实际上在谈论的是在 lexicographic 排序下找到正确的范围(直观地说,这只是字符串的自然字母顺序)。例如,前缀匹配 54661601 到 54661679 的数字集恰好是当写为字符串时,在字典上大于或等于“54661601”但在字典上小于“54661680”的数字集。所以你应该做的第一件事是在你的所有 high 边界上加 1,这样你就可以用这种方式表达你的查询。在 mongo 中,您的文档看起来像
{low: "54661601", high: "54661680", bin: "a"}
{low: "526219100", high: "526219200", bin: "b"}
{low: "4305870404", high: "4305870405", bin: "c"}
现在问题变成了:给定一组 [low, high) 形式的一维区间,我们如何快速找到哪些区间包含给定点?最简单的方法是在 low 或 high 字段上使用索引。让我们使用 high 字段。在 mongo 外壳中:
db.coll.ensureIndex({high : 1})
现在,让我们假设间隔根本不重叠。如果是这种情况,那么对于给定的查询点“x”,包含“x”的唯一可能区间是具有大于“x”的最小 high 值的区间。因此我们可以查询该文档并检查其 low 值是否也小于“x”。例如,这将打印出匹配间隔,如果有的话:
db.coll.find({high : {'$gt' : "5466160179125211"}}).sort({high : 1}).limit(1).forEach(
function(doc){ if (doc.low <= "5466160179125211") printjson(doc) }
)
现在假设不是假设区间根本不重叠,而是假设每个区间与小于 k 个相邻区间重叠(我不知道 k 的值是多少 会为你做到这一点,但希望它是一个小的)。在这种情况下,您可以在上面的“限制”中将 1 替换为 k,即
db.coll.find({high : {'$gt' : "5466160179125211"}}).sort({high : 1}).limit(k).forEach(
function(doc){ if (doc.low <= "5466160179125211") printjson(doc) }
)
这个算法的运行时间是多少?索引是使用 B-trees 存储的,因此如果您的数据集中有 n 个间隔,则需要 O(log n) 时间来查找第一个匹配的文档 high 值,然后 O(k) 时间迭代下一个 k 个文档,总共 O(log n em> + k) 时间。如果 k 是常数,或者实际上小于 O(log n),那么这是渐近最优的(这是在标准计算模型中;我不是计算外部存储器传输的数量或任何花哨的东西)。
只有当 k 很大时才会出现这种情况,例如,如果某个大区间几乎包含所有其他区间。在这种情况下,运行时间为 O(n)。如果您的数据是这样的结构,那么您可能需要使用不同的方法。一种方法是使用 mongo 的“2d”索引,您的 low 和 high 值编码 x 和 y 坐标.然后,您的查询将对应于查询 x - y 平面的给定区域中的点。这在实践中可能会做得很好,尽管使用当前的 2d 索引实现,最坏的情况仍然是 O(n)。
有许多理论结果对于 k 的所有值都实现了 O(log n) 性能。它们的名称包括优先级搜索树、段树、区间树等。但是,这些是您必须自己实现的专用数据结构。据我所知,目前没有流行的数据库实现它们。