1

我正在尝试减少此脚本的执行时间。它在一个循环中查询大约 1000 次大约 200 万条记录的数据库:

foreach ($ids as $id){
    $stmt=$dbh->query("SELECT SQL_CACHE * FROM `ids` 
                WHERE $id BETWEEN `id_start` AND `id_end`");
    $rows[] = $stmt->fetch();
}

在 4 核 8 GB 机器上需要很长时间(大约 800 秒!)。id 组不重叠,id 在每次执行中往往来自几个不同的组,我已经索引了 (id_start,id_end) 和 (id_end)。

缓存极大地改善了这种情况(多次运行相同的 1000 个值只需几秒钟),但我想知道如何加快非缓存查询的速度。

EXPLAIN 的示例输出:

"id"    "select_type"   "table"     "type"  "possible_keys"     "key"               "key_len"   "ref"   "rows"  "Extra"
"1"     "SIMPLE"        "ids"       "range" "id_start,id_end"   "id_start,id_end"   "5"          ""     "52508" "Using index condition"

编辑:而不是“使用索引条件”,我有时会得到“使用哪里”(不确定,但我认为来自高于 840771583 的 id 值)为什么?

编辑 2:完整的创建代码:

CREATE TABLE `ids` (
    `id_start` INT(10) UNSIGNED NULL DEFAULT NULL,
    `id_end` INT(10) UNSIGNED NULL DEFAULT NULL,
    `iso-639-1` VARCHAR(2) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
    `country_name` VARCHAR(64) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
    `region_name` VARCHAR(64) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
    `city_name` VARCHAR(64) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
    `area_code` VARCHAR(16) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
    `timezone` VARCHAR(6) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
    UNIQUE INDEX `id_startid_end` (`id_start`, `id_end`),
    INDEX `id_end` (`id_end`),
    INDEX `country_name` (`country_name`),
    INDEX `region_name` (`region_name`),
    INDEX `city_name` (`city_name`),
    INDEX `area_code` (`area_code`),
    INDEX `iso-639-1` (`iso-639-1`),
    INDEX `timezone` (`timezone`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB;
4

2 回答 2

4

由于间隔不重叠,请尝试重写查询:

SELECT * 
FROM ids
WHERE id_start =
      ( SELECT MAX(id_start) 
        FROM ids
        WHERE id_start <= $id
      )
  AND $id <= id_end ;
于 2013-09-06T10:34:00.527 回答
1
It queries a database with about 2 million records about 1000 times on a loop:
                                                         ^^^^^^^^^^^^^^^^^^^^   

这里

是你的问题。

它肯定应该是一个单一的查询。

还可以考虑调整 mysql 守护进程,以确保密钥缓冲区大小是否足够。

顺便说一句,这个问题与 PDO 完全无关。处理问题时,您必须尽可能缩小范围,去掉所有不必要的部分。说到查询,您必须将其带入控制台并在那里播放。

于 2013-09-06T07:04:47.660 回答