结合MariaDB与MongoDB:SQL与NoSQL的混合应用

假设你有一个使用Node.js(或其他平台)开发的应用程序,该应用连接到MongoDB数据库(NoSQL)来存储书籍评分(星级数量和评论)。同时,你还有另一个使用Java(或Python、C#、TypeScript等)开发的应用程序,它连接到MariaDB数据库(SQL,关系型)来管理书籍目录(书名、出版年份、页数)。

现在,你需要创建一个报告,显示每本书的书名和评分信息。注意,MongoDB数据库中并不包含书名,而关系型数据库中则没有评分信息。我们需要将由NoSQL应用生成的数据与SQL应用生成的数据进行整合。

A common approach to this is to query both databases independently (using different data sources) and process the data to match by, for example, ISBN (the id of a book) and put the combined information in a new object. This needs to be done in a programming language like Java, TypeScript, C#, Python, or any other imperative programming language that is able to connect to both databases.

A polyglot application

这种方法虽然可行,但数据关联本应是数据库的工作,它们专为此类数据操作而设计。此外,采用这种方法后,SQL应用不再是纯粹的SQL应用,它变成了多数据库混合应用,这增加了维护的复杂性。

通过使用数据库代理MaxScale,你可以在数据库层级上用最适合数据操作的语言——SQL来关联这些数据。你的SQL应用无需转变为多数据库混合应用。

尽管这需要在基础设施中增加一个额外的元素,但你也获得了数据库代理所提供的所有功能。例如自动故障转移透明数据屏蔽、拓扑隔离、缓存、安全过滤器等等。

MaxScale是一款强大的智能数据库代理,它理解SQL和NoSQL。它还理解Kafka(用于CDC或数据摄取),但这属于另一个话题。简而言之,通过MaxScale,你可以将NoSQL应用程序连接到一个完全符合ACID原则的关系数据库,并将数据直接存储在与其它SQL应用程序使用的表相邻的位置。

MaxScale允许SQL应用程序消费NoSQL数据。

让我们尝试用MaxScale快速进行一次简单易懂的实验。你需在你的计算机上安装以下内容:

设置MariaDB数据库

使用纯文本编辑器,创建一个新文件并将其保存为docker-compose.yml。文件应包含以下内容:

YAML

 

version: "3.9"
services:
  mariadb:
    image: alejandrodu/mariadb
    environment:
      - MARIADB_CREATE_DATABASE=demo
      - MARIADB_CREATE_USER=user:Password123!
      - MARIADB_CREATE_MAXSCALE_USER=maxscale_user:MaxScalePassword123!


  maxscale:
    image: alejandrodu/mariadb-maxscale
    command: --admin_host 0.0.0.0 --admin_secure_gui false
    ports:
      - "3306:4000"
      - "27017:27017"
      - "8989:8989"
    environment:
      - MAXSCALE_USER=maxscale_user:MaxScalePassword123!
      - MARIADB_HOST_1=mariadb 3306
      - MAXSCALE_CREATE_NOSQL_LISTENER=user:Password123!

这是一个DOCKER COMPOSE文件。它描述了由Docker创建的一组服务。我们将创建两个服务(或容器)——一个MariaDB数据库服务器和一个MaxScale数据库代理。它们将在您的机器上本地运行,但在生产环境中,通常会将它们部署在不同的物理机上。请记住,这些Docker镜像不适合生产环境!它们旨在适用于快速演示和测试。您可以在GitHub上找到这些镜像的源代码。对于MariaDB的官方Docker镜像,请访问MariaDB在Docker Hub的页面

之前的Docker Compose文件配置了一个名为demo的MariaDB数据库服务器(在MariaDB中,数据库和模式是同义词)。同时,创建了一个用户名为user,密码为Password123!的用户,该用户对demo数据库拥有适当的权限。还有一个名为maxscale_user的用户,密码为MaxScalePassword123!,这是MaxScale数据库代理用来连接MariaDB数据库的用户。

Docker Compose文件还通过禁用HTTPS(生产环境中切勿如此操作!)、暴露一组端口(稍后详述)以及配置数据库用户和MariaDB数据库代理的位置(通常是IP地址,但此处我们可使用Docker文件中先前定义的容器名称)来配置数据库代理。最后一行创建了一个NoSQL监听器,我们将利用它以MongoDB客户端身份在默认端口(27017)上进行连接。

若要通过命令行启动服务(容器),请转到保存Docker Compose文件的目录,并执行以下命令:

Shell

 

docker compose up -d

下载所有软件并启动容器后,您将获得一个预配置好的MariaDB数据库和MaxScale代理,专为此实验准备。

在MariaDB中创建SQL表

现在,让我们连接到关系型数据库。在命令行中执行以下操作:

Shell

 

mariadb-shell --dsn mariadb://user:'Password123!'@127.0.0.1

确认您能看到demo数据库:

MariaDB SQL

 

show databases;

切换到demo数据库:

MariaDB SQL

 

use demo;

使用MariaDB Shell连接到数据库。

创建books表:

MariaDB SQL

 

CREATE TABLE books(
  isbn VARCHAR(20) PRIMARY KEY,
  title VARCHAR(256),
  year INT
);

插入一些数据。我将采用插入自己的书籍这一陈词滥调:

MariaDB SQL

 

INSERT INTO books(title, isbn, year)
VALUES
  ("Vaadin 7 UI Design By Example", "978-1-78216-226-1", 2013),
  ("Data-Centric Applications with Vaadin 8", "978-1-78328-884-7", 2018),
  ("Practical Vaadin", "978-1-4842-7178-0", 2021);

通过运行以下命令检查书籍是否已存储在数据库中:

MariaDB SQL

 

SELECT * FROM books;

使用MariaDB Shell插入数据。

在MariaDB中创建JSON集合

我们尚未安装MongoDB,但我们仍可使用MongoDB客户端(或应用程序)连接并创建集合和文档,就像使用MongoDB一样,只是数据存储在一个功能强大、完全符合ACID且可扩展的关系数据库中。让我们来尝试一下!

在命令行中,使用MongoDB shell工具连接到MongoDB…等等…实际上是MariaDB数据库!运行以下命令:

Shell

 

mongosh

默认情况下,此工具尝试连接到运行在本地机器(127.0.0.1)上、使用默认端口(20017)的MongoDB服务器(这次实际上是MariaDB)。如果一切顺利,运行以下命令时您应该能看到列出的demo数据库:

Plain Text

 

show databases

切换到demo数据库:

Plain Text

 

use demo

使用Mongo Shell连接到MariaDB。

我们正通过一个非关系型客户端连接到关系型数据库!让我们创建ratings集合并插入一些数据:

Plain Text

 

db.ratings.insertMany([
	{
		"isbn": "978-1-78216-226-1",
		"starts": 5,
		"comment": "A good resource for beginners who want to learn Vaadin"
	},
	{
		"isbn": "978-1-78328-884-7",
		"starts": 4,
		"comment": "Explains Vaadin in the context of other Java technologies"
	},
	{
		"isbn": "978-1-4842-7178-0",
		"starts": 5,
		"comment": "The best resource to learn web development with Java and Vaadin"
	}
])

检查这些评分是否已持久化到数据库中:

Plain Text

 

db.ratings.find()

使用Mongo Shell查询MariaDB数据库。

在MariaDB中使用JSON函数

至此,我们拥有一个单一数据库,对外表现为既是NoSQL(MongoDB)数据库,也是关系型(MariaDB)数据库。我们能够连接到同一数据库,并从MongoDB客户端和SQL客户端进行数据的读写。所有数据均存储在MariaDB中,因此我们可以使用SQL将来自MongoDB客户端或应用程序的数据与来自MariaDB客户端或应用程序的数据进行联接。接下来,我们将探讨MaxScale如何利用MariaDB存储MongoDB数据(集合和文档)。

使用如mariadb-shell的SQL客户端连接到数据库,并展示demo模式下的表:

MariaDB SQL

 

show tables in demo;

你应能看到booksratings两张表。ratings作为MongoDB集合创建。MaxScale将来自MongoDB客户端的命令进行了转换,并创建了一张表以存储数据。现在,让我们查看这张表的结构:

MariaDB SQL

 

describe demo.ratings;

A NoSQL collection is stored as a MariaDB relational table.

ratings表包含两个列:

  1. id:对象ID。
  2. doc:以JSON格式表示的文档。

如果我们检查表的内容,我们会看到所有关于评分的数据都存储在doc列中,格式为JSON:

MariaDB SQL

 

SELECT doc FROM demo.ratings \G

NoSQL文档存储在MariaDB数据库中。

让我们回到最初的目标——展示带有评分信息的书籍标题。下面的情况并非如此,但让我们假设一下,ratings表是一个常规表,具有starscomment列。如果情况确实如此,将此表与books表连接将非常简单,我们的任务也就完成了:

MariaDB SQL

 

/* this doesn’t work */
SELECT b.title, r.stars, r.comment
FROM ratings r
JOIN books b USING(isbn)

回到现实。我们需要将实际ratings表的doc列转换为可以作为查询中新表使用的关系表达式。类似这样的操作:

MariaDB SQL

 

/* this still doesn’t work */
SELECT b.title, r.stars, r.comment
FROM ratings rt
JOIN ...something to convert rt.doc to a table... AS r
JOIN books b USING(isbn)

那个东西就是JSON_TABLE函数。MariaDB包含了一套全面的JSON函数,用于操作JSON字符串。我们将使用JSON_TABLE函数将doc列转换为我们可以用于执行SQL连接的关系形式。JSON_TABLE函数的一般语法如下:

MariaDB SQL

 

JSON_TABLE(json_document, context_path COLUMNS (
    column_definition_1,
    column_definition_2,
    ...
  )
) [AS] the_new_relational_table

其中:

  • json_document:一个字符串或表达式,返回用于处理的JSON文档。
  • `context_path`: 一个JSON路径表达式,定义了用于作为行来源的节点。

而列定义(如column_definition_1column_definition_2等)具有以下语法:

MariaDB SQL

 

new_column_name sql_type PATH path_in_the_json_doc [on_empty] [on_error]

结合这些知识,我们的SQL查询将如下所示:

MariaDB SQL

 

SELECT b.title, r.stars, r.comment
FROM ratings rt
JOIN JSON_TABLE(rt.doc, '$' COLUMNS(
    isbn VARCHAR(20) PATH '$.isbn',
    stars INT PATH '$.starts',
    comment TEXT PATH '$.comment'
  )
) AS r
JOIN books b USING(isbn);

在单个SQL查询中结合NoSQL和SQL数据。

我们可以将ISBN值用作MongoDB的ObjectID,进而作为ratings表中的id列,但这将留作你的练习(提示:使用_id而非isbn在通过MongoDB客户端或应用插入数据时)。

A Word on Scalability

有一种误解认为关系型数据库不能水平扩展(增加更多节点),而NoSQL数据库可以。然而,关系型数据库在不影响ACID属性的情况下进行扩展。MariaDB拥有多种存储引擎,针对不同工作负载量身定制。例如,你可以通过借助Spider实现数据分片来扩展MariaDB数据库。你还可以使用各种存储引擎,以每表为基础处理不同的工作负载。在单个SQL查询中实现跨引擎连接是可能的。

在单一逻辑MariaDB数据库中结合多种存储引擎。

另一种更现代的选择是采用分布式SQLMariaDB Xpand。分布式SQL数据库通过透明分片技术,对应用程序呈现为一个单一的逻辑关系数据库。它采用无共享架构,可同时扩展读写操作。

A distributed SQL database deployment.

总结

我们的任务至此完成!现在,无论数据是由SQL还是NoSQL应用生成,您的系统都能拥有符合ACID原则且可扩展的全方位数据视图。无需再将应用从NoSQL迁移到SQL,也无需使SQL应用成为数据库多面手。如需了解更多MaxScale的其他功能,请观看此视频或访问文档

Source:
https://dzone.com/articles/mixing-sql-and-nosql-with-mariadb-and-mongodb