大家好呀,我是小菜~
帥哥美女,知道你們時間寶貴,那麼就由小菜為你讀好一本書,讀一本好書,取其精華,與你共享~!
本文主要分享
《軟體架構設計:大型網站技術架構與業務架構融合之道》
如有需要,可以參考
如有幫助,不忘 點贊 ❥
微信公衆号已開啟,菜農曰,沒關注的同學們記得關注哦!
今天帶來的是 《軟體架構設計:大型網站技術架構與業務架構融合之道》 的讀書筆記
(文中使用到的例子貼圖均出于原書)
在正式進入分享之前,我們想看下這本樹的目錄架構
軟體架構設計:大型網站技術架構與業務架構融合之道
這本書總共分為
五
個部分,共計
17
章 ,總體來說内容還是挺多的。内容相對全面,但并沒有面面俱到,還是比較推薦閱讀的一本書,話不多說,進入正文!
第一部分:什麼是架構
第一部分由兩個章節組成,簡單的介紹了下什麼是架構
第一章:五花八門的架構師職業
1.1 架構師職業分類
現在随便找一個招聘網站或獵頭釋出的招聘廣告,我們都能看到各式各樣的架構師頭銜,比如有:Java 架構師,前端架構師,後端架構師,資料架構師,中間件架構師... 等等,而且年限的要求也各不一,3~5年,8~10年。
但是從這些崗位的需求我們可以看出,“架構師”中的
架構
是一個很虛的詞,不同領域和行業對員工要求的能力和工作經驗差異很大。
現在問起很多開發者的發展路線都不約而同的是要成為一名架構師,那麼對架構師的定義是怎麼樣的?架構師在項目體系和團隊結構中應當着一個怎麼樣的角色?如何成為一名架構師?這些你是否都有一個明确的答案,是否也為之目标而努力前行着!
1.2 架構的分類
單純以技術的角度來看,軟體系統自底向上可以分為三層
第一層:基礎架構
基礎架構是指雲平台、作業系統、網絡、存儲這些構成,一些中小公司大多會選擇使用大公司研發的雲計算平台,研發成本低,穩定有保障
第二層:中間件與大資料層
中間件屬于公司中必有的,類似消息中間件,資料庫中間件,緩存中間件,而大資料層對于中小公司來說比較少有沉澱,類似開源的 Hadoop 生态體系,Hive、Spark、Storm、Fink等
第三層:業務系統架構
對于第三層的劃分并不是絕對,圖中展現了三種架構類型:
通用軟體架構
、
離線業務系統架構
、
線上業務系統架構
,但由于現實中軟體的種類過多,比如還存在嵌入式系統。這裡簡單描述下圖中第三種具備的類種:
- 通用軟體架構:常用的辦公軟體、浏覽器、播放器等
- 離線業務系統: 基于大資料的 BI(商業智能) 分析、資料挖掘、報表與可視化等
- 線上業務系統架構: 搜尋、推薦、即時通信、電商、遊戲、廣告、企業ERP或CRM等
第二章:架構的道與術
2.1 何為道,何為術
不禁感歎這年頭聊架構,這可以上道與術的層面了。
這張圖是大多數項目的基本架構圖,可以将每層映射到你們的項目中,是不是不會覺得很陌生。
那麼實際中這張圖能夠反映出架構抉擇嗎,架構師的任務是否就是簡單的劃分層級結構,然後就可以埋頭進行開發了?
我們依賴這張圖将問題進行擴充:
- 如何拆分微服務?
- 如何組織服務與服務之間的層級關系?
- 如何設計接口?
- 如何保證高可用?如何分庫分表?如何保證資料一緻性?...
想要表達的問題實在是太多了,由此可見架構師的任務并不簡單。
2.2 道與術的辯證關系
問題那麼複雜,我們就以道與術來了解。假如你要成為一名武林高手,那麼花裡胡哨的招式對于某些人來說很重要,因為要追求好看,所謂的花架子,而招式我們便可了解為術,那麼追求高手的層面,我們是否要修煉内功心法,底子紮實,才能成為頂級高手。
那麼道重要還是術重要,這是個公說公有理婆說婆有理的問題,段譽的内功厲害,但使不出招式可能也有些徒然,招式好看,卻沒有内功支撐,也隻能成為花架子的笑談,而道術兼備,方能頂級。
第二部分:計算機功底
這部分的内容頗多,重在道的修煉
第三章:語言
語言是在是太多了,忍不住吐槽~ 盡管語言如此之多,市面上還是不斷地推陳出新,我們面對語言的不斷疊代要追求潮流還是巋然不動?在我看來,我們要追求道,底層掌握結實,管它日轉星移,我亦坦然相對。
語言再多再繁雜,都具備共同的典型特性,無外乎一些文法糖使用熟練與否
第四章:作業系統
I/O是繞不過去的一個基本問題。從檔案I/O到網絡I/O,存在着各式各樣的概念和I/O模型
4.1 緩存I/O 和 直接I/O
在了解兩個原理之前,我們先清楚幾個概念:
- 應用程式記憶體: 通常寫代碼用 malloc/free、new/delete 等配置設定出來的記憶體
- 使用者緩沖區: 位于使用者空間中緩沖區,如 C語言FILE 結構體裡面的 Buffer
- 核心緩沖區: Linux 作業系統的 Page Cache。一個 Page 的大小一般為 4K
以上三個概念了解後,我們繼續看 I/O 操作
-
緩沖I/O
讀:磁盤 -> 核心緩沖區 -> 使用者緩沖區 -> 應用程式
寫: 應用程式 -> 使用者緩沖區 -> 核心緩沖區 -> 磁盤
對于緩沖I/O,一個讀操作會有3次資料拷貝,一個寫操作會有反向的3次資料拷貝
-
直接I/O
讀: 磁盤 -> 核心緩沖區 -> 應用程式
寫: 應用程式 -> 核心緩沖區 -> 磁盤
對于直接I/O,一個讀操作會有3次資料拷貝,一個寫操作會有返現的2次資料拷貝
總結:直接I/O 并不是沒有緩沖,而是沒有使用者級的緩沖,對于作業系統本身的緩沖還是有的
4.2 記憶體映射檔案與零拷貝
1)記憶體映射檔案
從緩沖I/O到直接I/O,讀寫操作從3次的資料拷貝縮減到2次資料拷貝。而到了記憶體映射檔案,讀寫操作再次縮減到了1次資料拷貝,也就是:
- 讀:磁盤 -> 核心緩沖區
- 寫: 核心緩沖區 -> 磁盤
應用程式雖然讀寫的是自己的記憶體,但這個記憶體隻是一個 "邏輯位址",實際讀寫的是 核心緩沖區
2)零拷貝
零拷貝(Zero Copy)又是提升 I/O 效率的一大利器,在平時有問到 Kafka 是如何做到讀寫那麼快的時候,其中一個很大的原因便是 Kafka 用到了零拷貝技術。
- 這是一個利用直接I/O進行收發檔案的過程
磁盤→核心緩沖區→應用程式記憶體→Socket緩沖區→網絡
- 這是一個利用記憶體映射檔案進行收發檔案的過程
整個過程從4次的資料拷貝降低到了3次,不再經過應用程式記憶體,直接在核心空間中從核心緩沖區拷貝到 Socket 緩沖區
- 這是一個利用零拷貝進行收發檔案的過程
利用零拷貝的話,連記憶體緩沖區到Socket緩沖區的資料拷貝步驟都可以省略。在核心緩沖區和 Socket 緩沖區之間并沒有做資料拷貝,隻是一個位址的映射,底層的網卡驅動程式要讀取資料并發送到網絡的時候,看似讀的是Socket緩沖區中的資料,實際上讀得是核心緩沖區的資料。
總結:為什麼稱之為零拷貝呢,因為從記憶體的角度上看,資料在記憶體中沒有發生資料拷貝,隻在記憶體與I/O之間傳輸。利用的還是 核心緩沖區 與 Socket緩沖區之間的映射,資料本身隻有一份
4.3 網絡 I/O 模型
網絡 I/O 模型也是一個極易混淆的概念,至今為止我們聽過幾種網絡I/O模型呢
- 網絡阻塞 I/O
- 網絡非阻塞 I/O
- I/O 多路複用
- 異步 I/O
很多時候我們容易混淆的概念是 非阻塞 和 異步
1)網絡模型
1. 網絡阻塞 I/O
這種模型很好了解,就是調用的時候會被阻塞,直到資料讀取完成或寫入成功
2. 網絡非阻塞 I/O
和上述相反,但沒有資料的時候會立即傳回,不會阻塞,然後通過輪詢的方式不斷查詢直到擷取到資料
如果隻有幾十乃至上百個連接配接的時候,上面兩種 I/O 模型處理的方式問題都不大,當連接配接數達到幾十萬乃至上百萬時,那問題就很嚴重了
3. I/O 多路複用
該方式也是阻塞調用,一次性将所有的連接配接請求傳進來,當某個連接配接請求具備條件後,會立即将結果放回,告知應用程式有哪些連接配接可讀或可寫。常用的 I/O 多路複用的方法有:select、poll、epoll、Java的NIO,其中 epoll 的效率最高,也是目前最主流的。
epoll
整個 epoll 分為三個步驟
- 事件注冊
- 輪詢查詢是否就緒
- 事件就緒後進行讀寫
其中又可分為兩種模式:
LT(水準觸發/條件觸發)
和
ET(邊緣觸發/狀态觸發)
- LT 水準觸發:隻要讀緩沖區不空就會一直觸發讀事件;寫緩沖區不滿,就會一直觸發寫事件
- ET 邊緣觸發:讀緩沖區的狀态從空轉為非空的時候觸發一次;寫緩沖區的狀态從滿轉為非滿的時候觸發一次
總結:實際的開發中,大家一般傾向于 LT(預設模式)。但使用的時候需要避免 "寫死循環"的問題,因為寫緩沖區為滿的機率很小,會一直觸發寫事件
4. 異步 I/O
異步 I/O 是指所有的讀寫操作都由作業系統完成,當處理結束後,将結果通過指定的回調函數或其他機制告知應用程式
總結: 阻塞和非阻塞是從函數調用的角度來說,而同步和非同步是從 "讀寫是由誰來完成" 的角度來說
2)設計模式
除了上面幾種網絡I/O模型,我們還經常聽到 Reactor 模式與 Proactor 模型,這兩種并不是網絡I/O模型。而是網絡架構中的兩種設計模式,無論作業系統的網絡 I/O 模型的設計,還是上層網絡架構的網絡 I/O 模型的設計,用的都是這兩中設計模型之一
1. Reactor 模式
這是一種主動模式。應用程式會不斷地輪詢,詢問作業系統或網絡架構、I/O 是否就緒。select、poll、epoll、Java中的NIO 就屬于這種主動模式。
2. Proactor 模式
這是一種被動模式。應用程式會将所有的讀寫操作都交給作業系統完成,完成後再将結果通過一定的通知機制告知應用程式
4.4 程序、線程和協程
不同語言有不同的使用習慣。如 Java 一般是寫 單程序多線程 ,C++ 一般是 單程序多線程 或 多程序單線程
1)為什麼要使用多線程?
- 提高 CPU 使用率
- 提高 I/O 吞吐
2)多線程會帶來的問題?
- 鎖(悲觀鎖、樂觀鎖、互斥鎖、讀寫鎖、自旋鎖、公平/非公平鎖等)
- Wait 與 Signal 操作
- Condition
3)為什麼需要多程序?
- 線程間鎖的存在,會導緻并發效率下降,同時增加編碼難度
- 線程上下文切換需要時間,過多的切換會導緻效率低下
- 多程序互相獨立,其中一個崩潰後,其他程序可以繼續運作,提高可靠性
不要通過共享記憶體來實作通信,而應通過通信實作共享記憶體
通俗了解:盡可能通過消息通信, 而不是共享記憶體來實作程序或線程之間的同步
4)為什麼需要協程?
- 更好地利用CPU,協程可以由應用程式自己排程,而線程不行
- 更好地利用記憶體,協程的堆棧大小不是固定的,用多少申請多少
4.5 無鎖(記憶體屏障與CAS)
1)記憶體屏障
讀可以多線程、寫必須單線程,如果多線程寫,則做不到無鎖
基于記憶體屏障(防止代碼重排序),有了Java中的volatile關鍵字,再加上單線程寫的原則,就有了Java中的無鎖并發架構
2)CAS
如果是多線程寫,記憶體屏障并不适用,這是就需要用到 CAS。CAS是在CPU層面提供的一個硬體原子指令,實作對同一個值的Compare和Set 兩個操作的原子化。
第五章:網絡
網絡的具體認知可以空降:掌握《網絡》,見微才能知著
第六章:資料庫
6.1 範式與反範式
- 第一範式:每個字段都是原子的,不能再分解。(反例:某個字段是 JSON 串,或數組)
- 第二範式:表必須有主鍵,主鍵可以是單個屬性或幾個屬性的組合,非主屬性必須完全依賴,而不能部分依賴(反例:有張好友關系表,主鍵是 關注人ID+被關注人ID,但該表中還存儲了名字、頭像等字段,這些字段隻依賴組合主鍵中其中一個字段(關注人ID),而不是完全依賴主鍵)
- 第三範式:沒有傳遞依賴,非主屬性必須直接依賴主鍵,而不能間接依賴主鍵(反例:有張員工表,有個字段是部門ID,還有其他部門字段,比如部門名稱,部門描述等,這些字段直接依賴部門ID,而不是員工ID,不應該在員工表中存在)
看了三大範式不禁有些汗顔,實際開發中為了性能或便于開發,違背範式的設計比比皆是,但也無可厚非,雖然範式不一定要遵守,但還是需要仔細權衡。
6.2 分庫分表
分庫分表使分布式系統設計中一個非常普遍的問題。
1)分庫分表的目的
-
。通過業務拆分我們可以把一個大的複雜系統拆成多個業務子系統,系統與系統之間可以通過 RPC 或消息中間件的方式通信。業務拆分
-
。高并發我們可以具體分為是讀多寫少還是讀少寫多的并發場景。讀多我們可以利用緩存中間件減少壓力,而寫多我們就需要考慮是否要進行分庫分表應對高并發
-
。核心業務區分開來,差別對待,投入的開發和運維成本也可以側重點偏移資料隔離
2)拆分次元的選擇
- 按照 Id 次元拆分:根據 Id % 64 取模拆成 0~63 的64張表
- 固定位拆分:取 Id 指定二位,例如倒數 2 ,3位組成 00~99 張表
- hash值拆分:将 Id 取 hash 值,然後 % 表數
- range 拆分:按照 userId 指定範圍拆分,0 - 1千萬一張表,這種用的比較少,容易産生熱點資料問題
- 業務域拆分:把不同業務域的表拆分到不同庫中,例如訂單相關的表,使用者資訊相關的表,營銷相關的表分開在不同庫
- 把不常用的字段單獨拿出來存儲到一張表中
3)面對 JOIN 問題
- 拆分成多個單表查詢,在代碼層做邏輯拼裝
- 做寬表(JOIN 好的表),重寫輕讀
- 利用搜尋引擎,例如 Elasticsearch
6.3 B+樹
B+ 樹具備了哪些查詢特性:
- 範圍查詢
- 字首比對模糊查詢
- 排序和分頁
1)邏輯結構
B+ 樹結構
這個是一個 B+ 樹結構,相對來說比較抽象,我們提取下其中的幾個關鍵特征:
- 葉子節點之間所有記錄的主鍵,并按照
的順序排列,形成一個雙向連結清單。葉子節點的每一個key都指向一條記錄從小到大
- 非葉子節點取的是葉子節點裡面key的最小值。同層的非葉子節點也互相串聯,形成一個雙向連結清單
為什麼隻支援字首比對模糊查詢 - like abc%
字首比對模糊查詢可以轉換為範圍查詢,例如 abc% 可以轉換為 key in [abc, abcz],而如果是全模糊查詢是沒有辦法轉換的
2)實體結構
上面描述的樹隻是一個邏輯結構,而不是實際上的實體結構,因為資料最終都是要存儲到磁盤上的。
磁盤都是以 塊 為機關
在 InnoDB 引擎中預設的塊大小是 16 KB(可通過 innodb_page_size參數指定),這裡的塊,指的是邏輯機關,而不是磁盤扇區的實體塊,塊是 InnoDB 讀寫磁盤的基本機關,InnoDB 每次進行磁盤I/O讀取的都是 16 KB的整數倍,無論是葉子節點還是非葉子節點都是裝在
Page
裡面
三層的磁盤B+樹結構
一個Page大概可以裝1000個key(意味着B+樹有1000個分叉),每個Page大概可以裝200條記錄(葉子節點),那麼三層結構可以裝多少?1000 * 1000 * 200 = 2億條,約16GB的資料,這就是 B+ 數的強大之處
3)非主鍵索引
每一個非主鍵索引都對應一個 B+ 樹,與主鍵索引不同的是非主鍵索引每個葉子節點存儲的是主鍵的值而不是記錄的指針。也就是說對于非主鍵索引的查詢,會先查到主鍵的值,再拿主鍵的值去查詢主鍵的B+數,這就需要兩次 B+ 樹的查詢操作,也就我們常說的
回表查詢
6.4 事務與鎖
1)事務的隔離級别
什麼事務?事務就是一個
"代碼塊"
(一條船的螞蚱),要麼都不執行,要麼都執行。多個事務之間,為了想完成任務,那麼它們之間就很容易發生沖突,沖突産生就容易帶來問題,比如:有兩個事務分别是 小王 和 小李
- 髒讀:小王 讀取了 小李 不想要的東西,給 小王 帶來了髒資料
- 不可重複讀: 小王兩次讀取同一個記錄,發現兩次都不一樣,原來是小李在搞鬼,一直在更新資料
- 幻讀: 小王兩次讀取資料,發現讀出來的條數都不一樣,原來是小李在搞鬼,一直在增加/删除資料
- 丢失更新:小王正在将一條資料的值修改為 5,沒想到小李也在修改這條資料修改為4,在小李修改結束之前,小王先修改完成了,小李才結束修改,這是小王發現資料怎麼變成了 4
看了上面的 4 個問題,我們都覺得小李實在是太壞了,那有沒有什麼方法可以幫助到小王?
- RU(Read Uncommited):徒有其名,什麼都沒做,什麼問題都沒解決
- RC(Read Commited):可以解決 髒讀 問題
- RR(Repeatable Read):可以解決 髒讀、不可重複讀、幻讀 問題
- Serialization(串行化):解決所有問題
盡管串行化可以解決所有問題,但所有操作都是串行的,性能無法結局,是以常用的隔離級别是 RC 和 RR(預設)。RR 可以解決 髒讀、不可重複讀、幻讀 問題,那最後一個 丢失更新 我們就要另外想方法解決了
2)悲觀鎖
悲觀心态,認為資料發生沖突的機率很大,在讀之前就直接上鎖,可以利用
select xxx for update
語句。但會存在拿到鎖之後一直沒釋放的問題,在高并發場景下會造成大量請求阻塞
3)樂觀鎖
樂觀心态,認為資料發生沖突的機率很小,讀之前不上鎖,寫的時候才判斷原有的資料是否被其他事務修改了,也就是常說的 CAS。
CAS的核心思想是:資料讀出來的時候有一個版本v1,然後在記憶體裡面修改,當再寫回去的時候,如果發現資料庫中的版本不是v1(比v1大),說明在修改的期間内别的事務也在修改,則放棄更新,把資料重新讀出來,重新計算邏輯,再重新寫回去,如此不斷地重試。
6.5 事務實作原理之1:Redo Log
事務的四大核心屬性:
- 原子性: 事務要麼不執行,要麼完全執行。如果執行一半,當機重新開機,已執行的一半要復原回去
- 一緻性:事務的執行使得資料庫從一種正确狀态轉換成另外一種正确狀态
- 隔離性:在事務正确送出之前,不允許把該事務對資料的任何改變提供給其他事務
- 持久性:一旦事務送出,資料就不能丢
1)Write-Ahead
一個事務存在修改多張表的多條記錄,而多條記錄又可分布在不同的 Page 裡面,對應着磁盤的不同文職。如果每個事務都直接寫磁盤,性能勢必達不到要求。
解決的方式就是在記憶體中進行事務送出,然後通過背景線程異步地把記憶體中的資料寫入到磁盤中。但這個時候又會有個問題,那就是如果發生當機,記憶體中的資料沒來得及刷盤就丢失了。
而這個時候 Redo Log 就是用來解決這種問題
一樣是先在記憶體中送出事務,然後寫日志(Redo Log),然後背景任務把記憶體中的資料異步刷到磁盤中。日志是順序的記錄在尾部,這樣就可以避免一個事務發生多次磁盤随機I/O 問題。
從圖中我們可以看到,在事務送出之後,Redo Log先寫入到記憶體中的 Redo Log Buffer 中,然後異步地刷到磁盤的 Redo Log。是以不光光事務修改的操作是異步刷盤的,Redo Log 的寫入也是異步刷盤的。
既然都是先寫到記憶體中,那麼發生當機還是會出現丢失資料的問題,是以 InnoDB 有個參數 innodb_flush_log_at_trx_commit 可以控制刷盤政策:
- 0: 每秒刷一次,預設的政策
- 1: 每送出一個事務,就刷一次(最安全)
- 2: 不刷盤。然後根據參數innodb_flush_log_at_timeout設定的值決定刷盤頻率。
總結:0 和 2 都可能丢失資料,1 是最安全的,但是性能是最差的
2)日志結構
從實體結構上來看,日志是一個永不結束的位元組流,但從邏輯結構上看,日志不可能是一個永不結束的位元組流
實體結構
邏輯結構
是以在 Redo Log 中存在一個 LSN(Log Sequence Number)的編号(按照時間順序),在一定時間後之前的曆史日志就會歸檔,并從頭開始循環使用
在 Redo Log 中會采用邏輯和實體的方式總和記錄,先以 Page 為機關記錄日志,然後每個 Page 中在采用邏輯記法(記錄 Page 裡面的哪一行被修改了),這種記法也稱為 Physiological Logging
3)崩潰後恢複
不同僚務的日志在Redo Log 中是交叉存在的,也就意味着未送出的事務也在 Redo Log 中。而崩潰後恢複就會用到一個名為 ARIES 算法,不管事務有沒有送出,日志都會記錄到 Redo Log 中,當崩潰再恢複的時候就會把 Redo Log 全部重放一遍,送出和未送出的事務都會重放,進而讓資料庫回到當機之前的狀态,稱之為 Repeating History 。重放結束後再把當機之前未完成的事務找出來,然後逐一利用 Undo Log 進行復原。
4)總結
- 一個事務對應多條 Redo Log,并且是不連續存儲的
- Redo Log 隻保證事務的持久性,而無關原子性
- 未送出的事務復原是通過 Checkpoint 記錄的 “活躍事務表” + 每個事務日志的開始/結束辨別 + Undo Log實作的
- Redo Log 具有幂等性,通過 LSN 實作
- 無論是送出的、還是未送出的事務,其對應的 Page 資料都可能被刷到了磁盤中。未送出的事務對應的Page資料,在當機重新開機後會復原。
6.6 事務實作原理值2: Undo Log
上面說到進行 Redo Log 當機復原的時候,如果 Redo Log 中存在未送出的事務,那麼就需要借助 Undo Log進行輔助,換言之,如果 Redo Log 裡面記錄的都是已經送出的事務,那麼復原的時候也就不需要 Undo Log 的幫助
那麼 Undo Log 除了在當機恢複時對未送出的事務進行復原,還具備以下兩個核心作用:
- 實作 隔離性
- 高并發
在多線程的場景中應對并發問題的政策通常有三種:
- 互斥鎖: 一個資料對象上面隻有一個鎖,先到先得。(寫寫互斥,讀寫互斥,讀讀互斥)
- 讀寫鎖: 一個資料對象一個鎖,兩個視圖。(寫寫互斥,讀寫互斥,讀讀并發)
- CopyOnWrite: 寫時複制,寫完之後再把資料對象的指針一次性指派回去(寫寫并發,讀寫并發,讀讀并發)
Undo Log 的作用就是在 CopyOnWrite 部分。每個事務修改記錄之前,都會先把記錄拷貝一份出來,拷貝出來的那個備份就是存在Undo Log 裡面。每個事務都有唯一的編号,ID從小到大遞增,每一次修改就是一個版本,是以Undo Log負責的就是維護資料從舊到新的每個版本,各個版本之間的記錄通過連結清單串聯
為了不能讓事務讀取到正在修改的資料,隻能讀取曆史版本,這就實作了
隔離性
Undo Log 不是 log 而是資料
,因為 Undo Log 隻是臨時記錄,當事務送出之後,對應的 Undo Log 檔案就可以删除了,是以 Undo Log 成為記錄的備份資料更為準确
正是有了 MVCC 這種特性,通常的 select 語句都是不加鎖的,讀取的全部是資料的曆史版本,進而支撐高并發的查詢,也就是所謂的 快照讀,與之相對應的是 目前讀
快照讀/目前讀
讀取曆史資料的方式就叫做
快照讀
,而讀取資料庫最新版本資料的方式叫做
目前讀
- 快照讀
當執行 select 操作時,InnoDB 預設會執行快照讀,會記錄下這次 select 後的結果,之後 select 的時候就會傳回這次快照的資料,即使其他事務送出了不會影響目前 select 的資料,這就實作了可重複讀。
快照的生成當在第一次執行 select 的時候,也就是說假設當 A 開啟了事務,然後沒有執行任何操作,這時候 B insert 了一條資料然後 commit,這時 A 執行 select,那麼傳回的資料中就會有 B 添加的那條資料。之後無論再有其他事務 commit 都沒有關系,因為快照已經生成了,後面的 select 都是根據快照來的。
- 目前讀
對于會對資料修改的操作(update、insert、delete)都是采用 目前讀 的模式。在執行這幾個操作時會讀取最新的版本号記錄,寫操作後會把版本号改為目前事務的版本号,是以即使是别的事務送出的資料也可以查詢到。
假設要 update 一條記錄,但是在另一個事務中已經 delete 掉這條資料并且 commit 了,如果 update 就會産生沖突,也正是因為這樣是以會産生幻讀,是以在 update 的時候需要知道最新的記錄。
6.7 Binlog 與主從複制
Binlog 稱之為記錄日志,它與 Redo Log 和 Undo Log 不同之處在于,後兩者是 InnoDB 引擎層面的,而 Binlog 是Mysql 層面的,它的主要作用是用來做主從複制,它同樣具有刷盤機制:
- 0: 事務送出之後不主動刷盤,依靠作業系統自身的刷盤機制
- 1: 每送出一個事務,刷一次磁盤
- n: 每送出 n 個事務,刷一次磁盤
總結:0 和 n 都是不安全的,為了不丢失資料,一般都是建議雙 1 保證,即 sync_binlog 和 innodb_flush_log_at_trx_commit 的值都是 1
1)Binlog 與 Redo Log的差別
- Redo Log 和 Binlog 的産生方式不同。redo log是在實體存儲引擎産生,而Binlog是在 mysql 資料庫的 server 層産生。并且 Binlog不僅針對 InnDB 存儲引擎,MySQL 資料庫中的任何存儲引擎對資料庫的更改都會産生 Binlog
- Redo Log 和 binlog 記錄的方式不同。Binlog 記錄的是一種邏輯日志,即通過 sql 語句的方式來記錄資料庫的修改;而 InnoDB層産生的Redo Log 是一種實體格式的日志,記錄磁盤中每一個資料頁的修改
- Redo Log 和 Binlog 記錄的時間點不同。Binlog隻是在事務送出完成後進行一次寫入,而 Redo Log 則是在事務進行中不斷寫入,Redo Log 并不是随着事務送出的順序進行寫入的,這也就是說在 Redo Log 中針對一個事務會有多個不連續的記錄日志
2)主從複制
Mysql 有三種主從複制的方式
- 同步複制: 所有的 Slave 都接受完 Binlog 才認為事務送出成功,便傳回成功的結果
- 異步複制: 隻要 Master 事務送出成功,就對用戶端傳回成功,然後通過背景線程的方式把 Binlog 同步給 Slave(可能會丢資料)
- 半同步複制: Master 事務送出,同時把 Binlog 同步給 Slave,隻要部分 Slave 接收到了 Binlog(數量可設定),就認為事務送出成功,傳回成功結果
總結:無論異步複制,還是半異步複制(可能退化為異步複制),都可能在主從切換的時候丢資料。業務一般的做法是犧牲一緻性來換取高可用性,即在Master當機後切換到Slave,忍受少量的資料丢失,後續再人工修複
3)并行複制
原生的 MySQL 主從複制都是單線程的,将 Master 的 Binlog 發送到 Slave 上後生成 RelayLog 檔案,Slave 再對 RelayLog 檔案進行重放
串行複制
而所謂的并行複制實際上是并行回放,傳輸還是單線程,但是回放是使多線程
并行複制
第七章:架構、軟體與中間件
開源運作的興起,最不缺的便是開發架構,現市面上有各種各樣的輪子
常用軟體與中間件
第三部分:技術架構之道
第八章:高并發問題
任何問題都是速途同歸,到最後隻能通過兩種操作:讀和寫。
8.1 高并發讀
1. 加緩存
緩存可分為 本地緩存 和 集中式緩存 。使用緩存的同時我們需要思考緩存的更新政策:
- 主動更新: 當資料庫中的資料發生變更的時候,主動删除或更新緩存中的資料
- 被動更新: 當使用者查詢請求到來時,再對緩存進行更新
同樣使用緩存可能會面臨的幾個問題:
- 緩存雪崩: 即緩存的高可用問題。如果緩存當機/過期,所有請求會瞬間壓垮資料庫
- 緩存穿透: 查詢緩存中不存在的資料,導緻短時間内大量請求寫入并壓垮資料庫
- 緩存擊穿: 緩存中熱點資料過期,直接通路資料庫,導緻資料庫被壓垮
那麼緩存的使用無外乎都是對資料進行備援,達到空間換時間的效果
2. 并發讀
單線程不行,通常就會使用多線程。這種明顯治标不名額,容易達到性能瓶頸
3. 重寫輕讀
當微網誌這種大流量的平台,檢視關注人和自己釋出的微網誌清單看似很簡單需求,通常隻需要兩張表,一個是 關注關系表 ,一個是 微網誌釋出表。但是對于高并發查詢的時候很容易将資料庫打崩。
那我們就需要改成 重寫輕讀 的方式,不是查詢的時候才聚合,,而是提前為每個 userId 準備一個
收件箱
當某個被關注的使用者釋出微網誌時,隻需要将這條微網誌發送給所有關注自己每個使用者的收件箱中,這樣使用者查詢的時候隻需要檢視自己的收件箱即可。
但通過使用 重寫輕讀 容易帶來一個問題,那就是如果一個人擁有了 500 萬粉絲,那就意味着他需要往 500 萬個收件箱中推送,這對系統來說同樣是個不小的挑戰,那這個時候就需要采用
推拉結合
的方式
對于粉絲量少的使用者(設個門檻值),發送微網誌後可以直接推送到使用者的收件箱,對于粉絲較多的使用者,隻推送給線上的使用者,對于讀的一端,使用者有些可以通過收件箱擷取,有些需要自己手動去拉,這種就是推拉結合的方式
8.2 高并發寫
1. 資料分片
常見的有:
分庫分表
、
Java的ConcurrentHashMap
、
Kafka的partition
2. 任務分片
資料分片是對要處理的資料(或請求)進行分片,任務分片是對處理程式本身進行分片。
常見的有:
CPU 的指令流水線
、
Map/Reduce
、
Tomcat 的1+N+M網絡模型
1+N+M 模型
3. 異步化
通過消息中間件,分流處理
4. 批量處理
不管是Mysql、Redis、Kafka 通常上都不會将資料一條一條的進行處理,而是多條合并成一條,一次性寫入
8.3 容量規劃
高并發讀寫是一種定性分析,而壓力測試和容量規劃就是一種定量分析
1)吞吐量、響應時間與并發數
這三個概念都是比較常見的
- 吞吐量:機關時間内處理的請求數,例如 QPS、TPS 等名額
- 響應時間:處理每個請求需要的事件
- 并發數:伺服器同時并行處理的請求個數
三者關系:吞吐量 * 響應時間 = 并發數
關鍵點說明:談論吞吐量(QPS)的時候,一定需要談對應的響應時間是多少,随着QPS的增加,響應時間也在增加,雖然 QPS 提上來了,但使用者端的響應時間卻變長了,用戶端的逾時率增加,使用者體驗變差,是以這兩者需要權衡,不能一昧地提升 QPS,而不顧及響應時間
2)壓力測試與容量評估
容量評估的基本思路:
機器數 = 預估總流量/單機流量
其中分子是一個預估的值(通過曆史資料預估),分母通過壓力測試得到
在計算的時候需要使用峰值測算,而不能使用均值。盡管有時候峰值持續的時間很短,但不容忽視。
壓力測試方法:
- 線上壓力測試對比測試環境壓力測試
- 讀接口壓力測試對比寫接口壓力測試
- 單機壓力測試對比全鍊路壓力測試
第九章:高可用與穩定性
高并發使系統更有效率,高可用使系統更可靠
9.1 多副本
不要把所有雞蛋放到一個籃子裡
1)本地緩存多副本
利用消息中間(釋出/訂閱機制),一條消息發出,多台機器收到後更新自己的本地緩存
2)Redis多副本
Redis Cluster 提供了 Master - Slave 之間的複制機制,當 Master 當機後可以切換到 Slave。
3)MySQL 多副本
MySQL 之間可以用到異步複制或半異步複制,同步複制性能較差,比較少用
4)消息中間件多副本
對于Kafka類的消息中間件,一個Partition通常至少會指定三個副本,為此Kafka專門設計了一種稱為ISR的算法,在多個副本之間做消息的同步
9.2 隔離、限流、熔斷和降級
1)隔離
隔離是指将系統或資源分割開,在系統發生故障時能限定傳播範圍和影響範圍,即發生故障後不會出現滾雪球的效應
- 資料隔離
- 機器隔離
- 線程池隔離:核心業務的線程池需要和非核心業務的線程池隔離開
- 信号量隔離
信号量隔離是 Hystrix 提出的一種隔離方式,比線程池隔離更要輕量,由于線程池太多會導緻線程過多進而導緻切換的開銷大,而使用信号量隔離不會額外增加線程池,隻在調用線程内部執行。信号量本質上是一個數字,記錄目前通路某個資源的并發線程數,線上程通路資源之前擷取信号量,通路結束時釋放信号量,一旦信号量達到門檻值,便申請不到信号量,會直接 丢棄請求,而不是阻塞等待
2)限流
限流可以分為技術層面的限流和業務層面的限流。技術層面的限流比較通用,各種業務場景都可以用到;業務層面的限流需要根據具體的業務場景做開發。
具體操作可以空降:《餐廳小故事》| 服務限流的實施
3)熔斷
- 根據請求失敗率做熔斷
- 根據請求響應做熔斷
注意點: 限流是服務端,根據其能力上限設定一個過載保護;而熔斷是調用方對自己的一個保護。能熔斷的服務肯定不是核心鍊路上的必選服務,如果是的話,則服務逾時或者當機,前端就不能用了,而不是熔斷。熔斷其實也是降級的一種方式
4)降級
降級是一種兜底方案,是在系統出故障之後的一個盡力而為的措施,比較偏向業務層面
9.3 灰階釋出與復原
頻繁進行系統變更是個風險較高的操作。灰階與復原可以使該操作變的相對可靠穩定
1)新功能上線的灰階
當一個新的功能上線時,可以将一部分流量導入到這個新的功能,如果驗證功能沒有問題,再一點點增加流量,最終讓所有流量都切換到這個新功能上。
- 按 userId 進行流量劃分
- 固定位數進行流量劃分
- 屬性或标簽進行流量劃分
2)舊系統重構的灰階
如果舊的系統被重構了,我們不可能在一瞬間把所有舊的系統下線,完全變成新的系統,一般會持續一段時間,新舊系統同時共存,就需要增加流量配置設定機制。
3)復原
復原的方式:
- 安裝包復原: 這種方式最簡單,不需要開發額外的代碼,發現線上有問題直接重新部署之前的安裝版本
- 功能復原: 在開發新功能的時候,可以配置相應的配置開關,一旦發現新功能有問題,則關閉開關,讓所有流量進入老系統
第十章:事務一緻性
分布式解決方案
1)2 PC 理論
2 PC 中有兩個角色:事務協調者與事務參與者
每一個資料庫就是一個參與者,調用方也就是協調者,2 PC 将事務的送出分為兩個階段:
- 階段一:協調者向所有參與者詢問是否可以送出事務,并等待回複,各參與者執行事務操作,将 undo 和 redo 日志計入事務日志中,執行成功後給協調者回報 ack
- 階段二:如果階段一成功,則通知參與者送出事務,否則利用 undo 日志進行復原
這種方式也存在了許多問題:
- 性能問題:所有參與者在事務比較階段處于同步阻塞狀态,容易導緻性能瓶頸
- 可靠性問題:如果協調者出現問題,那麼會一直處于鎖定狀态
- 資料一緻性問題:在階段2中如果協調者和參與者都挂了,有可能導緻資料不一緻
2)3PC 理論
解決了 2PC 同時挂掉的問題,将 2PC 的準備階段再次一分為二
- 階段一:協調者向所有參與者發出包含事務内容的 canCommit 請求,詢問是否可以送出事務
- 階段二:如果階段一成功,協調者會再次發出 preCommit 請求,進入準備階段,參與者将 undo 和redo 日志計入事務日志中。如果階段一失敗,協調者則發出 abort 請求,參與者便會中斷事務
- 階段三:如果階段二成功,協調者發出 doCommit 請求,參與者便會真正送出事務。如果失敗,便會發出 rollback 請求,參與者會利用 undo 事務進行復原,并結束事務
該方式依然會造成資料不一緻問題:如果 preCommit 階段存在部分節點傳回 nack,那麼協調者剛要中斷事務便挂掉了,一定時間後參與者便會繼續送出事務,造成資料不一緻問題
3)補償事務 TCC
TCC(try-confirm-cancel)是服務化的二階段程式設計模型,核心思想是:針對每個操作都要注冊一個與其對應的确認和補償(撤銷操作)。他同樣也是分為三個步驟
- try 階段:主要是對業務系統做檢測及資源預留
- confirm 階段:主要是對業務系統做确認送出。try 階段執行成功并開始執行 confirm 階段,預設情況下 try 成功,confirm 一定會成功
- cancel 階段:主要是業務執行錯誤,執行復原,将預留的資源釋放
例子:轉賬操作,第一步在 try 階段,首先調用遠端接口把自己和對方的錢當機起來,第二步在 confirm 階段,執行轉賬操作,如果成功則進行解凍,否則執行 cancel
它解決了資料最終一緻性的問題,通過 confirm 和 cancel 的幂等性,保證資料一緻性
4)最終一緻性(消息中間件)
可以基于 RocketMQ 實作最終一緻性。為了能通過消息中間件解決該問題,同時又不和業務耦合,RocketMQ提出了“事務消息”的概念
- 步驟1:系統A調用Prepare接口,預發送消息。此時消息儲存在消息中間件裡,但消息中間件不會把消息給消費方消費,消息隻是暫存在那。
- 步驟2:系統A更新資料庫,進行扣錢操作。
- 步驟3:系統A調用Comfirm接口,确認發送消息。此時消息中間件才會把消息給消費方進行消費。
RocketMQ會定期(預設是1min)掃描所有的預發送但還沒有确認的消息,回調給發送方,詢問這條消息是要發出去,還是取消。發送方根據自己的業務資料,判斷這條消息是應該發出去(DB更新成功了),還是應該取消(DB更新失敗)
第十一章:多副本一緻性
無論是 MySQL的 Master/Slave,還是 Redis 的 Master/Slave,或是Kafka的多副本複制,都是通過犧牲一緻性來換取高可用性的。
本章主要對 Paxos、Zab、Raft 三種算法進行解析。做出的筆記内容較多,保證本篇篇幅的情況下,考慮單獨抽出講解,有興趣的小夥伴可以後續關注~!
第十二章:CAP理論
-
:是指所有節點同時看到相同的資料。強一緻性 Consistency
-
:任何時候,讀寫操作都是成功的,保證服務一直可用可用性 Availability
-
:當部分節點出現消息丢失或分區故障的時候,分布式系統仍然能夠運作分區容錯性 Partition tolerance
CP的系統追求強一緻性,比如Zookeeper,但犧牲了一定的性能
AP的系統追求高可用,犧牲了一定的一緻性,比如資料庫的主從複制、Kafka的主從複制
1)分布式鎖
1. 基于 Zookeeper 實作
可以利用 Zookeeper 的 瞬時節點 的特性。每次加鎖都是建立一個瞬時節點,釋放鎖則删除瞬時節點。因為 Zookeeper 和用戶端之間通過心跳探測用戶端是否當機,如果當機,則 Zookeeper 檢測到後自動删除瞬時節點,進而釋放鎖。
2. 基于 Redis 實作
Redis的性能比Zookeeper更好,是以通常用來實作分布式鎖。但 Redis 相對 Zookeeper 也存在些許問題
- 沒有強一緻性的Zab協定。如果Master 當機,Slave會丢失部分資料,造成多個程序拿到同一把鎖
- 沒有心跳檢測。在釋放鎖之前當機,會導緻鎖永遠不會釋放
第四部分:業務架構知道
第十三章:業務意識
1) 産品經理與需求分析師
産品經理從某種意義上來說就稱之為需求分析師。作為一個技術人員,不需要像産品經理或需求分析師那樣對需求了如指掌,但具有良好的業務意識确是做業務架構的基本條件
那麼什麼業務意識?
- 了解需求來自何處
有時需求來自何處,技術為誰而坐,往往和公司的基因、盈利模式緊密挂鈎,公司本身決定了需求從什麼地方來
- 判斷是真需求還是假需求
很多原因都會導緻僞需求,比如老闆的決定,面向 KPI 的需求。而其中存在一個因素便是:資訊傳播的遞減效應
當發生一個事件時,第一個人 A 看到事件的全過程,掌握 100 的資訊量,描述給 B 的時候,受制于記憶力、表達力等因素隻能描述出 90 的資訊,往下遞推,到 D 的時候可能隻剩 60 的資訊。
是以,作為一個技術人員,當從産品經理接到需求的時候,一定要回溯,明确需求是在什麼背景下提出的,究竟要解決使用者的什麼問題。
- 需求的優先級
人力資源和時間資源是有限的。如何合理配置設定尤為重要
2)業務是什麼
一個内容能稱為一個業務,往往具備一個特點,就是閉環。
什麼是閉環?
- 團隊閉環:有自己的産品、技術、營運和銷售聯合作戰
- 産品閉環:從内容的生成到消費,整條鍊路把控
- 商業閉環:具備自負盈虧的能力
- 縱向閉環:某個垂直領域,涵蓋從前到後
- 橫向閉環:平台模式,橫向覆寫某個橫切面
3)業務架構的雙重含義
業務架構既關乎組織架構,也關乎技術架構
- 從理論上講,合理的團隊的組織架構應該是根據業務的發展來決定的,不同的公司在不同的發展階段會根據業務的發展情況,将壯大的業務拆分,萎靡的業務合并
- 支援業務的技術架構,業務架構和計數架構會互相作用,互相影響
第十四章:業務架構思維
1)僞分層
不管是業務架構還是技術架構,C端業務還是B端業務,我們都會用到分層技術
經典分層技術
僞分層的特征
- 底層調用上層:設計分層的時候應深入思考 DIP(依賴反轉)原則
- 同層之間,服務之間各種雙向調用: 這個很容易造成循環依賴問題,考慮是否要抽取 Middle 層來作為中間層
- 層之間沒有隔離,參數層層透傳,一直穿透到最低層,導緻底層系統經常變動
總結
- 越底層的系統越單一、越簡單、越固化
- 越上層的系統花樣越多、越容易變化。要做到這一點,需要層與層之間有很好的隔離和抽象。
- 層與層之間的關系應該嚴格遵守上層調用下層的準則
2)邊界思維
1. 對象層面(SOLID 原則)
一個函數、一個類、一個子產品隻做一件事,不要把不同的職責糅在一起,這就是邊界思維的一種展現
2. 接口層面
首先想到的不是如何實作,而是把系統當做一個黑盒,看系統對外提供的接口是什麼,接口也就是系統的邊界,定義了系統可以支援什麼、不支援什麼。是以接口的設計往往比接口的實作更重要!
3. 産品層面
内部實作很複雜,使用者界面很簡單,把複雜留給自己,把簡單留給使用者
4. 組織結構層面
總結: 邊界思維的重點在于限制,是一個 "負方法" 的思維方式。架構強調的不是系統能支援什麼,而是系統的“限制”是什麼,不管是業務限制,還是技術限制。沒有“限制”,就沒有架構。一個設計或系統,如果“無所不能”
3)系統化思維
系統化系統不在于頭痛醫頭腳痛醫腳,而是追溯源頭,關注整體上的影響,把不同的東西串在一起考慮,而不是割裂後分開來看
4)利益相關者分析
當談到系統的時候,首先要确定的是系統為哪幾類人服務,同哪幾個外部系統互動,也就确定了系統的邊界。
5)非功能性需求分析(以終為始)
軟體有功能需求和非功能需求,非功能性需求有:
- 并發性:關注點在于系統能抵抗多大的流量
- 一緻性:資料一緻性問題
- 可用性:是否保證服務一直處于可用狀态
- 可維護性:關注點在于代碼的可了解性
- 可擴充性: 系統功能是否能夠靈活擴充,而不會遇到一個需求就需要大刀闊斧地修改
- 可重用性: 開發新的需求,舊的功能子產品可以拿過來直接用
6)抽象
語言隻是對現實中我們所注意到的事務特征的一種抽象,每一次命名,都是一個抽象化的過程,這個過程會忽略掉現實事務的許多特征。但是抽象的目的是為了交流提供便利,而不是給交流帶來負擔,是以我們需要對自己的每一次抽象負責,不能抽象到最後自己都不明白抽象的含義是什麼。
抽象的幾種特征:
- 越抽象的詞,在詞典中個數越少;越具象的詞,在詞典中個數越多。
- 越抽象的詞,本身所表達的特征越少;越具象的詞,特征越豐富。
- 越抽象的詞,意義越容易被多重解讀;越具象的詞,意義越明确
7)模組化
模組化的本質:把重要的東西進行顯性化,進而把這些顯性化的構造塊互相串聯起來,組成一個體系
8)正交分解
分解是一個很樸素的思維方式,把一個大的東西分成幾個部分。比分解更為嚴謹,更為系統的是 正交分解,需要保證兩個原則:
- 厘清:同一層次的多個部分之間要互相獨立,無重疊
- 分淨:完全窮盡,無遺漏
第十五章:技術架構與業務架構的融合
該章節主要是對 DDD(領域驅動模型) 做出解釋,比較泛化,這裡推薦一本好書
《實作領域驅動設計》
,書中對 DDD 解說的相對具體,這本書小菜最近也在啃讀中,後續會出相應的讀書筆記,請夥伴們點點關注,後續不會迷路!
第十六章:個人素質的替身
1)能力模型
對于程式員來說,我們是幹技術,很純粹,技術很好表示你能力越強。但是當你慢慢職位上漲的時候,會發現技術不能代表你的全部。
1. 格局
打開格局,打開格局,平時常說的一句調侃的話卻格外重要。
做技術我們需要開闊視野打開格局,我們才能了解更多的技術棧,更好的運用到項目中。
做産品我們需要開闊視野打開格局,我們才能了解市面上的競品是什麼樣子,更好的借鑒到自己的項目中。
2. 曆史觀
格局 是從 空間 的角度看待問題,而 曆史觀 則是從 時間的角度看待問題。任何一種技術,都不是憑空想出來的,任何一個需求,都不是憑空捏造的,我們需要進行回溯,了解它誕生的背景,才能知其是以然。
3. 抽象能力
有些人抽象出來的事物可以讓别人一眼貫通,有些人抽象出來的事物卻連自己的看不懂。這就是抽象能力的表現。
很多寫代碼的人習慣利用 自底向上 的思維解決問題,讨論需求的時候首先想到的是這個需求如何實作,而不是這個需求本身合不合理,對于很多新人來說 需求的合不合理,依賴于需求好不好實作,這樣的方式很容易導緻
隻見樹木,不見森林
,最後淹沒在各種錯綜複雜的細節中。
4. 深入思考的能力
深入思考的能力主要考察技術的深度
深度并不表示要在所有領域都很精通,而是專注于某個領域,對于專家和全棧工程師的差別,想想哪個職位的薪資可能會更高
5. 落地能力
落地能力值的就是執行力,有空頭畫大餅的能力,卻無落地去實作的能力,隻會阻礙項目的正常前行。這大概就是技術不喜銷售的原因吧
2)影響力的塑造
進入職場的前幾年尤為關鍵,有的人平步青雲,有的人卻止步不前。那就是沒能很好的塑造自己的影響力。影響力該如何塑造?
1. 關鍵時候能頂上
最怕的是 事不關己高高挂起 的心态,如果下次攤上事的是你如何?如果當團隊中遇到問題,這個時候能夠迎上,絕對可以讓人知道還有你這一号人物(當然要斟酌抗下的風險,迎難而上并不意味着逞強)
2. 打工思維和老闆思維
雖然我們常說自己是打勞工,但有的時候何不把自己當成合夥人?
打工的思維,安排的事需要幹一件,絕不多一點,隻管好自己的一畝三分地
老闆的思維,這個産品的價值在哪?這個産品存在哪些問題,需要如何改進?為何使用者一直投訴的事,還沒及時處理?
3. 空杯心态
術業有專攻,水準再高的人都需要謹記山外有山人外有人,否則就會一直待在自己的舒适圈中,剛愎自用
4. 建言獻策
不必害怕自己的回答是否正确,而瞻前顧後不敢發言,充分發揮 圓桌文化, 有建議有想法大膽提出,不然你是想留給自己的蛔蟲知道嗎
第十七章:團隊能力的提升
1)不确定性與風險把控
技術管理的首要任務就是項目管理,通常存在以下幾種不确定性
1. 需求的不确定性
由于各種外部條件,導緻需求提議的想法不是很成熟(可能隻是頭腦風暴),處于需要不斷優化的階段,那麼這個時候過早的進行開發容易浪費資源。作為技術負責人就需要和産品經理以及相關的業務方進行廣泛的頭痛,需要達成共識的情況,才能投入。
2. 技術的不确定性
啟動新項目的時,最怕的就是一開始技術沒有很好的選型,到中間開發階段時候再進行替換,這種勞民傷财的事情還是盡量避免發生。必須在項目早期的時候就進行過多的調研和測試。
3. 人員的不确定性
現在的大多數職員都是面向金線開發,大多數在職情況并不是那麼穩定,而将項目的大多權限與業務集中在一名成員上是個不明智的選擇,能夠進行 AB崗位開發是個不錯的選擇,兩人之間的業務互相熟悉,哪怕是因為請假的原因也能很快的進行替代補充
4. 組織的不确定性
公司越大,業務越複雜,部門越多。随便做一個項目,都可能與好幾個業務部門打交道。這些部門可能還在異地,平時隻能即時通信,或者遠端電話溝通。對于這種情況,在項目前期必須要做盡可能多的溝通,調研對方提供的業務能力,哪些目前有,哪些還在開發中,哪些還沒有開發。在充分溝通的基礎上,和對方敲定排期表,不定期地同步進度,保證對方的進度和自己在一個節奏上。