我知道这个问题一直被问到。但是,对于非常具体的情况,这是一个非常具体的问题。希望你能帮助我。
我运行一个记录数据库,大约有10个表。存储实际日志条目的主表有大约30个字段,其中5个是可搜索的。我说数据库最近变得中等大小,因为我们在该表中达到了2亿条目。其他表存储公共数据,其中最大的一个有4个字段,全部可搜索,有近100万个条目。所有其他表每个包含少于10万条记录。
插入物出现峰值。我每天凌晨2点收到前一天(格式很差的)csv文件的日志,直到上午8点才将它们(大约20个文件,每个10万行)插入到数据库中。然后我在工作日期间得到的选择很少(可能每天约1000个)。然后冲洗并重复。
SELECT查询非常简单,因为它们主要包含一个或两个带有一个或两个GROUP BY语句的连接。搜索此数据库的人希望立即获得结果,因此我在主表中有5个多列索引,这有助于我的精确搜索,目前,SELECT性能非常好。到目前为止,没有查询超过0.1秒。有一些报道,但这些报告大约需要10秒才能生成,这是可以接受的。
目前,我编写了一个C程序,用于读取CSV文件中的数据,清理它,并按INSERT查询分批插入1000行。这些INSERT并不完全愚蠢,因为我需要获取公共数据,看看它是否已经存在于其他表中,如果不存在则插入它,如果存在则将其缓存。它还以每秒插入的记录数量的形式提供了性能数据。这个程序非常快,没有将数据发送到数据库,我每秒大约有10万行。当然,这个程序和数据库位于同一台物理计算机上。
现在,我每天得到的数据呈线性增长,INSERT的性能呈对数下降趋势。昨天的数据需要5个半小时才能插入,每秒大约插入400行。
我通过将具有不同配置的前100万行插入到空数据库中获得了一些基准数据,这就是我所得到的:
MyISAM表:从每秒1500行开始,在插入第100万行时以对数方式减少到每秒约700行 InnoDB表:与MyISAM相同,每秒只有大约100行 在主表上禁用所有索引的InnoDB:以每秒2100行开始,每秒减少到1000行。 InnoDB使用索引,文件系统安装了数据写回(ext3):与InnoDB相同,只是略微但几乎无法显着加快。
innodb_buffer_pool_size设置为1000MB
避免创建索引不是一种选择,但很明显它对性能有很大影响。但是,我需要更快的插入。如数据所示,随着数据库的增长,插入将花费更长的时间,因此每天获得的数据越来越大,我需要在插入性能方面取得巨大飞跃。如果我能够达到每秒10000次插入或更多,那将非常棒。
系统监视器告诉我我的主要资源消耗是磁盘I / O,插入时几乎达到100%。因此,我需要一种超快速的方式来插入数据。我的理论限制是SATA总线,但这仍然很遥远。内存使用量似乎没有那么高,约为20%(或MySQL没有正确使用内存)
要实现这一点,可以在几天内重新创建数据库,然后从阅读器应用程序进行热交换,可以更改操作系统和MySQL中的任何设置,如果可以接受添加内存需要。如有必要,甚至可以更改数据库结构。
所以我对这里的想法非常开放。谁知道有什么可以帮助我?
编辑:我正在考虑在MEMORY表中插入新行,然后在真实表中执行SELECT INTO。希望它只会在插入所有行后更新并刷新索引。我会在星期一试试。有人曾尝试这样的事吗?
答案 0 :(得分:7)
经过一整天的大量小事,我构建了一件巨大的东西。最重要的是,我将插入性能提高了大约8倍,达到每秒近10000条记录。
以下是我所做的事情:
重写加载程序。我说它是在C中,但它实际上是在C ++中。使用mmap将字符串更改为char *,fstream,以及其他类似的东西,我的性能几乎翻了一番。 (很多人仍然声称C ++与C一样快,或者比C更快。我甚至不想在C#/ Java中尝试这个)
我找到了这个页面: http://kevin.vanzonneveld.net/techblog/article/improve_mysql_insert_performance/ 这是一个很好的资源(我不隶属于他们),这几乎解释了我将要尝试的所有内容,以及各种各样的结果。实际上,唯一可以超级插入性能的是使用LOAD DATA INFILE。调整我的表结构,所以我可以像这样插入几乎四倍!插入的表现。
我在ON DUPLICATE KEY UPDATE中使用复杂表达式重写了无法使用LOAD DATA INFILE执行的插入(大量插入(每插入命令几行),而不是为每一行执行SELECT / INSERT。这也提供了非常好的性能提升。这也需要对表结构进行一些修改。
重新创建已经超过20亿行的数据库时,创建表,这些表获取没有索引的LOAD DATA INFILE插入,并在完成后重新创建它们。我的所有基准测试表明,没有索引的插入时间加上创建它们的时间比插入带索引的表的时间要短。差异并不大,但却很明显(大约快了1.2倍)。我认为B树也会以这种方式更好地平衡。
使用MyISAM。我以前的基准测试并不是那么有用,但是当使用LOAD DATA INFILE时,InnoDB每次都会丢失。在本地测试,我在MyISAM /无索引中获得大约16000条记录/秒,在MyISAM /索引中获得12000条记录/秒,在InnoDB /无索引中获得9000条记录/秒,在InnoDB /索引中获得大约7500条记录/秒。 MySQL版本是5.1.47。
对于LOAD DATA INFILE的文件,在tmpfs挂载的分区中创建它们。这也是一个巨大的性能提升,特别是因为您需要编写写入文件并将其刷新到磁盘,因此MySQL可以读取它。如果无法使用此tmpfs,则应该可以使用命名管道来执行此操作。
经验教训:当MySQL速度很慢时,你很可能通过改变你的代码来做更多的事情,而不是获得更强大的硬件。
祝你好运,谢谢大家的帮助。答案 1 :(得分:3)
6,5小时内200万行? 您存储的数据集有多大?
我使用以下背后的计算来得出一个有用的数字:
假设1
单个蹩脚磁盘每秒吞下35
mb,您应该能够在该时间范围内写入(35 * 6,5 * 3600)=〜800 gb
。向后计算(800 gb / 2 mrows),得出平均行大小为400
kb。
如果这些数字看似合适,您需要加强硬件以提高速度。如果它们完全关闭,可能还有其他一些问题。
另外,请查看ServerFault上的comparisons of disk i/o for a dedicated MySQL server,了解I / O的测量方法。
以下是一些随机建议(如果您怀疑其他问题)
<强>被修改强>
更正计算(400kb)
答案 2 :(得分:1)
在提到磁盘I / O时,你点击它。如果您的磁盘最多使用插入,除非您升级,否则您不会更快。您没有提到是否可以接受进行磁盘升级,但我会考虑使用基于SCSI或闪存的磁盘。即使你没有达到SATA的总线限制,你的磁盘绝对是瓶颈。
答案 3 :(得分:1)
我会尝试增加innodb缓冲池大小,看看会发生什么。对于Innodb,我还会使用innodb_flush_log_at_trx_commit = 0(或= 2)禁用永久刷新。默认设置为1,这是写密集型工作负载的瓶颈。 0或2将在刷新之间给出1秒的延迟。您还可以使用事务执行更大的批处理(如果您没有明确地使用事务,那么每个插入都是它自己的事务)。
如前所述,预排序输入(通过主键)可以通过消除页面加载中的随机性来帮助减少缓冲池中的数据量。
以上所有都与innodb有关。