创建辅助表以提高大型MySQL表的性能?

时间:2014-07-10 14:21:07

标签: mysql performance indexing

我有一位客户要求我调整他的MySQL数据库,以便实现一些新功能并提高现有网络应用程序的性能。

最大的表(~90 GB)有超过200M的行,并且定期增长(每次访问他所拥有的任何网站一次)。拥有连续的INSERT,从后端页面执行的每个SELECT查询都需要一段时间才能完成,因为索引每次都会重新生成。

我在自己的服务器上完成了从BTREE索引切换到HASH索引的模拟。 SELECT和INSERT都没有运行得更快。该表使用MyISAM作为存储引擎。只有INSERT和SELECT,没有UPDATE或DELETE。

我想到了创建一个与每个INSERT一起更新的辅助表的想法,以加速来自后端的每个SELECT查询。我知道这是不好的做法,但是,我确信统计页面的性能会有所改善。

我不是数据库性能专家,您可能已经注意到了......有更好的方法吗?

顺便说一下,从phpMyAdmin我已经看到表上的大多数索引的基数为0.在我的模拟中,这没有发生。我不确定为什么会这样。

非常感谢。

第一次更新:我刚刚了解到哈希索引不适用于MyISAM引擎。

第二次更新:好的。这是表格架构。

CREATE TABLE `visits` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `datetime` int(8) NOT NULL,
 `webmaster_id` char(18) NOT NULL,
 `country` char(2) NOT NULL,
 `connection` varchar(15) NOT NULL,
 `device` varchar(15) NOT NULL,
 `provider` varchar(100) NOT NULL,
 `ip_address` varchar(15) NOT NULL,
 `url` varchar(300) NOT NULL,
 `user_agent` varchar(300) NOT NULL,
 PRIMARY KEY (`id`),
 KEY `datetime` (`datetime`),
 KEY `webmaster_id` (`webmaster_id`),
 KEY `country` (`country`),
 KEY `connection` (`connection`),
 KEY `device` (`device`),
 KEY `provider` (`provider`)
) ENGINE=InnoDB;

因此,不是执行select count(*) from visits where datetime=20140715 and device="ios"之类的查询,而是最好从select count from visits_stats where datetime=20140715 and device="ios"获取此内容吗?

如上所述,INSERT比SELECT更频繁,但我的客户希望提高用于检索聚合数据的后端的性能。使用我的方法,每次访问都意味着一个INSERT和一个INSERT / UPDATE(或REPLACE)会增加一个或多个计数器(我还没有确定visits_stats表的模式,上面的查询只是一个例子)。 / p>

除此之外,我决定用外表中的相应ID替换一些字段。到目前为止,数据存储在诸如connection = cable,device = android等字符串中。我不确定这会如何影响性能。

再次感谢。

3 个答案:

答案 0 :(得分:4)

编辑:之前我说不要使用分区。但比尔说他的描述方式是正确的。你唯一关心的是如果你试图在101个分区中进行选择,那么整个事情就会陷入停顿。如果您不打算这样做,那么分区可以解决问题。首先修复索引。

你的主要问题是MyISAM不是最好的引擎,也不是InnoDB。 TokuDB是你最好的选择,但你必须在服务器上安装它。

现在,您需要修剪索引。这是缓慢的主要原因。删除不属于常见SELECT语句的所有内容的索引。在WHERE语句的SELECT中准确添加多列索引。

因此(根据您的主键),根据您发布的datetime, device语句,您希望SELECT上的索引仅作为多列索引。

如果您更改为TokuDB,插入速度会快得多,如果您坚持使用MyISAM,那么您可以使用INSERT DELAYED代替INSERT加快整体速度。唯一的问题是插入不会生效,但只要MySQL决定没有太多负载就会添加。

或者,如果上述方法仍无效,那么您的最终选择是使用两个表格。您SELECT的一个表格,以及INSERT的另一个表格。大约一天后,您可以将插入表复制到选择表。虽然这意味着您的选择表中的数据最长可达24小时。

除此之外,你必须彻底改变表结构,我无法告诉你该怎么做,因为它完全取决于你使用它的是什么,或者使用MySQL以外的东西。但是,我的上述优化应该有效。

答案 1 :(得分:3)

我建议调查分区。由于MySQL的限制,您必须将datetime添加到主键才能使其正常工作。主键或唯一键必须包含用于对表进行分区的列。

同时将datetime上的索引转换为(datetime, device)上的复合索引。这将是您显示的查询的覆盖索引,因此查询可以单独从索引获得答案,而无需触摸表行。

CREATE TABLE `visits` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `datetime` int(8) NOT NULL,
 `webmaster_id` char(18) NOT NULL,
 `country` char(2) NOT NULL,
 `connection` varchar(15) NOT NULL,
 `device` varchar(15) NOT NULL,
 `provider` varchar(100) NOT NULL,
 `ip_address` varchar(15) NOT NULL,
 `url` varchar(300) NOT NULL,
 `user_agent` varchar(300) NOT NULL,
 PRIMARY KEY (`id`, `datetime`), -- compound primary key is necessary in this case
 KEY `datetime` (`datetime`,`device`), -- compound index for the SELECT
 KEY `webmaster_id` (`webmaster_id`),
 KEY `country` (`country`),
 KEY `connection` (`connection`),
 KEY `device` (`device`),
 KEY `provider` (`provider`)
) ENGINE=InnoDB
PARTITION BY HASH(datetime) PARTITIONS 101;

因此,当您查询select count(*) from visits where datetime=20140715 and device='ios'时,您的查询仅扫描一个分区,其中约有1%的行在表中。然后在该分区内,使用索引进一步缩小范围。

插入也应该改进,因为它们正在更新更小的索引。

我在进行散列分区时使用素数,以便在插入日期遵循常规模式的情况下帮助分区更均匀地填充。

将90GB表转换为分区需要很长时间。您可以使用pt-online-schema-change来避免阻止您的申请。

如果需要,你甚至可以制作更多的分区,理论上MySQL5.5中的1024和MySQL 5.6中的8192。虽然有数千个分区,但您可能遇到不同的瓶颈,例如打开文件的数量。

P.S。:HASH索引不受MyISAM或InnoDB的支持。 HASH索引仅受MEMORY和NDB存储引擎支持。

答案 2 :(得分:2)

您现在遇到了一个名为大数据查询/大数据处理的问题。对于处理大数据,有许多解决方案可用,但遗憾的是,它们都不容易实现。您始终需要一个团队来构建大数据以满足您的需求。我在这里定义的一些解决方案是Under。 1.大桌子  谷歌使用这种技术创建了一个包含数千列的大量表格。(最小化垂直记录)。为此,您必须分析数据,然后根据相似性进行分区,然后使用适当的名称标记这些相似性。现在,您必须编写将首先由某种算法分析的Query,以检查必须查询的列空间。 不够简单 2.跨多台机器分发数据库    Hadoop文件系统是一个开源Apache项目,完全用于解决存储和查询大数据的问题。在早期,太空问题,系统足以处理小数据,但现在空间不是问题。甚至小型组织也有本地存储的tera字节数据。但是这个TB级的数据无法在一台机器上一次性处理。即使是巨型机器也可能需要数天时间来处理集合操作。这就是hadoop在那里的原因。

如果你是个人,那么你肯定遇到麻烦,你需要资源为你做这个痛苦的任务。但是,您可以在不使用这些技术的情况下使用这些技术的本质。
您可以自由尝试这些技术。只研究有关处理大数据的文章。关系数据库查询不适用于您的情况