Mysql提高了SELECT速度

时间:2011-01-23 18:22:21

标签: sql mysql database-design query-optimization database-partitioning

我目前正在尝试提高MySQL表的SELECTS速度,并会对如何改进它表示赞赏。

表格中有超过3亿条记录,表格中包含结构标记,日期,值。主键是标签和日期的组合键。该表包含大约600个唯一标记的信息,这些标记最多包含大约400,000行,但行数可以从2000行到超过1,100万行。

针对该表运行的查询是:

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

....如果有任何INSERTS,则很少。

我尝试按标记将数据划分为不同数量的分区,但这似乎没有增加速度。

8 个答案:

答案 0 :(得分:4)

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

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

MySQL and NoSQL: Help me to choose the right one

然后将表引擎修改为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

但仅当值不是某些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表中。

注意我在导出过程中对数据进行排序 - 聚簇索引是KEY!

导出

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...

答案 1 :(得分:1)

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

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

答案 2 :(得分: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

答案 3 :(得分:1)

您的查询要求一些东西 - 并且在行数较高的情况下,数据外观可以改变最佳方法。

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

有些事情可能会减慢此选择查询的速度。

  1. 必须排序的非常大的结果集(按顺序排序)。
  2. 非常大的结果集。如果标记和日期在索引中(并且假设它的效果一样好),则每个结果行都必须保留索引以查找值字段。想到这就像需要书中每章的第一句话。如果您只需要知道章节名称,那么您可以从目录中获取它,但由于您需要第一句话,您必须转到实际的章节。在某些情况下,优化器可能只选择翻阅整本书(查询计划术语中的表扫描)来获取第一句话。
  3. 首先使用错误的where子句进行过滤。如果索引在订单标记中,则date ... then标记应该(对于大多数查询而言)是两列中更严格的。所以基本上,除非你有比日期更多的标签(或者可能是典型日期范围内的日期),否则日期应该是索引中两列中的第一列。
  4. 一些建议:

    1. 考虑是否有可能截断一些数据,如果它太大而无法关注大部分时间。
    2. 尝试使用当前索引播放 - 即更改其中的项目顺序。
    3. 取消当前索引并将其替换为覆盖索引(其中包含所有3个字段)
    4. 运行一些EXPLAIN并确保它正在使用您的索引。
    5. 切换到其他一些数据存储(mongo db?)或以其他方式确保此怪物表在内存中保留尽可能多的内容。

答案 4 :(得分:0)

我认为你进一步改进它的唯一机会是覆盖索引,包含所有三列(标签,数据,值)。这样可以避免表访问。

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

答案 5 :(得分:0)

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

alter table table add index (tag, date);

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

答案 6 :(得分:0)

我认为value列是您性能问题的最底层。它不是索引的一部分,因此我们将拥有表访问权限。此外,我认为ORDER BY不太可能严重影响性能,因为它是您索引的一部分,应该订购。

我将通过分区不会真正减少查询的执行时间这一事实来论证我对value列的怀疑。你可以在没有value的情况下执行查询,并进一步给出一些结果以及EXPLAIN吗?你真的需要每一行吗?它是什么样的专栏?

干杯!

答案 7 :(得分: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;