6

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

我想知道是否有任何其他方法可以提高性能,或者有什么方法可以让“创建索引”在表上工作?

谢谢你。

4

3 回答 3

19

创建索引需要时间,与表中的行数成正比。1 亿行对于 MySQL 表来说是相当多的。在该表上创建索引可能需要很多小时。具体多长时间会有所不同,具体取决于其他因素,包括您的服务器硬件、您为其创建索引的列的数据类型、数据库上的其他当前负载等。

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

您可以在此处查看有关此工具的网络研讨会: MySQL 中的零停机模式更改(免费查看,但需要注册)。

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

pt-archiver这样的工具可以帮助您逐步复制数据,而不会给服务器带来过多的负载。如果您尝试在一个事务中复制 1 亿行,那么简单地执行INSERT INTO... SELECT操作对您的数据库服务器的健康不利。它还会在原始表上加锁。pt-archiver 通过一次只复制一小块行来工作,因此它避免了如此大的事务的高成本。

如果您使用自动增量主键,请注意将值调整为高于原始表中的最大值,然后再让日志事件开始写入它,这样您就不会意外地多次 id 值。

于 2013-08-09T00:15:20.287 回答
8

利用

 create table newtable like oldtable;

然后在新表为空时将索引应用于新表。

然后

 insert into newtable select * from oldtable;

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

于 2013-08-09T00:15:51.380 回答
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 字段,您要对其进行索引,因为您需要按其州搜索 USA 或 CANADA 公司。

因此,在 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

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

于 2021-03-23T13:36:07.670 回答