MariaDB 创建的视图耗时太长

Posted

技术标签:

【中文标题】MariaDB 创建的视图耗时太长【英文标题】:MariaDB Created view takes too long 【发布时间】:2021-11-18 10:51:50 【问题描述】:

我有问题。我有一张表,里面有 600 万条记录。每条记录都有一个日期时间列,对于我的代码,我需要按升序排列的最新 16 条记录。直接从原始表中查询花费的时间太长,因此我使用以下查询创建了一个视图:

SELECT openTime, high, low, a, b, c, d, e FROM  Candlestick WHERE market = 'USDT' AND coin = 'ETH' AND period = '5m' ORDER BY openTime DESC LIMIT 16

这意味着视图只包含 16 条记录。然后在我的代码中,我使用带有以下查询的视图:

SELECT high, low, a, b, c, d, e FROM vwCI_USDT_ETH_5m ORDER BY openTime ASC

此查询获取所有(16 条记录)记录并将其按升序排列,但即使对于这 16 行,查询也需要大约 25 秒,如下图所示: 有没有办法加快这个选择查询?

更新

我在 Candlestick 表上创建了一个索引,就像 @The Impaler 告诉我的那样,我现在正在使用以下没有视图的查询:

SELECT a.high, a.low, a.a, a.b, a.c, a.d, a.e FROM (SELECT openTime, high, low, a, b, c, d, e FROM Candlestick WHERE market = 'USDT' AND coin = 'ETH' AND period = '5m' ORDER BY openTime DESC LIMIT 16 ) AS a ORDER BY a.openTime ASC

这是我现在所有的索引: 但仍然在索引之后,此查询大约需要 20 - 25 秒。我可以做些什么来改进它?

show create table Candlestick;的结果:

CREATE TABLE `Candlestick` (
  `dateTimeChanged` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
  `openTime` bigint(20) NOT NULL,
  `closeTime` bigint(20) NOT NULL,
  `market` varchar(10) NOT NULL,
  `coin` varchar(10) NOT NULL,
  `period` varchar(10) NOT NULL,
  `open` decimal(14,6) NOT NULL DEFAULT 0.000000,
  `high` decimal(14,6) NOT NULL DEFAULT 0.000000,
  `low` decimal(14,6) NOT NULL DEFAULT 0.000000,
  `close` decimal(14,6) NOT NULL DEFAULT 0.000000,
  `volume` decimal(20,8) NOT NULL DEFAULT 0.00000000,
  `a` decimal(6,3) NOT NULL DEFAULT 0.000,
  `b` decimal(3,0) NOT NULL DEFAULT 0,
  `c` decimal(3,0) NOT NULL DEFAULT 0,
  `d` decimal(3,0) NOT NULL DEFAULT 0,
  `e` varchar(1) NOT NULL DEFAULT '0',
  `ma5` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `ema5` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `ema10` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `ema12` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `ema20` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `ema26` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `ema50` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `ema55` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `ema100` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `ema200` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `rsi14AvgGain` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `rsi14AvgLoss` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `rsi14` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `macd` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `signal` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `bbLower` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `bbMiddle` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `bbUpper` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `dmiDIPositive` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `dmiDINegative` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  `dmiADX` decimal(16,8) NOT NULL DEFAULT 0.00000000,
  PRIMARY KEY (`openTime`,`market`,`coin`,`period`) USING BTREE,
  KEY `OpenTime` (`openTime`) USING BTREE,
  KEY `MarketCoinPeriod` (`market`,`coin`,`period`) USING BTREE,
  KEY `ix1` (`market`,`coin`,`period`,`openTime`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

【问题讨论】:

显示以下结果:SELECT version();show create table Candlestick; Version: 10.3.31-MariaDB-0ubuntu0.20.04.1-log show create table Candlestick: CREATE TABLE Candlestick (dateTimeChanged t... 这是我唯一能看到的,但创建查询有 2157 字符,因为我有 37 列! 啊。所以它不是 mysql。使用MySQL命令行客户端获取完整结果:show create table Candlestick; 啊,我会在问题的底部添加它! openTime 列的正确顺序的覆盖索引似乎确实有帮助。请参阅提供的小提琴。它包含我创建的表,以及您提供详细信息后的实际表。如果openTime 的顺序不是您需要的顺序,通常不会选择索引。 MariaDB 支持降序索引来解决这种情况。 对不起。它允许语法。我看到了一些相互矛盾的文档。 我没有明确的细节。但该计划似乎表明它有所帮助。 【参考方案1】:

查询很简单。您不需要视图,但需要一个好的索引。

将提高该查询性能的索引是:

create index ix1 on Candlestick (market, coin, period, openTime);

【讨论】:

我已经添加了关于索引的问题的更新! @A.Vreeswijk 这是一个不同的查询。请告诉我们您要优化哪个查询,并在问题中包含当前的执行计划。 不,这是同一个查询,只是我没有在更新后的查询中使用视图。所以我首先选择前 16 条记录,然后将这 16 条记录颠倒为升序。此查询的结果相同【参考方案2】:

即使您正在查询您的视图。依然是查询表源表,获取所有行,按openTime降序排序,才选择前16行。

之后,您将按照 openTime 的升序对选定的 16 位数字进行排序。

我同意@The Impaler。您可能需要该表上的适当索引。

【讨论】:

我已经为我的关于索引的问题添加了更新!【参考方案3】:

覆盖索引可能会有所帮助。

这是基于以下评论的最终建议索引:

create index ix3 on Candlestick (market, coin, period, openTime DESC, high, low, a, b, c, d, e);

这是测试用例中使用的所有 SQL(小提琴):

Working test case for MariaDB and generated sample data

以下内容基于对实际 create table 语句的猜测,但讨论一些可能的索引问题很有用。

这是包含当前建议索引的计划和一些示例数据:

这是索引的descending 版本的计划:

create index ix2 on Candlestick (market, coin, period, openTime DESC);

这是建议的覆盖索引:

create index ix3 on Candlestick (market, coin, period, openTime DESC, high, low, a, b, c, d, e);

并使用您的实际表格,以及之前的ix1 索引:

现在使用新的建议索引(使用openTime DESC 顺序):

更新:MariaDB 似乎支持降序索引语法,但可能不完全支持该功能。在较新的 Maria 版本(例如 10.5)中,新索引 (ix3) 未用于此测试用例。

如果发现有帮助,我们可以force 索引:

SELECT a.high, a.low, a.a, a.b, a.c, a.d, a.e
  FROM (
         SELECT openTime, high, low, a, b, c, d, e
           FROM Candlestick FORCE INDEX (ix3)
          WHERE market = 'USDT' AND coin = 'ETH' AND period = '5m'
          ORDER BY openTime DESC
          LIMIT 16
       ) AS a
 ORDER BY a.openTime ASC
;

如果我们在添加索引后查看表,我们注意到 DESC 术语被忽略了:

KEY `ix3` (`market`,`coin`,`period`,`openTime`,`high`,`low`,`a`,`b`,`c`,`d`,`e`)

【讨论】:

是的!!!强制索引有效。所以如果我理解正确的话,MariaDB并没有真正自动支持降序索引的这个功能,所以我需要强制它使用它? @A.Vreeswijk 不完全是。不支持降序索引,因此索引没有它可能的帮助。但它仍然是一个覆盖索引,这意味着它包含满足查询数据要求所需的所有列,即使顺序不完全正确。这避免了在扫描不同的非覆盖索引后必须扫描表或点访问表。有时您可以获得优化器可能无法找到的更好的行为(更好的访问路径)。 @A.Vreeswijk 如果 MDEV-13756 曾经完成/发布,您可能需要删除/重新创建该索引,因为在创建新的 ix3 索引时忽略了 DESC 术语。 MySQL(最新版本)目前确实支持这一点。玛丽亚只是还没有开始。 @A.Vreeswijk 在 MySQL 8.0 中创建该索引后,我们在show create table 的结果中看到以下索引:KEY ix3 (market,coin,period,openTime DESC,high,low,a,b,c,d,e)

以上是关于MariaDB 创建的视图耗时太长的主要内容,如果未能解决你的问题,请参考以下文章

MySQL/MariaDB:创建数据透视表视图

MariaDB:使用 SET @variable 从查询创建视图

如何在 from 子句中使用子查询创建视图 - Mariadb

Symfony 3.4.4 + MariaDB 10.0.1 - SQLSTATE [42000]:语法错误或访问冲突:1071 指定的密钥太长;最大密钥长度为 767 字节

MySQL/MariaDB视图

MariaDB 视图与触发器(11)