我有一个查询,在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);
其他想法?
@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;
其他条件有助于缩小要处理的行的数量。在更多列上添加索引会进一步加快此功能,但我建议先尝试一下。
我必须做一些猜测,直到看到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的开头,它将"聚集"一个SELECT
的所有必要行,从而最大程度地减少您的巨大桌子中的I/O。
注意2。是的,可以只有INDEX(...)
的CC_6。
另一个提示...因为BIGINT
总是8个字节,而INT
为4个字节;您真的需要这么大的专栏吗?缩小柱子尺寸将有助于减少I/O,这将大大加快查询。MEDIUMINT UNSIGNED
只有3个字节,范围为0..16m;等。
基于我有关整体数据库设计的后续问题的答案,结论是:
- 我所有的数据类型和索引都是正确的。
- 我使用枚举列的设计不是很优雅,而是适合基于行的数据库(例如MySQL),并在这种引擎上提供了最佳性能。
- 为了真正解决此性能问题,我应该使用我的其他问题的评论中所述的更好的设计(在其中汇总数据将在同一列中,但几行)。
您可以将查询放入类别。对于每个类别,您可以保留预先计算的列。您可以根据所需的计算组合从表中选择相关字段。当然,如果您可以对查询进行分类。