iT邦幫忙

2024 iThome 鐵人賽

DAY 3
0
Software Development

Datomic,內建事件溯源的資料庫。系列 第 3

一些 SQL 資料庫共同的缺陷

  • 分享至 

  • xImage
  •  

之前,我跟朋友介紹 Datomic 時,有一些朋友會在第一時間反問:

你講的這個資料庫是 SQL 還是 noSQL ?

我通常是這樣子回答:

  1. 不是 SQL。Datomic 提供的查詢語言不是 SQL ,而是 Datalog。
  2. 多數的 noSQL 資料庫,它們預設要處理的使用情境通常是 SQL 資料庫相對不適用的,比方說,要做到水平擴展 (scale out),分散式的應用、極致的效能等等。然而,Datomic 在設計的最初,就是要來跟 SQL 資料庫競爭,它們是用來處理同樣的使用情境,也就是應用軟體需要搭配的營運資料庫 (operational database)。

既然是設計來取代 SQL 資料庫的,那 SQL 資料庫有什麼共同的設計缺陷嗎?有的,而且還不少。只是說,由於有很多缺陷都已經有了所謂的權宜之計 (workaround),久了,大家也就久而不聞其臭,當作這是自然的現象。

以下列出幾項值得討論的缺陷:

  1. 資料庫的狀態無法回溯
  2. 字串型態的查詢語言 (string based query language)
  3. 鎖與隔離的問題
  4. Schema 難以查詢
  5. Schema 僵化且固定
  6. 阻抗不匹配 (Impedance mismatch)

資料庫的狀態無法回溯

假設你在經營一間水果行,8/1 開店時庫存資料庫的狀態如下:

id 產品 存量
蘋果 100
橘子 200

8/1 一整天銷售了 60 顆蘋果。於是, 8/2 開店時庫存如下:

id 產品 存量
蘋果 40
橘子 200

於是,到了月底 8/31 時,我們臨時想要知道月初時的庫存,很遺憾,辦不到。

因為如果沒有特別把資料存下來的話,SQL 資料庫沒有辦法像版本控管軟體一樣,做到先前狀態的回溯。

字串型態的查詢語言

對 SQL 有點熟悉度的讀者可能心想,咦,這有什麼問題嗎?像下方的 SQL 查詢,不是也都使用了好多年,滿順手的啊?

SELECT * 
FROM table_name
WHERE column_A = 5; 

像上述的查詢,如果可以更改設計,改成資料結構型態的查詢語言,就會好得多,比方說:

{:select :*
 :from "table_name"
 :where [:= "column_A" 5]}

資料結構

在解釋為何字串型態的查詢語言不佳之前,我們需要先定義一個詞彙:『資料結構』(data structure)

在傳統的資訊工程課程,資料結構是指:雜湊 (hash)、堆積 (heap)、樹 (tree) 、堆疊 (stack)、佇列 (queue) 等東西,重點著重在於不同的儲存資料方式會有不同的效能特性。然而,在程式語言的領域,資料結構則是指:集合向量字典,此處的重點則著重在於程式語言所提供的表示法。

以 Clojure 語言為例:

  • 集合 #{:a :b :c}
  • 向量 [:a :b :c]
  • 字典 {:a "hello" :b 15}

資料結構型態帶來的優點

比較前述 SQL 查詢的兩種表示法之後,我們可以自問兩個問題:

  1. 哪一種比較容易看得懂?
  2. 哪一種比較容易用程式語言加以操作?

以 SQL 查詢為例,通常凡是程式語言的字串,它都內含有所謂的語法樹 (syntax tree)。學會寫 SQL 的同時,這個語法樹的概念也就自然地內建到寫作者的腦海中,然而,更直接的方式,是在寫查詢語言時,就透過資料結構型態來呈現語法樹,讓重要的概念更加明顯,所以是資料結構型態更容易看得懂。

此外,讀者可以仔細觀察,在資料結構型態的表示法裡,字串、整數都需要清楚地寫出來,而在上頭的例子裡的字串 "column_A" 、例子裡的整數 5 ,這兩個值的資料型態也都會被編譯器檢查。

現在,多數的程式語言都提供強大的資料結構操作函式庫。換言之,如果今天我們需要寫另一個查詢,它的判斷條件需要做一些變化,需要是第 B 欄的值恰好為 16

{:select :*
 :from "table_name"
 :where [:= "column_B" 16]}

如果是用 Clojure 語言來做操作的話,我們可以寫成:

(def old-query 
  {:select :*
   :from "table_name"
   :where [:= "column_A" 5]})

(def new-query 
  (assoc old-query :where [:= "column_B" 16]))
  
(prn new-query)
;; =>
;;    {:select :*
;;     :from "table_name"
;;     :where [:= "column_B" 16]}

(assoc old-query :where [:= "column_B" 16]) 的這一小段 code snippet,就可以將 new-query 的內容做出。

其它資源:

  1. 歡迎訂閱PruningSuccess電子報,主要談論軟體開發、資料處理、資料分析等議題。
  2. 歡迎加入Clojure社群

上一篇
事件溯源
下一篇
一些 SQL 資料庫共同的缺陷 - 續
系列文
Datomic,內建事件溯源的資料庫。25
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言