【发布时间】:2014-08-06 05:19:32
【问题描述】:
注意:
PHP的sn-ps仅帮助说明本题的架构,本题主要针对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();
一线希望:
我对此进行了测试,插入过程非常快,即使有 2+ 百万行,PHP 内存甚至不超过 2.5%,Mysql 仅使用大约 8%(4GB RAM Xubuntu 64 位)。
砖墙:
一旦我添加了脚本以在插入后使用索引更新表,我的努力就失败了。尽管它一直持续到完成(因为它没有崩溃或冻结,这是一个优点),但它仍然花费了与开始添加索引时大致相同的时间。
我现在正在寻找优化架构或插入顺序的方法,以期更快地为表建立索引。
【问题讨论】:
标签: php mysql performance optimization