miércoles, 15 de julio de 2009

¿Cuál es el mejor tipo de datos para almacenar IPs?

Esta pregunta surgió en Oracle Wiki, foro en el cual habitualmente colaboro.

"Alguien ha desarrollado un tipo datos para IP address? Necesito que sea indexable y que pueda usar operaciones OR, AND, XOR para chequear que una IP está en el rango especificado por la red y la máscara."

Como todos sabemos, las IPs están compuestas por 4 números que van de 0 a 255, separados por un punto. Esta es una representación posible, la que hace fácil su lectura en términos de redes. Sin embargo, también podemos convertirlas a notación binaria, decimal o hexadecimal.
El modo que elijamos va a beneficiar un aspecto u otro, dependiendo el uso y aplicación que le estemos dando a la información.

Algunas formas de almacenar IPs

Hay muchas maneras de almacenar direcciones IP, y nos bastan los tipos de datos nativos que Oracle ofrece. Estas son las más comunmente utilizadas.

Notación de Punto
Es el típico formato de IP que todos conocemos.
Ejemplo: 127.0.0.1
Tipo de datos sugerido: VARCHAR2(15)
Ventajas: Fácil de leer y recuperar, listo para reportes relacionados a redes.
Desventajas: Requiere convertir para poder usar algebra booleana, no muy eficiente en uso de espacio.


Hexadecimal
Es el formato de IP removiendo los puntos y pasando cada componente a hexa.
Ejemplo:
7F000001
Tipo de datos sugerido: VARCHAR2(8)
Ventajas: Fácil de convertir a los otros modos, ocupa menos espacio que la notación de punto.
Desventajas: Requiere conversión para lectura y operaciones booleanas.


Decimal
Es la representación interna de la IP. Usado comúnmente por browsers y algunos protocolos, es la representacion decimal del formato binario.
Ejemplo:
2130706433 (equivale a 127.0.0.1)
Tipo de datos sugerido: NUMBER(10)
Ventajas: Uso de espacio muy optimizado, no requiere conversión para integración con algunos protocolos.
Desventajas: Es el formato más ilegible (para los humanos) de todos.

Nota: Multiplicando cada componente de la IP por una potencia 2^24, 2^16, 2^8 y 2^0, y sumándolos, obtendremos el entero equivalente.
Hagan la prueba de convertir una IP y colocarla en el browser!

Notación Binaria
Podemos tomar cada número de la representación por punto y convertirlo a un octeto binario, completando con ceros a la izquierda.
Ejemplo: 01111111000000000000000000000001 (equivale a 127.0.0.1)
Tipo de datos sugerido: VARCHAR2(32)
Ventajas: Listo para algebra booleana, fácil de convertir a otros formatos.
Desventajas: Poco legible, uso muy ineficiente de espacio.


Conclusiones

No hay formatos malos ni buenos, sólo que algunas opciones serán mejores que otras a la hora de decidir. La idea es minimizar ese impacto según nuestro objetivo.
  • El formato de punto es óptimo para lectura de redes, ya que con una vista rápida es posible consultar direcciones IP directamente y presentar reportes sin esfuerzo. Por su claridad, es recomendado para el desarrollo de aplicaciones que registran logs de tráfico.
  • El formato hexadecimal es una variante para intentar mejorar el uso de espacio, ya que solamente 8 bytes serán necesarios. Por otro lado, requiere convertir cada par para obtener el formato tradicional y hacerlo más comprensible en lenguaje de red.
  • El formato decimal es el más eficaz utilizador de espacio. En sistemas real-time, BD embebidas y dispositivos móviles, el uso de espacio es crítico. Nótese que el tipo sugerido ocuparía 6 bytes en el peor caso, contra 8 fijos que utiliza la forma hexadecimal.
  • Finalmente el formato binario es para programadores 'perezosos' que validan IPs, ya que no requieren convertirla. Por ejemplo, si nuestra IP es de tipo A, B o C, realizando la operación (IP) AND (Netmask correspondiente), obtenemos el ID de red. Del lado de las desventajas, ademas de ser difícil de leer, es un gran derrochador de espacio.

1 comentario:

Anónimo dijo...

pfff...

falto el CHAR(4)...

create table tmp_tmp ( x char(4) );

insert into tmp_tmp values (chr(127)||chr(0)||chr(0)||chr(1));

select ascii(substr(x,1))||'.'||ascii(substr(x,2))||'.'||ascii(substr(x,3))||'.'||ascii(substr(x,4)) from tm_tmp ;