Fundamentos del diseño de bases de datos: comparación de PostgreSQL, Cassandra y MongoDB

Hola amigos. Antes de partir para la segunda parte de las vacaciones de mayo, compartimos con ustedes el material que hemos traducido en previsión del lanzamiento de una nueva transmisión a la tarifa "DBMS relacional".

Fundamentos del diseño de bases de datos: comparación de PostgreSQL, Cassandra y MongoDB

Los desarrolladores de aplicaciones dedican mucho tiempo a comparar varias bases de datos operativas para elegir la que mejor se adapte a su carga de trabajo prevista. Las necesidades pueden incluir modelado de datos simplificado, garantías transaccionales, rendimiento de lectura/escritura, escalado horizontal y tolerancia a fallas. Tradicionalmente, la elección comienza con la categoría de la base de datos, SQL o NoSQL, ya que cada categoría ofrece un conjunto claro de ventajas y desventajas. El alto rendimiento en términos de baja latencia y alto rendimiento generalmente se considera un requisito que no se puede comprometer y, por lo tanto, es esencial para cualquier base de datos de la muestra.

El propósito de este artículo es ayudar a los desarrolladores de aplicaciones a elegir correctamente entre SQL y NoSQL en el contexto del modelado de datos de aplicaciones. Veremos una base de datos SQL, a saber, PostgreSQL, y dos bases de datos NoSQL, Cassandra y MongoDB, para cubrir los conceptos básicos del diseño de bases de datos, como crear tablas, completarlas, leer datos de una tabla y eliminarlos. En el próximo artículo, definitivamente veremos índices, transacciones, JOIN, directivas TTL y diseño de base de datos basado en JSON.

¿Cuál es la diferencia entre SQL y NoSQL?

Las bases de datos SQL aumentan la flexibilidad de las aplicaciones a través de garantías transaccionales ACID, así como su capacidad para consultar datos utilizando JOIN de formas inesperadas además de los modelos de bases de datos relacionales normalizados existentes.

Dada su arquitectura monolítica/de un solo nodo y el uso de un modelo de replicación maestro-esclavo para la redundancia, las bases de datos SQL tradicionales carecen de dos características importantes: escalabilidad de escritura lineal (es decir, división automática entre múltiples nodos) y pérdida de datos automática/cero. Esto significa que la cantidad de datos recibidos no puede exceder el rendimiento de escritura máximo de un solo nodo. Además, se debe tener en cuenta cierta pérdida temporal de datos para la tolerancia a fallos (en una arquitectura no compartida). Aquí debes tener en cuenta que las confirmaciones recientes aún no se han reflejado en la copia esclava. También es difícil lograr actualizaciones sin tiempo de inactividad en las bases de datos SQL.

Las bases de datos NoSQL suelen tener una naturaleza distribuida, es decir, en ellos, los datos se dividen en secciones y se distribuyen en varios nodos. Requieren desnormalización. Esto significa que los datos ingresados ​​​​también deben copiarse varias veces para responder a las solicitudes específicas que envíe. El objetivo general es obtener un alto rendimiento al reducir la cantidad de fragmentos disponibles en el momento de la lectura. Esto implica que NoSQL requiere que modeles tus consultas, mientras que SQL requiere que modeles tus datos.

NoSQL enfatiza el logro de un alto rendimiento en un clúster distribuido y esta es la razón principal para muchas compensaciones en el diseño de bases de datos, que incluyen la pérdida de transacciones ACID, JOIN e índices secundarios globales consistentes.

Existe la opinión de que, aunque las bases de datos NoSQL brindan escalabilidad de escritura lineal y alta tolerancia a fallas, la pérdida de garantías transaccionales las hace inadecuadas para datos críticos.

La siguiente tabla muestra cómo el modelado de datos en NoSQL difiere de SQL.

Fundamentos del diseño de bases de datos: comparación de PostgreSQL, Cassandra y MongoDB

SQL y NoSQL: ¿Por qué son necesarios ambos?

Las aplicaciones de la vida real con una gran cantidad de usuarios, como Amazon.com, Netflix, Uber y Airbnb, se encargan de realizar tareas complejas de diversa índole. Por ejemplo, una aplicación de comercio electrónico como Amazon.com necesita almacenar datos livianos y altamente confidenciales, como información sobre usuarios, productos, pedidos, facturas, junto con datos pesados ​​pero menos confidenciales, como reseñas de productos, mensajes de soporte y actividad del usuario. , opiniones y recomendaciones de usuarios. Naturalmente, estas aplicaciones dependen de al menos una base de datos SQL junto con al menos una base de datos NoSQL. En sistemas interregionales y globales, la base de datos NoSQL funciona como un caché distribuido geográficamente para los datos almacenados en una fuente confiable, la base de datos SQL, que opera en cualquier región.

¿Cómo combina YugaByte DB SQL y NoSQL?

Basado en un motor de almacenamiento mixto orientado a registros, fragmentación automática, replicación de consenso distribuida fragmentada y transacciones distribuidas ACID (inspiradas en Google Spanner), YugaByte DB es la primera base de datos de código abierto del mundo que es simultáneamente compatible con NoSQL (Cassandra y Redis). ) y SQL (PostgreSQL). Como se muestra en la siguiente tabla, YCQL, una API de base de datos de YugaByte compatible con Cassandra, agrega los conceptos de transacciones ACID de clave única y múltiple e índices secundarios globales a la API de NoSQL, marcando así el comienzo de la era de las bases de datos transaccionales de NoSQL. Además, YCQL, una API de base de datos de YugaByte compatible con PostgreSQL, agrega los conceptos de escalado de escritura lineal y conmutación por error automática a la API de SQL, trayendo las bases de datos SQL distribuidas al mundo. Dado que la base de datos YugaByte DB es inherentemente transaccional, la API NoSQL ahora se puede usar en el contexto de datos críticos.

Fundamentos del diseño de bases de datos: comparación de PostgreSQL, Cassandra y MongoDB

Como ya se dijo en el artículo "Presentación de YSQL: una API de SQL distribuido compatible con PostgreSQL para YugaByte DB", la elección entre SQL o NoSQL en YugaByte DB depende completamente de las características de la carga de trabajo subyacente:

  • Si su carga de trabajo principal son las operaciones JOIN de varias claves, al elegir YSQL, tenga en cuenta que sus claves pueden estar distribuidas en varios nodos, lo que resulta en una latencia más alta y/o un rendimiento más bajo que NoSQL.
  • De lo contrario, elija cualquiera de las dos API NoSQL, teniendo en cuenta que obtendrá un mejor rendimiento como resultado de las consultas atendidas desde un nodo a la vez. YugaByte DB puede servir como una base de datos operativa única para aplicaciones realmente complejas que necesitan administrar múltiples cargas de trabajo al mismo tiempo.

El laboratorio de modelado de datos de la siguiente sección se basa en las API de bases de datos YugaByte DB compatibles con PostgreSQL y Cassandra, a diferencia de las bases de datos originales. Este enfoque enfatiza la facilidad de interactuar con dos API diferentes (en dos puertos diferentes) del mismo clúster de bases de datos, en lugar de utilizar clústeres completamente independientes de dos bases de datos diferentes.
En las siguientes secciones, echaremos un vistazo al Laboratorio de modelado de datos para ilustrar la diferencia y algunos de los puntos en común de las bases de datos en cuestión.

Laboratorio de modelado de datos

Instalación de bases de datos

Dado el enfoque en el diseño del modelo de datos (en lugar de arquitecturas de implementación complejas), instalaremos las bases de datos en contenedores Docker en la máquina local y luego interactuaremos con ellos usando sus respectivos shells de línea de comandos.

Compatible con PostgreSQL y Cassandra, base de datos YugaByte DB

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

Acceso a la línea de comandos

Conectémonos a las bases de datos utilizando el shell de línea de comandos para las API respectivas.

PostgreSQL

psql es un shell de línea de comandos para interactuar con PostgreSQL. Para facilitar su uso, YugaByte DB viene con psql directamente en la carpeta bin.

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

Cassandra

sqlsh es un shell de línea de comandos para interactuar con Cassandra y sus bases de datos compatibles a través de CQL (Cassandra Query Language). Para facilitar su uso, YugaByte DB viene con cqlsh en el catalogo bin.
Tenga en cuenta que CQL se inspiró en SQL y tiene conceptos similares de tablas, filas, columnas e índices. Sin embargo, como lenguaje NoSQL, agrega un cierto conjunto de restricciones, la mayoría de las cuales también cubriremos en otros artículos.

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

MongoDB

mongo es un shell de línea de comandos para interactuar con MongoDB. Se puede encontrar en el directorio bin de la instalación de MongoDB.

docker exec -it my-mongo bash 
cd bin
mongo

crear una tabla

Ahora podemos interactuar con la base de datos para realizar varias operaciones usando la línea de comando. Comencemos por crear una tabla que almacene información sobre canciones escritas por diferentes artistas. Estas canciones pueden ser parte de un álbum. También los atributos opcionales para la canción son el año de lanzamiento, el precio, el género y la clasificación. Necesitamos tener en cuenta atributos adicionales que puedan ser necesarios en el futuro a través del campo "etiquetas". Puede almacenar datos semiestructurados como pares clave-valor.

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

Crear una tabla en Cassandra es muy similar a PostgreSQL. Una de las principales diferencias es la ausencia de restricciones de integridad (como NOT NULL), pero esto es responsabilidad de la aplicación, no de la base de datos NoSQL.. La clave principal consta de una clave de partición (columna Artista en el ejemplo a continuación) y un conjunto de columnas de agrupación (columna Título de canción en el ejemplo a continuación). La clave de partición determina en qué partición/fragmento colocar la fila, y las columnas de agrupación indican cómo se deben organizar los datos dentro del fragmento actual.

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

MongoDB organiza datos en bases de datos (Base de datos) (similar a Keyspace en Cassandra), donde hay colecciones (Colecciones) (similares a tablas) que contienen documentos (Documentos) (similares a filas en una tabla). En MongoDB, en principio, no se requiere ninguna definición de esquema inicial. Equipo "utilizar base de datos", que se muestra a continuación, crea una instancia de la base de datos en la primera llamada y cambia el contexto de la base de datos recién creada. Incluso las colecciones no necesitan crearse explícitamente, se crean automáticamente, justo cuando se agrega el primer documento a una nueva colección. Tenga en cuenta que MongoDB utiliza una base de datos de prueba de forma predeterminada, por lo que cualquier operación a nivel de colección sin especificar una base de datos específica se realizará en ella de forma predeterminada.

use myNewDatabase;

Obtener información sobre una tabla
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;

Introducir datos en una tabla
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

En general, la expresión INSERT en Cassandra se parece mucho al de PostgreSQL. Sin embargo, hay una gran diferencia en la semántica. En Casandra INSERT en realidad es una operacion UPSERT, donde se agregan los últimos valores a la cadena, en caso de que la cadena ya exista.

La entrada de datos es similar a PostgreSQL INSERT arriba

.

MongoDB

Aunque MongoDB es una base de datos NoSQL como Cassandra, su operación de entrada de datos no tiene nada que ver con el comportamiento semántico de Cassandra. En Mongo DB insertar() no tiene oportunidad UPSERT, lo que lo hace similar a PostgreSQL. Adición de datos predeterminados sin _idspecified dará como resultado que se agregue un nuevo documento a la colección.

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 tabla

Quizás la diferencia más significativa entre SQL y NoSQL en términos de consultas es el uso de FROM и WHERE. SQL permite después de la expresión FROM seleccione varias tablas y una expresión con WHERE puede ser de cualquier complejidad (incluidas las operaciones JOIN entre mesas). Sin embargo, NoSQL tiende a imponer un límite estricto a FROM, y trabajar con una sola tabla especificada, y en WHERE, siempre se debe especificar la clave principal. Esto se debe al deseo de mejorar el rendimiento de NoSQL, del que hablamos anteriormente. Este deseo conduce a la reducción posible de cualquier interacción entre tabulaciones y claves cruzadas. Puede introducir un gran retraso en la comunicación entre nodos al responder a una solicitud y, por lo tanto, en principio es mejor evitarlo. Por ejemplo, Cassandra requiere que las solicitudes se restrinjan a ciertos operadores (solo se permiten =, IN, <, >, =>, <=) en claves de partición, excepto cuando se consulta un índice secundario (aquí solo se permite el operador =).

PostgreSQL

Los siguientes son tres ejemplos de consultas que una base de datos SQL puede ejecutar fácilmente.

  • Mostrar todas las canciones del artista;
  • Mostrar todas las canciones del artista que coincidan con la primera parte del título;
  • Muestra todas las canciones del artista que tienen una determinada palabra en el título y tienen un precio 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

De las consultas de PostgreSQL enumeradas anteriormente, solo la primera funcionará sin cambios en Cassandra, porque la declaración LIKE no se puede aplicar a columnas de agrupación como SongTitle. En este caso, solo se permiten operadores = и 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

Como se muestra en los ejemplos anteriores, el método principal para crear consultas en MongoDB es db.colección.buscar(). Este método contiene explícitamente el nombre de la colección (music en el ejemplo siguiente), por lo que no se permite consultar varias colecciones.

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

Leer todas las filas de una tabla

Leer todas las filas es sólo un caso especial del patrón de consulta que analizamos anteriormente.

PostgreSQL

SELECT * 
FROM Music;

Cassandra

Similar al ejemplo de PostgreSQL anterior.

MongoDB

db.music.find( {} );

Edición de datos en una tabla

PostgreSQL

PostgreSQL proporciona una declaración UPDATE para cambiar datos. ella no tiene oportunidad UPSERT, por lo que esta declaración fallará si la fila ya no está en la base de datos.

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

Cassandra

Casandra tiene UPDATE similar a PostgreSQL. UPDATE tiene la misma semántica UPSERT, como INSERT.

Similar al ejemplo de PostgreSQL anterior.

MongoDB
operación actualizar() en MongoDB puede actualizar completamente un documento existente o actualizar solo ciertos campos. Por defecto solo actualiza un documento con la semántica deshabilitada UPSERT. Actualizar varios documentos y comportamiento similar UPSERT se puede aplicar configurando banderas adicionales para la operación. Como por ejemplo en el ejemplo de abajo, el género de un artista en particular es actualizado por su canción.

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

Eliminar datos de una tabla

PostgreSQL

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

Cassandra

Similar al ejemplo de PostgreSQL anterior.

MongoDB

MongoDB tiene dos tipos de operaciones para eliminar documentos: eliminarUno() /eliminarmuchos() и eliminar(). Ambos tipos eliminan documentos pero devuelven resultados diferentes.

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

Eliminación de una tabla

PostgreSQL

DROP TABLE Music;

Cassandra

Similar al ejemplo de PostgreSQL anterior.

MongoDB

db.music.drop();

Conclusión

El debate sobre la elección entre SQL y NoSQL se ha prolongado durante más de 10 años. Hay dos aspectos principales en este debate: la arquitectura del motor de la base de datos (SQL monolítico y transaccional frente a NoSQL distribuido y no transaccional) y el enfoque del diseño de la base de datos (modelado de datos en SQL frente a modelado de consultas en NoSQL).

Con una base de datos transaccional distribuida como YugaByte DB, el debate sobre la arquitectura de la base de datos se puede disipar fácilmente. A medida que los volúmenes de datos crecen más de lo que se puede escribir en un solo nodo, se hace necesaria una arquitectura completamente distribuida que admita escalabilidad de escritura lineal con fragmentación/reequilibrio automático.

Además de lo dicho en uno de los artículos. Google CloudLas arquitecturas altamente consistentes y transaccionales ahora se adoptan más ampliamente para proporcionar una mayor flexibilidad de desarrollo que las arquitecturas no transaccionales y finalmente consistentes.

Volviendo a la discusión sobre el diseño de bases de datos, es justo decir que ambos enfoques de diseño (SQL y NoSQL) son necesarios para cualquier aplicación compleja del mundo real. El enfoque de "modelado de datos" de SQL permite a los desarrolladores satisfacer más fácilmente los requisitos comerciales cambiantes, mientras que el enfoque de "modelado de consultas" de NoSQL permite a esos mismos desarrolladores manejar grandes cantidades de datos con baja latencia y alto rendimiento. Es por esta razón que YugaByte DB proporciona API de SQL y NoSQL en un núcleo común y no recomienda ninguno de los enfoques. Además, al proporcionar compatibilidad con lenguajes de bases de datos populares, incluidos PostgreSQL y Cassandra, YugaByte DB garantiza que los desarrolladores no tengan que aprender otro idioma para trabajar con un motor de base de datos distribuido y fuertemente consistente.

En este artículo, analizamos en qué se diferencian los fundamentos del diseño de bases de datos entre PostgreSQL, Cassandra y MongoDB. En los siguientes artículos, profundizaremos en conceptos de diseño avanzados como índices, transacciones, JOIN, directivas TTL y documentos JSON.

Les deseamos un excelente fin de semana y los invitamos a seminario web gratuitoque tendrá lugar el 14 de mayo.

Fuente: habr.com

Añadir un comentario