SQL,NoSQL 以及數(shù)據(jù)庫的實質(zhì)

(王垠 yinwang.org 版權(quán)所有豺型,未經(jīng)許可赏陵,請勿轉(zhuǎn)載)

在之前的一些博文里(比如這篇)饼齿,我多次提到過關(guān)系式數(shù)據(jù)庫和 SQL 的問題。我覺得它們是制造了問題又自己來解決蝙搔,而且永遠沒法解決好缕溉。可是由于時間原因杂瘸,一直沒有來得及解釋我的觀點倒淫,以至于很多人不理解我在說什么伙菊,還以為是信口開河败玉。所以現(xiàn)在有了點閑暇時間,我就把這里面的細節(jié)稍微說一下镜硕。也許你會發(fā)現(xiàn)运翼,得出這些結(jié)論所需要的背景知識,比你想象的多得多兴枯。

SQL 與 Prolog 的淵源

在我批評 SQL 帶來的問題時血淌,總是避免不了有人批駁說:“SQL 是描述性的語言。你只告訴它 What财剖,而不是告訴它 How悠夯。”我發(fā)現(xiàn)每一次有人批駁我的觀點躺坟,總是拿一些我多年前就聽膩了沦补,看透了的“廣告詞”,而現(xiàn)在這同樣的事又發(fā)生在 SQL 身上咪橙。他們沒有發(fā)現(xiàn)夕膀,我不但能實現(xiàn) SQL,而且已經(jīng)實現(xiàn)過比 SQL 強大很多的語言美侦,所以我其實早已看透了所有這些語言的實質(zhì)产舞。

Prolog 與人工智能的沒落

可以說,“只告訴它 What菠剩,而不是告訴它 How”易猫,只是一個不切實際的妄想,而且它并不是 SQL 首創(chuàng)的想法具壮。在 SQL 誕生兩年以前准颓,有人發(fā)明了 Prolog违霞,著名的“邏輯式語言運動”的先鋒。Prolog 使用了與 SQL 非常類似的廣告詞瞬场,聲稱自己能夠一勞永逸的解決人工智能和自動編程的問題买鸽,這樣人們不需要寫程序,只需要告訴電腦“想要什么”贯被,然后電腦就能自動生成算法眼五,自動生成代碼來解決這問題。

呵呵彤灶,世界上總是有很多這種類似“減肥藥”的東西看幼,每一個都聲稱自己是“不需運動,不需節(jié)食幌陕,一個星期瘦 20 斤诵姜!”然而由于人類的智力和經(jīng)驗參差不齊,總會有人上當搏熄。Prolog 當年的風頭之大棚唆,以至于它被日本政府采用并且大力推廣,作為他們所謂的“第五代計算機”的編程語言心例∠瑁可惜的是,減肥藥畢竟是減肥藥止后,科學(xué)道理決定了 Prolog 必定失敗瞎惫,以及“人工智能冬天”(AI winter)的到來。

為什么 Prolog 會失敗呢译株?這是因為 Prolog 雖然“終究”有可能自動解決某些問題瓜喇,然而由于它的算法復(fù)雜度太高,所以沒法在我們有生之年完成歉糜。說白了乘寒,Prolog 采用的“計算”方式就是“窮舉法”。為了得到用戶“描述”的問題的答案现恼,而不需要用戶指定具體的數(shù)據(jù)結(jié)構(gòu)和算法肃续,Prolog 必須對非常大的圖狀解空間進行完全的遍歷(Prolog 采用深度優(yōu)先搜索)。而這種解空間的“狀態(tài)”數(shù)量往往是指數(shù)增長叉袍,這就決定了 Prolog 雖然“最終”可能找到問題的答案始锚,卻很有可能在地球毀滅之前都沒法完成它的搜索。而且由于 Prolog 無法表達真正意義上的“邏輯否”操作喳逛,所以對于很多問題它永遠無法得到正確的答案(這是一個非常深入的問題瞧捌,30 多年的研究,仍然沒有結(jié)果)。

過于具體的細節(jié)我不想在這里解釋姐呐,你只需要明白的是我并不是在信口開河殿怜。在 Indiana 的日子里,我學(xué)會并且重新實現(xiàn)了一種與 Prolog 類似的邏輯式語言叫做 miniKanren曙砂,它也就是 Dan Friedman 的新書《The Reasoned Schemer》的主題头谜。雖然 miniKanren 比起 Prolog 更加優(yōu)雅,而且在搜索算法上有所改進(廣度優(yōu)先而非深度優(yōu)先)鸠澈,它本質(zhì)上采用的搜索方式也是一樣的:窮舉法柱告。所以在很多時候它的效率很低,用法不靈活笑陈。像 Prolog 一樣际度,miniKanren 并不能用來解決很多實際的問題。

只舉一個例子涵妥,在 IU 的時候總有一些人喜歡用 miniKanren 來實現(xiàn)類似 Haskell 的 Hindley-Milner 類型系統(tǒng)乖菱。最基本的基于 unification 的類型推導(dǎo),miniKanren 確實能做到蓬网,然而如果遇到一些必要的擴展窒所,比如 let-polymorphism,你就需要對 miniKanren 語言本身進行擴展拳缠。也就是說墩新,你不再是用 miniKanren 實現(xiàn)你的算法贸弥,而是用 Scheme 把你的算法加到 miniKanren 里面窟坐,然后再利用這個你已經(jīng)實現(xiàn)的“新特性”來“實現(xiàn)”你的算法。于是你就發(fā)現(xiàn)绵疲,其實 miniKanren 本身并沒有足夠的表達力表示完整的 Hindley-Milner 類型推導(dǎo)算法哲鸳。這就是為什么雖然我很感謝 miniKanren 教會了我邏輯編程的原理,然而我實現(xiàn)過的所有強大的類型系統(tǒng)盔憨,全都是用最普通的過程式或者函數(shù)式語言徙菠。

從 Prolog 到 SQL

Prolog (miniKanren)的性能問題在 SQL 里面得到了部分的緩解,這是因為 SQL 對于基本的數(shù)據(jù)結(jié)構(gòu)進行了“索引”郁岩。比如婿奔,對于基本的算數(shù)操作?x < 10,它能夠通過對索引(B樹)的查找來進行“優(yōu)化”问慎,從而避免了對?x?所有可能的值(一個非常大的空間)進行完全的遍歷萍摊。

然而 SQL 的表達力也受到比 Prolog 更大的限制。很多 Prolog 可以表達的問題如叼,SQL 沒法表示冰木。所以后來你就發(fā)現(xiàn),SQL 其實只能用于非常簡單的,適合會計等人員使用的查詢操作踊沸。一旦遇到程序員需要的歇终,稍微復(fù)雜一點的數(shù)據(jù)結(jié)構(gòu),它就會引起諸多的性能問題逼龟。

更要命的是评凝,這種性能問題的來源是根本性的,不可解決的腺律,而不只是因為某些數(shù)據(jù)庫的 SQL 編譯器不夠“智能”肥哎。很多人不理解這一點,總是辯論說“我們?yōu)楹涡枰?Java 而不是寫匯編疾渣,也就是我們?yōu)楹涡枰?SQL篡诽。”然而榴捡,把 Java 編譯成高效的匯編杈女,和把 SQL 編譯成高效的匯編,是兩種本質(zhì)上不同的問題吊圾。前者可以比較容易的解決达椰,而后者是不可能的(除了非常個別的情況)。

我只舉一個例子來說明這個問題项乒。如果你需要迅速地在地圖上找到一個點附近的城市啰劲,SQL 無法自動在平面點集上建造像 KD-tree 那樣的數(shù)據(jù)結(jié)構(gòu)。這是很顯然的檀何,因為 SQL 根本就不知道你的數(shù)據(jù)所表示的是平面上的點集蝇裤,也不理解平面幾何的公理和定理。跟 B-tree 類似频鉴,知道什么時候需要這種特殊的索引結(jié)構(gòu)栓辜,需要非常多的潛在數(shù)學(xué)知識(比如高等平面幾何),所以你肯定需要手動的建立這種數(shù)據(jù)結(jié)構(gòu)垛孔。你發(fā)現(xiàn)了嗎藕甩,你其實已經(jīng)失去了所謂的“描述性”語言帶來的好處,因為你完全可以用最普通的語言周荐,加上一些構(gòu)造 B-tree, KD-tree 的“庫代碼”狭莱,來實現(xiàn)你所需要的所有復(fù)雜查詢操作。你的 SQL 代碼并不會比直接的過程式代碼更加清晰和簡潔概作。再加上 SQL 本身的很多設(shè)計失誤腋妙,你就發(fā)現(xiàn)使用 SQL 數(shù)據(jù)庫其實比自己手工實現(xiàn)這些數(shù)據(jù)結(jié)構(gòu)還要痛苦。你學(xué)會 SQL 是為了避免編程仆嗦,結(jié)果你不得不做比編程還要苦逼的工作辉阶,美其名曰,“SQL performance tuning”。

到這里也許有人仍然會說谆甜,這只是因為現(xiàn)在的 SQL 編譯器不夠智能垃僚,總有一天我們能夠制造出能夠“自動發(fā)明”像 B-tree, KD-tree 這樣索引結(jié)構(gòu)的“優(yōu)化算法”。我對此持非常不樂觀的態(tài)度规辱。首先你要意識到谆棺,哪怕最基本的數(shù)學(xué)知識,也是經(jīng)過了人類幾千年的實踐和研究才得到的罕袋。計算機雖然越來越快改淑,它卻缺乏對于世界最直接的觀察和探索能力,所以在相當長的時間內(nèi)浴讯,計算機是根本不可能自動“想到”這些數(shù)學(xué)和算法問題的朵夏,就不要談解決它們。其次榆纽,即使計算機有一天長了腳可以走路仰猖,有了眼睛可以看見東西,有了“自由意志”奈籽,可以自己去觀察世界饥侵,它卻不一定能夠比人類快很多的發(fā)現(xiàn)并且解決“人類關(guān)心的數(shù)學(xué)問題”。最后衣屏,我們需要在有生之年解決這些迫切的問題躏升,我們無法等待幾十年幾百年,就為了讓計算機自己想出像 KD-tree 一類眾所皆知的數(shù)據(jù)結(jié)構(gòu)狼忱。

這就是為什么你幾乎總是需要手動指定索引的原因膨疏,而且這種索引需要數(shù)據(jù)庫“內(nèi)部支持”。你一次又一次的希望 SQL 能夠自動為你生成高效的索引和算法藕赞,卻一次又一次的失望成肘,也就是這個原因。當然斧蜕,你永遠可以使用所謂的 stored procedure 來擴展你的數(shù)據(jù)庫,然而這就像是我的 IU 同學(xué)們用 miniKanren 來實現(xiàn) HM 類型系統(tǒng)的方式——他們總是先使用一種過程式語言(Scheme)來添加這種描述性語言的“相關(guān)特性”砚偶,然后歡呼:“哇批销,miniKanren 解決了這個問題!”而其實呢染坯,還不如直接使用過程式語言來得直接和容易均芽。

關(guān)系式模型的實質(zhì)

每當我批評 SQL,就有人說我其實不理解關(guān)系式模型单鹿,說關(guān)系式模型本身并沒有問題掀宋。所以現(xiàn)在我就來分析一下什么是關(guān)系式模型的實質(zhì)。

我想很多有經(jīng)驗的數(shù)據(jù)庫使用者都理解,關(guān)系式模型的每一個“關(guān)系”或者“行”(row)劲妙,其實表示的不過是一個普通語言里的“結(jié)構(gòu)”(比如 C 的 struct)湃鹊。一個表(table),其實不過是一個裝滿結(jié)構(gòu)的數(shù)組镣奋。每一個 join币呵,其實就是沿著行里的“指針”進行“尋址”,找到它所指向的東西侨颈。當然余赢,這些操作都是基于“集合”的,但這并不妨礙你使用普通的語言(比如 C 或者 Java)來完成這種操作哈垢,它們都可以通過很簡單的“庫代碼”來完成妻柒。

所以,關(guān)系式模型所能表達的東西耘分,絕對不會超過普通過程式語言所用的數(shù)據(jù)結(jié)構(gòu)蛤奢,然而關(guān)系式模型卻有過程式數(shù)據(jù)結(jié)構(gòu)所不具有的局限性。由于經(jīng)典的關(guān)系“行”只能有固定的寬度陶贼,所以導(dǎo)致了你沒法在結(jié)構(gòu)里面放進任何“變長”的東西啤贩。比如,如果你有一個變長的數(shù)組需要放進結(jié)構(gòu)拜秧,你就需要把它單獨拿出來痹屹,旋轉(zhuǎn) 90 度,做成另外一個表枉氮,然后在原來的表里用一些“key”指向它們志衍。這通常被叫做 normalization。這種方法雖然可行聊替,然而我不得不說這是一個“變通”楼肪。它的存在是為了繞過關(guān)系式模型的這一無須有的限制,它終究導(dǎo)致了關(guān)系式數(shù)據(jù)庫的繁瑣惹悄。

NoSQL 的革命和終結(jié)

上面的這一系列問題春叫,終究引發(fā)了所謂 NoSQL 的誕生。然而我并不覺得有很多 NoSQL 數(shù)據(jù)庫的設(shè)計者們看到了以上我所看到的問題泣港,所以他們的設(shè)計并沒有完全擺脫關(guān)系式模型以及 SQL 帶來的思維枷鎖暂殖。

最早試圖沖破關(guān)系式模型和 SQL 限制的一種數(shù)據(jù)庫叫做“列模式數(shù)據(jù)庫”(column-based database),其代表包括 Vertica 等新興產(chǎn)品当纱。這種數(shù)據(jù)庫其實就是針對了我剛剛提到的呛每,關(guān)系式模型無法保存可變長度數(shù)組的問題。這些列模式數(shù)據(jù)庫所謂的“壓縮”坡氯,其實不過是在“行結(jié)構(gòu)”里面增加了對“數(shù)組”的表示和實現(xiàn)晨横。很顯然洋腮,每一個數(shù)組需要一個字段來表示它的長度,剩下的空間用來依次保存每一個元素手形。這也就是你在這些列模式數(shù)據(jù)庫的設(shè)計里所看到的啥供。

最新的一些 NoSQL 數(shù)據(jù)庫,比如 Neo4j, MongoDB 等叁幢,部分的針對了 SQL 的表達力問題滤灯。Neo4j 設(shè)計了自己的查詢語言 Cypher,MongoDB 使用普通的 JavaScript 來對數(shù)據(jù)進行查詢曼玩。所以到現(xiàn)在看來鳞骤,數(shù)據(jù)庫的主要問題已經(jīng)轉(zhuǎn)移到了語言設(shè)計的問題。

只要你有一個好的程序語言黍判,你就可以發(fā)送這種語言的代碼到“數(shù)據(jù)庫服務(wù)器”豫尽,這個服務(wù)器可以遠程執(zhí)行你的代碼,調(diào)用服務(wù)器上的“庫代碼”對數(shù)據(jù)進行索引顷帖,查詢和重構(gòu)美旧,然后返回代碼指定的結(jié)果。如果你看清了 SQL 的實質(zhì)贬墩,就會發(fā)現(xiàn)這樣的“過程式設(shè)計”其實并不會損失 SQL 的“描述性語言”的表達能力榴嗅。反而由于過程式語言使用的簡單性,直接性和普遍性陶舞,使得開發(fā)效率有很大提高嗽测。

然而,NoSQL 的終極問題在于肿孵,設(shè)計他們的人并不是經(jīng)過了專業(yè)的程序語言設(shè)計訓(xùn)練的唠粥。我經(jīng)歷過好些 NoSQL 數(shù)據(jù)庫之后發(fā)現(xiàn),它們的查詢語言停做,要么沒有逃脫 SQL 的圈套(比如 Neo4j 的 Cypher)晤愧,要么就沒有逃脫普通程序語言的圈套(比如 MongoDB 的 JavaScript)。而且由于具體的實現(xiàn)質(zhì)量以及商業(yè)動機蛉腌,這些數(shù)據(jù)庫往往有各種各樣惱人的問題官份。這是必然的現(xiàn)象。因為這些數(shù)據(jù)庫公司靠的就是咨詢和服務(wù)作為收入眉抬,如果他們把這些數(shù)據(jù)庫高質(zhì)量又開源的實現(xiàn)贯吓,沒有煩人的問題,誰會給他們付費呢蜀变?

所以,這些 NoSQL 數(shù)據(jù)庫問題的存在介评,也許并不是因為人們都很笨库北,而是因為世界的經(jīng)濟體制仍然是資本主義爬舰,大家都需要騙錢糊口,大家都舍不得給“小費”寒瓦。

總結(jié)

說了這么多情屹,其實主要的只有幾點:

SQL,Prolog 等所謂“描述性語言”的價值被大大的高估了杂腰。使用它們的人往往有“避免編程”的心理垃你,結(jié)果不得不做比編程還要痛苦的工作:數(shù)據(jù)庫查詢優(yōu)化。數(shù)據(jù)庫完全可以使用普通的程序語言(Java喂很,Scheme 等)的“遠程執(zhí)行”來進行查詢惜颇,而不需要專門的查詢語言。這在某種程度上就是 NoSQL 數(shù)據(jù)庫的實質(zhì)和發(fā)展方向少辣。關(guān)系式模型嚴重束縛了人們的思想凌摄,其本質(zhì)并不如普通的數(shù)據(jù)結(jié)構(gòu)簡單和高效。

對這個問題有興趣的人漓帅,可以參考我的一篇相關(guān)的英文博客锨亏,以及這篇《一種新的操作系統(tǒng)的設(shè)計》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市忙干,隨后出現(xiàn)的幾起案子器予,更是在濱河造成了極大的恐慌,老刑警劉巖捐迫,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乾翔,死亡現(xiàn)場離奇詭異,居然都是意外死亡弓乙,警方通過查閱死者的電腦和手機末融,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來暇韧,“玉大人勾习,你說我怎么就攤上這事⌒覆#” “怎么了巧婶?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長涂乌。 經(jīng)常有香客問我艺栈,道長,這世上最難降的妖魔是什么湾盒? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任湿右,我火速辦了婚禮,結(jié)果婚禮上罚勾,老公的妹妹穿的比我還像新娘毅人。我一直安慰自己吭狡,他們只是感情好,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布丈莺。 她就那樣靜靜地躺著划煮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缔俄。 梳的紋絲不亂的頭發(fā)上弛秋,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機與錄音俐载,去河邊找鬼蟹略。 笑死,一個胖子當著我的面吹牛瞎疼,可吹牛的內(nèi)容都是我干的科乎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼贼急,長吁一口氣:“原來是場噩夢啊……” “哼茅茂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起太抓,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤空闲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后走敌,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碴倾,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年掉丽,在試婚紗的時候發(fā)現(xiàn)自己被綠了跌榔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡捶障,死狀恐怖僧须,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情项炼,我是刑警寧澤担平,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站锭部,受9級特大地震影響暂论,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拌禾,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一取胎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧湃窍,春花似錦扼菠、人聲如沸摄杂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至墨坚,卻和暖如春秧饮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背泽篮。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工盗尸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人帽撑。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓泼各,卻偏偏與公主長得像,于是被迫代替她去往敵國和親亏拉。 傳聞我的和親對象是個殘疾皇子扣蜻,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容