为什么我的自定义MySQL函数比查询中的内联函数慢得多?

时间:2011-08-12 00:05:37

标签: mysql performance user-defined-functions

我反复使用这个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秒才能执行。

我认为creating a custom stored function用于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倍似乎过度。 预计会这么慢吗?

我在Ubuntu 10.10上使用现成的MySQL服务器5.1而没有更改配置。


要重现我的测试,请创建一个表并填充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 个答案:

答案 0 :(得分:21)

请勿重新发明轮子,请使用INET_NTOA()

mysql> SELECT INET_NTOA(167773449);
    -> '10.0.5.9'

答案 1 :(得分:11)

使用这个可以获得更好的性能:

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秒(四核上的2gB mysql 5.1实例(fedora 64位)。

编辑: 期待这么慢吗?

是的,存储过程很慢,比解释/编译代码慢一堆。当你需要绑定一些你想要远离你的应用程序的数据库逻辑时,它们变得很有用,因为它超出了特定的域(即日志/管理任务)。如果存储的函数不包含任何查询,那么以您选择的语言编写实用程序函数总是更好的做法,因为它不会阻止重用(没有查询),并且运行速度会快得多。

这就是为什么在这种特殊情况下,你应该使用INET_NTOA函数,这可以满足你的需求,如sanmai的回答所示。