1. 程式人生 > >為什麼MySQL將會是一個更好的NoSQL

為什麼MySQL將會是一個更好的NoSQL

前言

MySQL是一個更好的NoSQL資料庫。當考慮到NoSQL的使用案例,比如對Key/Value鍵值儲存來講,MySQL在效能、易用性和穩定性方面更有意義。MySQL畢竟是一款成熟穩定的產品,在網際網路上有大量的線上教程,範圍從操作到失敗案例,從主從複製到其它不同模式的應用,不一而足。基於這個原因,MySQL相比其他新興並沒有經過多年洗禮的NoSQL來講,確實有一定的優勢。

近些年來,NoSQL慢慢成為了主流。許多開發者把這些NoSQL資料庫,比如MongoDB、Cassandra、Redis或者Hadoop等,當作他們構建應用的資料庫首選,而把老舊的傳統資料庫廢棄不用。

選用NoSQL資料庫,經常是建立在其不實或者誇大的宣傳,和對傳統關係型資料庫效能不佳的假定上。開發者在選擇資料庫時,會特別看重操作成本,以及穩定性和成熟性。更多的不同NoSQL和關係型資料庫的侷限對比,可以參考Aphyr上Jepsen的一些列文章。

這篇文章會解釋給大家為什麼我們發現MySQL對於鍵值儲存場景來說,比大多數專有NoSQL引擎還要好。另外,本文也會提供給大家在MySQL中此應用的參考。

歸一化模型的問題

當用戶點選一個連結到Wix網站時,他/她的瀏覽器會發送一個帶有網站地址的HTTP請求給Wix的伺服器。無論是自定義域名(比 如:domain.com)請求一個Wix的優質地址,還是一個在Wix域名下的免費的子域名(比如:user.wix.com/site),這個 HTTP請求都會發生。伺服器不得不在網站地址中執行鍵值查詢來處理使用者對某個地址的請求。我們用路由來表示URL,進行下面的討論。

路由表用於將站點地址解析為一個站點物件。因為站點可以暴露在多個路由中,所以是多對一的關係。一旦網站被發現,則應用將其載入以備使用。站點物件本身具有 複雜的結構,其中包涵兩個子列表,列表中的物件表示了站點使用的不同服務。下面是一個物件的模型圖,假設使用了標準SQL資料庫和歸一化模型:

當使用傳統的歸一化模型更新網站時,我們需要一個事務來更新多表,以確保保持資料一致性(注意,這裡的事務使用的是資料庫級別的鎖來避免併發寫,有些時候用 來避免在受影響的表中併發讀)。繼續使用這一模型,我們可能會有每張表的序列鍵、外來鍵以及在路由表中對於URL列的索引。

然而,這裡有幾個使用歸一化模型帶來的問題:

  • 資料庫鎖限制了對錶的訪問,所以在高吞吐量的場景下,我們的效能可能會受一些影響

  • 讀取物件涉及幾個SQL查詢(在本例中使用了4個)或聯接——再次影響了延遲

  • 序列鍵施加鎖和再次限制寫入吞吐量

這些問題相當於限制了我們使用MySQL(或者其它任何SQL引擎)時的吞吐量和併發。因為這些短板,外加事實上我們需要的是鍵值儲存,所以許多開發者傾向於使用NoSQL作為解決方案以提供更高的吞吐和更多的併發,甚至是更好的穩定性、一致性和高可用。

MySQL來作為引擎的優勢

在Wix,我們發現,當我們“有創造性的”使用MySQL作為鍵值儲存時,能夠提供比上面提到的使用歸一化資料模型或者其它大多數NoSQL資料庫引擎更好的效能。簡單的使用MySQL來作為NoSQL引擎,我們現有的系統,無論是在伸縮性、吞吐量、併發和延遲指數上,相較NoSQL都具有令人驚豔的效能。 下面列舉一些資料:

  • 橫跨三個資料中心的異地多活

  • 吞吐量在200,000RPM的數量級

  • 路由表記錄在100,000,000的數量級,10GB的儲存

  • 站點表記錄在100,000,000的數量級,200GB的儲存

  • 讀延遲平均在1.0-1.5毫秒(事實上,在一個數據中心,延遲為0.2-03毫秒)

值得注意的是,對於絕大多數的鍵值引擎,無論是開源資料庫還是雲資料庫,1.0毫秒的延遲被認為是相當令人驚訝的水平。然而我們用MySQL就實現了這一壯舉(考慮到還使用了基本的SQL引擎)

這是我們實際使用的模型:

任何未被當做查詢條件的欄位,都被放置在一個單一的blob欄位(上面的site_data欄位)。其中包含子物件表,和其他表本身的欄位。另外注意,我們 並未使用序列鍵,相反的,我們使用了一個varchar(50)的欄位,用於儲存客戶端生成的GUID值。

下面是我們使用的一個查詢,具備高吞吐的同時,還具備了低延遲:

工作原理是這樣的,首先使用唯一索引在路由表上執行查詢,應該盡的到一條記錄。接著使用這條記錄的主鍵,在站點表執行查詢,返回的記錄也是一條。

巢狀的查詢語法可以確保這兩個 SQL查詢僅在資料庫記錄中查詢一次。

上面的結果顯示,平均延遲在1毫秒以下,並且在高流量和高更新率的情況下能保證一致性。雖然沒有使用事務,但是update卻是半事務的。因為我們在一條插入語句中輸入了完整的網站地址,直到進入路由表,這條記錄才會被發現。

所以如果我們首先輸入了站點網址,然後是路由,我們就能確保有一個一致性的狀態,即使是在最邊緣的情況下,我們也僅僅是在站點表中有一些“孤兒”資料。

使用從上面例子(或者在Wix的其它案例)中的到的經驗,我們簡要的列舉出了一個使用MySQL當做NoSQL引擎使用的參考。

當使用MySQL當做NoSQL引擎使用時,首先要記住的一點是避免使用資料庫級別的鎖或者是複雜查詢:

  • 不要使用事務,因為事務引入了鎖。相反的,使用應用來控制事務

  • 不要使用序列鍵。序列鍵引入了鎖和其它敷在的啟動配置

  • 使用客戶端生成唯一鍵,我們使用了GUID

當為優化讀設計模型時,鞋面是額外的一些經驗僅供參考:

  • 不使用歸一化模型

  • 所有的欄位在被索引時才有必要存在。如果欄位在查詢時不需要,則將其放到一個blob/text欄位中(如JSON或者XML)

  • 不要使用外來鍵

  • 設計你的模型,來確保查詢時僅讀取單獨的一行

  • 不要在表上使用alter命令。修改表的命令引入了鎖和停機時間,相反的,嘗試使用實時遷移

當查詢時:

  • 使用主鍵或者索引查詢記錄

  • 避免使用表連線

  • 避免使用聚合函式

  • 儘量在從庫上執行較為複雜的查詢(如BI,資料研究等),避免在master資料庫上執行上述操作

總結

最值得在這篇文章中看到的是如何打破思維嘗試不同的思考。使用MySQL來當做NoSQL引擎,看起來是不錯的,雖然MySQL最開始並不是為此而設計的。 本文中演示的,是一個使用MySQL而不是NoSQL引擎來構建鍵值訪問。在Wix,MySQL是我們的鍵值儲存場景的選擇是因為操作簡單、使用簡單,並且MySQL本身有極好的生態。此外,MySQL還能提供額外的低延遲、高吞吐和資料一致性保證。