SO,
问题
我有一个非常简单的 - 乍一看 - 问题。假设我的数据集包含两个有意义的列:from
和till
。此数据集尚未在DB中。我需要搜索此数据集以及某些X
查找条件from < X < till
为真的行。例如,我有行(id
仅用于标识行,它并不意味着行在DB中):
id from till ------------ 1 100 200 2 120 200 3 1000 1050 4 1100 1500
我希望找到X = 125
的行。这将是行#1
和2
。即间隔可能相交,但它们始终是正确的(from
总是小于till
)。此外,严格的条件是所有三个:from
,till
和X
都是无符号整数。此外,在很大的概率下,间隔不会过于嵌套 - 因此,如果相交,则不会出现这种情况,例如,当某个间隔与所有其他间隔嵌套时(实际上这意味着某个间隔是可靠的)条件并不意味着完整的表格)
转向交易。我的数据集可能很大(约500,000,000行) - 我需要以某种方式将其存储在DB中。 DB结构没有任何限制 - 它可以是任何东西,我可以自由选择适当的解决方案(这就是为什么我的数据集还没有在DB中)。那么,问题是 - 如何在DB中存储它以尽可能快地为给定X
查询行?
我的方法
乍一看 - 这很简单。我们只为from
和till
创建列,并使用我们的数据集填充它们。真?不。为什么?因为这样的表结构不允许在查询中构建任何好的索引。如果我们要在两列(from, till)
上创建索引,那么就我们的问题而言没有意义 - 如果我们在两列from
和till
上创建两个单独的索引 - 它们两者的选择性都很低。为什么?想象一下,我们排在from = 100.000.000
和till = 100.000.200
之间。然后查询WHERE 100.000.000 < X AND X < 100.000.200
将不使用索引 - 因为具有拆分索引的条件将为每个索引生成接近完全扫描。并且有一个棘手的部分 - 显然,这个条件指定非常狭窄表的一部分(即逻辑上,它是好的) - 但如果我们谈论单独的条件 - 它是废话,因为它们中的每一个接近完全扫描。
我的下一步是创建一些函数,它将接受两个参数并创建然后bijective过渡到某些行数集。由于我的from
和till
是整数 - 而且,重要的是 - 正整数,还有from
&lt;总是till
,这样的函数的样本将是from^2 + till^2
。所以,好的,我们会将间隔转换为某些数字。但是,不幸的是,要对这些数字进行操作而X
我们将不得不依赖于原始的from
和till
- 即似乎不是这种想法的情况。但可能是我错过了什么?
问题
目前,我还没有完全明确的想法 - 如何实现这一点。所以 - 再次,我可以自由选择任何架构,但它应该适合X
快速查询所需行的要求。问题是 - 这里可以提出什么样的表结构(列,索引e t.c.)?我们也可以自由地存储额外的表格(但是,如果它们的尺寸不会太高,那将是很好的)。当然,由于我们可以自由定义表结构,我们也可以更改X
的查询(即,如果某个结构需要为该查询添加一些条件 - 没关系,唯一需要的是实现最终目标)。
答案 0 :(得分:1)
此处的一个选项是partition您的牌桌。特别是使用range partitioning。这与您的from
和till
列上的索引相结合,可以为您提供可接受的效果级别。
这是一个基本的例子:
CREATE TABLE myTable (
`id` INT NOT NULL,
`from` bigint unsigned not null,
`till` bigint unsigned not null,
PRIMARY KEY (`from`,`till`),
INDEX myTableIdx1 (`from`),
INDEX myTableIdx2 (`till`)
)
PARTITION BY RANGE (`from`) (
PARTITION p0 VALUES LESS THAN (200000),
PARTITION p1 VALUES LESS THAN (400000),
PARTITION p2 VALUES LESS THAN (600000),
PARTITION p3 VALUES LESS THAN (800000),
PARTITION p4 VALUES LESS THAN (1000000),
PARTITION p5 VALUES LESS THAN (1200000),
PARTITION p6 VALUES LESS THAN (1400000),
PARTITION p7 VALUES LESS THAN (1600000),
PARTITION p8 VALUES LESS THAN (1800000),
PARTITION p9 VALUES LESS THAN (2000000),
-- etc etc
PARTITION pEnd VALUES LESS THAN MAXVALUE
);
这种方法确实假设您的MySQL版本支持分区,并且您可以根据数据将表划分为有意义的分区!
PS您可能想要选择from
....
答案 1 :(得分:1)
选项1
我认为这就是你所需要的。 但仍然需要对125个案例进行全索引扫描,2001年将触发更好的范围扫描。
SELECT
data.id
, data.`from`
, data.`till`
FROM
data
WHERE
`from` < 125 and 125 < `till`
参见演示http://sqlfiddle.com/#!2/208ca/20
选项2
过滤掉非匹配的DERIVED表
SET @x = 125;
SELECT
data.id
, data.`from`
, data.`till`
FROM (
SELECT
id
, `till`
FROM
data
WHERE
`from` < @x -- from should always be smaller than @x
) from_filter
INNER JOIN
data
ON
from_filter.id = data.id
AND
@x < from_filter.`till` -- @x should always be smaller then till
;
参见演示http://sqlfiddle.com/#!2/208ca/27
选项3
R树索引可能是最佳选择
答案 2 :(得分:1)
您希望减少查询对运行比较函数的所有行的影响,以确定该行是否与X所在的数字范围相匹配。
正如您所概述的那样,由于数量/行比的绝对数量,一些常见指数的有效性并没有多大用处。
这是我要开始的地方。为什么不降低分辨率并将其用作索引?
跨度有多大?到目前为止你有100,80,50,400。
假设跨度的大小不是所有值的超集,而是通常只是它的一小部分(例如,由500 000 000值的超集最多1 000),为什么不索引from
但是在较低的结果,例如除以1 000。
这将在这样一个低分辨率辅助列上将索引空间大大减少到500 000个条目。然后你可以使用标准。查询的WHERE
部分中的数学也用于查找可能匹配的行的超集。然后可以仅对这些可能匹配的行进行更昂贵的比较(确切的BETWEEN
)。
这可能不是解决问题的学术解决方案,但可能会为您提供所需的性能。
编辑:正如@NikiC所指出的那样,对于学术解决方案,有一篇由Hans-Peter Kriegel,MarcoPötke和Thomas Seidl撰写的论文: