我的 MySQL 表很少——这些表大约有 300 列和 1 亿行。这些存储日志文件的数据,因此大小。我正在使用 InnoDB 引擎。很少有涉及这些表连接的查询显然不起作用。我尝试向这些添加索引,但查询根本没有完成。
我想知道是否有任何其他方法可以提高性能,或者有什么方法可以让“创建索引”在表上工作?
谢谢你。
我的 MySQL 表很少——这些表大约有 300 列和 1 亿行。这些存储日志文件的数据,因此大小。我正在使用 InnoDB 引擎。很少有涉及这些表连接的查询显然不起作用。我尝试向这些添加索引,但查询根本没有完成。
我想知道是否有任何其他方法可以提高性能,或者有什么方法可以让“创建索引”在表上工作?
谢谢你。
创建索引需要时间,与表中的行数成正比。1 亿行对于 MySQL 表来说是相当多的。在该表上创建索引可能需要很多小时。具体多长时间会有所不同,具体取决于其他因素,包括您的服务器硬件、您为其创建索引的列的数据类型、数据库上的其他当前负载等。
一种可以帮助您的工具是pt-online-schema-change。构建索引实际上需要更长的时间,但是您可以在原始表工作的同时继续对其进行读写。使用较小的表进行测试,以便您获得使用此工具的一些经验。
您可以在此处查看有关此工具的网络研讨会: MySQL 中的零停机模式更改(免费查看,但需要注册)。
另一种技术是像原始表一样创建一个空表,在该表中创建索引,然后开始将原始表中的数据逐渐复制到新表中。如果这是一个日志表,您对表的写入可能比从表中读取的多,因此您可以立即交换表并立即开始记录新事件,并随着时间的推移回填。
像pt-archiver这样的工具可以帮助您逐步复制数据,而不会给服务器带来过多的负载。如果您尝试在一个事务中复制 1 亿行,那么简单地执行INSERT INTO... SELECT
操作对您的数据库服务器的健康不利。它还会在原始表上加锁。pt-archiver 通过一次只复制一小块行来工作,因此它避免了如此大的事务的高成本。
如果您使用自动增量主键,请注意将值调整为高于原始表中的最大值,然后再让日志事件开始写入它,这样您就不会意外地多次 id 值。
利用
create table newtable like oldtable;
然后在新表为空时将索引应用于新表。
然后
insert into newtable select * from oldtable;
这也可能需要很长时间才能完成。
在使用 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 into
或replace 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
如果您从中学习,请喜欢并分享此解决方案,我正在制作一些有关数据库的材料并感谢您的反馈。