【问题标题】:MySQL: Optimize adding of IndexMySQL:优化索引的添加
【发布时间】: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


    【解决方案1】:

    只需使用:LOAD DATA INFILE

    在此处阅读更多信息:http://dev.mysql.com/doc/refman/5.1/en/load-data.html

    这是加载 CSV 的最佳方法。

    【讨论】:

    • 文件需要在插入前进行处理。插入本身也不再是问题,而是添加索引。
    猜你喜欢
    • 2023-03-15
    • 2011-09-03
    • 2016-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-09
    • 2011-03-18
    相关资源
    最近更新 更多