PG Driver vs Knex.js vs Sequelize vs TypeORM

Welington Fidelis
11 min readMar 19, 2021

RESUMO

Este artigo é o resultado de um estudo sobre desempenho de algumas das bibliotecas mais utilizadas no mercado para manipulação de dados em bancos relacionais através do Node.js. Para este trabalho, o banco de dados utilizado nos testes foi PostgreSQL.

Das bibliotecas estudadas, estão o Sequelize e o TypeORM, que são do tipo mapeamento objeto-relacional (Object-Relational Mapping — ORM). Também foram analisadas a biblioteca Knex.js, que é do tipo construtor de consulta (Query Builder). E por fim, Node-postgres (pg), um driver de conexão direta com banco de dados para execução de queries.

Para cada um dos itens estudados, foi utilizado um mesmo padrão de código construído com Node.js e Typescript, resultando em um CRUD (Create, Read, Update and Delete) de usuário. O código é simplificado e pode não seguir todas as boas práticas de desenvolvimento, uma vez que o objetivo deste é servir de base para a realização e avaliação de testes para manipulação do banco de dados através das bibliotecas previamente citadas. Para cada item do CRUD, um conjunto de 310 iterações foram realizadas através de requisições utilizando o Postman, um cliente HTTP muito popular no mercado. Destas 310 iterações, os 10 primeiros retornos foram desconsiderados nas análises dos resultados, a fim de equilibrar o tempo de conexão/pooling de cada uma das bibliotecas.

INTRODUÇÃO

Por vários motivos diferentes, há a necessidade de incluir um banco de dados em um projeto a ser desenvolvido, o que parece ser uma tarefa fácil de apenas escolher qual tipo de banco de dados (SQL, NoSQL, etc) que melhor se aplica ao objetivo do projeto e dentre estes tipos, qual o banco em si (PostgreSQL, MySQL, MongoDB, etc). No entanto, esta etapa na arquitetura de um projeto é mais delicada, tendo em vista as inúmeras bibliotecas disponíveis no mercado que prometem ajudar na construção, manutenção e gerenciamento de banco ao longo do desenvolvimento e após a conclusão e lançamento de uma versão estável do projeto. Muitas destas bibliotecas oferecem praticidade e clareza na construção e manipulação dos dados, como uso de modelos de entidades, muito ligado ao padrão de programação orientado a objetos. Mas em contrapartida à praticidade destes, pode haver uma queda no desempenho da execução das queries no banco, que pode impactar o desempenho do projeto adicionando alguns milissegundos a mais na execução e resposta ao cliente.

Com tantas opções no mercado, fatores de desempenho, manutenibilidade e clareza de código, a escolha pode se tornar um pouco mais complicada.

É importante ressaltar que este artigo não tem o objetivo de dizer se estas bibliotecas devem ou não serem usadas, ou ainda qual delas é a melhor do mercado atual. Há aqui apenas um estudo e demonstração de resultados de testes que podem auxiliar na escolha de uma tecnologia.

MATERIAIS/MÉTODOS

Das bibliotecas escolhidas para o estudo, na categoria de ORM estão Sequelize e TypeORM. Também foi incluído no estudo, o Knex.js, uma biblioteca para execução de queries através de código Javascript com uma maior abstração de um código diretamente escrito com SQL (linguagem de banco de dados). Por fim, para incluir nos testes uma construção mais “pura” de SQL para execução no banco de dados, utilizou-se a biblioteca Node-postgres. Em seguida falaremos mais detalhado de cada um destes citados.

  • Sequelize: Um ORM para Postgres, MySQL, MariaDB, SQLite e MSSQL (Microsoft SQL Server). Muito popular no mercado, esta biblioteca possui inúmeras ferramentas integradas que possibilitam, por exemplo, a replicação entre diferentes bancos e leitura e escrita em bancos distintos.
  • TypeORM: Muito semelhante à anterior, esta biblioteca fornece suporte aos bancos MySQL, MariaDB, PostgreSQL, CockroachDB, SQLite, MSSQL, Oracle, SAP Hana e MongoDB (experimental). Um diferencial desta biblioteca está na possibilidade já integrada de se usar repositories (repositórios — camada de abstração muito usada no padrão de programação orientado a objeto para execução de comandos no banco de dados). Também é importante ressaltar a alta integração e adaptação deste com uso de TypeScript.
  • Knex.js: Esta biblioteca por sua vez não é baseada em modelos como as anteriores, mas sim em um construtor de queries focada em desempenho e maior facilidade na construção de SQL. Ela suporta bancos como PostgreSQL, MSSQL, MySQL, MariaDB, SQLite3, Oracle, e Amazon Redshift.
  • Node-postgres (pg): Por fim, uma biblioteca do tipo driver de conexão construída para execução de queries exclusivamente no banco de dados PostgreSQL. Através desta biblioteca é possível construir código SQL com maior liberdade, uma vez que através dela, é necessário escrever completamente as queries para execução. Este trabalho geralmente demanda um maior conhecimento sobre bancos relacionais para criação de bons códigos SQL, mas garante um maior desempenho por ser uma “camada” mais próxima ao banco de dados.

Algumas informações importantes, Sequelize, TypeORM e Knex.js possuem a possibilidade de construção de migrations (migrações — queries específicas para construção de bancos, tabelas, inclusão, alteração e exclusão de colunas que podem ser executadas de maneira rápida e descomplicada a fim de padronizar/criar um banco), seeds (sementes — queries específicas para inserção de um ou vários dados em tabelas de maneira automatizada) e por fim, a grande vantagem de trabalharem com diferentes bancos relacionais, possibilitando a troca de tipos de bancos no decorrer do projeto por n fatores que venham a criar esta necessidade. Claro, existem peculiaridades entre os diferentes tipos de bancos no mercado que demandam uma análise maior prévia à uma mudança no projeto, tendo em vista que alguns possuem tipos de dados e estruturas específicas que outros não possuem.

Pela escolha do banco de dados PostgreSQL, as três primeiras bibliotecas fazem uso da biblioteca pg, uma vez que esta é um driver de conexão com banco de dados.

A ferramenta utilizada para efetuar as requisições à API durante os testes foi o Postman, um cliente HTTP que simula requisições feitas por aplicações do tipo cliente. Na execução dos testes, como citado anteriormente no resumo, um conjunto de 310 requisições foram efetuadas através do módulo Collection Runner incluído no Postman. Esta ferramenta permite a execução automática de um número pré-estabelecido de requisições a uma ou mais APIs. É importante lembrar que neste estudo não foram considerados os tempos de resposta de cada requisição, apenas o tempo de execução de cada query usada internamente no código do projeto.

Para cada execução de uma query dentro do código, um novo registro em um arquivo .txt era inserido, permitindo ao final das execuções, a coleta e análise dos resultados obtidos.

Quanto ao hardware utilizado na construção do código e execução dos testes, a seguir estão as especificações técnicas.

Tipo: Laptop

Marca: Acer

Modelo: Aspire 3 A315–53-C2SS

Processador: Intel core i5–8250U 1.6GHz (Turbo Boost até 3.4GHz)

Memoria: DDR4 8GB 2400MHZ

Armazenamento: SSD 512GB NVMe

RESULTADOS

Nesta seção faremos a análise dos resultados obtidos nos testes na seguinte ordem: execução de queries para inserção, atualização, busca por ID, exclusão e por fim, leitura de 10, 100, 1000, 10000, 100000 e 1000000 registros de usuários cadastrados.

Importante ressaltar que a medida de tempo dos resultados estará em milisegundos.

Inserção

No gráfico 1 podemos observar a média de tempo na execução de 300 inserções para cada uma das bibliotecas. O menor tempo de execução foi da biblioteca pg, seguida pela construtora de queries Knex.js com uma média pouco acima. Já entre os ORMs, o sequelize demonstrou um maior desempenho na execução que seu concorrente TypeORM, ficando à frente com pouco mais de 1ms.

Gráfico 1 — Média de execução durante 300 inserções

Fonte: Autor

Aqui no gráfico 2 podemos ver a variação de tempo de execução de cada query. As bibliotecas pg e Knex.js apresentaram uma variação no tempo de execução menor que as demais, ficando entre os ORMs um melhor desempenho com Sequelize.

Gráfico 2 — Variação de tempo durante 300 inserções

Fonte: Autor

Atualização

Atualizações tendem a ser mais lentas que inserções, uma vez que é necessário uma busca de dados no banco através de um parâmetro passado como referência e em seguida, atualizar estes dados. Neste teste utilizou-se a chave de identificação (ID) de cada usuário como referência. No gráfico 3 podemos ver que a biblioteca pg não obteve um desempenho muito distante do Knex.js, mas manteve-se relativamente distante dos ORMs, entre os quais também não houve uma grande diferença no tempo médio de execução.

Gráfico 3 — Média de execução durante 300 atualizações

Fonte: Autor

No gráfico 4 podemos perceber uma variação mais destacável de tempo do que o gráfico 2, provavelmente se dando à natureza da operação de um atualização, como descrito anteriormente. Aqui vale um destaque para a biblioteca Knex.js, que no tempo médio geral ficou próximo do pg, mas teve o maior pico de variação entre as 4 bibliotecas neste conjunto de testes.

Gráfico 4 — Variação de tempo durante 300 atualizações

Fonte: Autor

Busca por ID

Efetuando buscas de usuários por ID, no gráfico 5 percebemos uma mudança nas comparações entre os ORMs até o momento, onde o TypeORM apresentou um melhor desempenho em relação ao Sequelize e relativamente próximo do Knex.js.

Gráfico 5 — Média de execução durante 300 buscas por ID

Fonte: Autor

Agora, em relação à variação de tempo, notamos que no gráfico 6 a biblioteca TypeORM apresenta uma estabilidade maior do que em seus testes anteriores, tendo sua maior variação sendo inferior ainda que o pg. O knex.js apresenta uma taxa média próxima, mas com uma única variação de pico muito fora do seu padrão, seguida pelo Sequelize com duas variações destacáveis entre sua média.

Gráfico 6 — Variação de tempo durante 300 buscas por ID

Fonte: Autor

Exclusão

Seguindo para exclusão de dados, no gráfico 7 vemos que, embora pequeno, houve um melhor desempenho por parte da biblioteca TypeORM em comparação ao Sequelize e mais uma vez, próximo do Knex.js. É interessante notar que o tempo médio entre o Knex.js e os ORMs no geral, ficou próximo neste teste, formando um grupo mais separado do pg com o menor tempo.

Gráfico 7 — Média de execução durante 300 exclusões

Fonte: Auto

Na variação de tempo no gráfico 8, as bibliotecas de uma forma geral apresentaram uma maior instabilidade nos tempos de execução, valendo destaque para um pico até então não visto no pg.

Gráfico 8 — Variação de tempo durante 300 exclusões

Fonte: Autor

Listagem por quantidade

Considerando que em grande parte dos projetos construídos, há um número maior na leituras dos dados do que outras ações, neste teste o ciclo de 310 requisições foi repetido alternando entre as quantidades de dados buscados no banco para que fosse avaliado o desempenho de cada biblioteca em pequenas e grandes quantidades de buscas.

10, 100 e 1000 usuários

Observando o gráfico 9, notamos que em quantidades pequenas, até 10 itens por vez, não há uma grande diferença no tempo de execução entre as bibliotecas, diferente do que acontece a partir de 100 itens, onde as bibliotecas pg e Knex.js começam a se distanciar das demais, mas não muito entre elas mesmas. Já entre os ORMs, quanto maior a quantidade de dados buscadas, maior é o desempenho do TypeORM sobre o Sequelize.

No gráfico 9 vemos esta diferença de tempo crescente entre os ORMs.

Gráfico 9 — Média de execução durante 300 buscas indo de 0 a 1000

Fonte: Autor

10000, 100000 e 1000000 usuários

Chegando ao último teste, no gráfico 10 observamos que quanto maior a quantidade de dados buscados, mais próximo ficou o desempenho entre as bibliotecas pg, Knex.js e TypeORM, ficando o Sequelize cada vez mais isolado no gráfico. Vale um destaque para a biblioteca TypeORM que apresentou um melhor desempenho que o Knex.js na busca por um milhão de registros.

Gráfico 10 — Média de execução durante 300 buscas indo de 10000 a 1000000

Fonte: Autor

CONCLUSÃO

Como era de se esperar em relação a desempenho, a biblioteca pg apresentou o melhor desempenho nos testes, principalmente pelo fato desta biblioteca ser “mais próxima” ao banco de dados, sem aplicação de camadas ou abstrações antes da execução das queries. A biblioteca Knex.js teve um bom desempenho também, sendo a mais próxima do pg na maioria dos testes, o que também era esperado levando em consideração que esta biblioteca tem o intuito principal de aplicar uma camada única de abstração na construção das queries com uso de Javascript, gerando uma forma mais intuitiva de escrever SQL. Já entre os ORMs, o tempo médio ganha alguns milissegundos a mais em prol de uma maior usabilidade e abstração nas camadas de desenvolvimento, como uso de modelos e manipulação de dados sem oneração do entendimento profundo e escrita de SQL por exemplo. Todavia, como visto em testes de buscas, o TypeORM apresentou um bom desempenho mesmo em relação às outras bibliotecas com menos abstrações.

Acreditamos que para uma escolha mais apropriada em relação às bibliotecas de manipulação de banco, o desenvolvedor deve analisar o tipo do projeto que irá desenvolver, levando em consideração a necessidade de ter um altíssimo nível de desempenho, quantidade de integrantes do time que auxiliará no desenvolvimento, manutenibilidade ao longo do tempo e conhecimento geral sobre determinadas tecnologias. Algumas bibliotecas como o pg fornecerão, provavelmente em todos os casos, menos milissegundos de execução no uso do banco, mas em contrapartida aumentam o nível de trabalho na construção e manutenção de código, exigindo às vezes, um conhecimento profundo sobre SQL para construção de um código que garanta o bom desempenho oferecido pelo driver. Os ORMs como Sequelize e TypeORM oferecem vários tipos de abstrações e ferramentas já integradas que visam facilitar o desenvolvimento de novas features e manutenção de antigas por novos desenvolvedores, uma vez que são baseados em modelos e tornam o entendimento do projeto mais fácil, todavia, podem gerar um tempo menor de execução de certos tipos de ações no banco. Arriscariamos dizer que, para os indecisos entre as bibliotecas, um construtor de queries como Knex.js seria uma boa escolha considerando desempenho, usabilidade e aprendizado.

Todo código utilizado nos testes apresentados neste artigo estão neste repositório https://github.com/welingtonfidelis/orm_comparation.

REFERÊNCIAS

Node-postgres: https://node-postgres.com/

Knex.js: http://knexjs.org/

Sequelize: https://sequelize.org/master/manual/getting-started.html

TypeORM: https://typeorm.io/#/

--

--