Почему версия PHP работает быстрее, чем MySQL - PullRequest
7 голосов
/ 18 мая 2011

У меня есть две очень большие таблицы для объединения, поэтому я пытался оптимизировать скорость обновления.Я заметил, что выполнение обновления частично в PHP значительно ускорило его, поэтому я предположил, что это означает, что я не буду правильно работать с MySQL.

Я упростил задачу, чтобы попытаться сузить ее ...

GRID_TABLE                                  POSTCODE_TABLE
idNo, lat,  lng,  nearestPostcode           postcode,  lat,   lng
________________________________            _____________________
1     57.1  -2.3  -                         AB12 3BA   56.3  -2.5
2     56.8  -1.9  -                         AB12 1YA   56.2  -2.3
. . .                                       . . .

(200 entries)                               (35,000 entries)

Я хочу обновить GRID_TABLE с помощью ближайшего почтового индекса из POSTCODE_TABLE, используя широту (широту) и долготу (lng), чтобы найти ближайший почтовый индекс для каждой точки сетки ...

update grid_table set nearestPostcode = (
    select postcode from postcode_table 
    where lat > grid_table.lat -0.0037 and lat < grid_table.lat +0.0037 
        and lng > grid_table.lng -0.0068 and lng < grid_table.lng +0.0068
    order by POW(lat - grid_table.lat,2) + POW((lng - grid_table.lng) *0.546,2) 
    limit 1 
    )

Идея состоит в том, что предложение 'where' ускоряет поиск, используя индексы, чтобы сузить набор до нескольких кандидатов, а затем предложение 'order by' находит ближайший в этом наборе.

Это обновление MySQLзанимает 30 секунд, но если я вместо этого обновлю каждую строку GRID_TABLE отдельно в PHP, это будет в мгновение ока.

$queryStg = "select * from grid_table ;";
$sqlQuery1 = mysqli_query($mysqliLink, $queryStg);

while( $sqlRow = mysqli_fetch_assoc( $sqlQuery1 ) ) {

    $idNo = $sqlRow['idNo'];
    $lat = $sqlRow['lat'];
    $lng = $sqlRow['lng'];

    $queryStg = "
        update grid_table
            set nearestPostcode = (
                SELECT postcode
                FROM postcode_table
                where
                    lat > " . ($lat - 0.0037) . " and
                    lat < " . ($lat + 0.0037) . " and
                    lng > " . ($lng - 0.0068) . " and
                    lng < " . ($lng + 0.0068) . "
                ORDER BY
                    POW(lat - $lat, 2) +
                    POW((lng - $lng) * 0.546, 2)
                    ASC
                limit 1
                )
            where idNo= $idNo;
        ";

    $sqlQuery2 = mysqli_query($mysqliLink, $queryStg);

}

Конечно, версия MySQL должна быть быстрее, чем версия PHP?

Вот MySQL для таблиц ...

CREATE TABLE `grid_table` (
    `idNo` INT(11) NOT NULL AUTO_INCREMENT,
    `lat` FLOAT(6,4) NOT NULL COMMENT 'latitude',
    `lng` FLOAT(6,4) NOT NULL COMMENT 'longitude',
    `nearestPostcode` CHAR(8) NOT NULL,
    PRIMARY KEY (`idNo`),
    INDEX `lat_lng` (`lat`, `lng`)
)
ENGINE=MyISAM
ROW_FORMAT=DEFAULT
AUTO_INCREMENT=30047
CREATE TABLE `postcode_table` (
    `postcode` CHAR(8) NOT NULL,
    `lat` FLOAT(6,4) NOT NULL COMMENT 'latitude',
    `lng` FLOAT(6,4) NOT NULL COMMENT 'longitude',
    PRIMARY KEY (`postcode`),
    INDEX `lat` (`lat`),
    INDEX `lng` (`lng`),
    INDEX `lat_lng` (`lat`, `lng`)
)
ENGINE=MyISAM
ROW_FORMAT=DEFAULT

Файл импорта MySQL находится здесь ... https://docs.google.com/leaf?id=0B93lksnTC7_cM2Y2ZDk1Y2YtMGQ3Yy00OTIxLTk0ZDAtZmE2NmQ3YTc1ZWRm&hl=en

(если вы запуститеОБНОВЛЕНИЕ, будет добавлено 10 ближайших почтовых индексов).

ОБНОВЛЕНИЕ ПОСЛЕ ОТВЕТА ...

Я запустил это ...

explain extended
 SELECT postcode FROM postcode_table 
 where lat > 57.0 and lat < 57.0074
 and lng > -2.013 and lng < -2
 ORDER BY POW(lat - 57.0, 2) + POW((lng - -2) * 0.546, 2) ASC 

Возвращено ...

id,select_type,table,type,possible_keys,key,key_len,ref,rows,filtered,Extra
1,SIMPLE,postcode_table,range,lat,lng,lat_lng,lat_lng,8,NULL,65,100.00,Using where; Using filesort

Удаление команды 'order by' -> нет разницы в скорости.

Упрощение предложения 'where' путем удаления 'lng', то есть

where lat between grid_table.lat - 0.0037 and grid_table.lat + 0.0037 
-> быстрее: 3 секунды, а не30 секунд.

Использование пространственного столбца и индекса (см. Ниже) -> намного медленнее (190 секунд).Не уверен, правильно ли я это реализовал.

ALTER TABLE `grid_table` ADD COLUMN `coords` POINT NOT NULL;
update grid_table set coords = POINT(lat, lng);
ALTER TABLE `grid_table` ADD SPATIAL INDEX `coords` (`coords`);

ALTER TABLE `postcode_table` ADD COLUMN `coords` POINT NOT NULL;
update postcode_table set coords = POINT(lat, lng);
ALTER TABLE `postcode_table` ADD SPATIAL INDEX `coords` (`coords`);

analyze table grid_table;
optimize table grid_table;
analyze table postcode_table;
optimize table postcode_table;
update grid_table set nearestPostcode = (
    select postcode from postcode_table 
    WHERE MBRContains(GeomFromText(concat(
         'POLYGON((', 
          grid_table.lat - 0.0037, ' ', grid_table.lng - 0.0068, ', ',
          grid_table.lat - 0.0037, ' ', grid_table.lng + 0.0068, ', ',
          grid_table.lat + 0.0037, ' ', grid_table.lng - 0.0068, ', ',
          grid_table.lat - 0.0037, ' ', grid_table.lng - 0.0068, 
          '))')), postcode_table.coords)
     order by POW(lat - grid_table.lat,2) + POW((lng - grid_table.lng) *0.546,2)
     limit 1 
     )

Ответы [ 3 ]

4 голосов
/ 18 мая 2011

В вашей версии MySQL ваш подзапрос работает со всеми 30000 записями grid_table, будь то в вашей версии PHP - только один.Когда вы добавляете куда во внешней таблице PK.

, я предлагаю вам изменить запрос на обновление.Например, попробуйте сделать это без подзапроса, несколько обновлений, как здесь http://dev.mysql.com/doc/refman/5.0/en/update.html.

Я считаю, что это должно помочь.

Что-то вроде:

update grid_table, postcode_table
set grid_table.nearestPostcode = postcode_table.postcode
where postcode_table.lat > grid_table.lat - 0.0037
and postcode_table.lat < grid_table.lat + 0.0037 
and postcode_table.lng > grid_table.lng - 0.0068
and lng < grid_table.lng + 0.0068
group by grid_table.idNo
having (POW(lat - grid_table.lat,2) + POW((lng - grid_table.lng) *0.546,2)) = min(POW(lat - grid_table.lat,2) + POW((lng - grid_table.lng) *0.546,2))

Может бытьэта версия может помочь, но я не уверен.Я предполагаю, что основная корневая проблема в вашей первой версии - это подзапрос для всех записей.

Чтобы объяснить обновление , вы можете "преобразовать" его в аналогичный выбор:

explain
select
    *,
    (
        select postcode from postcode_table
        where lat > grid_table.lat -0.0037 and lat < grid_table.lat +0.0037
            and lng > grid_table.lng -0.0068 and lng < grid_table.lng +0.0068
        order by POW(lat - grid_table.lat,2) + POW((lng - grid_table.lng) *0.546,2)
        limit 1
    ) nearestPostcode   
from grid_table

И вы увидите:

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   PRIMARY grid_table  ALL                 224 
2   DEPENDENT SUBQUERY  postcode_table  ALL lat,lng,lat_lng             35605   Using where; Using temporary; Using filesort

Но в случае idNo у нас есть:

explain
select
    *,
    (
        select postcode from postcode_table
        where lat > grid_table.lat -0.0037 and lat < grid_table.lat +0.0037
            and lng > grid_table.lng -0.0068 and lng < grid_table.lng +0.0068
        order by POW(lat - grid_table.lat,2) + POW((lng - grid_table.lng) *0.546,2)
        limit 1
    ) nearestPostcode   
from grid_table
where idNo = 1487;

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   PRIMARY grid_table  const   PRIMARY PRIMARY 4   const   1   
2   DEPENDENT SUBQUERY  postcode_table  range   lat,lng,lat_lng lat 4       18  Using where; Using filesort

Итак, у нас 35605 строк против ~ 18 * 224 (~ 4000).

Чтобы найти правильный запрос, попробуйте найти хороший, выберите 1-й.

Обновление

Подзапрос здесь не является корневым :( Поэтому я думаю, что мы должны попробоватьможет быть какой-то предварительно рассчитанный + проиндексированный столбец. Цель - избежать order by SOMEFUNC()

0 голосов
/ 18 мая 2011

Я предполагаю, что разница заключается в том, что вы указали значение $lat в запросе строка за строкой, что позволило сохранить большие просмотры для поиска здесь: -

order by POW(lat - grid_table.lat,2)

Как говорит г-н 47, вы сможете увидеть, Объяснив операторы SQL.

0 голосов
/ 18 мая 2011

Посмотрите на план выполнения, чтобы узнать, что занимает так много времени.http://dev.mysql.com/doc/refman/5.5/en/execution-plan-information.html

...