MySQL против производительности PostgreSQL со сложными шаблонами сопоставления запросов - PullRequest
0 голосов
/ 17 апреля 2011

У меня сложная база данных, около 30 таблиц. Одна таблица содержит более 500 000 строк, а другая - более 15 000, и я использую обе в отдельной базе данных до сегодняшнего дня. Я решил внедрить ее только в одну базу данных.

До сегодняшнего дня таблица с 500 000 строк находилась в базе данных MySQL, а таблица с 15 000 строк - в PostgreSQL. На одной странице интенсивного использования это был результат теста PHP:

getSimilarAvaiable - 0.0287 s
getUnavaiable - 0.27 s
ProcessDataOfUnavaiable - 1.4701 s
Process - 1.8622 s
TotalPageTime - 3.631 s

После того, как я перенесу все на PostgreSQL и использую тот же код SQL без каких-либо изменений, результат на той же странице был такой:

getSimilarAvaiable - 2.7465 s
getUnavaiableCars - 9.0763 s
ProcesseDataOfUnavaiableCars - 1.4167 s
ProcessCars - 1.7207 s
TotalPageTime - 14.9602 s

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

РЕДАКТИРОВАТЬ : теперь лучше объяснить.

Таблица 500.00 состоит из следующей структуры:

id - bigint (primary key)
plate- varchar(10) Unique key
manufacturer - varchar(30)
vin - varchar(30)

Основной запрос выглядит примерно так:

SELECT plate, vin, 1 as n, substr(plate,1,2) as l 
FROM imtt_vin WHERE substr(plate,1,1) >= 'A' and substr(plate,1,1) <= 'Z' AND
(manufacturer ILIKE '%".self::$Manufacturer."%') AND vin LIKE ?
UNION
SELECT plate, vin, 3 as n, substr(plate,4,2) as l 
FROM imtt_vin WHERE substr(plate,4,1) >= 'A' and substr(plate,4,1) <= 'Z' AND
(manufacturer ILIKE '%".self::$Manufacturer."%') AND vin LIKE ?
UNION
SELECT plate, vin, 2 as n, substr(plate,7,2) as l 
FROM imtt_vin WHERE substr(plate,7,1) >= 'A' and substr(plate,7,1) <= 'Z' AND 
(manufacturer ILIKE '%".self::$Manufacturer."%') AND vin LIKE ?
ORDER BY n, l, plate;

EDIT2: протестирован со сложным одиночным запросом, и я сократил его с 15 до 8/9 секунд. Даже для меня это слишком.

Ответы [ 6 ]

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

Вам необходимо опубликовать EXPLAIN yourquery (для mysql) и EXPLAIN ANALYZE yourquery (для postgres);без этого невозможно сказать что-либо релевантное.

Также ВЫБЕРИТЕ pg_relation_size ('imtt_vin')

Например, каково значение "?"в этом запросе?

SELECT plate, vin, 1 as n, substr(plate,1,2) as l 
FROM imtt_vin WHERE substr(plate,1,1) >= 'A' and substr(plate,1,1) <= 'Z' AND
(manufacturer ILIKE '%".self::$Manufacturer."%') AND vin LIKE ?

Я не знаю о номерных знаках, где вы работаете, но эта часть:

WHERE substr(plate,1,1) >= 'A' and substr(plate,1,1) <= 'Z'

, вероятно, выбирает все строки в базе данных, поэтому его единственное назначение -записывать циклы процессора.Вы можете по крайней мере переписать его (и все остальные) следующим образом, чтобы избежать вызова substr ():

WHERE substr(plate,1,1) BETWEEN 'A' AND 'Z'

И, конечно, удалить условие, когда оно бесполезно.

Тогда мы имеем:

manufacturer ILIKE '%".self::$Manufacturer."%'

Плохой дизайн базы данных: есть ли в мире 500 000 производителей автомобилей?Возможно нет.Вы должны поместить производителей в другую таблицу и использовать внешний ключ.Это превратит это неиндексируемое условие в индексируемое.

В остальном, публикуйте EXPLAIN / EXPLAIN ANALYZE.

3 голосов
/ 17 апреля 2011

MySQL по умолчанию использует больше памяти. Я думаю, что он назначен для использования более 256 МБ при установке def. Не уверен в точном количестве. PostgreSQL по умолчанию настроен на использование чего-то вроде 32 МБ. Попробуйте увеличить до 1 ГБ оперативной памяти в конфигурационном файле, а затем запустить тесты и вернуться к нам.

3 голосов
/ 17 апреля 2011

Если бы вы использовали MyISAM в MySQL, теоретически можно было бы объяснить разницу в производительности (потому что не так много было сказано относительно дизайна вашей базы данных и выполненных запросов). Что касается кросс-производительности между двумя СУБД, я бы порекомендовал вам взглянуть на на этой странице сравнения (привязанной к разделу MyISAM).

2 голосов
/ 19 октября 2012

Запрос

(
SELECT 1 AS n, left(plate, 2) AS l, plate, vin
FROM   imtt_vin
WHERE  left(plate, 1) BETWEEN 'A' AND 'Z'
AND    manufacturer ILIKE '%".self::$Manufacturer."%'
AND    vin LIKE ?   -- You probably mean: vin = ?
ORDER  BY l, plate
)

UNION ALL
(
SELECT 3 AS n, substr(plate, 4, 2) AS l, plate, vin
FROM   imtt_vin
WHERE  substr(plate, 4, 1) BETWEEN 'A' AND 'Z'
AND    manufacturer ILIKE '%".self::$Manufacturer."%'
AND    vin LIKE ?
ORDER  BY l, plate
)

UNION  ALL ...
  • Использование UNION ALL. UNION будет использоваться для свертывания дубликатов, что, очевидно, здесь не так, и будет дороже.
  • Поскольку ваш ведущий элемент ORDER BY - n, возможно, более эффективно упорядочивать отдельные ветви запроса. Для этого необходим дополнительный набор скобок.
  • left (plate, 2) немного быстрее, чем substr(plate, 1, 2). Работает только для ведущих подстрок (ваша первая SELECT).

Индекс

Значение по умолчанию Индекс B-дерева работает только для выражений LIKE с левой привязкой . Но триграмма GiST или индекс GIN могут использоваться для паттернов без привязки к левому краю. Вам нужен дополнительный модуль pg_trgm. Установите один раз для каждой базы данных с CREATE EXTENSION в PostgreSQL 9.1 или новее Обратитесь к руководству для более старых версий.

CREATE EXTENSION pg_trgm;

У меня не так много информации, базовые частичные индексы GIN должны работать чудеса :

CREATE INDEX imtt_vin_partial_gist_idx ON imtt_vin
USING  gin (manufacturer gin_trgm_ops)
WHERE  left(plate, 1) BETWEEN 'A' AND 'Z';

CREATE INDEX imtt_vin_partial_gist_idx ON imtt_vin
USING  gin (manufacturer gin_trgm_ops)
WHERE  substr(plate, 4, 1) BETWEEN 'A' AND 'Z';

-- more ...
  • Я не включил vin в индекс, поскольку вам, вероятно, нужен оператор равенства =.
  • Предикаты по частичному индексу должны повторяться (более или менее) в запросах, чтобы планировщик запросов понимал, что индекс применим.
  • Индекс триграмм работает для нечувствительных к регистру совпадений.
  • Проверьте с помощью EXPLAIN ANALYZE, действительно ли используется индекс. Если это так, время запроса должно составлять миллисекунд , а не секунд.
  • Скорость достигается за счет (небольших) затрат на операции записи для обслуживания индекса. Индекс обычно в несколько раз превышает размер таблицы на диске.
  • Вы не можете делать ничего с MySQL.
2 голосов
/ 17 апреля 2011

Мне кажется, что вы, вероятно, не обновляли статистику в базе данных Postgres.При неправильной статистике база данных будет работать не очень хорошо.

1 голос
/ 18 апреля 2011

Вы все еще не предоставили достаточно информации - какие у вас есть индексы, EXPLAIN ANALYZE для медленных запросов и т. Д.

Некоторые мысли по оптимизации вашего примера запроса:

1: строковые функции UTF-8 обычно не очень быстрые. Если вы хотите ускорить строковые функции, используйте для этого столбца тип bytea вместо varchar (или измените кодировку всей базы данных на SQL_ASCII, но это не рекомендуется)

2: С учетом ваших запросов база данных, вероятно, должна пройти через все строки в таблице и вычислить эти строковые функции для каждой.

Я не знаю, сколько у них совпадений, поэтому индекс может оказаться бесполезным, но функциональные индексы могут вам помочь:

 CREATE INDEX imtt_vin_plate_1 ON imtt_vin (substr(plate,1,1));
 CREATE INDEX imtt_vin_plate_4 ON imtt_vin (substr(plate,4,1));
 CREATE INDEX imtt_vin_plate_7 ON imtt_vin (substr(plate,7,1));

3: Если вы можете допускать дублирующиеся выходные данные, используйте UNION ALL вместо UNION в ваших запросах - это сэкономит вам некоторую обработку с большими наборами результатов.

4: по возможности избегайте LIKE / ILIKE.

...