大家好呀,我是小菜~
帥哥美女,知道你們時間寶貴,那麼就由小菜為你讀好一本書,讀一本好書,取其精華,與你共享~!
本文主要分享
《軟體架構設計:大型網站技術架構與業務架構融合之道》
如有需要,可以參考
如有幫助,不忘 點贊 ❥
微信公眾號已開啟,菜農曰,沒關注的同學們記得關注哦!
今天帶來的是 《軟體架構設計:大型網站技術架構與業務架構融合之道》 的讀書筆記
(文中使用到的例子貼圖均出於原書)
在正式進入分享之前,我們想看下這本樹的目錄架構
軟體架構設計:大型網站技術架構與業務架構融合之道
這本書總共分為 五
個部分,共計 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進行收發檔案的過程
- 這是一個利用記憶體對映檔案進行收發檔案的過程
整個過程從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+ 樹結構,相對來說比較抽象,我們提取下其中的幾個關鍵特徵:
- 葉子節點之間所有記錄的主鍵,並按照
從小到大
的順序排列,形成一個雙向連結串列。葉子節點的每一個key都指向一條記錄 - 非葉子節點取的是葉子節點裡面key的最小值。同層的非葉子節點也相互串聯,形成一個雙向連結串列
為什麼只支援字首匹配模糊查詢 - like abc%
字首匹配模糊查詢可以轉換為範圍查詢,例如 abc% 可以轉換為 key in [abc, abcz],而如果是全模糊查詢是沒有辦法轉換的
2)物理結構
上面描述的樹只是一個邏輯結構,而不是實際上的物理結構,因為資料最終都是要儲存到磁碟上的。
磁碟都是以 塊 為單位
在 InnoDB 引擎中預設的塊大小是 16 KB(可通過 innodb_page_size引數指定),這裡的塊,指的是邏輯單位,而不是磁碟扇區的物理塊,塊是 InnoDB 讀寫磁碟的基本單位,InnoDB 每次進行磁碟I/O讀取的都是 16 KB的整數倍,無論是葉子節點還是非葉子節點都是裝在 Page
裡面
一個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網路模型
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. 組織的不確定性
公司越大,業務越複雜,部門越多。隨便做一個專案,都可能與好幾個業務部門打交道。這些部門可能還在異地,平時只能即時通訊,或者遠端電話溝通。對於這種情況,在專案前期必須要做盡可能多的溝通,調研對方提供的業務能力,哪些目前有,哪些還在開發中,哪些還沒有開發。在充分溝通的基礎上,和對方敲定排期表,不定期地同步進度,保證對方的進度和自己在一個節奏上。