mysql在表上创建索引,行数为1亿行

时间:2013-08-09 00:02:34

标签: mysql performance join indexing

我有很少的MySQL表 - 这些表有大约300列和1亿行。这些存储日志文件的数据,因此大小。我正在使用InnoDB引擎。很少有涉及这些表的连接的查询显然不起作用。我尝试为这些添加索引,但查询根本没有完成。

我想知道是否有其他方法可以提高性能,或者某些方法可以让“创建索引”在表格上运行?

谢谢。

3 个答案:

答案 0 :(得分:12)

创建索引需要时间,与表中的行数成比例。对于MySQL表,1亿行是相当多的。在该表上创建索引可能需要许多小时。根据其他因素(包括您的服务器硬件,您要为其创建索引的列的数据类型,数据库上的其他当前负载等),确切地说有多长时间会有所不同。

可以帮助您的一个工具是pt-online-schema-change。构建索引实际上需要更长的时间,但您可以在工作时继续读取和写入原始表。使用较小的表进行测试,以便您获得使用此工具的一些经验。

您可以在此处查看有关此工具的在线讲座:Zero-Downtime Schema Changes in MySQL(免费查看,但需要注册)。

另一种技术是创建一个像原始表一样的空表,在该表中创建索引,然后开始逐步将原始表中的数据复制到新表中。如果这是一个日志表,那么您写入表的次数可能比从表中读取的数据多,因此您可以立即交换表并立即开始记录新事件,并随着时间的推移进行回填。

pt-archiver之类的工具可以帮助您逐步复制数据,而不会对服务器造成太大负担。如果您尝试在一个事务中复制1亿行,那么简单地执行INSERT INTO... SELECT对您的数据库服务器的运行状况不利。它还会锁定原始表。 pt-archiver一次只复制一小块行,所以它避免了这么大的事务的高成本。

如果使用自动增量主键,在让日志事件开始写入之前,请注意将值调整为高于原始表中的最大值,这样就不会意外地将值设置为超过一次。

答案 1 :(得分:5)

使用

 create table newtable like oldtable;

然后将索引应用于newtable,当它为空时。

然后

 insert into newtable select * from oldtable;

这可能还需要很长时间才能完成。

答案 2 :(得分:0)

漏洞

在使用 MyISAM 引擎的 MySQL 表上,创建新的二级索引存在一些问题。

MyISAM 引擎的一个已知问题,在某些 MySQL 版本(例如 5.7.24(例如随 Wamp)上)不仅会导致表扫描,如预期的那样,而且在创建索引时需要重建全表。如果你只是删除一个索引,表也会被重建:-(

参考:https://bugs.mysql.com/bug.php?id=93530

替代方案

有时您无法升级 MySQL 或无法要求客户这样做以运行您的解决方案。如果您不需要 InnoDB 提供的所有功能,将引擎更改为 InnoDB 可能会导致另一个问题。

索引表

因此,有一种方法包括手动创建“索引表”,好处是您可以过滤真正需要的记录,如下所述:

想象一下,一张桌子上有 1 亿条世界公司的记录,其中大约 3000 万条是美国公司,1000 万条来自加拿大,还有其他公司。

每家公司都有一个 COUNTRY 和一个 STATE 字段,您需要对其进行索引,因为您需要按州搜索美国或加拿大公司。

因此,在 MySQL 中,如果您为 Country 和 State 创建索引,则所有 100M 记录都将被索引,即使状态为 NULL。

为了解决这个问题,你创建了一个索引表和一个真正的索引,就像这样:

create table index_tb_companies (
   company_id int unique,
   company_country char(2),  -- US/CA
   company_state char(2)     -- AL/AK/.../WI/WY
);

create index index_tb_companies_index 
  on index_tb_companies (company_country, company_state);

填写索引表

现在您可以使用简单的 insert intoreplace into 和过滤后的 select 将原始数据导入索引表。

replace into index_tb_companies(
  company_id, company_country, company_state)
(select 
    company_id, company_country, company_state 
from original_company_table
    where country in ('US', 'CA')
);  

这将需要一段时间,因为您可能还没有国家/地区的索引,需要进行全表扫描。但最终的索引表大小将小于 MySQL 索引大小,因为只有 US/CA 数据会在那里。

如何选择

现在,最后一部分是将索引表与您的美国和加拿大公司的具体报告一起使用,因为该索引不涵盖其他国家/地区。

select  o.*
from 
    original_company_table o INNER JOIN
    index_tb_companies idx ON idx.company_id = o.company_id
where
    idx.company_country = 'US' 
    and idx.company_state = 'NY'

当您想在 MySQL 上索引一小部分数据时,这种方法特别好,因此索引大小很小。

部分索引

其他数据库,如 PostgreSQL,有一个“部分索引”,您可以创建常规索引并在创建时传递 where 子句。

PG 部分索引:https://www.postgresql.org/docs/8.0/indexes-partial.html

喜欢并分享这个解决方案,如果你从中学习,我正在制作一些关于数据库的材料并感谢反馈。