Artículo
· 1 jun, 2023 Lectura de 10 min

Pruebas de rendimiento IRIS - PostgreSQL - MySQL

Como antiguo programador de JAVA siempre ha sido un reto decidir qué base de datos era la más adecuada para el proyecto que íbamos a desarrollar, uno de los principales criterios que usaba era el del rendimiento de las mismas, así como sus capacidades de configuración HA (high availability o alta disponibilidad). Pues bien, es el momento de poner a prueba a IRIS respecto a algunas de las bases de datos más comunmente usadas, por eso me he propuesto elaborar un pequeño proyecto de Java basado en SpringBoot que conecte vía JDBC con una base de datos MySQL, otra de PostgreSQL y finalmente con IRIS.

Vamos a aprovechar que disponemos de imágenes de Docker de estas bases de datos para usarlas en nuestro proyecto y permitiros probarlo vosotros mismos sin necesitar de realizar ninguna instalación. Podemos comprobar la configuración de docker en nuestro archivo docker-compose.yml

version: "2.2"
services:
  # mysql
  mysql:
    build:
      context: mysql
    container_name: mysql
    restart: always
    command: --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_ROOT_PASSWORD: SYS
      MYSQL_USER: testuser
      MYSQL_PASSWORD: testpassword
      MYSQL_DATABASE: test
    volumes:
    - ./mysql/sql/dump.sql:/docker-entrypoint-initdb.d/dump.sql
    ports:
      - 3306:3306
  # postgres
  postgres:
    build:
      context: postgres
    container_name: postgres
    restart: always
    environment:
      POSTGRES_USER: testuser
      POSTGRES_PASSWORD: testpassword
    volumes:
    - ./postgres/sql/dump.sql:/docker-entrypoint-initdb.d/dump.sql
    ports:
      - 5432:5432
  adminer:
    container_name: adminer
    image: adminer
    restart: always
    depends_on:
      - mysql
      - postgres
    ports:
      - 8081:8080
  # iris
  iris:
    init: true
    container_name: iris
    build:
      context: .
      dockerfile: iris/Dockerfile
    ports:
      - 52773:52773
      - 1972:1972
    command: --check-caps false
  # tomcat
  tomcat:
    init: true
    container_name: tomcat
    build:
      context: .
      dockerfile: tomcat/Dockerfile
    volumes:
      - ./tomcat/performance.war:/usr/local/tomcat/webapps/performance.war
    ports:
      - 8080:8080

Con un rápido vistazo veremos que estamos usando las siguientes imágenes:

  • IRIS: instancia de IRIS Community a la que nos conectaremos vía JDBC.
  • Postgres: imagen de la base de datos PostgreSQL escuchando en el puerto 5432.
  • MySQL: imagen de la base de datos MySQL escuchando en el puerto 3306.
  • Tomcat: imagen de Docker configurado con un servidor de aplicaciones Apache Tomcat sobre la que desplegaremos el archivo WAR de nuestra aplicación.
  • Adminer: administrador de base de datos que nos permitirá consultar las bases de datos de Postgres y MySQL.

Como véis tenemos configurado los puertos de escucha para que se mapeen también en nuestro equipo, no sólo dentro de Docker. Para el caso de las bases de datos no sería necesario, ya que la conexión se realizará dentro de los contenedores de Docker, por lo que, si tenéis algún problema por los puertos podéis borrar la linea ports del archivo docker-compose.yml

Cada imagen de base de datos está ejecutando un script previo que creará las tablas necesarias para las pruebas de rendimiento, veamos uno de los archivos dump.sql

CREATE SCHEMA test;

DROP TABLE IF EXISTS test.patient;

CREATE TABLE test.country (
    id INT PRIMARY KEY,
    name VARCHAR(225)
);

CREATE TABLE test.city (
    id INT PRIMARY KEY,
    name VARCHAR(225),
    lastname VARCHAR(225),
    photo BYTEA,
    phone VARCHAR(14),
    address VARCHAR(225),
    country INT,
    CONSTRAINT fk_country
        FOREIGN KEY(country)
            REFERENCES test.country(id)
);

CREATE TABLE test.patient (
    id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    name VARCHAR(225),
    lastname VARCHAR(225),
    photo BYTEA,
    phone VARCHAR(14),
    address VARCHAR(225),
    city INT,
    CONSTRAINT fk_city
        FOREIGN KEY(city)
            REFERENCES test.city(id)
);

INSERT INTO test.country VALUES (1,'Spain'), (2,'France'), (3,'Portugal'), (4,'Germany');

INSERT INTO test.city VALUES (1,'Madrid',1), (2,'Valencia',1), (3,'Paris',2), (4,'Bordeaux',2), (5,'Lisbon',3), (6,'Porto',3), (7,'Berlin',4), (8,'Frankfurt',4);

Vamos a crear 3 tablas para nuestras pruebas, patient, city y country, estas dos últimas van a tener datos precargados de ciudades y países.

Perfecto, a continuación vamos a ver como realizaremos las conexiones a la base de datos.

Para ello hemos creado nuestro proyecto de Java utilizando un proyecto preconfigurado de Spring Boot disponible desde Visual Studio Code que nos proporciona la estructura básica.

No os preocupéis si no entendéis a primera vista la estructura del proyecto, el objetivo no es aprender Java, pero aún así vamos a explicar un poco más en detalle los principales documentos.

MyDataSourceFactory.java

Clase de java que a abre las conexiones a las diferentes bases de datos.

PerformancerController.java

Controlador encargado de la publicación de los endpoints a los que llamaremos desde Postman.

application.properties

Archivo de configuración con las diferentes conexiones a las bases de datos desplegadas en nuestro Docker.

Como podéis ver las URL de conexión utilizan el nombre del contenedor ya que, al desplegarse en un contenedor de Tomcat, las bases de datos serán accesibles por nuestra aplicación de Java únicamente con el nombre del contenedor correspondiente. También  podemos comprobar como la URL está realizando una conexión vía JDBC a nuestras bases de datos. Las librerías de Java usadas en el proyecto están definidas en el archivo pom.xml

Si modificáis el código fuente sólo deberéis ejecutar el comando:

mvn package

Y este os generará un archivo performance-0.0.1-SNAPSHOT.war, renombrarlo a performance.war y movedlo al directorio /tomcat, reemplazando al ya existente.

Tal y como está el proyecto en GitHub sólo necesitaremos clonarlo en nuestro equipo desde Visual Studio y ejecutar en el terminal los siguientes comandos:

docker-compose build
docker-compose up -d

Comprobemos el portal del Docker:

¡Genial! Contenedores del Docker funcionando. Ahora comprobemos desde nuestro Adminer y el portal de gestión de IRIS que nuestras tablas se han creado correctamente.

 Accedamos primeramente a la base de datos MySQL. Si consultáis el archivo docker-compose.yml veremos que el usuario y la contraseña definidas para MySQL y PostgreSQL son los mismos testuser/testpassword

Aquí tenemos nuestras tres tablas dentro de nuestra base de datos Test, veamos nuestra base de datos PostgreSQL:

Seleccionemos la base de datos testuser y el esquema test:

Aquí tenemos nuestras tablas perfectamente creadas en PostgreSQL. Comprobemos finalmente que todo está bien configurado en IRIS:

Todo correcto, tenemos nuestras tablas creadas en el Namespace USER bajo el schema Test.

Muy bien, una vez terminadas las comprobaciones, let's rock! Para ello usaremos Postman, en el que cargaremos el fichero adjunto al proyecto performance.postman_collection.json

Estas son las diferentes pruebas que vamos a lanzar, empezaremos con inserciones y continuaremos con consultas sobre la base de datos. No he incluido ningún tipo de índice más allá de los que se crean automáticamente al definir claves primarias en las diferentes bases de datos.

Insert

Llamada REST: GET http://localhost:8080/performance/tests/insert/{database}?total=1000

La variable {database} podrá tener los siguientes valores:

  • postgres
  • mysql
  • iris

Y el atributo total será el que modificaremos para indicar el total de inserciones que queremos realizar.

El método que se invocará se llama insertRecords y lo podéis encontrar en el archivo de java PerformanceController.java ubicado en /src/main/java/com/performance/controller/, podéis comprobar que es un insert extremadamente sencillo:

INSERT INTO test.patient VALUES (null, ?, ?, null, ?, ?, ?)

El primer indicado es nulo al ser el índice autogenerado y el segundo nulo corresponde a un campo de tipo BLOB/BYTEA/LONGVARBINARY donde registraremos una foto posteriormente.

Vamos a lanzar los siguientes lotes de inserciones: 100, 1000 , 10000, 20000 y comprobaremos los tiempos de respuesta que recibiremos en el Postman. Para cada medida haremos 3 pruebas y calcularemos la media de los 3 valores que obtengamos.

  100 1000 10000 20000
MySQL 0.754 8.91 s 88 s 192 s
PostgreSQL 0.23 s 2.24 s 20.92 s 40.35 s
IRIS 0.07 s 0.33 s 2.6 s 5 s

Veamoslo gráficamente.

 

Insert con archivo

En el anterior ejemplo hemos realizado inserciones simples, vamos a forzar un poco más la máquina incluyendo en el insert una imagen de 50 KB a modo de foto de cada paciente.

Llamada REST: GET http://localhost:8080/performance/tests/insertBlob/{database}?total=1000

La variable {database} podrá tener los siguientes valores:

  • postgres
  • mysql
  • iris

Y el atributo total será el que modificaremos para indicar el total de inserciones que queremos realizar.

El método que se invocará se llama insertBlobRecords y lo podéis encontrar en el archivo de java PerformanceController.java ubicado en /src/main/java/com/performance/controller/, podéis comprobar que es un insert similar al anterior con la salvedad de que estamos pasando el archivo en en insert:

INSERT INTO test.patient (Name, Lastname, Photo, Phone, Address, City) VALUES (?, ?, ?, ?, ?, ?)

Modifiquemos ligeramente el número de inserciones anterior para evitar que se eternice la prueba y limpiare el Docker de las imágenes para partir nuevamente de una total igualdad de condiciones.

  100 1000 5000 10000
MySQL 1.87 s 17 s 149 s 234 s
PostgreSQL 0.6 s 5.22 s 23.93 s 60.43 s
IRIS 0.13 s 0.88 s 4.58 s 12.57 s

 

Consultemos la gráfica:

 

Select

Comprobemos el rendimiento con una consulta simple que nos obtenga todos los registros de la tabla Patient. 

Llamada REST: GET http://localhost:8080/performance/tests/select/{database}

 

La variable {database} podrá tener los siguientes valores:

  • postgres
  • mysql
  • iris

El método que se invocará se llama selectRecords y lo podéis encontrar en el archivo de java PerformanceController.java ubicado en /src/main/java/com/performance/controller/, la consulta es extremadamente básica:

SELECT * FROM test.patient

Probaremos la consulta con el mismo conjunto de elementos que usamos para la primera prueba de inserción.

  100 1000 10000 20000
MySQL 0.03 s 0,02 s 0.03 s 0.04 s
PostgreSQL 0.03 s 0.02 s 0.04 s 0.03 s
IRIS 0.02 s 0.02 s 0.04 s 0.05 s

 

Y graficamente:

 

Select group by

Comprobemos el rendimiento con una consulta que incluya un left join así como funciones de agregación. 

Llamada REST: GET http://localhost:8080/performance/tests/selectGroupBy/{database}

La variable {database} podrá tener los siguientes valores:

  • postgres
  • mysql
  • iris

El método que se invocará se llama selectGroupBy y lo podéis encontrar en el archivo de java PerformanceController.java ubicado en /src/main/java/com/performance/controller/, veamos la consulta:

SELECT count(p.Name), c.Name FROM test.patient p left join test.city c on p.City = c.Id GROUP BY c.Name

Probaremos la consulta nuevamente con el mismo conjunto de elementos que usamos para la primera prueba de inserción.

  100 1000 10000 20000
MySQL 0.02 s 0.02 s 0.03 s 0.03 s
PostgreSQL 0.02 s 0.02 s 0.02 s 0.02 s
IRIS 0.02 s 0.02 0.03 s 0.04 s

 

Y gráficamente:

 

Update

Para el update vamos a lanzar una query con una subquery asociada dentro de sus condiciones.

Llamada REST: GET http://localhost:8080/performance/tests/update/{database}

La variable {database} podrá tener los siguientes valores:

  • postgres
  • mysql
  • iris

El método que se invocará se llama UpdateRecords y lo podéis encontrar en el archivo de java PerformanceController.java ubicado en /src/main/java/com/performance/controller/, veamos la consulta:

UPDATE test.patient SET Phone = '+15553535301' WHERE Name in (SELECT Name FROM test.patient where Name like '%12')

Lancemos la consulta y veamos los resultados.

  100 1000 10000 20000
MySQL X X X X
PostgreSQL 0.02 s 0.02 s 0.02 s 0.03 s
IRIS 0.02 s 0.02 s 0.02 s 0.04 s

Observamos que MySQL no permite este tipo de subqueries sobre la misma tabla que vamos a actualizar, por lo tanto no podemos medir sus tiempos en igualdad de condiciones. Obviaremos en este caso la gráfica al ser tan simple.

Delete

Para el delete vamos a lanzar una query con una subquery asociada dentro de sus condiciones.

Llamada REST: GET http://localhost:8080/performance/tests/delete/{database}

La variable {database} podrá tener los siguientes valores:

  • postgres
  • mysql
  • iris

El método que se invocará se llama DeleteRecords y lo podéis encontrar en el archivo de java PerformanceController.java ubicado en /src/main/java/com/performance/controller/, veamos la consulta:

DELETE test.patient WHERE Name in (SELECT Name FROM test.patient where Name like '%12')

Lancemos la consulta y veamos los resultados.

  100 1000 10000 20000
MySQL X X X X
PostgreSQL 0.01 s 0.02 s 0.02 s 0.03 s
IRIS 0.02 s 0.02 s 0.02 s 0.04 s

Observamos nuevamente que MySQL no permite este tipo de subqueries sobre la misma tabla de la que vamos a eliminar, por lo tanto no podemos medir sus tiempos en igualdad de condiciones.

Conclusiones

Podemos afirmar que todas ellas están bastante afinadas de cara a la consulta de datos, lo mismo que en las actualizaciones y eliminaciones de resgistros (salvo la incidencia con MySQL). Donde encontramos la mayor diferencia es en la gestión de las inserciones. IRIS es, sin lugar a dudas, la mejor de las 3 con diferencia,  es 6 veces más rápida que PostgreSQL y hasta 20 veces más rápida que MySQL en la ingesta de datos.

De cara a operar con grandes conjuntos de datos IRIS es sin duda la mejor opción dentro de las pruebas realizadas.

Así que...¡ya tenemos vencedor! ¡IRIS WINS!

 

Pd.: Estos son unos pequeños ejemplo de pruebas que podéis realizar, sentíos libres de modificar el código a vuestro antojo.

Comentarios (0)1
Inicie sesión o regístrese para continuar