Fundamentos de design de banco de dados – Comparando PostgreSQL, Cassandra e MongoDB

Olá amigos. Antes de partirmos para a segunda parte das férias de maio, compartilhamos com vocês o material que traduzimos em antecipação ao lançamento de uma nova stream do curso "SGBD relacional".

Fundamentos de design de banco de dados – Comparando PostgreSQL, Cassandra e MongoDB

Os desenvolvedores de aplicativos gastam muito tempo comparando vários bancos de dados operacionais para selecionar aquele que melhor se adapta à carga de trabalho pretendida. As necessidades podem incluir modelagem de dados simplificada, garantias transacionais, desempenho de leitura/gravação, escalabilidade horizontal e tolerância a falhas. Tradicionalmente, a escolha começa com a categoria do banco de dados, SQL ou NoSQL, já que cada categoria apresenta um conjunto claro de compensações. O alto desempenho em termos de baixa latência e alto rendimento é geralmente visto como um requisito sem compromisso e, portanto, essencial para qualquer banco de dados de amostra.

O objetivo deste artigo é ajudar os desenvolvedores de aplicativos a fazer a escolha certa entre SQL e NoSQL no contexto da modelagem de dados de aplicativos. Veremos um banco de dados SQL, chamado PostgreSQL, e dois bancos de dados NoSQL, Cassandra e MongoDB, para cobrir os conceitos básicos do design de banco de dados, como criar tabelas, preenchê-las, ler dados de uma tabela e excluí-los. No próximo artigo, veremos índices, transações, JOINs, diretivas TTL e design de banco de dados baseado em JSON.

Qual é a diferença entre SQL e NoSQL?

Os bancos de dados SQL aumentam a flexibilidade dos aplicativos por meio de garantias transacionais ACID, bem como sua capacidade de consultar dados usando JOINs de maneiras inesperadas, além de modelos de bancos de dados relacionais normalizados existentes.

Dada a sua arquitetura monolítica/nó único e o uso de um modelo de replicação mestre-escravo para redundância, os bancos de dados SQL tradicionais carecem de dois recursos importantes: escalabilidade de gravação linear (ou seja, particionamento automático em vários nós) e perda automática/zero de dados. Isso significa que a quantidade de dados recebidos não pode exceder o rendimento máximo de gravação de um único nó. Além disso, alguma perda temporária de dados deve ser levada em consideração na tolerância a falhas (em uma arquitetura sem compartilhamento). Aqui você precisa ter em mente que os commits recentes ainda não foram refletidos na cópia escrava. Atualizações sem tempo de inatividade também são difíceis de obter em bancos de dados SQL.

Os bancos de dados NoSQL são geralmente distribuídos por natureza, ou seja, neles, os dados são divididos em seções e distribuídos por vários nós. Eles exigem desnormalização. Isso significa que os dados inseridos também devem ser copiados diversas vezes para responder às solicitações específicas que você enviar. O objetivo geral é obter alto desempenho reduzindo o número de shards disponíveis durante as leituras. Isso implica que o NoSQL exige que você modele suas consultas, enquanto o SQL exige que você modele seus dados.

O NoSQL se concentra em alcançar alto desempenho em um cluster distribuído e esta é a justificativa subjacente para muitas compensações no design de banco de dados que incluem perda de transações ACID, JOINs e índices secundários globais consistentes.

Há um argumento de que, embora os bancos de dados NoSQL forneçam escalabilidade de gravação linear e alta tolerância a falhas, a perda de garantias transacionais os torna inadequados para dados de missão crítica.

A tabela a seguir mostra como a modelagem de dados no NoSQL difere do SQL.

Fundamentos de design de banco de dados – Comparando PostgreSQL, Cassandra e MongoDB

SQL e NoSQL: por que ambos são necessários?

Aplicativos do mundo real com grande número de usuários, como Amazon.com, Netflix, Uber e Airbnb, têm a tarefa de realizar tarefas complexas e multifacetadas. Por exemplo, um aplicativo de comércio eletrônico como Amazon.com precisa armazenar dados leves e altamente críticos, como informações do usuário, produtos, pedidos, faturas, junto com dados pesados ​​e menos confidenciais, como análises de produtos, mensagens de suporte, atividade do usuário, comentários e recomendações de usuários. Naturalmente, esses aplicativos dependem de pelo menos um banco de dados SQL junto com pelo menos um banco de dados NoSQL. Em sistemas inter-regionais e globais, um banco de dados NoSQL opera como um cache distribuído geograficamente para dados armazenados em um banco de dados SQL de origem confiável executado em uma região específica.

Como o YugaByte DB combina SQL e NoSQL?

Construído em um mecanismo de armazenamento misto orientado a log, fragmentação automática, replicação de consenso distribuída fragmentada e transações distribuídas ACID (inspirado no Google Spanner), YugaByte DB é o primeiro banco de dados de código aberto do mundo que é simultaneamente compatível com NoSQL (Cassandra & Redis) e SQL (PostgreSQL). Conforme mostrado na tabela abaixo, YCQL, a API YugaByte DB compatível com Cassandra, adiciona os conceitos de transações ACID de chave única e múltipla e índices secundários globais à API NoSQL, inaugurando assim a era dos bancos de dados NoSQL transacionais. Além disso, YCQL, a API YugaByte DB compatível com PostgreSQL, adiciona os conceitos de escala de gravação linear e tolerância automática a falhas à API SQL, trazendo bancos de dados SQL distribuídos para o mundo. Como o YugaByte DB é de natureza transacional, a API NoSQL agora pode ser usada no contexto de dados de missão crítica.

Fundamentos de design de banco de dados – Comparando PostgreSQL, Cassandra e MongoDB

Como dito anteriormente no artigo "Apresentando YSQL: uma API SQL distribuída compatível com PostgreSQL para banco de dados YugaByte", a escolha entre SQL ou NoSQL no banco de dados YugaByte depende inteiramente das características da carga de trabalho subjacente:

  • Se sua carga de trabalho principal for operações JOIN com várias chaves, ao escolher YSQL, entenda que suas chaves podem ser distribuídas entre vários nós, resultando em maior latência e/ou menor rendimento do que NoSQL.
  • Caso contrário, escolha uma das duas APIs NoSQL, tendo em mente que você obterá melhor desempenho como resultado de consultas atendidas em um nó por vez. O YugaByte DB pode servir como um único banco de dados operacional para aplicativos complexos do mundo real que precisam gerenciar várias cargas de trabalho simultaneamente.

O laboratório de modelagem de dados na próxima seção é baseado em bancos de dados YugaByte DB compatíveis com PostgreSQL e Cassandra API, em oposição a bancos de dados nativos. Esta abordagem enfatiza a facilidade de interação com duas APIs diferentes (em duas portas diferentes) do mesmo cluster de banco de dados, em vez de usar clusters completamente independentes de dois bancos de dados diferentes.
Nas seções a seguir, daremos uma olhada no laboratório de modelagem de dados para ilustrar as diferenças e alguns pontos em comum dos bancos de dados abordados.

Laboratório de Modelagem de Dados

Instalação de banco de dados

Dada a ênfase no design do modelo de dados (em vez de arquiteturas de implantação complexas), instalaremos bancos de dados em contêineres Docker na máquina local e, em seguida, interagiremos com eles usando seus respectivos shells de linha de comando.

Banco de dados YugaByte DB compatível com PostgreSQL e Cassandra

mkdir ~/yugabyte && cd ~/yugabyte
wget https://downloads.yugabyte.com/yb-docker-ctl && chmod +x yb-docker-ctl
docker pull yugabytedb/yugabyte
./yb-docker-ctl create --enable_postgres

MongoDB

docker run --name my-mongo -d mongo:latest

Acesso à linha de comando

Vamos nos conectar aos bancos de dados usando o shell de linha de comando para as APIs correspondentes.

PostgreSQL

psql é um shell de linha de comando para interagir com PostgreSQL. Para facilidade de uso, o YugaByte DB vem com o psql direto na pasta bin.

docker exec -it yb-postgres-n1 /home/yugabyte/postgres/bin/psql -p 5433 -U postgres

Cassandra

cqlsh é um shell de linha de comando para interagir com Cassandra e seus bancos de dados compatíveis via CQL (Cassandra Query Language). Para facilidade de uso, YugaByte DB vem com cqlsh no catálogo bin.
Observe que o CQL foi inspirado no SQL e possui conceitos semelhantes de tabelas, linhas, colunas e índices. No entanto, como linguagem NoSQL, ela adiciona um certo conjunto de limitações, muitas das quais também abordaremos em outros artigos.

docker exec -it yb-tserver-n1 /home/yugabyte/bin/cqlsh

MongoDB

mongo é um shell de linha de comando para interagir com o MongoDB. Ele pode ser encontrado no diretório bin da instalação do MongoDB.

docker exec -it my-mongo bash 
cd bin
mongo

Criar uma tabela

Agora podemos interagir com o banco de dados para realizar diversas operações usando a linha de comando. Vamos começar criando uma tabela que armazena informações sobre músicas escritas por diferentes artistas. Essas músicas podem fazer parte de um álbum. Também atributos opcionais para uma música são ano de lançamento, preço, gênero e classificação. Precisamos levar em conta atributos adicionais que podem ser necessários no futuro através do campo “tags”. Ele pode armazenar dados semiestruturados na forma de pares de valores-chave.

PostgreSQL

CREATE TABLE Music (
    Artist VARCHAR(20) NOT NULL, 
    SongTitle VARCHAR(30) NOT NULL,
    AlbumTitle VARCHAR(25),
    Year INT,
    Price FLOAT,
    Genre VARCHAR(10),
    CriticRating FLOAT,
    Tags TEXT,
    PRIMARY KEY(Artist, SongTitle)
);	

Cassandra

Criar uma tabela no Cassandra é muito semelhante ao PostgreSQL. Uma das principais diferenças é a falta de restrições de integridade (por exemplo, NOT NULL), mas isso é responsabilidade da aplicação, não do banco de dados NoSQL. A chave primária consiste em uma chave de partição (a coluna Artista no exemplo abaixo) e um conjunto de colunas de cluster (a coluna SongTitle no exemplo abaixo). A chave de partição determina em qual partição/fragmento a linha deve ser colocada e as colunas de cluster indicam como os dados devem ser organizados no fragmento atual.

CREATE KEYSPACE myapp;
USE myapp;
CREATE TABLE Music (
    Artist TEXT, 
    SongTitle TEXT,
    AlbumTitle TEXT,
    Year INT,
    Price FLOAT,
    Genre TEXT,
    CriticRating FLOAT,
    Tags TEXT,
    PRIMARY KEY(Artist, SongTitle)
);

MongoDB

O MongoDB organiza os dados em bancos de dados (Database) (semelhante ao Keyspace no Cassandra), onde existem Coleções (semelhantes a tabelas) que contêm Documentos (semelhantes a linhas de uma tabela). No MongoDB, basicamente não há necessidade de definir um esquema inicial. Equipe "usar banco de dados", mostrado abaixo, instancia o banco de dados na primeira chamada e altera o contexto do banco de dados recém-criado. Mesmo as coleções não precisam ser criadas explicitamente; elas são criadas automaticamente, simplesmente quando você adiciona o primeiro documento a uma nova coleção. Observe que o MongoDB usa o banco de dados de teste por padrão, portanto, qualquer operação em nível de coleção sem especificar um banco de dados específico será executada nele por padrão.

use myNewDatabase;

Obtendo informações sobre uma tabela
PostgreSQL

d Music
Table "public.music"
    Column    |         Type          | Collation | Nullable | Default 
--------------+-----------------------+-----------+----------+--------
 artist       | character varying(20) |           | not null | 
 songtitle    | character varying(30) |           | not null | 
 albumtitle   | character varying(25) |           |          | 
 year         | integer               |           |          | 
 price        | double precision      |           |          | 
 genre        | character varying(10) |           |          | 
 criticrating | double precision      |           |          | 
 tags         | text                  |           |          | 
Indexes:
    "music_pkey" PRIMARY KEY, btree (artist, songtitle)

Cassandra

DESCRIBE TABLE MUSIC;
CREATE TABLE myapp.music (
    artist text,
    songtitle text,
    albumtitle text,
    year int,
    price float,
    genre text,
    tags text,
    PRIMARY KEY (artist, songtitle)
) WITH CLUSTERING ORDER BY (songtitle ASC)
    AND default_time_to_live = 0
    AND transactions = {'enabled': 'false'};

MongoDB

use myNewDatabase;
show collections;

Inserindo dados em uma tabela
PostgreSQL

INSERT INTO Music 
    (Artist, SongTitle, AlbumTitle, 
    Year, Price, Genre, CriticRating, 
    Tags)
VALUES(
    'No One You Know', 'Call Me Today', 'Somewhat Famous',
    2015, 2.14, 'Country', 7.8,
    '{"Composers": ["Smith", "Jones", "Davis"],"LengthInSeconds": 214}'
);
INSERT INTO Music 
    (Artist, SongTitle, AlbumTitle, 
    Price, Genre, CriticRating)
VALUES(
    'No One You Know', 'My Dog Spot', 'Hey Now',
    1.98, 'Country', 8.4
);
INSERT INTO Music 
    (Artist, SongTitle, AlbumTitle, 
    Price, Genre)
VALUES(
    'The Acme Band', 'Look Out, World', 'The Buck Starts Here',
    0.99, 'Rock'
);
INSERT INTO Music 
    (Artist, SongTitle, AlbumTitle, 
    Price, Genre, 
    Tags)
VALUES(
    'The Acme Band', 'Still In Love', 'The Buck Starts Here',
    2.47, 'Rock', 
    '{"radioStationsPlaying": ["KHCR", "KBQX", "WTNR", "WJJH"], "tourDates": { "Seattle": "20150625", "Cleveland": "20150630"}, "rotation": Heavy}'
);

Cassandra

Expressão geral INSERT no Cassandra é muito semelhante ao do PostgreSQL. No entanto, há uma grande diferença na semântica. Em Cassandra INSERT na verdade é uma operação UPSERT, onde os últimos valores são adicionados à linha se a linha já existir.

A entrada de dados é semelhante ao PostgreSQL INSERT acima

.

MongoDB

Embora o MongoDB seja um banco de dados NoSQL como o Cassandra, sua operação de inserção não tem nada em comum com o comportamento semântico do Cassandra. No MongoDB inserir() não tem oportunidades UPSERT, o que o torna semelhante ao PostgreSQL. Adicionando dados padrão sem _idspecified fará com que um novo documento seja adicionado à coleção.

db.music.insert( {
artist: "No One You Know",
songTitle: "Call Me Today",
albumTitle: "Somewhat Famous",
year: 2015,
price: 2.14,
genre: "Country",
tags: {
Composers: ["Smith", "Jones", "Davis"],
LengthInSeconds: 214
}
}
);
db.music.insert( {
artist: "No One You Know",
songTitle: "My Dog Spot",
albumTitle: "Hey Now",
price: 1.98,
genre: "Country",
criticRating: 8.4
}
);
db.music.insert( {
artist: "The Acme Band",
songTitle: "Look Out, World",
albumTitle:"The Buck Starts Here",
price: 0.99,
genre: "Rock"
}
);
db.music.insert( {
artist: "The Acme Band",
songTitle: "Still In Love",
albumTitle:"The Buck Starts Here",
price: 2.47,
genre: "Rock",
tags: {
radioStationsPlaying:["KHCR", "KBQX", "WTNR", "WJJH"],
tourDates: {
Seattle: "20150625",
Cleveland: "20150630"
},
rotation: "Heavy"
}
}
);

Consulta de tabela

Talvez a diferença mais significativa entre SQL e NoSQL em termos de construção de consultas seja a linguagem usada FROM и WHERE. SQL permite após expressão FROM selecione várias tabelas e expressão com WHERE pode ser de qualquer complexidade (incluindo operações JOIN entre tabelas). No entanto, o NoSQL tende a impor uma limitação severa na FROM, e trabalhar apenas com uma tabela especificada, e em WHERE, a chave primária deve sempre ser especificada. Isso está relacionado ao impulso de desempenho do NoSQL de que falamos anteriormente. Esse desejo leva a todas as reduções possíveis em qualquer interação entre tabelas e chaves cruzadas. Pode introduzir um grande atraso na comunicação entre nós ao responder a uma solicitação e, portanto, é melhor evitá-lo em geral. Por exemplo, Cassandra exige que as consultas sejam limitadas a determinados operadores (apenas =, IN, <, >, =>, <=) em chaves de partição, exceto ao solicitar um índice secundário (somente o operador = é permitido aqui).

PostgreSQL

Abaixo estão três exemplos de consultas que podem ser facilmente executadas por um banco de dados SQL.

  • Exibir todas as músicas de um artista;
  • Exibe todas as músicas do artista que correspondem à primeira parte do título;
  • Exibe todas as músicas de um artista que possuem uma determinada palavra no título e têm preço inferior a 1.00.
SELECT * FROM Music
WHERE Artist='No One You Know';
SELECT * FROM Music
WHERE Artist='No One You Know' AND SongTitle LIKE 'Call%';
SELECT * FROM Music
WHERE Artist='No One You Know' AND SongTitle LIKE '%Today%'
AND Price > 1.00;

Cassandra

Das consultas PostgreSQL listadas acima, apenas a primeira funcionará inalterada no Cassandra, pois o operador LIKE não pode ser aplicado a colunas de cluster, como SongTitle. Neste caso, apenas operadores são permitidos = и IN.

SELECT * FROM Music
WHERE Artist='No One You Know';
SELECT * FROM Music
WHERE Artist='No One You Know' AND SongTitle IN ('Call Me Today', 'My Dog Spot')
AND Price > 1.00;

MongoDB

Conforme mostrado nos exemplos anteriores, o principal método para criar consultas no MongoDB é db.collection.find(). Este método contém explicitamente o nome da coleção (music no exemplo abaixo), portanto é proibido consultar múltiplas coleções.

db.music.find( {
  artist: "No One You Know"
 } 
);
db.music.find( {
  artist: "No One You Know",
  songTitle: /Call/
 } 
);

Lendo todas as linhas de uma tabela

A leitura de todas as linhas é simplesmente um caso especial do padrão de consulta que vimos anteriormente.

PostgreSQL

SELECT * 
FROM Music;

Cassandra

Semelhante ao exemplo do PostgreSQL acima.

MongoDB

db.music.find( {} );

Editando dados em uma tabela

PostgreSQL

PostgreSQL fornece instruções UPDATE para alterar dados. Ela não tem oportunidades UPSERT, portanto, esta instrução falhará se a linha não estiver mais no banco de dados.

UPDATE Music
SET Genre = 'Disco'
WHERE Artist = 'The Acme Band' AND SongTitle = 'Still In Love';

Cassandra

Cassandra tem UPDATE semelhante ao PostgreSQL. UPDATE tem a mesma semântica UPSERT, como INSERT.

Semelhante ao exemplo do PostgreSQL acima.

MongoDB
Operação atualizar () no MongoDB pode atualizar completamente um documento existente ou atualizar apenas determinados campos. Por padrão, ele atualiza apenas um documento com a semântica desabilitada UPSERT. Atualizando vários documentos e comportamento semelhante UPSERT pode ser aplicado definindo sinalizadores adicionais para a operação. Por exemplo, no exemplo abaixo, o gênero de um artista específico é atualizado com base em sua música.

db.music.update(
  {"artist": "The Acme Band"},
  { 
    $set: {
      "genre": "Disco"
    }
  },
  {"multi": true, "upsert": true}
);

Removendo dados de uma tabela

PostgreSQL

DELETE FROM Music
WHERE Artist = 'The Acme Band' AND SongTitle = 'Look Out, World';

Cassandra

Semelhante ao exemplo do PostgreSQL acima.

MongoDB

MongoDB possui dois tipos de operações para excluir documentos - deletarUm() /deleteMany() и retirar(). Ambos os tipos excluem documentos, mas retornam resultados diferentes.

db.music.deleteMany( {
        artist: "The Acme Band"
    }
);

Excluindo uma tabela

PostgreSQL

DROP TABLE Music;

Cassandra

Semelhante ao exemplo do PostgreSQL acima.

MongoDB

db.music.drop();

Conclusão

O debate sobre a escolha entre SQL e NoSQL já dura há mais de 10 anos. Existem dois aspectos principais neste debate: arquitetura do mecanismo de banco de dados (SQL monolítico, transacional versus NoSQL distribuído e não transacional) e abordagem de design de banco de dados (modelagem de seus dados em SQL versus modelagem de suas consultas em NoSQL).

Com um banco de dados transacional distribuído como o YugaByte DB, o debate sobre a arquitetura do banco de dados pode ser facilmente encerrado. À medida que os volumes de dados se tornam maiores do que o que pode ser gravado em um único nó, torna-se necessária uma arquitetura totalmente distribuída que suporte escalabilidade de gravação linear com fragmentação/rebalanceamento automático.

Além disso, como afirma um dos artigos Parceria Arquiteturas transacionais e fortemente consistentes são agora mais usadas para fornecer melhor agilidade de desenvolvimento do que arquiteturas não transacionais e eventualmente consistentes.

Voltando à discussão sobre design de banco de dados, é justo dizer que ambas as abordagens de design (SQL e NoSQL) são necessárias para qualquer aplicação complexa do mundo real. A abordagem de “modelagem de dados” SQL permite que os desenvolvedores atendam mais facilmente aos requisitos de negócios em constante mudança, enquanto a abordagem de “modelagem de consulta” NoSQL permite que os mesmos desenvolvedores operem em grandes volumes de dados com baixa latência e alto rendimento. É por esta razão que YugaByte DB fornece APIs SQL e NoSQL em um núcleo comum, em vez de promover uma das abordagens. Além disso, ao fornecer compatibilidade com linguagens de banco de dados populares, incluindo PostgreSQL e Cassandra, o YugaByte DB garante que os desenvolvedores não precisem aprender outra linguagem para trabalhar com um mecanismo de banco de dados distribuído e altamente consistente.

Neste artigo, vimos como os fundamentos do design de banco de dados diferem entre PostgreSQL, Cassandra e MongoDB. Em artigos futuros, mergulharemos em conceitos avançados de design, como índices, transações, JOINs, diretivas TTL e documentos JSON.

Desejamos-lhe um excelente resto de fim de semana e convidamo-lo a webinar grátis, que acontecerá no dia 14 de maio.

Fonte: habr.com

Adicionar um comentário