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

Day*_*Day 21 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;
Run Code Online (Sandbox Code Playgroud)

使用我的测试数据,此查询需要3.6秒才能执行.

我认为为int-> string转换创建自定义存储函数将允许更容易读取查询并允许重用,所以我这样做:

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)
  );
Run Code Online (Sandbox Code Playgroud)

使用此功能,我的查询如下所示:

SELECT IntToIp(ip) FROM ips;
Run Code Online (Sandbox Code Playgroud)

但是使用我的测试数据,这需要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();
Run Code Online (Sandbox Code Playgroud)

san*_*mai 22

不要重新发明轮子,使用INET_NTOA():

mysql> SELECT INET_NTOA(167773449);
    -> '10.0.5.9'
Run Code Online (Sandbox Code Playgroud)

  • 哇.RTFM FTW.+1用于加速我的特定情况的英里数,但接受的是guido谁回答了通用的"为什么功能比内联慢"问题. (3认同)
  • 为了与其他解决方案进行比较,使用这个内置函数只需0.7秒:`time mysql -u root test -e"SELECT INET_NTOA(ip)FROM ips">/dev/null` (3认同)

gui*_*ido 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)
Run Code Online (Sandbox Code Playgroud)

在添加测试数据后启动原始SELECT在我的系统上花了4.78秒(四核上的2gB mysql 5.1实例(fedora 64位).

编辑:预计这么慢吗?

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

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

  • 当然,对于任何读过这个被接受的答案的评论的人来说,在这个特定的情况下,你应该使用内置的INET_NTOA函数,如sanmai的回答中所建议的那样 (2认同)