Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my custom MySQL function so much slower than inlining same in query?

Tags:

I repeatedly use this SELECT query to read unsigned integers representing IPv4 addresses and present them as human readable dotted quad strings.

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; 

With my test data, this query takes 3.6 seconds to execute.

I thought that creating a custom stored function for the int->string conversion would allow for easier to read queries and allow reuse, so I made this:

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)   ); 

With this function my query looks like this:

SELECT IntToIp(ip) FROM ips; 

but with my test data, this takes 13.6 seconds to execute.

I would expect this to be slower on first run, as there is an extra level of indirection involved, but nearly 4 times slower seems excessive. Is this much slowness expected?

I'm using out of the box MySQL server 5.1 on Ubuntu 10.10 with no configuration changes.


To reproduce my test, create a table and populate with 1,221,201 rows:

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(); 
like image 201
Day Avatar asked Aug 12 '11 00:08

Day


Video Answer


2 Answers

Don't reinvent the wheel, use INET_NTOA():

mysql> SELECT INET_NTOA(167773449);     -> '10.0.5.9' 
like image 159
sanmai Avatar answered Oct 25 '22 08:10

sanmai


Using this one you could get better performance:

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) 

Launching your original SELECT just after adding your test data took 4.78 secs on my system (2gB mysql 5.1 instance on quad core (fedora 64 bit).

EDIT: Is this much slowness expected?

Yes, stored procedures are slow, a bunch of magnitudes slower than interpreted/compiled code. They turn out useful when you need to tie up some database logic which you want to keep out of your application because it's out of the specific domain (ie, logging/administrative tasks). If a stored function contains no queries, it's always better practice to write an utility function in your chosen language, as that wont prevent reuse (there are no queries), and will run much faster.

And that's the reason for which, in this particular case, you should use the INET_NTOA function instead, which is available and fulfils your needs, as suggested in sanmai answer.

like image 25
guido Avatar answered Oct 25 '22 09:10

guido