有一个非常大的表,包含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,time,s1。
我正在寻找有关优化表结构和查询的提示。问题是时间字段不是按升序/降序排列。所以这可能是一个需要一些时间的查询。我想知道索引我的时间字段。那么我是否必须更改我的查询?我那样的,怎么样?
我的查询是否有任何问题我没看到?这在我的应用程序中运行缓慢。 谢谢!
答案 0 :(得分: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
这表明theree不是一个普遍完美的聚簇索引。那么,你如何选择聚集的内容,因为你只能有一个聚簇索引?
这取决于您的数据和查询。对于每个查询,您需要查看将要提取的多少个不同的连续数据块。尝试最小化这些块的数量是一个非常好的主意。但维护数据的顺序也是如此,以便它适合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
左边的版本必须创建一个新页面。右边的版本继续填充现有页面。
当出现碎片时,通常有维护计划可以弥补碎片。这通常是一个过夜过程。
这有点复杂不是吗?好吧,关于这个主题,有完整的书籍。
在成为问题之前,我通常不会过多担心碎片。但这确实是值得记住的事情。
答案 1 :(得分:1)
我建议按时+ d1 + s1(按此顺序)构建聚簇索引(主键)。这将确保数据按时间顺序物理存储,然后是d1和s1
答案 2 :(得分:1)
首先,正如npe所说,你不应该把时间用作主要的。我认为在time - d1 - s1
上添加主要索引。通过这种方式,您将有时间作为主要主要索引,因此所有之间的关系都非常快。只有这样才会出现d1和s1。此外,将d1和s1放在尽可能小的数据类型中。如果它只有1和0,则将其放入bool中,依此类推。这样可以加快检查速度。