只會寫代碼的是碼農;學好數據庫,基本能混口飯吃;在此基礎上再學好操作系統和計算機網絡,就能當一個不錯的程序員。如果能再把離散數學、數字電路、體系結構、數據結構/算法、編譯原理學通透,再加上豐富的實踐經驗與領域特定知識,就能算是一個優秀的工程師了。
計算機其實就是存儲/IO/CPU三大件; 而計算說穿了就是兩個東西:數據與算法(狀態與轉移函數)。常見的軟件應用,除了各種模擬仿真、模型訓練、視頻游戲這些屬于計算密集型應用外,絕大多數都屬于數據密集型應用。從最抽象的意義上講,這些應用干的事兒就是把數據拿進來,存進數據庫,需要的時候再拿出來。
抽象是應對復雜度的最強武器。操作系統提供了對存儲的基本抽象:內存尋址空間與磁盤邏輯塊號。文件系統在此基礎上提供了文件名到地址空間的KV存儲抽象。而數據庫則在其基礎上提供了對應用通用存儲需求的高級抽象。
互聯網應用大多屬于數據密集型應用,對于真實世界的數據密集型應用而言,除非你準備從基礎組件的輪子造起,不然根本沒那么多機會去擺弄花哨的數據結構和算法。甚至寫代碼的本事可能也沒那么重要:可能只會有那么一兩個Ad Hoc算法需要在應用層實現,大部分需求都有現成的輪子可以使用,主要的創造性工作往往在數據模型與數據流設計上。實際生產中,數據表就是數據結構,索引與查詢就是算法。而應用代碼往往扮演的是膠水的角色,處理IO與業務邏輯,其他大部分工作都是在數據系統之間搬運數據。
在最寬泛的意義上,有狀態的地方就有數據庫。它無所不在,網站的背后、應用的內部,單機軟件,區塊鏈里,甚至在離數據庫最遠的Web瀏覽器中,也逐漸出現了其雛形:各類狀態管理框架與本地存儲?!皵祿臁笨梢院唵蔚刂皇莾却嬷械墓1?磁盤上的日志,也可以復雜到由多種數據系統集成而來。關系型數據庫只是數據系統的冰山一角(或者說冰山之巔),實際上存在著各種各樣的數據系統組件:
數據庫:存儲數據,以便自己或其他應用程序之后能再次找到(PostgreSQL,MySQL,Oracle)緩存:記住開銷昂貴操作的結果,加快讀取速度(Redis,Memcached)搜索索引:允許用戶按關鍵字搜索數據,或以各種方式對數據進行過濾(ElasticSearch)流處理:向其他進程發送消息,進行異步處理(Kafka,Flink,Storm)批處理:定期處理累積的大批量數據(Hadoop)架構師最重要的能力之一,就是了解這些組件的性能特點與應用場景,能夠靈活地權衡取舍、集成拼接這些數據系統。絕大多數工程師都不會去從零開始編寫存儲引擎,因為在開發應用時,數據庫已經是足夠完美的工具了。關系型數據庫則是目前所有數據系統中使用最廣泛的組件,可以說是程序員吃飯的主要家伙,重要性不言而喻。
對玩具應用而言,使用內存變量與文件來保存狀態也許已經綽綽有余了。但隨著系統的增長,我們會遇到越來越多的挑戰:軟硬件故障把數據搞成一團漿糊(可靠性);狀態太多而內存太小放不下(可伸縮性);并發訪問控制導致代碼復雜度發生爆炸(可維護性),諸如此類。這些問題相當棘手,卻又相當普遍,數據庫就是用來解決這些問題的。分拆是架構演化的重要方法論,數據庫將狀態管理的職能從應用程序中分拆出來,即所謂的“狀態與計算相分離”。數據庫將程序員從重復造輪子的泥潭中解救出來,極大地解放了生產力。
每個系統都服務于一個目的,解決一類問題。問題比方法更重要。但現實很遺憾,以大多數學生,甚至相當一部分公司能接觸到的現實問題而言,拿幾個文件甚至在內存里放著估計都能應付大多數場景了(需求簡單到低級抽象就可以Handle)。沒什么機會接觸到數據庫真正要解決的問題,也就難有真正使用與學習數據庫的驅動力,更別提數據庫原理了。
所以我也理解當前這種填鴨教學現狀的苦衷:工作之后很難有這么大把的完整時間來學習原理了,所以老師只好先使勁灌輸,多少讓學生對這些知識有個印象。等學生參加工作后真正遇到這些問題,也許會想起大學好像還學了個叫數據庫的東西,這些知識就會開始反芻。
數據庫,尤其是關系型數據庫,非常重要。那為什么要學習其原理呢?
對優秀的工程師來說,只會用數據庫是遠遠不夠的。學習原理對于當CRUD BOY搬磚收益并不大,但當通用組件真的無解需要自己擼起袖子上時,沒有金坷垃怎么種莊稼?設計系統時,理解原理能讓你以最少的復雜度代價寫出更可靠高效的代碼;遇到疑難雜癥需要排查時,理解原理能帶來精準的直覺與深刻的洞察。
數據庫是一個博大精深的領域,存儲I/O計算無所不包。其主要原理也可以粗略分為幾個部分:數據模型設計原理(應用)、存儲引擎原理(基礎)、索引與查詢優化器的原理(性能)、事務與并發控制的原理(正確性)、故障恢復與復制系統的原理(可靠性)。 所有的原理都有其存在意義:為了解決實際問題。
例如數據模型設計中的范式理論,就是為了解決數據冗余這一問題而提出的,它是為了把事情做漂亮(可維護)。它是模型設計中一個很重要的設計權衡:通常而言,冗余少則復雜度小/可維護性強,冗余高則性能好。具體來說,冗余字段能加快特定類型的讀?。ㄍㄟ^消除連接),但在寫入時就需要做更多的工作:維護多對象副本間的一致性,避免多對象事務并發執行時發生踩踏。這就需要仔細權衡利弊,選擇合適的規范化等級。數據模型設計,就是生產中的數據結構設計。不了解這些原理,就難以提取良好的抽象,其他工作也就無從談起。
而關系代數與索引的原理,則在查詢優化中扮演重要的角色,它是為了把事情做得快(性能,可擴展)。當數據量越來越大,SQL寫的越來越復雜時,它的意義就會體現出來:怎樣寫出等價但是更高效的查詢?當查詢優化器沒那么智能時,就需要人來干這件事。這種優化往往有四兩撥千斤的效果,比如一個需要幾秒的KNN查詢,如果知道R樹索引的原理,就可以通過改寫查詢,創建GIST索引優化到1毫秒內,千倍的性能提升。不了解索引與查詢設計原理,就難以充分發揮數據庫的性能。
事務與并發控制的原理,是為了把事情做正確。事務是數據處理領域最偉大的抽象之一,它提供了很多有用的保證(ACID),但這些保證到底意味著什么?事務的原子性讓你在提交前能隨時中止事務并丟棄所有寫入,相應地,事務的持久性則承諾一旦事務成功提交,即使發生硬件故障或數據庫崩潰,寫入的任何數據也不會丟失。這讓錯誤處理變得無比簡單,所有可能的結果被歸結為兩種情況:要么成功完事,要么失敗了事(或重試)。有了后悔藥,程序員不用再擔心半路翻車會留下慘不忍睹的車禍現場了。
另一方面,事務的隔離性則保證同時執行的事務無法相互影響(在可序列化隔離等級下)。更進一步,數據庫提供了不同的隔離等級保證,以供程序員在性能與正確性之間進行權衡。編寫并發程序并不容易,在幾萬TPS的負載下,各種極低概率,匪夷所思的問題都會出現:事務之間相互踩踏,丟失更新,幻讀與寫入偏差,慢查詢拖慢快查詢導致連接堆積,單表數據庫并發增大后的性能急劇惡化,比如我遇到的一個最靈異的例子是:快慢查詢總量都減少,但因相對比例變化導致數據庫被壓垮。這些問題,在低負載的情況下會潛伏著,隨著規模量級增長突然跳出來,給你一個大大的驚喜?,F實中真正可能出現的各類異常,也絕非SQL標準中簡單的幾種異常能說清的。 不理解事務的原理,意味著應用的正確性與數據的完整性可能遭受不必要的損失。
故障恢復與復制的原理,可能對于普通程序員沒有那么重要,但架構師與DBA必須清楚。高可用是很多應用的追求目標,但什么是高可用,高可用怎么保證?讀寫分離?快慢分離?異地多活?x地x中心?說穿了底下的核心技術其實就是復制(Replication)(或再加上自動故障切換(Failover))。這里有無窮無盡的坑:復制延遲帶來的各種靈異現象,網絡分區與腦裂,存疑事務 ,諸如此類。 不理解復制的原理,高可用就無從談起。
對于一些程序員而言,可能數據庫就是“增刪改查”,包一包接口,原理似乎屬于“屠龍之技”。如果止步于此,那原理確實沒什么好學的,但有志者應當打破砂鍋問到底的精神。私認為只了解自己本領域知識是不夠的,只有把當前領域賴以建立的上層領域摸清楚,才能稱為專家。在數據庫面前,后端也是前端;對于程序員的知識棧而言,數據庫是一個合適的棧底。
上面講了WHY,下面就說一下 HOW
數據庫教學的一個矛盾是:如果連數據庫都不會用,那學數據庫原理有個卵用呢?
學數據庫的原則是學以致用。只有實踐,才能帶來對問題的深刻理解;只有先知其然,才有條件去知其所以然。教材可以先草草的過一遍,然后直接去看數據庫文檔,上手去把數據庫用起來,做個東西出來。通過實踐掌握數據庫的使用,再去學習原理就會事半功倍(以及充滿動力)。對于學習而言,有條件去實習當然最好,沒有條件那最好的辦法就是自己創造場景,自己挖掘需求。
比如,從解決個人需求開始:管理個人密碼,體重跟蹤,記賬,做個小網站、在線聊天App,實用微信小程序。當它演化的越來越復雜,開始有多個用戶,出現各種蛋疼問題之后,你就會開始意識到事務的意義。
再比如,結合爬蟲,抓一些房價、股價、地理、社交網絡的數據存在數據庫里,做一些挖掘與分析。當你積累的數據越來越多,分析查詢越來越復雜;SQL長得沒法讀,跑起來慢出豬叫,這時候關系代數的理論就能指導你進一步進行優化。
當你意識到這些設計都是為了解決現實生產中的問題,并親自遇到過這些問題之后,再去學習原理,才能相互印證,并知其所以然。當你發現查詢時間隨數據增長而指數增長時;當你遇到成千上萬的用戶同時讀寫為并發控制焦頭爛額時;當你碰上軟硬件故障把數據攪得稀巴爛時;當你發現數據冗余讓代碼復雜度快速爆炸時;你就會發現這些設計存在的意義。
教材、書籍、文檔、視頻、郵件組、博客都是很好的學習資源。教材的話華章的黑皮系列教材都還不錯,《數據庫系統概念》這本就挺好的。但我推薦先看看這本書:《設計數據密集型應用》 ,寫的非常好,我覺得不錯就義務翻譯了一下。
紙上得來終覺淺,絕知此事要躬行。寫了這么多,不帶點“私貨”也不合適哈?實踐方能出真知,新手上路選哪家?我個人推薦PostgreSQL,如果能再選一樣就加個Redis。對開發而言,這是相當實用的組合。PostgreSQL號稱世界上最先進的開源關系型數據庫,源代碼寫的非常漂亮,有很多值得學習的地方。很多國外的數據庫課程與教科書都使用PostgreSQL作為教學樣例。
PostgreSQL在現實世界中也表現不俗,在我們的實踐中,在250WTPS與200TB數據的量級下,單一PostgreSQL選型依然能穩如狗地支撐業務。而且其功能豐富到不可思議,能在很可觀的規模內做到一專多長,除了本職的OLTP,Pg還在相當長的時間里兼任了緩存,OLAP,批處理,甚至消息隊列的角色。當然如“架構演進”一圖所示,神龜雖壽,猶有竟時。最終這些兼職功能還是要逐漸分拆出去由專用組件負責,但那已經是近千萬日活時的事了。
所以,關系型數據庫雖然強大,卻絕非數據處理的終章。數據庫的世界非常精彩,盡可能地去嘗試各種各樣的組件吧~。