我有一位客户要求我调整他的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"
获取此内容吗?
除此之外,我决定用外表中的相应ID替换一些字段。到目前为止,数据存储在诸如connection = cable,device = android等字符串中。我不确定这会如何影响性能。
再次感谢。
答案 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在那里的原因。
如果你是个人,那么你肯定遇到麻烦,你需要资源为你做这个痛苦的任务。但是,您可以在不使用这些技术的情况下使用这些技术的本质。
您可以自由尝试这些技术。只研究有关处理大数据的文章。关系数据库查询不适用于您的情况