從Git設計原理到業務系統設計與開發

做者簡介linux

近鐵Jeff Dean, 餓了麼物流研發部搬磚工, 崇拜各類大牛, 因爲Jeff Dean的做品深而廣, 本文特地專一於小而美的Linus Torvalds 第二做品:Git, 但願從中汲取養分, 向大師致敬。git

文介紹Git核心優勢和實現思路,重點引伸出對業務系統設計與開發的啓示; 因爲篇幅有限,本文不詳細介紹Git命令的具體執行細節,若是須要了解執行Git命令時,底層具體發生了什麼, 請移步Git官方文檔第十章 Git Internals程序員

爲何關注Git?

Git做爲一個前無古人,極可能後無來者的內容地址跟蹤器 , 席捲各大公司,深受全部程序員喜好(以爲Git難用的除外), 確定有他的過人之處。github

先搬出互聯網技術關鍵詞標配:redis

分佈式、去中心化、可靠性、容錯性、可用性、數據一致性、性能是一個功能, 而不是一個優化等。算法

Git都知足有木有! Linus用兩週就實現自舉的Git十幾年後依然健壯如初。 假如我寫一個接口或設計一個微服務或寫一個小工具, 自帶擴展技能,歷來不掛,QPS暴增也無需推翻重構,外部依賴抖動時自動降級,數據量增大而沒有拖垮性能, 能與Git有不少共同點,豈不頗有成就感?編程

Git最初設計時要達到的目的

分佈式:1)多人同一個分支不一樣地點不一樣時間並行開發;2)單人本地多分支並行開發。api

性能: 速度要快。慢是Linus Torvalds本人沒法接受的,由於linux內核天天有成百上千次提交。緩存

安全與信任: 便可靠性, 我push上去的代碼pull下來必定仍是個人代碼, 沒有丟失或被惡意篡改過。安全

Git 數據建模

插播一條語錄: Bad programmers worry about the code. Good programmers worry about data structures and their relationships. - by Linus Torvarlds. (通常的程序員只關心代碼,而優秀的軟件工程師更關心數據結構以及他們之間的關係。)

啓示

Linus本人寫C語言比較多,我的認爲這裏的data structures and their relationships 若是脫離C語言 的特定背景,能夠泛指技術對場景或業務邏輯的抽象, 好比: 面向對象建模,領域驅動, 甚至更宏觀的架構設計或微服務怎樣劃分。

根據我的經驗,若是系統設計的好, 代碼稍微寫low一點,整個系統的表現也不會太差, 並且代碼的坑比較容易填; 反之,再漂亮的代碼也很難填上系統設計的坑, 只能面臨重構。

Git 內部文件處理:

  1. 代碼庫裏全部文件都是數據,以數據爲中心,全部操做、存儲和處理邏輯圍繞數據展開。
  2. 跟蹤整個項目總體狀態,每次創建全局快照,而不是跟蹤每一個文件的變動。
  3. 針對git目錄下的每一個文件計算一個hash值, 文件內容做爲value, hash值爲文件key。
  4. 若是單個文件內容發生變化, 下次從新計算hash值。若是文件沒有發生變動,當前快照指向歷史hash。
  5. 整個代碼庫的變化歷史和文件組成用樹形結構表示。
  6. Git每次commit記錄整個代碼庫的一次快照, 當前快照包含發生變化的文件和歷史快照(子樹)。
    圖1 - Git數據建模
    Git數據建模

Git 執行效率與時間複雜度

對於Git內部結構能夠簡單的理解爲: Git內部是一棵 , 每一個節點都是一個指針(key), 這個指針(key)可能表明一個文件,或一次commit或一個分支起點或一次merge或一個tag, key對應的value就是內容, 若是key是表明一個文件, value就是文件內容; 若是key是表明一次commit, value是一顆子樹, 包含這次commit對整個項目的snapshot。

平時不少git操做均可以近似理解爲: 在樹上執行遍歷查找O(lg(n)),切換指針O(1),而後根據指針取文件內容O(1))。 這些操做速度都是很快的,只有在網絡交互,文件壓縮與解壓和計算diff時,人肉能夠感知到有時間等待。

啓示

  1. 數據結構和算法原來是這麼用的,基本功仍是要紮實呀。
  2. LeetCode仍是要常刷的。
  3. 建模很重要。越好的設計模型離被推翻重構的距離越遠。 關於架構設計和規劃的 作技術選型和概要設計,P7級別的就要作到, 1年之後當別人接手時就不須要考慮重構,若是是P8的就有信心作到2年之後,而P9的則是3年或更長時間。 Git已經經歷了超過10年的考驗了。

Git 空間壓縮與訪問效率平衡

對於Git,近期發生變化的數據屬於熱數據,Git假設這些數據會被頻繁訪問或使用到。其餘數據爲歷史數據。 對於熱數據, 即便發生微小變化,Git也會全量冗餘存儲,提升訪問效率。 當熱數據文件數量達到必定值時,會觸發打包壓縮邏輯, delta差別存儲,節省空間。

啓示

對於不一樣的存儲介質,例如db, redis, mq, 選用不一樣的存儲邏輯或策略,以達到訪問效率與存儲空間的平衡。

Git 安全性與容錯性

Git對文件內容和項目總體snapshot都使用hash值表示,hash值與內容一一對應, 若是文件內容被篡改或硬盤損壞 致使數據丟失,hash值校驗都會失敗。 此時Git設計時已經假設:

  1. 硬盤是隨時崩潰的,即存儲是不可靠的
  2. 有人惡意引入Bug或偷偷修改代碼

啓示

在分佈式環境下,設計系統或接口,可否保持容錯性,自帶降級,建議多向本身提出假設:

  1. 網絡是不可靠的。 接口調用timeout和失敗是必然存在的, 邏輯應該怎樣處理?是否自帶降級技能。
  2. DB是會抖動的,緩存也會失效,主從延遲必定存在的,此時代碼邏輯可否兼容?若是fail fast, 是否有報警機制?
  3. 對外的接口,調用方的傳參要考慮最壞的場景,是否有合理的入參校驗/防重/冪等處理?

Merge 算法思想: 三路合併

先找出兩個分支的公共祖先, 而後兩個分支分別與公共祖先diff,指出有衝突的地方。 Git merge並無試圖智能的去解決衝突,只是指出衝突,而後將merge交給最合適最高效的人去解決: 即引發衝突的開發者。

啓示

  1. 選用最合理的數據結構、索引結構或算法思路,不斷優化系統處理速度。
  2. 劃清邊界,只作本身最擅長和應該作的事情, 儘可能保證高內聚,低耦合 。 微服務環境下,在設計系統時,應該多考慮: 各個服務調用時,不一樣的異常誰負責處理; 接口超時誰負責補償; 數據一致性交給誰來保證等。

Linus Torvalds眼中好的代碼和好的軟件工程師

好的軟件工程師要有good taste, 堅持不懈的追求用正確的方式解決問題。

好的代碼, 舉個例子:移除鏈表中某個節點

通常的代碼, 用if else 判斷邊界值:

void remove_list_entry(entry) {
    prev = NULL;
    walk = head;

    // Walk the list
    while (walk != entry){
        prev = walk;
        walk = walk->next;
    }

    //Remove the entry by updating the
    //head or the previous entry

    if(!prev)
        head = entry->next;
    else
        prev->next = entry->next;
}
複製代碼

好的代碼, 換一種寫法,使正常處理邏輯能夠兼容邊界值:

void remove_list_entry(entry) {
    //The "indirect" pointer points to the
    // *address* of the thing we'll update

    indirect = &head;

    //Walk the list,looking for the thing that
    //points to the entry we want to remove

    while ((*indirect) != entry)
        indirect = &(*indirect)->next;

    // .. and just remove it
    *indirect = entry->next
}

複製代碼

啓示

平時編碼中對邊界值的處理是否優雅?

如今一個變量的傳遞會通過多種編程語言和中間件,中間過程通常有序列化和反序列化, 給空對象賦默認值等邏輯, 怎樣保證明際結果與預想的徹底一致? 邊界值處理很差,一是代碼很差維護,二是容易引入Bug。 平常碰到的邊界值有: null, int默認值0等。

其餘

  1. 從用戶角度出發設計系統。Linus是Git的第一個也是最粘性的用戶, 在寫Git以前他就已經瞭解了當時的各個版本控制 軟件的優缺點,他清楚Git須要實現哪些功能, 哪些功能無需實現。
  2. Git在命令行執行命令時,響應的提示信息及時且準確。
  3. 站在巨人肩膀上,向已有系統學習。Git最初的核心命令都是複用操做系統已有的功能,例如diff, 壓縮,文件處理等。

啓示

  1. 本身寫的系統或功能,本身要從最終用戶角度嘗試使用或模擬使用。
  2. 對於api接口,研發負責自測; 寫單元測試時,能夠從調用方角度思考接口名稱和參數設計是否合理,是否拋出異常, 返回信息中是否包含錯誤碼和提示信息。
  3. 既要有造輪子的能力,又要有不重複造輪子的覺悟。 多向開源系統學習,多研究底層原理,大部分原理或設計思路都是相通的。

彩蛋

(以下爲Linus Torvalds語錄,僅供娛樂。 Linus Torvalds以噴人聞名,網絡上不少人指責他人品不行, 不多有人評價他的技術水平,多是沒有能力評價吧。)

  1. "In fact, I am a very cynical and untrusting person. I think most of you are completely incompetent". In front of a large group of Google developers. 翻譯:在一羣谷歌軟件工程師面前說: 各位不要誤會, 我不是針對你, 我是說在座的各位寫代碼的水平徹底都是垃圾。
  2. "Because nobody actually creates perfect code at first time around except me, but there's only one of me. " 翻譯:實際上沒人能一次就寫出完美的代碼,除了我。可是世界上只有一個我。ps: 這句話也是在一羣谷歌軟件工程師面前說的。

參考

  1. Linus Torvalds 2007年在谷歌分享Git
  2. Linus Torvalds TED 採訪
  3. Git官方文檔





閱讀博客還不過癮?

歡迎你們掃二維碼經過添加羣助手,加入交流羣,討論和博客有關的技術問題,還能夠和博主有更多互動

博客轉載、線下活動及合做等問題請郵件至 shadowfly_zyl@hotmail.com 進行溝通
相關文章
相關標籤/搜索