10

我目前正在尝试提高 MySQL 表的 SELECTS 速度,并希望对改进它的方法提出任何建议。

我们在表中有超过 3 亿条记录,并且该表具有结构标签、日期、值。主键是标签和日期的组合键。该表包含大约 600 个唯一标签的信息,其中大多数平均包含大约 400,000 行,但范围从 2000 到超过 1100 万行。

针对该表运行的查询是:

  SELECT date,
         value 
    FROM table 
   WHERE tag = "a" 
     AND date BETWEEN 'x' and 'y' 
ORDER BY date

....如果有插入,则很少。

我曾尝试将数据按标签划分为不同数量的分区,但这似乎并没有提高速度。

4

8 回答 8

4

花点时间在这里阅读我的答案:(与您的内容相似)

5 亿行,1500 万行范围扫描在 0.02 秒内。

MySQL 和 NoSQL:帮我选一个合适的

然后将您的表引擎修改为 innodb,如下所示:

create table tag_date_value
(
tag_id smallint unsigned not null, -- i prefer ints to chars
tag_date datetime not null, -- can we make this date vs datetime ?
value int unsigned not null default 0, -- or whatever datatype you require
primary key (tag_id, tag_date) -- clustered composite PK
)
engine=innodb;

您可能会将以下内容视为主键:

primary key (tag_id, tag_date, value) -- added value save some I/O

但前提是 value 不是一些 LARGE varchar 类型!

像以前一样查询:

select
 tag_date, 
 value
from
 tag_date_value
where
 tag_id = 1 and
 tag_date between 'x' and 'y'
order by
 tag_date;

希望这可以帮助 :)

编辑

哦忘了提 - 不要使用 alter table 将引擎类型从 mysiam 更改为 innodb,而是将数据转储到 csv 文件中,然后重新导入到新创建的空 innodb 表中。

请注意,我在导出过程中订购数据 - 聚集索引是关键!

出口

select * into outfile 'tag_dat_value_001.dat' 
fields terminated by '|' optionally enclosed by '"'
lines terminated by '\r\n'
from
 tag_date_value
where
 tag_id between 1 and 50
order by
 tag_id, tag_date;

select * into outfile 'tag_dat_value_002.dat' 
fields terminated by '|' optionally enclosed by '"'
lines terminated by '\r\n'
from
 tag_date_value
where
 tag_id between 51 and 100
order by
 tag_id, tag_date;

-- etc...

进口

以正确的顺序导入表中!

start transaction;

load data infile 'tag_dat_value_001.dat' 
into table tag_date_value
fields terminated by '|' optionally enclosed by '"'
lines terminated by '\r\n'
(
tag_id,
tag_date,
value
);

commit;

-- etc...
于 2011-03-10T22:33:04.710 回答
1

日期字段的基数是多少(即该字段中出现了多少个不同的值)?如果日期 BETWEEN 'x' AND 'y' 比 WHERE 子句的 tag = 'a' 部分更具限制性,请尝试使用主键 (date, tag) 而不是 (tag, date),允许使用日期作为索引值。

此外,请注意如何在 WHERE 子句中指定“x”和“y”。在某些情况下,MySQL 将强制转换每个日期字段以匹配您比较的值的非日期隐含类型。

于 2011-01-23T19:52:41.813 回答
1

我会做两件事 - 首先在标签和日期周围放置一些索引,如上所述:

alter table table add index (tag, date);

接下来将您的查询分解为一个主查询和子选择,当您进入主查询时,您将在其中缩小结果范围:

SELECT date, value
FROM table
WHERE date BETWEEN 'x' and 'y'
AND tag IN ( SELECT tag FROM table WHERE tag = 'a' )
ORDER BY date
于 2011-03-09T16:41:27.133 回答
1

您的查询要求一些事情 - 并且由于行数如此之高,数据的外观可能会改变最佳方法。

   SELECT date, value 
   FROM table 
   WHERE tag = "a" 
     AND date BETWEEN 'x' and 'y' 
   ORDER BY date

有一些事情可以减慢这个选择查询。

  1. 一个非常大的结果集,必须进行排序(排序依据)。
  2. 一个非常大的结果集。如果标签和日期在索引中(让我们假设它和它一样好)每个结果行都必须离开索引来查找值字段。把这想象成需要一本书每一章的第一句话。如果您只需要知道章节名称,很容易 - 您可以从目录中获取它,但由于您需要第一句话,因此您必须转到实际章节。在某些情况下,优化器可能会选择仅翻阅整本书(查询计划术语中的表扫描)以获取那些第一句话。
  3. 首先过滤错误的 where 子句。如果索引在订单标签中,日期...那么标签(对于您的大多数查询)应该是两列中更严格的。所以基本上,除非你有比日期更多的标签(或者可能比典型日期范围内的日期),那么日期应该是索引中两列中的第一列。

几个建议:

  1. 考虑是否可以截断一些数据,如果它太旧而无法在大多数时间关心。
  2. 尝试使用您当前的索引 - 即更改其中项目的顺序。
  3. 删除当前索引并用覆盖索引替换它(其中包含所有 3 个字段)
  4. 运行一些 EXPLAIN 并确保它完全使用您的索引。
  5. 切换到其他一些数据存储(mongo db?)或以其他方式确保这个怪物表尽可能多地保存在内存中。
于 2011-03-10T21:59:06.287 回答
0

我想说您进一步改进它的唯一机会是包含所有三列(标签、数据、值)的覆盖索引。这避免了表访问。

我不认为分区可以帮助解决这个问题。

于 2011-01-23T18:26:54.857 回答
0

我猜想添加索引(tag, date)会有所帮助:

alter table table add index (tag, date);

请在此查询上发布解释的结果(EXPLAIN SELECT date, value FROM ......)

于 2011-01-23T18:28:39.060 回答
0

我认为该value专栏是您的绩效问题的底部。它不是索引的一部分,所以我们可以访问表。此外,我认为 ORDER BY 不太可能对性能产生如此严重的影响,因为它是您的索引的一部分,应该订购。

我将value通过分区并没有真正减少查询的执行时间这一事实来证明我对该列的怀疑。您是否可以在没有解释的情况下执行查询value并进一步给我们一些结果以及解释?你真的需要它的每一行吗?它是什么类型的列?

干杯!

于 2011-01-23T19:00:18.783 回答
0

尝试仅将所需的日期插入到临时表中,然后在临时表上选择标签和排序来完成。

CREATE temporary table foo
SELECT date, value 
FROM table 
WHERE date BETWEEN 'x' and 'y' ;

ALTER TABLE foo ADD INDEX index( tag );

SELECT date, value 
FROM foo 
WHERE tag = "a" 
ORDER BY date;

如果这不起作用,请尝试从标签选择中创建 foo 。

CREATE temporary table foo
SELECT date, value 
FROM table 
WHERE tag = "a";    

ALTER TABLE foo ADD INDEX index( date );

SELECT date, value 
FROM foo 
WHERE date BETWEEN 'x' and 'y' 
ORDER BY date;
于 2011-02-09T19:47:02.540 回答