SQL:王者歸來

AirSwing發表於2017-12-20

"SQL已經過時了“,”傳統關係型資料庫已經無法滿足業務需求“,”NoSQL,大資料是新時代的產物“。我們已經聽到太多關於這方面的話題,不光是在資料庫範圍內,在資料庫之外的領域大家仍然探討這這樣的話題。但是,這麼說真的正確嗎?在我看來,SQL不但沒有過時,他的光芒才剛剛開始。在這篇文章中,我們來探討一下SQL語句在現如今的開發過程中承擔著一種怎麼樣的角色。

#SQL的誕生

SQL最早可以追溯到1970 IBM研究中心,這裡正是關係型資料庫誕生的地方。那個時候,資料查詢是一件非常煩碎的工作,需要大量的計算,中間結果,各種數學公式和定義交織在一起。Donald Chamberlin和Raymond Boyce兩位博士發現了資料查詢的瓶頸,開始對查詢語句做全新的定義。他們的出發點是要讓沒有數學基礎的人也可以通過查詢語句來從資料庫中檢索資料。在當時那個年代,C語言還算是高階語言的領軍人物,當時SQL的設計者認為,他們要設計一種類似人類語言的資料庫查詢語句,經過幾年努力,在1974年SQL終於誕生了,之後的幾十年裡SQL很快發展成為資料庫語言的標準,幾乎所有的關係型資料庫,System R, Ingres, DB2, Oracle, SQL Server, PostgreSQL, MySQL都實現了對SQL的支援。

SQL確實很好用,也收到大家的喜愛,但是事情在1989年網際網路發明以後出現了變化。我們都已經見證了網際網路的崛起在方方面面改變了人們的生活節奏。資料的產生以指數級數增長,於是人們發現了關係型資料庫的侷限,資料模型的死板,資料庫伺服器的擴充套件等等。網際網路兩大巨頭google和亞馬遜先後釋出了非關係型資料庫產品,google於2004年釋出MapReduce並在2年後釋出Bigtable,2007年亞馬遜釋出Dynamo。在這以後非關係型資料庫如雨後春筍般湧現,受Bigtable和Dynamo的影響,Cassandra於2008年釋出,接下來一年內基於文件的非關係型資料庫MongoDB也被建立出來。由於這些系統都是從新實現的產品,他們或多或少都選擇避開SQL的老套路。

#NoSQL好的點子,錯誤的命名

我先給大家澄清一下SQL和NoSQL的卻別:

- SQL (Structured Query Language)資料庫,指關係型資料庫。主要代表:SQL Server,Oracle,MySQL(開源),PostgreSQL(開源)。

- NoSQL(Not Only SQL)泛指非關係型資料庫。主要代表:MongoDB,Redis,CouchDB。
複製程式碼

NoSQL之所以發展的如此之快,主要得益於沒有表結構的約束,開發簡單,分散式結構,查詢效能高等特點。但是以此帶來了很多負面影響,光是NoSQL的種類就多達兩百多種,選型就成為一個讓人頭疼的問題。其次,在沒有SQL語句的幫助下進行資料分析會令廣大的DBA抓狂,為了得到一個資料分析結果,往往要進行多個步驟的計算,然後統計計算結果最後得出結論,在SQL中可能一句話就解決的問題要在NoSQL中經歷若干步驟才能完成。

最常見的例子就是在NoSQL中進行多表聯合查詢,這在SQL中是簡單的不能再簡單的操作,只要將多個關係表JOIN在一起就可以查詢他們的資料。但是,由於NoSQL沒有表結構的概念,而且像MongoDB這樣的NoSQL產品建議把相關資料全部存在一個Collection內,那麼當出現要查詢多個Collection之間的資料很多人只能在應用程式層面解決,但這會帶來幾個問題: NoSQL之所以發展的如此之快,主要得益於沒有表結構的約束,開發簡單,分散式結構,查詢效能高等特點。但是以此帶來了很多負面影響,光是NoSQL的種類就多達兩百多種,選型就成為一個讓人頭疼的問題。其次,在沒有SQL語句的幫助下進行資料分析會令廣大的DBA抓狂,為了得到一個資料分析結果,往往要進行多個步驟的計算,然後統計計算結果最後得出結論,在SQL中可能一句話就解決的問題要在NoSQL中經歷若干步驟才能完成。

最常見的例子就是在NoSQL中進行多表聯合查詢,這在SQL中是簡單的不能再簡單的操作,只要將多個關係表JOIN在一起就可以查詢他們的資料。但是,由於NoSQL沒有表結構的概念,而且像MongoDB這樣的NoSQL產品建議把相關資料全部存在一個Collection內,那麼當出現要查詢多個Collection之間的資料很多人只能在應用程式層面解決,但這會帶來幾個問題:

- 應用程式只能由開發人員維護,DBA很難對應用程式進行開發和除錯。

- 應用程式層邏輯比較複雜,將多表聯合操作放到裡面增加了程式的維護難度。

- 由於相關資料都載入到應用程式層,這需要更多的記憶體來進行資料的聯合操作,如果演算法寫的不好有可能還會出現O(n2)的時間複雜度程式碼,嚴重影響程式效能。

- 像SQL語句那樣的JOIN操作轉換成程式程式碼可能需要多次搜尋資料庫,這需要多次磁碟IO操作,再次影響了效能。
複製程式碼

當然,為了解決這個問題一些NoSQL資料庫提供了聯合查詢的支援,比如像MongoDB支援Aggregation Framework,可以通過下面的方式進行

db.orders.aggregate([     {       $lookup:         {          

    from: "inventory",          

    localField: "item",          

    foreignField: "sku",          

   as: "inventory_docs"        

}    } ])
複製程式碼

上面的程式碼對orders表和inventory表進行聯合查詢,這只是一次簡單查詢,要是牽扯到三張或者四張表程式碼會更復雜,可以看到這樣的操作語句DBA很難掌握,相比SQL而言它確實複雜不少。當然有一些工具可以幫助大家簡化上面的操作,例如dbKoda提供了圖形化的查詢方法如下所示。但是這樣的操作仍然過於複雜。

image.png

這裡並不是說NoSQL資料庫不再流行,相反NoSQL為我們帶來了前所未有的體驗。例如,傳統關係型資料庫是基於磁碟儲存,在NoSQL資料庫中,我們可以對資料庫進行幾戶無限制的水平擴充套件,從而避免了由於磁碟速度所產生的瓶頸。在沒有表結構約束的情況下進行資料擴充套件,升級是一件令人愉快的事情。但是NoSQL不能作為一種資料庫語言存在,在SQL世界中,不論什麼樣的關係型資料庫都支援標準SQL語句。但是在種類繁多的NoSQL資料庫中,不存在一個標準語言可以相容所有NoSQL資料庫的操作。幾乎所有NoSQL產品都是基於自身的特點和需求來完成的,所以他們之間沒有太多的共通點,開發人員在資料庫中的遷移遇到前所未有的挑戰。在關係型資料庫裡,如果你從事Oracle開發,想轉投SQL Server陣營,你要花費的代價會少很多,學習成本相對較低,可以說不需要再從新入門。關係型資料庫匯出來的資料基本上都可以保證和標準SQL語法相容,這在資料遷移方面具有得天獨厚的優勢。反觀NoSQL,你在跨越兩種不同NoSQL產品時基本需要從頭開始學習,學習成本時時刻刻困擾著廣大開發人員。我為什麼說NoSQL是一個失敗的命名,可以看到NoSQL鼓吹的賣點是脫離SQL,但是又把很多完全不同種類的資料庫放到一個名稱空間中,比如圖資料庫Neo4j和基於列的Cassandra是完全不同型別的產品,但卻要放到一個叫NoSQL的名稱空間下,感覺差強人意。所以,從這一點上說NoSQL的發展遇到了瓶頸,人類作為一種高度智慧的物種,一定會找到替代的方法來解決當下的問題。

#大資料

資料的增長不光是量上的增加,更多的是資料來源的增加,多種資料來源的資料整合到一起必然會出現資料格式的不匹配,儲存空間的限制等問題。於是大資料框架接踵而來,其中的佼佼者可以算是Hapoop和Apache Lucene兩大資料平臺。但是,上面提到的問題對於大資料的搜尋同樣存在,以Hadoop為例看一下下面的例子,Hadoop的Mapreduce用來統計文章中相同字出現的個數:

public void map(Object key, Text value, Context context

) throws IOException, InterruptedException {

StringTokenizer itr = new StringTokenizer(value.toString());

while (itr.hasMoreTokens()) {

word.set(itr.nextToken());

context.write(word, one);

}

}

}

public static class IntSumReducer

extends Reducer {

private IntWritable result = new IntWritable();

public void reduce(Text key, Iterable values,

Context context

) throws IOException, InterruptedException {

int sum = 0;

for (IntWritable val : values) {

sum += val.get();

}

result.set(sum);

context.write(key, result);

}

}

public static void main(String[] args) throws Exception {

Configuration conf = new Configuration();

Job job = Job.getInstance(conf, "word count");

job.setJarByClass(WordCount.class);

job.setMapperClass(TokenizerMapper.class);

job.setCombinerClass(IntSumReducer.class);

job.setReducerClass(IntSumReducer.class);

job.setOutputKeyClass(Text.class);

job.setOutputValueClass(IntWritable.class);

FileInputFormat.addInputPath(job, new Path(args[0]));
    FileOutputFormat.setOutputPath(job, new Path(args[1]));
    System.exit(job.waitForCompletion(true) ? 0 : 1);
  }
}
複製程式碼

可以看到為了實現一個基本的查詢,我不過多的解釋上面語句的功能和作用,列在這裡只是想距離說明一下在NoSQL領域存在著如此繁瑣的查詢功能。MapReduce要求開發人員寫出上面的程式碼,對於Java開發人員來說還算能夠接收,但是對於DBA來講要求他們完成上面程式碼的實現有點牽強。從資料庫角度來說我們也不願意看到如此繁瑣的資料庫查詢語言。

因此,越來越多的NoSQL設計者開始設計類似SQL語言的查詢語句,有些甚至重新在NoSQL中建立了SQL語句,例如Hive實現了SQL語句對Hadoop的操作,例如上面那段MapReduce程式碼如果改用Hive來實現就只需類似下面的語句就可以完成:

select words,count(words) CntWords from
(select explode(words) words from temp) i group by words order by CntWords desc
複製程式碼

除了Hive以外,Apache Drill實現了SQL對多種不同資料來源的操作等。再有,一些關係型資料庫廠家也開始設計對複雜資料結構的支援,並實現通過SQL語句進行水平擴充套件,例如:ClustrixDB, DeepSQL, MemSQL, 和VoltDB。一些資料庫雲提供商Amazon Aurora 和 Google Cloud SQL也提供了支援對SQL語句的擴充套件。正是由於SQL語句可以對大資料的操作,這使得DBA的工作變得越來越簡單,順手。DBA可以想操作一個臺關係型資料庫那樣在上千個資料節點之間進行大資料查詢。

#SQL歸來 SQL語言已經被大家廣泛接受,那麼我們又離不開NoSQL的分散式支援以及鬆散的資料結構定義。為了完美的解決人們遇到的問題,我們勢必要把兩者進行一定程度上的結合,對於SQL和NoSQL資料庫而言,是NoSQL向SQL語言靠近現實還是把SQL資料庫變成像NoSQL那樣可以輕易的進行分散式擴充套件容易呢?用過關係型資料庫的人都清楚,很難想象需要花多長時間才能讓MySQL、Postgres或者Oracle支援幾百或者幾千臺節點上的分散式部署。相反,如果讓NoSQL支援SQL語言可能會更容易一些。所以在SQL和NoSQL兩者結合中,把SQL帶進NoSQL領域,看上去還是比較可行的,前面已經提到過有些公司早已開始做了,像Hive,Apache Drill等。

在計算機網路領域中有這麼一個術語:網路中的細腰(narrow waist)。“細腰”是一種比喻的說法,如下圖所示:

image.png
中間一層常被稱為"網路層"。 這一層只有IP (Internet Protocol) 這一個協議, 所以形象上很"細"。在其只上:傳輸層, 應用層, 這些層中的協議包羅永珍,例如TCP,UDP,HTTP等等。它們只能通過IP與下面的各層通訊, 這種設計可以用”一切基於IP“來描述。同樣的, 在網路層下面的物理層、資料鏈路層內容也十分豐富。它們也只能通過IP與上面的各層通訊, 這種設計對應地被描述為”IP兼顧一切“。這種設計有好有壞,益處是高層和底層的通訊都通過IP來完成,實現比較簡單;缺點是修改IP層會帶來很大的影響。不過這樣的構架已經存在幾十年,至今為止還算使用的比較成功。

這裡我沒什麼要提到網路結構呢?我們想一下,資料庫領域中的SQL,是不是就是網路層的“細腰”呢?看下面的結構圖:

image.png
就像我們現在沒發生存在沒有網路的時代一樣,我們也同樣無法生存在沒有資料的時代。與網路7層結構類似的是,資料也同樣存在7層結構,從SQL向上的應用層以及下面一直到資料儲存的構架層。我們所需要的正是一個通用的介面構成應用到構架的橋樑。這就是SQL的力量,這如同IP一樣,他就是資料構架中的通用介面。值得一提的是,SQL實際上承擔的不僅僅是通用的資料介面,除此之外,SQL還是一種具有可讀性的資料語言,這一點早已被廣大資料開發人員所接受。

#小結

SQL歸來,是的,我們不希望像NoSQL那樣去寫大量的程式碼來進行資料查詢和檢索,我們不怕學習新的語言或者新的技術,但是我們更希望保留高效率的工作節奏。這個世界充滿資料,資料無處不在,在我們身們,在我們身後。計算機軟硬體技術幫我們處理解析這些資料,他們可以被設計的足夠聰明,我們可以選擇通過成千上萬種介面來認知我們身邊的世界,或者我們可以選擇SQL,因為他是恢復資料平衡的源動力。

#作者簡介 趙翼,從北京理工大學畢業以後從事IT工作已經10餘年,接觸過的專案種類繁多,有Web,Mobile,醫療器械,社交網路,大資料儲存等。目前就職於SouthbankSoftware,從事NoSQL,MongoDB方面的開發工作。曾在GE,ThoughtWorks,元氣兔擔任前後端開發,技術總監等職位。

相關文章