Почему моя пользовательская функция MySQL намного медленнее, чем встроенная в запрос? - PullRequest
21 голосов
/ 12 августа 2011

Я неоднократно использую этот запрос SELECT для чтения беззнаковых целых чисел, представляющих адреса IPv4, и представления их в виде читаемых человеком точечных четырехугольных строк.

SELECT CONCAT_WS('.', 
  FLOOR(ip/POW(256,3)),
  MOD(FLOOR(ip/POW(256,2)), 256),
  MOD(FLOOR(ip/256), 256),
  MOD(ip, 256))
FROM ips;

С моими тестовыми данными этот запрос занимает 3,6 секунды. для выполнения.

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

CREATE FUNCTION IntToIp(value INT UNSIGNED)
  RETURNS char(15)
  DETERMINISTIC
  RETURN CONCAT_WS(
    '.', 
    FLOOR(value/POW(256,3)),
    MOD(FLOOR(value/POW(256,2)), 256),
    MOD(FLOOR(value/256), 256),
    MOD(value, 256)
  );

С этой функцией мой запрос выглядит следующим образом:

SELECT IntToIp(ip) FROM ips;

, но с моими тестовыми данными для выполнения требуется 13,6 секунд .

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

Я использую готовый сервер MySQL 5.1 на Ubuntu 10.10 без изменений конфигурации.


Для воспроизведения моего тестасоздайте таблицу и заполните 1 221 201 строк:

CREATE TABLE ips (ip INT UNSIGNED NOT NULL);

DELIMITER //
CREATE PROCEDURE AddIps ()
BEGIN
  DECLARE i INT UNSIGNED DEFAULT POW(2,32)-1;
  WHILE (i>0) DO
    INSERT INTO ips (ip) VALUES (i);
    SET i = IF(i<3517,0,i-3517);
  END WHILE;
END//
DELIMITER ;

CALL AddIps();

Ответы [ 2 ]

22 голосов
/ 12 августа 2011

Не изобретайте велосипед, используйте INET_NTOA () :

mysql> SELECT INET_NTOA(167773449);
    -> '10.0.5.9'
11 голосов
/ 12 августа 2011

С помощью этого вы можете получить лучшую производительность:

CREATE FUNCTION IntToIp2(value INT UNSIGNED)
  RETURNS char(15)
  DETERMINISTIC
  RETURN CONCAT_WS(
    '.', 
    (value >> 24),
    (value >> 16) & 255,
    (value >>  8) & 255,
     value        & 255
  );

> SELECT IntToIp(ip) FROM ips;
1221202 rows in set (18.52 sec)

> SELECT IntToIp2(ip) FROM ips;
1221202 rows in set (10.21 sec)

Запуск вашего исходного SELECT сразу после добавления ваших тестовых данных занял 4,78 с в моей системе (2 ГБ mysql 5.1 на четырехъядерном процессоре (Fedora 64 бит).

РЕДАКТИРОВАТЬ: Ожидается ли такая медлительность?

Да, хранимые процедуры работают медленно, на несколько порядков медленнее, чем интерпретируемый / скомпилированный код. Они оказываются полезными, когда вам нужно связать некоторую логику базы данных, которую вы хотите сохранить в своем приложении, потому что она находится вне конкретного домена (т. Е. Журналирование / административные задачи). Если хранимая функция не содержит запросов, всегда лучше написать вспомогательную функцию на выбранном вами языке, так как это не помешает повторному использованию (нет запросов) и будет выполняться намного быстрее.

И это причина, по которой в данном конкретном случае вам следует вместо этого использовать функцию INET_NTOA, которая доступна и удовлетворяет вашим потребностям, как указано в ответе sanmai.

...