【问题标题】:What can I do to make this MySQL query faster (<1s response)我该怎么做才能使这个 MySQL 查询更快(<1s 响应)
【发布时间】:2020-07-24 03:00:25
【问题描述】:

解释上下文。在工作中,我们正在运行一个使用 MySQL 作为数据缓存的 Java 应用程序。 我们正在运行一些缓慢的蒙特卡罗过程以获得一些结果。 然后我们将它们插入数据库以供以后使用,这样我们就不需要为相同的配置再次执行昂贵的蒙特卡罗。

这里是建表代码:

CREATE TABLE `cache_lte_perfbe_saved` (
`CNUMZONE` DECIMAL(10,5) NOT NULL,
`IDDATASET` INT(11) NOT NULL,
`CRB` INT(11) NOT NULL,
`CENVIRONMENT` VARCHAR(45) NOT NULL,
`CPROPAGATION` VARCHAR(45) NOT NULL,
`CSECTORCONF` VARCHAR(60) NOT NULL,
`CFREQUENCY` INT(11) NOT NULL,
`CINTERCELLLOAD` INT(11) NOT NULL,
`CNBST` INT(11) NOT NULL,
`CNBBE` INT(11) NOT NULL,
`IDLTECONF` INT(11) NOT NULL,
`CTHROUGHPUT` VARCHAR(50) NOT NULL,
`CTHROUGHPUT_ARITHMETIC` DECIMAL(9,2) NOT NULL,
`CDISTRIB` DECIMAL(12,10) NOT NULL,
`VOLTETRAFFICGB` VARCHAR(50) NOT NULL,
`PID` VARCHAR(45) NOT NULL DEFAULT '0',
`SERVICENAME` VARCHAR(50) NOT NULL,
`VOLTETRAFFICERL` VARCHAR(50) NOT NULL,
PRIMARY KEY (`IDDATASET`, `CRB`, `CNBST`, `CNBBE`, `IDLTECONF`, `CNUMZONE`, `CENVIRONMENT`, `CPROPAGATION`, `CFREQUENCY`, `CINTERCELLLOAD`, `SERVICENAME`, `PID`, `VOLTETRAFFICGB`, `VOLTETRAFFICERL`, `CSECTORCONF`) USING HASH,
INDEX `PK_noVoltenoSectorConf` (`IDDATASET`, `CRB`, `CENVIRONMENT`, `CPROPAGATION`, `CFREQUENCY`, `CINTERCELLLOAD`, `CNBST`, `CNBBE`, `IDLTECONF`, `PID`) USING HASH,
INDEX `PK_noVoLTE` (`IDDATASET`, `CRB`, `CENVIRONMENT`, `CPROPAGATION`, `CSECTORCONF`, `CFREQUENCY`, `CINTERCELLLOAD`, `CNBST`, `CNBBE`, `IDLTECONF`, `PID`) USING HASH,
INDEX `PK_noVoLTEnoFrequency` (`IDDATASET`, `CRB`, `CENVIRONMENT`, `CPROPAGATION`, `CSECTORCONF`, `CNBST`, `CNBBE`, `PID`, `IDLTECONF`) USING HASH)COLLATE='utf8_general_ci' ENGINE=InnoDB ;

这是一个大约需要 3 秒的查询:

SELECT  *
    FROM  cache_lte_perfbe_saved
    WHERE  IDDATASET = 6
      AND  CRB = 100
      AND  CENVIRONMENT = 'high'
      AND  CPROPAGATION = 'IndDay'
      AND  CFREQUENCY = 1800
      AND  CINTERCELLLOAD = 82.0
      AND  CNBST >= 0
      AND  CNBST <= 0
      AND  CNBBE <= 30
      AND  PID = 1586536071412
      AND  IDLTECONF IN ( 2, 17, 18 )
      AND  VOLTETRAFFICERL = 40.0
    UNION 
 SELECT  *
    FROM  cache_lte_perfbe
    WHERE  IDDATASET = 6
      AND  CRB = 100
      AND  CENVIRONMENT = 'high'
      AND  CPROPAGATION = 'IndDay'
      AND  CFREQUENCY = 2600
      AND  CINTERCELLLOAD = 88.0
      AND  CNBST >= 0
      AND  CNBST <= 0
      AND  CNBBE <= 30
      AND  PID = 1586536071412
      AND  IDLTECONF IN ( 2, 17, 18 )
      AND  VOLTETRAFFICERL = 40.0
    UNION 
 SELECT  *
    FROM  cache_lte_perfbe
    WHERE  IDDATASET = 6
      AND  CRB = 50
      AND  CENVIRONMENT = 'high'
      AND  CPROPAGATION = 'IndDay'
      AND  CFREQUENCY = 800
      AND  CINTERCELLLOAD = 84.0
      AND  CNBST >= 0
      AND  CNBST <= 0
      AND  CNBBE <= 30
      AND  PID = 1586536071412
      AND  IDLTECONF IN ( 2, 17, 18 )
      AND  VOLTETRAFFICERL = 40.0 

[/代码]

这里是这个查询的解释结果: click

查询返回 3000 行。

就目前而言,我们可以插入 200 万行,这将使查询变得非常慢。 我有多个索引,因为我们需要根据模拟选项查询不同的东西。这意味着我们不会总是使用主键中的所有列,因此索引将毫无用处。

你能帮我优化这个查询吗?我正在考虑是否应该为每种情况使用不同的表格。也许使用Mysql作为缓存不是一个好主意..也许我应该尝试在内存中制作这张表?

让我知道你的想法。 干杯!

【问题讨论】:

  • 您在 EXPLAIN 中列出的第一个键长度为 302 的表阻碍了典型性能,并且使表中悬挂的三个索引膨胀。
  • 如果所有辅助列都已在 PK 中,则不会膨胀。
  • VOLTETRAFFICERL = 40.0 看起来是数字,但声明为 VARCHARCINTERCELLLOAD 有小数点,但声明为 INT??
  • @RickJames 很好地观察了这个流量,我已经把它变成了十进制的 10,2。

标签: mysql caching indexing


【解决方案1】:
  • 重新排列PRIMARY KEY,使这些列在前:IDDATASET、CRB、CENVIRONMENT、CPROPAGATION、CFREQUENCY、CINTERCELLLOAD、PID、VOLTETRAFFICERL。它们的顺序无关紧要。(更多内容见下文)
  • 扔掉任何麻烦多于价值的二级索引。也许你可以摆脱所有三个。 (这不会直接加速 SELECT,但会让 INSERTs 运行得更快,因此不要碍事。)
  • 如果您确实需要这些索引,请至少重新排列列,让所有列首先使用= constant 进行测试。在那之后,拥有多个“范围”列几乎没有任何用处。
  • 即使您仍然需要所有 3 个索引,最好只使用常用列的初始列表。 (显示各种SELECTs/UPDATEs/DELETEs 以获得进一步解释。)
  • 请注意,“HASH”被“Btree”替换。
  • 使用UNION ALL 而不是UNION DISTINCTDISTINCT 是默认值,但速度较慢)。 (如果您期待重复但不想要它们,请忽略此建议。)
  • 5.7.3 (10.1) 对UNION ALL 进行了优化;如果需要,请升级。
  • 如果表太大以至于受 I/O 限制,请将 INTs(每个 4 字节)缩小为更小的数据类型。并考虑使用 1 字节的 ENUM 表示 CENVIRONMENT 等。
  • 重要!将VARCHAR 与数值进行比较时,无法有效使用索引。我特别想VOLTETRAFFICERL,但请检查其他人!

PK中顺序的问题……

注意EXPLAIN 中的“key_len = 12”。 12 = 3 * 4。也就是说,它使用了前 3 列,而忽略了其余部分,

 PRIMARY KEY (`IDDATASET`, `CRB`, `CNBST`, ...
               ---------    ---    -----

INT 是 4 个字节。 WHERE

    WHERE      IDDATASET = 6
      AND                   CRB = 100
      AND                          CNBST >= 0
               ---------    ---    -----

其余列未使用。为什么?在许多情况下,只会使用索引的前几列:

  • 首先,使用= 测试的所有列
  • 然后,一个范围列。
  • 之后,剩下的测试就是暴力破解了。

因此,我建议重新安排 PK。

警告:重新安排 PK 需要很长时间; ALTER 将完全重建表(并使用大量磁盘空间)。

【讨论】:

  • 亲爱的@RickJames,感谢您的详细建议。我重新排列了主键顺序。解释现在看起来像这样imgur.com/a/tbKNBmV 但是没有明显的改进,因为表变大(250 万)并且查询时间似乎缩小了,在 3.5 秒我在想这个解决方案可能已经达到关键大众,我们应该重新考虑它。您认为将这张表移入内存是否值得?我已经对其进行了测试,它似乎很快(1.5s)。但是有一点内存消耗..
  • @RaduCristianNeagoe - 如果您有多个连接,由于表锁定,MEMORY 可能比InnoDB 慢。此外,您需要缩小 innodb 的 buffer_pool,这会巧妙地减慢其他查询的速度。你有没有解决我的其他建议?
  • 您好,最终我没有选择 MEMORY 引擎。我已将表拆分为两个表,因为有些查询并未按照创建顺序使用所有索引列。 VOLTETRAFFICGB 和 VOLTETRAFFICERL 已成为 VOLTETRAFFIC。一个表丢失了 FREQUENCY 列,另一个丢失了 CSECTORCONF 列,因为它们从未真正一起查询过。我为 VOLTETRAFFIC 添加了一个单独的索引,现在我的 EXPLAIN 的 Extra 说:使用索引条件和行数显着减少。我收到 0.1-0.2 秒的查询。谢谢!
猜你喜欢
  • 1970-01-01
  • 2013-10-21
  • 1970-01-01
  • 2021-08-01
  • 1970-01-01
  • 2015-03-16
  • 1970-01-01
  • 1970-01-01
  • 2012-01-08
相关资源
最近更新 更多