1

有一个由 8 个字段组成的非常大的表(我知道,不精确)。

在我的应用程序中,我反复运行此 SELECT:

SELECT d1, time, s1, s2 from Collection WHERE (d1 = 1) and (s1 = 1) and (time BETWEEN 5666300000 AND 566630700);

我用不同的时间范围运行这个 SELECT。d1 是一个具有 200-300 个不同值的字段,与 s1 相同。主键是 d1、时间、s1。

我正在寻找优化我的表结构和查询的技巧。问题是时间字段不是按升序/降序排列的。所以这可能是一个需要一些时间的查询。我想知道索引我的时间字段。那么我是否必须更改我的查询?我这种情况,怎么办?

我没有看到的查询是否有任何 obv 错误?这在我的应用程序中运行缓慢。谢谢!

4

3 回答 3

2

我不同意其他答案中提出的主键顺序。

您的理想方案(对于您的确切示例查询)是将所有相关记录彼此相邻。这将启用对您的数据的单次搜索。例如,(d1, s1, time)用作集群主键,您将存储数据如下...

 d1 | s1 | time 
----+----+------
  1 |  1 | 1234
  1 |  1 | 1235    \
  1 |  1 | 1236     SELECT * FROM table WHERE d1 = 1 AND s1 = 1 AND time BETWEEN 1235 AND 1237
  1 |  1 | 1237    /
  1 |  1 | 1238
  1 |  2 | 1234
  1 |  2 | 1235
  1 |  2 | 1236
  1 |  2 | 1237
  1 |  2 | 1238

如果按照其他人的建议,您time将聚集索引中的第一个字段作为第一个字段,那么您不会将所有数据放在一个连续的块中。相反,您会为每个单独的时间值获得一次搜索...

 time | d1 | s1
------+----+----
 1234 |  1 |  1     *Desired Row 1
 1234 |  1 |  2
 1235 |  1 |  1     *Desired Row 2
 1235 |  1 |  2
 1236 |  1 |  1     *Desired Row 3
 1236 |  1 |  2
 1237 |  1 |  1     *Desired Row 4
 1237 |  1 |  2
 1238 |  1 |  1     *Desired Row 5
 1238 |  1 |  2

这种结构实际上非常适合不同的查询......

SELECT * FROM yourTable WHERE time = 1234 AND d1 = 1 AND s2 BETWEEN 2 AND 3

这表明不存在单一的普遍完美的聚集索引。那么,由于只能有一个聚集索引,您如何选择创建聚集索引?

这取决于您的数据和查询。对于每个查询,您需要查看要提取多少不同的连续数据块。尽量减少这些块的数量是一个非常好的主意。但是维护数据的顺序也是如此,以便它适合您的 GROUP BY 或 ORDER by 子句。JOIN 进一步加重了这一点。

对于您的示例查询,我建议的第一个索引确实是最好的。但不适用于您的所有查询。

此外,您需要考虑碎片。数据存储在页面中,您需要考虑插入数据的方式(考虑时将更新视为删除和插入)。因为很可能任何插入通常会比现有数据更新时间值,所以time在聚集索引中具有第一个将减少碎片。

例如,假设每个页面只能容纳三行数据。上面建议的两个索引看起来像这样......

 d1 | s1 | time            time | d1 | s1 
----+----+------          ------+----+----
  1 |  1 | 1234 \          1234 |  1 |  1   \
  1 |  1 | 1235  Page 1    1234 |  1 |  2    Page 1
  1 |  1 | 1236 /          1235 |  1 |  1   /
----+----+------          ------+----+----
  1 |  1 | 1237 \          1235 |  1 |  2   \
  1 |  1 | 1238  Page 2    1236 |  1 |  1    Page 2
  1 |  2 | 1234 /          1236 |  1 |  2   /
----+----+------          ------+----+----
  1 |  2 | 1235 \          1237 |  1 |  1   \
  1 |  2 | 1236  Page 3    1237 |  1 |  2    Page 3
  1 |  2 | 1237 /          1238 |  1 |  1   /
----+----+------          ------+----+----
  1 |  2 | 1238 -Page 4    1238 |  1 |  2   -Page 4

现在,尝试插入d1 = 1, s1 = 1, time = 1239.

 d1 | s1 | time            time | d1 | s1 
----+----+------          ------+----+----
  1 |  1 | 1234 \          1234 |  1 |  1   \
  1 |  1 | 1235  Page 1    1234 |  1 |  2    Page 1
  1 |  1 | 1236 /          1235 |  1 |  1   /
----+----+------          ------+----+----
  1 |  1 | 1237 \          1235 |  1 |  2   \
  1 |  1 | 1238  Page 2    1236 |  1 |  1    Page 2
 *1 |  1 | 1239*/          1236 |  1 |  2   /
----+----+------          ------+----+----
  1 |  2 | 1234 -Page 3    1237 |  1 |  1   \
----+----+------           1237 |  1 |  2    Page 3
  1 |  2 | 1235 \          1238 |  1 |  1   /
  1 |  2 | 1236  Page 4   ------+----+---- 
  1 |  2 | 1237 /          1238 |  1 |  2   -Page 4
----+----+------           1239 |  1 |  1   /
  1 |  2 | 1238 -Page 5

左边的版本必须创建一个新页面。右边的版本只是继续填充现有页面。

当出现碎片时,通常有可以修复碎片的维护计划。这通常是一个通宵的过程。

这有点复杂不是吗?嗯,有整本书都是关于这个主题的。

在它成为一个问题之前,我通常不会太担心碎片化。但这确实是值得牢记的事情。

于 2012-06-20T08:08:14.890 回答
1

我建议您按时间 + d1 + s1(按此顺序)构建聚集索引(主键)。这将确保数据按时间顺序物理存储,然后是 d1 和 s1

于 2012-06-20T07:31:42.260 回答
1

首先,正如 npe 所说,您不应该将时间用作 Primary。我认为在time - d1 - s1. 这样,您将有时间作为一种主要的主索引,因此所有中间索引都会非常快。只有这样才会出现 d1 和 s1。此外,将 d1 和 s1 放在尽可能小的数据类型中。如果只有 1 和 0,则放入 bool 等。这将加快检查速度。

于 2012-06-20T07:34:54.320 回答