結合SQL與NoSQL:MariaDB與MongoDB的應用

假設您有一個使用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映像,請前往Docker Hub上的MariaDB頁面

先前的Docker Compose檔案設定了名為demo的MariaDB資料庫伺服器(資料庫與schema在MariaDB中為同義詞)。同時,創建了一個用戶名user,其密碼為Password123!。此用戶對demo資料庫擁有適當的權限。另外還有一個用戶名maxscale_user,密碼為MaxScalePassword123!,這是MaxScale資料庫代理將用來連接到MariaDB資料庫的用戶。

Docker Compose檔案還配置了資料庫代理,禁用HTTPS(生產環境中切勿如此操作!),開放一組埠(稍後詳述),並配置了資料庫用戶及MariaDB資料庫代理的位置(通常是IP地址,但在此我們可以使用Docker檔案中先前定義的容器名稱)。最後一行創建了一個NoSQL listener,我們將用它以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數據(集合和文檔)的。

使用SQL客戶端如mariadb-shell連接到資料庫,並顯示演示模式中的表:

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)

那個something即為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文件,可以是字串或返回JSON文件的表達式。
  • context_path: 一個JSON Path表達式,定義用作行來源的節點。

而列定義(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數據庫中結合多個存儲引擎。

另一種更現代的替代方案是分散式SQL搭配MariaDB Xpand。分散式SQL資料庫透過透明分片,對應用程式呈現為單一邏輯關聯式資料庫。它採用無共享架構,能同時擴展讀取和寫入操作。

A distributed SQL database deployment.

結論

我們的任務已圓滿完成!現在,您的系統能夠擁有一個符合ACID原則、可擴展的360度數據視圖,無論數據是由SQL或NoSQL應用程式產生。從NoSQL遷移至SQL或使SQL應用程式成為多資料庫通用型的需求減少。如欲了解更多MaxScale的其他功能,請觀看此影片或訪問文件

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