注意:
PHP的片段仅有助于说明此问题的架构,问题主要针对MySQL。
问题:
我遇到了加速将数据插入数据库的问题。数据量从几千行到几百万不等。这些数据需要尽快插入。
这是背景:
我创建了一个小型库,用于加载包含文件系统详细信息的文件。 该文件作为CSV文件逐行读取,然后处理列并将其插入生成的表中。
创建表格
这是一个使用Doctrine2的创作,但它应该是相当自我解释的。
$schema = new Schema();
$table = $schema->createTable($tableName);
$table->addOption('engine', 'MyISAM');
$table->addColumn('id', 'integer', array('unsigned' => true, 'autoincrement' => true));
$table->addColumn('path', 'string', array('length' => 255));
$table->addColumn('name', 'string', array('length' => 255));
$table->addColumn('pathname', 'string', array('length' => 255));
$table->addColumn('atime', 'integer');
$table->addColumn('mtime', 'integer');
$table->addColumn('is_dir', 'boolean');
$table->addColumn('length', 'integer');
$table->setPrimaryKey(array('id'));
$queries = $schema->toSql($this->conn->getDatabasePlatform());
foreach($queries as $query) {
$this->conn->executeQuery($query);
}
首次尝试:
在插入过程中进行优化之前,表创建还包括这些索引。这些是我需要添加的索引。
$table->addUniqueIndex(array('pathname'), 'IDX_PATHNAME');
$table->addIndex(array('path'), 'IDX_PATH');
$table->addIndex(array('name'), 'IDX_NAME');
$table->addIndex(array('atime'), 'IDX_ACCESSED_TIME');
$table->addIndex(array('mtime'), 'IDX_MODIFIED_TIME');
$table->addIndex(array('is_dir'), 'IDX_IS_DIR');
$table->addIndex(array('length'), 'IDX_LENGTH');
$queries = $schema->toSql($this->conn->getDatabasePlatform());
foreach($queries as $query) {
$this->conn->executeQuery($query);
}
$stmt = $this->conn->prepare(sprintf('CREATE FULLTEXT INDEX IDX_PATHNAME_FULLTEXT ON %s (pathname)', $this->table));
$stmt->execute();
当我插入超过200,000行的任何内容时,通常Mysql会慢慢爬行,PHP可能会耗尽内存(不确定原因)。
插入后的索引:
我在这里读到:Is it better to create an index before filling a table with data, or after the data is in place? 在插入更快插入后创建索引。
幸运的是,一旦数据插入一次,就不需要再次修改了(除了不再需要时删除表),所以添加索引后插件非常适合我。
$diff = new TableDiff($this->table);
$indexes = array(
new Index('IDX_PATHNAME', array('pathname'), true),
new Index('IDX_PATH', array('path')),
new Index('IDX_NAME', array('name')),
new Index('IDX_ACCESSED_TIME', array('atime')),
new Index('IDX_MODIFIED_TIME', array('mtime')),
new Index('IDX_IS_DIR', array('is_dir')),
new Index('IDX_LENGTH', array('length')),
);
$diff->addedIndexes = $indexes;
$this->schemaManager->alterTable($diff);
$stmt = $this->conn->prepare(sprintf('CREATE FULLTEXT INDEX IDX_PATHNAME_FULLTEXT ON %s (pathname)', $this->table));
$stmt->execute();
希望的微光:
我测试了这个并且插入过程非常快,即使有超过200万行,而且PHP内存甚至没有超过2.5%,而Mysql仅使用了大约8%(4GB RAM Xubuntu 64bit)。
砖墙:
一旦我添加了脚本以在插入后用索引更新表格,我的努力就被打败了。虽然它一直持续到完成(因为它没有崩溃或冻结这是一个加号),但它仍然花费与开始时添加索引的时间大致相同。
我现在正在寻找优化架构或插入订单的方法,以期更快地索引表。
答案 0 :(得分:0)