【问题标题】:MySQL performance of query making addition of columns in where clauseMySQL 在 where 子句中添加列的查询性能
【发布时间】:2017-03-14 06:32:39
【问题描述】:

我有一个查询,在 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:我同意,我可以再发一篇文章来质疑整个工作流程、计算、输出和存储。但这就是我今天必须处理的事情:) 你觉得我的第二个想法怎么样?
  • 枚举列是设计不佳的症状 - 这反过来会影响性能
  • 也许可以试试 MySQL 的列存储引擎?像 ICE 或 InfiniDB。您不需要索引,因为它们存储的数据类似于基于行的存储索引。这种类型的存储在某些用例中运行速度更快,而在其他用例中运行速度较慢。
  • @e4c5:好的,按照你的说法,我在另一个问题中描述了我的实际问题:stackoverflow.com/q/42781299/1768736

标签: mysql sql indexing sqlperformance


【解决方案1】:

@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;

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

【讨论】:

  • Mysql 每个表只使用一个索引
  • 是的 @ec45。上面的附加条件应该使用包含最少行数的索引
【解决方案2】:

在看到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. 是的,AUTO_INCREMENT 可以只有 INDEX(...)

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

【讨论】:

    【解决方案3】:

    根据my follow-up question about the overall database design 中的回答,得出的结论是:

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 这将构成今天的 15 种组合,未来更多。我试图避免这样的设计:/
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-11
      • 1970-01-01
      • 2012-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多