查询的MySQL性能在where子句中添加列

时间:2017-03-14 06:32:40

标签: mysql sql indexing sqlperformance

我有一个查询在WHERE子句中添加了几个列值。我无法在单个列中预先计算此添加项,因为要使用的列组合因查询而异。我的问题是我的桌子非常大(几亿行)并且表现非常糟糕。

示例表:

+---------+------------+--------+--------+--------+--------+
| tableId | categoryId | value1 | value2 | value3 | value4 |
+---------+------------+--------+--------+--------+--------+
|       1 |          1 |      1 |      0 |      5 |      7 |
|       2 |          1 |      8 |      1 |      7 |      0 |
|       3 |          1 |     10 |      5 |      0 |     20 |
|       4 |          2 |      0 |     15 |      0 |     22 |
|       5 |          2 |     20 |      0 |     11 |      0 |
+---------+------------+--------+--------+--------+--------+

示例查询:

SELECT * FROM myTable WHERE categoryId = 1 AND (value1 + value2 + value3 + value4) > 9;
SELECT * FROM myTable WHERE categoryId = 1 AND (value1 + value3 + value4) > 5;

提高此类查询效果的最佳策略是什么? (编辑:我已经在categoryId上有一个索引,这没有帮助)

使用索引帮助进行此类查询吗?那么我是否必须为所有可能的列组合创建所有可能的索引?结果索引不会非常大吗?

ALTER TABLE myTable
ADD INDEX(categoryId, value1),
ADD INDEX(categoryId, value2),
ADD INDEX(categoryId, value3),
ADD INDEX(categoryId, value4),
ADD INDEX(categoryId, value1, value2),
ADD INDEX(categoryId, value1, value3),
ADD INDEX(categoryId, value1, value4),
etc

或者也许创建一个链接表,使用布尔值字段指定使用哪些列?但这会产生一个有几十亿行的表,不确定这是否更好......

+---------+-----------+-----------+-----------+-----------+----------+
| tableId | useValue1 | useValue2 | useValue3 | useValue4 | valueSum |
+---------+-----------+-----------+-----------+-----------+----------+
|       1 |         1 |         1 |         1 |         1 |       13 |
|       1 |         1 |         1 |         1 |         0 |        6 |
|       1 |         1 |         1 |         0 |         0 |        1 |
|       1 |         1 |         1 |         0 |         1 |        8 |
|       1 |         1 |         0 |         1 |         1 |       13 |
|       1 |         1 |         0 |         1 |         0 |        6 |
|       1 |         1 |         0 |         0 |         0 |        1 |
|       1 |         1 |         0 |         0 |         1 |        8 |
|       1 |         0 |         1 |         1 |         1 |       12 |
|       1 |         0 |         1 |         1 |         0 |        5 |
|       1 |         0 |         1 |         0 |         0 |        0 |
|       1 |         0 |         1 |         0 |         1 |        7 |
|       1 |         0 |         0 |         1 |         1 |       12 |
|       1 |         0 |         0 |         1 |         0 |        5 |
|       1 |         0 |         0 |         0 |         1 |        7 |
+---------+-----------+-----------+-----------+-----------+----------+

使用索引:

ALTER TABLE linkTable INDEX(tableId, useValue1, useValue2, useValue3, useValue4, valueSum);

还有其他想法吗?

4 个答案:

答案 0 :(得分:0)

@ e4c5是正确的,没有任何索引可以帮助当前查询。您可以首先添加以下索引并使用其他条件更改查询,以便使用索引:

ALTER TABLE myTable
ADD INDEX(categoryId, value1),
ADD INDEX(categoryId, value2),
ADD INDEX(categoryId, value3),
ADD INDEX(categoryId, value4);

并像这样更新查询:

SELECT * FROM myTable WHERE categoryId = 1 AND (value1 <= 9) AND (value2 <= 9) AND (value3 <= 9) AND (value4 <= 9) AND (value1 + value2 + value3 + value4) > 9;
SELECT * FROM myTable WHERE categoryId = 1 AND (value1 <= 5) AND (value3 <= 5) AND (value4 <= 5) AND (value1 + value3 + value4) > 5;

附加条件有助于缩小要处理的行数。在更多列上添加索引可以进一步提高速度,但我建议先尝试这一点。

答案 1 :(得分:0)

我必须做出一些猜测,直到我看到SHOW CREATE TABLE ......

如果你有这个:

tableId INT UNSIGNED AUTO_INCREMENT NOT NULL,
categoryId INT UNSIGNED NOT NULL,
...
PRIMARY KEY(tableId),

然后改为

tableId INT UNSIGNED AUTO_INCREMENT NOT NULL,  -- same
categoryId INT UNSIGNED NOT NULL,              -- same
...
PRIMARY KEY(categoryId, tableId),  -- different, see Note 1
INDEX(tableId)                     -- different, see Note 2

注意1.以categoryId开头的索引(PK)将有助于您提出的查询。此外,通过处于PK的开头,它将会#&#34; cluster&#34;一个SELECT的所有必要行,从而最大限度地减少了巨大的表中的I / O.

注意2.是的,INDEX(...)只能AUTO_INCREMENT

另一个提示......因为BIGINT总是8个字节而INT是4个字节;你真的需要那么大的专栏吗?缩小列大小将有助于减少I / O,这将显着加快查询速度。 MEDIUMINT UNSIGNED只有3个字节,范围为0..16M;等

答案 2 :(得分:0)

根据my follow-up question about the overall database design中的答案,结论如下:

  • 我的所有数据类型和索引都是正确的。
  • 我使用枚举列的设计不是很优雅,但适用于基于行的数据库,如MySQL,并在这种引擎上提供最佳性能。
  • 要真正解决这个性能问题,我应该转移到基于列的数据库,使用更好的设计,如我的其他问题的评论中所述(其中要聚合的数据将在同一列但有几行)。

答案 3 :(得分:-1)

您可以将查询分类。对于每个类别,您可以保留预先计算的列。您可以根据所需的计算组合从表中选择相关字段。当然,您可以对查询进行分类。