Wix是如何把MySQL當NoSQL用的
近年來,NoSQL引擎已經成為主流。許多開發人員都將NoSQL引擎(如MongoDB、Cassandra或Redis)作為構建應用程序的首選。Yoav Abrahami是免費建站平臺 Wix 的首席架構師。他認為,MySQL是一個更好的NoSQL選項,理由是:MySQL是一個可靠的引擎,有大量的在線資料可供參考,而且作為鍵/值存儲,能夠提供卓越的性能、易用性和穩定性。近日,他用Wix的實踐 證明 了這一觀點。
當用戶點擊指向Wix站點的鏈接時,瀏覽器會向Wix服務器發送一個HTTP請求。服務器會執行一次鍵/值查找,將URL(Yoav將其成為路由)解析成一個站點,并加載該站點。由于站點可能暴露到多個路由上,所以路由與站點之間是多對一的關系。站點對象本身有一個復雜的結構,包含兩個子對象列表。下面是在標準SQL數據庫中記錄上述對象的一個規范化模式:
在這樣一個傳統模型中,為了在更新站點的時候保持數據一致性,需要使用事務同時對多個表進行更新。事物會使用數據庫級鎖阻止并發寫入。而且,這個模型中的每個表都有“序列鍵(serial key)”、外鍵,路由表的URL字段上建有索引。這個模型有如下幾個問題:
- 鎖限制了表的訪問,在吞吐量比較高的情況下,會影響性能;
- 讀取一個對象需要數個SQL查詢或多個表關聯,會增加延遲;
- 序列鍵會使用鎖,再次限制了寫吞吐量。
這些問題限制了MySQL的吞吐量和并發性。考慮到這實際上是一個鍵/值存儲場景,許多開發人員會優先考慮尋找一種NoSQL解決方案。但在 Wix,他們創造性地將MySQL當作一個鍵/值存儲來用,而且取得了很好的效果。例如,讀延遲平均只有1~1.5毫秒 。這一延遲可以同大多數鍵/值存儲引擎相媲美。
以下是他們實際使用的數據庫模式:
CREATE TABLE `routes` ( `route` varchar(255) NOT NULL, `site_id` varchar(50) NOT NULL, `last_update_date` bigint NOT NULL, PRIMARY KEY (`key`), KEY (`site_id`) ) CREATE TABLE `sites` ( `site_id` varchar(50) NOT NULL, `owner_id` varchar(50) NOT NULL, `schema_version` varchar(10) NOT NULL DEFAULT '1.0', `site_data` text NOT NULL, `last_update_date` bigint NOT NULL, PRIMARY KEY (`site_id`) ) /*ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=16*/;
任何不在查詢條件中使用的字段都并入一個Blob字段(site_data字段)。另外,使用varchar2(50)取代了序列鍵,用于存儲客戶生成的GUID值。這樣,使用下面的查詢就可以查找HTTP請求對應的站點:
select * from sites where site_id = ( select site_id from routes where route = ? )
該查詢首先通過唯一索引查找站點標識,然后通過主鍵查找對應的站點,只需訪問一次數據庫就可以完成請求解析,可以提供很高的吞吐量和很低的延遲。在這種模式下,更新甚至都不需要事務,因為在一個插入語句中插入整個站點的信息后,該信息在路由插入之前并不會被讀取。
根據從上述實例中獲取的經驗,Yoav對如何將MySQL用作NoSQL引擎進行了總結。首先,要避免使用數據庫鎖或復雜查詢:
- 不要使用事務,那會引入鎖,使用“應用事務(applicative transactions)”代替;
- 不要使用序列鍵,那會引入鎖,使雙活配置復雜化;
- 使用客戶端生成的唯一鍵。
其次,在設計數據庫模式時要專門針對數據讀取操作進行優化:
- 不要規范化;
- 只保留索引字段,其他字段存入Blob/text字段;
- 不要使用外鍵;
- 模式設計要支持單行查詢;
- 不要執行alter命令。
最后,在查詢數據時要遵循如下原則:
- 通過主鍵或索引查詢記錄;
- 不要使用表連接;
- 不要使用聚合;
- 在副本上運行內務處理(如BI、數據挖掘等)查詢,而不要在主數據庫上。
總之,MySQL很適合作為NoSQL引擎使用,Wix將其用作鍵/值存儲獲得了很好的效果,MySQL為Wix提供了堪與大多數NoSQL引擎相媲美的延遲、吞吐量和并發性。