數據庫深度解析 :從NoSQL歷史看未來(轉)

原本地址:http://it.dataguru.cn/article-7976-1.html

的付出查非 本文根據王晶昱(花名沈詢)老師在“高可用架構”微信群所做的《NoSQLVS SQL》分享整理而成,轉發請注明來自微信公眾號ArchNotes。

王晶昱,花名沈詢,阿里資深技術專家,專注分布式數據庫七年。畢業加入淘寶,在淘寶分布式數據層干了七年,參與了大部分的淘寶數據庫業務架構設計工作。目前關注于分布式數據庫DRDS和分布式消息系統ONS的研發工作。

以下為分享內容實錄:

前言

今天想介紹的東西是NoSQL到SQL。我之所以選擇這個題目,其實就是因為看到了一張圖:

(圖1)

看完以后我真的噗嗤就笑了,黑的漂亮。

今天要分享的主題有如下幾個方面:

1970:We have no SQL

1980:Know SQL

2005:Not only SQL

2013:No,SQL!

阿里的技術選擇

對一個對數據庫歷史有點了解的人來說,這張圖真的是反映了我們在數據庫存儲領域螺旋上升式發展歷程的最佳代表。這哥們真的是天賦異稟啊!

為什么我會笑呢? 希望我做完了這次分享,大家也能跟我一樣笑一下。

那么我們先來到第一章。

1970:We have no SQL

恩!我們沒有SQL。

要介紹這個問題,我們就要先來看看什么叫數據庫,以及數據庫這個東西是怎么來的。

程序員一般都會碰到類似這樣的需求:用計算機表示一輛車子。這輛車呢,它有一個外殼,四扇玻璃,四個輪子。

我應該如何用程序來表述它呢?首先能想到的一定是使用結構體(Java的話是Class)。

但當我們發現這個車子的項不夠用了,比如我需要在車子上面裝一對反光鏡,怎么辦呢?我們只能往里面增加一個新的屬性來表示這個反光鏡。如果這種需求越來越多,越來越多,我們就會發現,每次都改一下這個結構體、編譯、發布,是個非常麻煩的事情。

于是,就有了這種非常純粹的需求:有沒有可能把它弄成動態的。這時候,我們最常用到的一個數據結構就是“映射”。

對Java程序員來說,映射就是一個Map。一般來說Map有兩類實現:一類是Hash,一類是有序樹。有了這個隨需應變的集合,我們就可以把事情變成這樣:

map.put("輪子",輪子對象);

map.put("鏡子",鏡子對象) ......

有些時候我們又會擔心數據丟失,對不對?

所以還得想辦法把這個對象以非常高效的方式持久化下來,放到磁盤上,這樣就不容易丟失了。

map.put/get操作其實都會有一次尋找的過程的,這個尋找過程對于磁盤來說會轉變為一次隨機尋道過程。有很多種方式能夠用磁盤結構來存儲類似Map這樣的概念。我今天只介紹一種,就是B-tree,不知道大家對這個詞兒是不是熟悉,反正我面試基本都會問問。

需要先提的一件事是B樹 == B-樹。所謂B-樹,并不是B減樹的意思,希望大家不要跟我一樣土鱉。

(圖2)

這就是一個最簡單的B樹,觀察一下就會發現,其實B樹的出發點很簡單:既然磁盤尋道時間很多,那就減少它,一次尋道能夠從磁盤取更多數據就行了。所以,它是以“數組” 為單位存儲數據的(數組其實就是一片連續并且有界的空間)。數組難以擴展,并且維護數組內元素有序也是有一定代價的。數組滿了以后怎么辦呢? 這就是B樹會做的事兒,分裂。如果這里大家能夠聯想到另外一個東西,那就算學明白了,HBase其實就是棵巨大的、分布式的B樹。其他容我最后吐槽~。

相信很多人都聽過一個名詞:層次數據庫。這東西似乎就是在上古時代的神器,現在則不見了蹤影。層次數據庫到底是個什么玩意兒呢?我們來看一張圖。

(圖3)注:圖片來自網絡

抽象來看,層次模型其實就是這樣的東西,我再用小汽車來表述一下:一個小車由四扇玻璃,四個輪子,兩個反光鏡組成。車有自己的屬性,輪子有自己的屬性,反光鏡有自己的屬性。

給大家兩個例子相信大家應該立刻就能明白了,所謂的層次模型,如果用Java代碼來寫的話,就是Map套Map ,每個Map有一些固定的屬性,比如這個Map的名字是什么,這個Map的屬性是什么,而這就是我們最開始在使用的數據庫了。非常簡單,一個Map結構搞定所有需求。 看起來世界大同了。

1980:Know SQL

下面,我們就來到了1980年,Know SQL,知道SQL了。

為什么這么寫?其實就是雖然關系數據庫是上世紀70年代發明的,但是直到80年代,IBM發布了第一代全功能的關系數據庫系統System R后,我們才正式進入到關系數據庫模型。

相信很多人都覺得自己了解關系模型,似乎每個人提到它,都說“對對對”。絕對對,因為這是有數學支持的,不應該被懷疑。可惜的是,如果大家了解科學發現的歷史就會發現,自從愛因斯坦把牛頓那由完美數學保證的自洽理論踢出了神壇,數學自洽就再也不是真理的標準了。哪個的用戶最多哪個就是真理。為什么關系模型最終贏得了比賽,而層次模型死掉了呢?很簡單,因為人類都是蠢蛋和傻瓜啊。哪個簡單易用,哪個就贏了。

下面,我們就以一個例子來看看關系模型易用在哪里。還是以車子為例,如果我要做這樣的一個查詢:把廠里生產的所有汽車里面,左輪子供應商是DRDS的輪胎都找出來。采用層次模型的代碼是:

遍歷每一輛車,從車對象中找到左面的輪子,查看輪子的屬性,如果是DRDS,留下,不是則丟棄。

如果是關系模型呢?

select * from 輪子表 where 輪子位置='左' and 輪子供應商='DRDS'

完成。

我看了都覺得是個世界性的創舉,不知道您是什么感覺?下一步,我們來看看關系模型將會怎么處理這條SQL。

(圖4)

其實用這一張圖就可以表示一個最簡單的關系模型了。基本上所有的數據庫都是這個組織形式:最上面的用戶API就是執行的SQL和事務命令、

中間的就是關系代數轉換層和事務處理層、最后最底層是個KV存儲。啊? KV這不是NoSQL的概念么,你憑什么盜用它?呵呵,誰盜用誰還不一定呢。

最底層的KV存儲其實就是我們一開始說過的“映射”結構,對應內存可能有Hash和有序樹結構,對應磁盤則主要是btree樹系和LSM樹系。因為每個數據結構都有自己好玩的屬性,講起來太多了,這里就不展開了,大家可以看我博客。我們直接來聊聊關系代數引擎,這是數據庫最關鍵的部分之一,但從功能目標來說卻并不是很復雜。

(圖5)

這張圖就是整個關系代數引擎所經過的步驟:

最原始的是SQL字符串,類似select * from tab where id = 1,它經過的過程叫SQL解析,會生成一個AST 抽象語法樹。

如圖6所示:

(圖6)

select 被拆解為了fromList/WhereClause等細碎的字串。這個過程的主要作用是作為計算機編寫代碼而言,我們更容易識別這種結構化的數據,而文檔屬于非結構化數據;有了這棵樹,下面就是執行優化,其入參是AST樹+索引源信息。簡單說來,AST使得你可以很容易地通過在樹中來回的跳躍來尋找所需的關鍵字信息,比如where條件是什么,返回哪些列等。索引源信息又是個什么鬼?要講明白這個,得先看看關系模型和Map是怎么對應起來的,我用幾張PPT(很多人可能見過)來說明:

(圖7)

第一個SQL是 select * from tab where id = ?,上面的那個則是一個表格,如果我們用Map來表示,可以表示成這樣:

Map:key -> primaryKey, value -> [pk,user_id,Name]

也就是以PK值作為Map的key,以一個包含了pk, user_id, Name的值的結構作為Map的value(當然只包含[user_id,Name]也OK) 。有了這個Map,我們只需要從AST里面取出id = ? (假設id = 0),通過map.get(0)拿到對應的user_id數據和Name數據,加上輸入的id=0這個數據,拼成對象返回就可以了。

再來看另一個需求。

(圖8)

我們來看看這個圖,這里面我們的查詢條件發生了變化,不是id了而是user_id。

我們剛才只有一個Map:

Map: key -> id , value -> [user_id,Name]

我應該如何利用這個Map去找到所有符合要求的結果呢?我能想到的第一種方式是遍歷Map里面的每一個Entry,取出每一個Entry以后看看User_id是不是等于我要求的值,如果不等于就丟棄,等于的話返回即可。然而這種方式帶來的問題是,如果我有1億條記錄,我就要做這件事1億次。明顯的 O(N)效率太慢了。怎么加快一下?有需求就有人響應。于是我們可以用個空間換時間的法子:

(圖9)

看看上面的圖,里面增加了一個新的Map:

Map: key -> user_id, value -> [id]

這個Map以user_id作為key,于是我們又可以愉快而高效地用第二個Map的get接口來獲取所有符合要求的id列表,然后再根據這個符合要求的id列表,去查第一個Map,獲得對應的數據了。剛才介紹的這塊其實就是關系模型如何映射到Map(也就是KV模型)的關鍵方法了。當然,還會有很多擴展性的方式和方法,不過這就不是今天的主題咯。這個數據比較小,只有三列,一個索引。如果我有十幾個甚至幾十個索引的時候我們又會面對另一個問題。

(圖10)

如果我有一個user_id的二級索引,又有一個Name的二級索引,我應該選擇哪一個作為查詢用的索引呢?是不是我需要有一種機制來選擇那個最“便宜”的索引,這就是索引選擇的過程。要進行索引選擇就必然要知道每個索引的區分度高還是低(說白了就是一個key對應的pklist.size()少還是多),而索引的區分度高|低,就是所謂索引源信息的最簡單模式。在真實的數據庫中還有很多其他信息也是索引的源信息,不過為了方便大家理解簡化了一下。有了索引源信息和AST樹就可以生成執行計劃了:

(圖11)

這時候再嘗試看看這個,相信大家就大概都能猜到這里的東西所表示的含義了。所以很多事兒呢不用去背,了解了背后的原理,優化就是信手拈來的事兒。這里我省略了事務步驟,這個更復雜,各位感興趣可以看我的視頻了解。

2000:No SQL!

No SQL! 豪言壯語,SQL數據庫has gone,新時代來臨了!似乎一夜之間,這世界就翻了天,Facebook開源了Cassandra,HadoopHbase橫空出世,似乎這就是未來啊。

我們來看看知乎的問題:“互聯網領域,傳統的SQL很力不從心,一些更具有針對性的NoSQL會越來越火。以后會不會出來各種強力NoSQL?”,最雷我的一個問題大概是:“我們是一個新業務,想用NoSQL來提升開發效率,不知道Cassandra和HBase應該選哪個?”。

再見,看完以后我真的是覺得沒辦法好好做朋友了。

Digg采用Cassandra遭遇失敗,工程副總裁離職,希望大家不是這個人。好啦,閑話扯完,回到正題。

No SQL是怎么來的?這還是得從事務說起,自從第一代產品化的數據庫在上世紀80年代開發完成以后,我們的數據庫主要演進模型里面只有幾個有限的里程碑,我目前能記住的就這么幾件事:

mvcc 多版本并發控制;

存儲過程;

各類OLAP的分析類引擎。

實際上大家心里都知道,有一件事一定會發生,只是不知道什么時候會發生。這就是分布式系統。分布式系統能夠具備無限的擴展能力,按需伸縮,只要有錢我們的系統就不會down,不會死。這種能力其實在上世紀80年代就早已深入人心了。 還記得SUN公司提出的口號么?網絡就是計算機。傻瓜都知道未來一定是分布式系統的天下,單機系統還有什么玩頭?單機系統不就應該是那待宰的羔羊么?等著DRDS 異軍突起不就好了么?但是等啊等啊,30年過去了,卻沒等到自己壽終正寢的那一天,反而似乎活的越來越好了,這是為什么?理由很簡單,技術沒突破。

如果一個分布式系統做的跟單機系統一樣方便,又能擴展,性能又好,那這世界上早就沒有單機系統了。而且,從上世紀80年代到21世紀的前幾年,我們實際上都不需要分布式系統。大部分的系統都是諸如“圖書館管理系統”,“客戶關系管理系統”等等企業內部管理系統,不需要很高的并發,只需要容易操作就行了,而單機的關系數據庫系統自然地最容易操作啊,所以單機系統大行其道。

然而,云計算和互聯網的時代到來了,我們服務的對象從頂天了幾千人一下就變成了十幾億人,計算機要管理的數據量呈指數級別地飛速上漲,而我們卻完全無法對用戶數做出準確預估。這時候,擴展性、性能的要求就變得更為重要。不擴展的話業務就掛了,擴展的話開發難度少量上升,這兩件事情做權衡,相信大家都能立刻知道哪個更重要。我們當然選擴展了!然而數據庫卻無法提供這樣的擴展性,當年的淘寶也是用Oracle的,配置算不錯的,也算是有小黑柜子。然而,今天不火的網站明天可能突然就火了,我們的用戶數在一年內就會突破這個柜子的容量,折舊都來不及。很明顯的,時代變了。

傳統關系數據庫,哪怕是RAC都不能滿足我們對于數據庫擴展性的追求了,這時候肯定有人在想:“這個有問題,我們就解決它啊”。這類技術就是Oracle RAC啊、MySQL Cluster啊這類玩具,它們希望能夠不改變用戶行為來實現擴展性,可是做了好多年,發現玩不轉。為了支撐更大的訪問量和數據量,我們必然需要分布式數據庫系統,然而分布式系統又必然會面對強一致性所帶來的延遲提高的問題,因為網絡通信本身比單機內通信代價高很多,這種通信的代價就會直接增加系統單次提交的延遲,延遲提高會導致數據庫鎖持有時間變長,使得高沖突條件下分布式事務的性能不升反降(具體可了解下Amdahl定律),甚至性能距離單機數據庫有明顯差距。

說了這么多我們可以發現問題的關鍵并不是分布式事務做不出來,而是做出來了卻因為性能太差而沒有什么卵用。數據庫領域的高手們努力了40年,但至今仍然沒有人能夠很好地解決這個問題,Google Spanner的開發負責人就經常在他的Blog上談論延遲的問題,相信也是飽受這個問題的困擾。于是乎有一群人認為,既然強一致性不怎么靠譜,那徹底繞開這個問題是不是更好的選擇?他們發現確實有那么一些場景是不需要強一致事務的,甚至連SQL都可以不要。最典型的就是日志流水的記錄與分析這類場景。去掉了事務和SQL,接口簡單了,性能就更容易得到提升,擴展性也更容易實現,這就是NoSQL系統的起源。

他們喊出了非常響亮的口號:No SQL! No SQL標志著他們時代的到來。

2005:Not only SQL . 不僅僅是SQL

經過5年的忽悠,有很多人愿意相信NoSQL似乎確有其事。于是有一批先行者就開始探索各種玩法:

http://www.lampchina.net/mod-news_do-show_id-8272.html

Digg采用Cassandra遭遇失敗,工程副總裁離職:

(圖12)

點個蠟。

玩著玩著,大家發現還是不靠譜。這不行啊,這東西不就是讓我們每個業務都把關系數據庫從新實現一把么?讓我們退回到層次模型上去啊。對于人類這種懶惰而笨拙的動物,開歷史的倒車明顯是不受待見的。于是,有一批人站出來了,說No什么SQL,還是得有數據庫。但NoSQL開發者們已經忽悠了那么多投資人的錢,總得有個交代啊,既然沒辦法顛覆,咱們就共存吧,什么NoSQL和SQL,大家一家人,各自發展就好了。這就是Not only SQL的來源。

NoSQL有哪些明確的場景呢?請注意前方高能。

比如HDFS比較火,于是就有人發現:“哎? 我們如果學Google ,也弄個分布式KV是不是也能火?” 呵呵,我想這就是某個base最大的價值。不過在這個平緩期還是能看到一些創新性的想法的,他們幫助數據庫領域往前走了不大的一步。MongoDB 是個不錯的思路(我個人覺得),json替代了臃腫的XML成為了一個小的標準,而在這個上面做很多索引,也是很聰明的做法,借鑒了數據庫的核心思路,這也算是共存。其他的NoSQL也在往SQL上面努力,比如Cassandra的CQL 、HBase的各類SQL引擎,其實都是對關系數據模型的一種妥協。畢竟,NoSQL還沒有好到能夠顛覆整個生態。

2013:No,SQL!

不,我們還是要關系數據庫,這就是現在我們的感覺咯。經過了10年的折騰,我們還是發現關系模型目前來說是最方便表達數據存取的語言,比其他都要方便的多,所以還是妥協吧。于是所有的NoSQL都在想辦法嘗試支持關系數據庫。然而回到初始,我們不就是因為關系數據庫不能滿足用戶要求,所以才要去做NoSQL的么。難道NoSQL 弄個關系代數引擎,就能做出魔法么?其實,也不行,該有的限制一個都沒少,最終大家殊途同歸,還是回到了如何能夠讓關系數據庫更具有擴展性,性能更好這條路上來,條條大路通羅馬嘛,和氣生財。這就是NewSQL的來源。

DRDS 也是NewSQL的一員,其實說實話,我挺有感觸的。作為這么多年來一直堅守在分布式數據庫這個領域的人來說,能夠堅持下來真的不容易,在外面有太多的誘惑,最火的時候,連DBA都去學了各種NoSQL的運維技術。然而,我們能夠堅守,其實就是因為我們懂得歷史也看得見現在。我們深刻地知道,科學就是承認自己并非無所不能,然后不斷地往那無所不能的地方努力的一種精神。一直以來,我們都盡可能地協助用戶保留關系數據庫的方便性,然后想辦法告知用戶哪些地方目前還缺少技術突破,應該使用哪些工具來替代,所以也算是積累了非常多的經驗和工具。

同時我們也在努力追求數據庫領域的那個圣杯:更快的存取數據,可以按需擴縮以承載更大的訪問量和更大的數據量,開發容易,硬件成本低。這是大家夢寐以求在追求的東西。也是我們在追求的。雖不能至,吾心向往之。

阿里的技術選擇

最后,來聊聊阿里的技術選擇。其實,所有大公司似乎都在釋放各種信號,xxx在用什么系統了,xxx在用什么系統了。阿里可能不大一樣,從內部來說,他也是個生態系統,用戶選擇什么實際上主要都是由用戶自己決定的,所以阿里能夠出現任何一種選擇,只要能解決問題即可。TDDL DRDS這套體系,只能說是目前用的最廣的一套,原因也很簡單,改變行為習慣少。

雙11 對DRDS這套體系來說其實沒什么壓力,我前幾年的雙十一雖然都在核心作戰室,不過我一般的做法都是到了那里:“您辛苦了,您也是,大家辛苦了”,然后吃吃吃。因為確實沒什么好擔心的啊,沒有不平穩的理由,DRDS的能力更多地體現在雙11開始和雙11結束,我們需要在那之前機器擴容,以及那之后要機器縮容,這些才是DRDS的核心能力。上上次我確實是很緊張,因為新接了一個消息系統。哎!這種用來消峰填谷的系統才是壓力山大,其余的時候基本上就沒啥了,更多地跟大家一樣,也都是普通的功能開發而已。

好了,我的故事講完了,謝謝大家收聽。

Q&A

Q1:ADS是什么?它和DRDS的關系是?

沈詢:ADS主打adhoc查詢,目前不支持事務。DRDS則支持事務,這兩個系統是互補的,一個針對離線,一個針對在線。

Q2:請問阿里的DRDS如何實現Join SQL語句來執行多表關聯查詢的?如何兼容單機存儲的SQL?需要注意那些坑?

沈詢:方案很多,其實如果大家對Join有了解,也就那么幾種,hash/index nest loop / sort merge ,沒什么魔法。

http://coding-geek.com/how-databases-work/ ,這個不錯,我比較推薦。

Q3:請問schema less/free你怎么看?

沈詢:這倆似乎沒見過拼一起哈,我分開。 但本質是同一個東西,我個人覺得有市場。不過能有多大,不知道。

優勢:業務模型更靈活

劣勢:額外的空間占用

技術債也一定是要還的。我清楚地記得當年我維護的一個CMS系統,所有數據都是map。結果最后有一些詭異的數據不知道什么時候被塞到里面。然后最后也沒人知道是在哪里塞的。debug都很難找到。所以一般來說,結合會更好一些。 目前pg/mysql都開始支持json了。 這東西其實只是工作量問題。沒什么技術上的難度。

Q4:最近很多聲音說不要再用MongoDB,你怎么看?

沈詢:這個就純屬個人意見了啊。我個人不喜歡MongoDB那幫人的嘴臉。MongoDB核心貢獻者:不是MongoDB不行,而是你不懂!

http://www.cnblogs.com/shanyou/archive/2012/11/17/2774344.html

然而,為什么會這樣,還不是某些人為了騙分,默認配置特別激進么。而開發者不會告訴你,如果改成安全配置,他們的性能沒比SQL強哪里去。

一個存儲,最少10年才能穩定。 前些天剛碰到一個游戲客戶說某NoSQL數據文件損壞無法恢復,問我們有沒有辦法,我說,下次選擇謹慎點,性能不是唯一,這次請節哀。

Q5:海量,低延時(豪秒級),高并發(十萬以上),目前關系型數據庫是否有并存的方案?

沈詢:10w并發不算是很高。DRDS是你最好的選擇 :)

Q6:非結構化數據,如文本,樹圖等,這些SQL無法處理的,是否使用NoSQL更合理?

沈詢:非結構化數據也是一個重要的門類,一直都存在,以后也會存在,但NoSQL為了宣傳,把所有的東西都拉到自己陣營,這其實與其初衷已經違背。

Twitter的圖數據庫其實也是依托MySQL做的。你給我錢給我人,給我時間,我能用匯編寫任何東西。

Q7:能說一下DRDS和RDS的關系么?

沈詢:一個偏重分布式,一個偏重單機

Q8:SQL模型已經那么多年了,數據庫領域有其他可能更好使的語言模型方向么?

沈詢:目前還沒見到更好地抽象,所謂好使就是你發現他占據了更多江山。在歷史上有很多次嘗試,比如對象數據庫等,但實際上也都是針對SQL問題的一些改善。不過目前還沒有特別成功的。

Q9:分享中有提到,分布式數據庫網路延遲方面的瓶頸一直是一個需要突破的點,你是否認為新型網路架構,或是網路設備的出現能夠逐步彌補這一點呢?

沈詢:能夠做到一些改善,但完美解決不大可能。目前系統需要更大的理論突破。

Q10:能不能簡單用一句來概括DRDS的強大?

沈詢:SQL數據庫里擴展性最好的,NoSQL數據庫里最方便的。

Q11:WebScaleSQL與DRDS是什么關系?

沈詢:沒關系,當成競爭對手似乎也可以。

Q12:DRDS和NewSQL有何異同?

沈詢:DRDS屬于NewSQL。

(完)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容