1. 程式人生 > >後端好書閱讀與推薦(續五)

後端好書閱讀與推薦(續五)

Redis設計與實現

通過前面這本書我們已經知道redis怎麼用比較好了,現在我們來看看 Redis 的實現原理。這本書是作者自己看著原始碼寫出來的,不得不佩服作者的智慧與毅力。這本書基於redis3.0,此刻redis最新版是4.0.9,我們看書的時候可以自己去看看原始碼,看看redis有啥變化沒,原始碼在此

亮點:

  • redis並沒有用C語言的字串作為其基準字串表示,而是使用了SDS(簡單動態字串)這一抽象型別,被用於鍵、字串的表示,還有AOF中的緩衝區、客戶端輸出緩衝區等buffer的表示。 SDS的優勢在於:記錄了長度資訊不需要遍歷(空間換時間)、有長度所以會先檢查空間是否足夠而不會溢位、記錄了free(未使用空間)長度從而避免頻繁的記憶體重新分配(實現了空間預分配和惰性釋放)、二進位制安全等等
  • redis還定義了連結串列Linkedlist(用於實現List結構、釋出訂閱、慢查詢、監視器等功能)、字典Dict(基於兩個hash表,一個平時用、一個rehash用,主要用於實現資料庫、Hash結構,需要注意漸進式rehash而不是java中Hashmap的一次性rehash,漸進式主要是避免鍵數量太大導致伺服器暫時停止服務)、跳躍表SkipList(用於實現SortedSet結構、叢集節點)、整數集合(用於實現Set結構)、壓縮列表(用於實現ListHash結構,主要是面向小整數和短字串,可以節省空間)、物件redisObject(redis的五種資料結構就是五種物件,key value都是物件,是對前面所有資料結構的一種封裝,實現型別判斷、編碼、引用計數與物件共享和記憶體回收、過期刪除等功能)等資料結構來實現特定功能
  • redis伺服器是一個redisServer資料結構,包含一個redisDb的連結串列,每一個數據庫都是一個數據結構redisDb,中間有一個dict的資料結構儲存了這個資料庫所有的鍵值對,叫做鍵空間(key space)。鍵空間會維護命中次數、最後使用時間、過期清除、鍵監視、鍵空間(關注鍵本身)/鍵事件(關注操作本身如del、sadd、set等)通知等事件。此外還有一個clients欄位儲存了所有客戶端
  • redisDb有一個dict型別的連結串列expire,儲存了所有鍵的過期時間。過期鍵有三種刪除策略:定時刪除,設定timer,過期立即刪除,對記憶體友好,但是浪費CPU時間片,而且大量的timer也不現實;惰性刪除
    ,放任不管,獲取的時候檢查是否過期,過期再刪除,會浪費一定的記憶體,但是節省CPU時間;定期刪除,每隔一段時間就檢查過期鍵並刪除,需要好好設計執行時長與頻率。redis實際使用的是後兩者
  • 持久化分為AOFRDB。RDB可以手動執行也可定期執行(BGSAVE)用記憶體資料生成壓縮過的二進位制RDB檔案,SAVE命令會阻塞伺服器,而BGSAVE會fork一個子程序來建立RDB檔案,所以期間新的資料變化是不能提現到RDB檔案中的,此外,RDB檔案的載入是伺服器啟動自動完成的;AOF不直接記錄資料,而是記錄每一個redis命令,分為append、檔案寫入(可能在系統緩衝中)、檔案同步sync(強制刷到磁碟)三個步驟,檔案同步有三種選項always(寫入檔案就刷盤)、everysec(預設)、no(由作業系統決定何時刷盤)
  • redis是一個事件驅動的程式,這種程式一般會有一個類似於while(true)的無限迴圈,叫做事件迴圈,每個迴圈裡伺服器要處理兩種事件:檔案事件,完成網路通訊(基於Reactor模式);時間事件,完成定時、週期操作之類。
  • Sentinel(哨兵)是redis的高可用解決方案,由>=1個Sentinel例項監視任意多的redis主伺服器以及所屬的從伺服器(從伺服器複製主伺服器的資料),完成主備切換等功能。實現原理是Sentinel每隔10秒就向所有主伺服器傳送info命令,獲取各個主伺服器及其對應的從伺服器的資訊,然後也會以相同形式給從伺服器傳送info命令。另外會以兩秒一次的頻率向所有主從伺服器傳送一個publish的命令,然後會subscribe這個頻道,這樣就能獲得每個伺服器的相關資訊了。Sentinel之間只有命令連線,沒有頻道連線。
  • 線上檢測通過每秒的ping來實現,當超過一個Sentinel的時限down-after-milliseconds還沒獲得一個伺服器的回覆這個Sentinel就認為此伺服器主觀下線,然後會詢問其他Sentinel,如果超過quorumSentinel都認為此伺服器主觀下線那麼這些Sentinel就認為這個伺服器就客觀下線了。當一個主伺服器下線時,Sentinel們會選舉一個領頭SentinelRaft leader選舉)來對這個伺服器群進行主備切換,具體演算法就不說了
  • redis叢集通過槽指派實現,所有資料分配到16284個槽,所有槽都有節點處理時叢集才能對外服務,每個節點可以處理多個槽(一個節點可以是主從伺服器群,提高該節點可用性),當一個節點收到命令發現該槽不在本節點時會向客戶端返回重定向指令,讓其請求對應的節點。重新分槽可以線上分,由redis-trib實現。
  • redis 支援慢查詢,超過showlog-log-slower-than選項執行時間的命令會被記錄到慢查詢日誌,日誌採用儲存最近的,數量由slowlog-max-len決定。

本書配合著官方文件來學習是最佳的,文件在此中文文件

MySQL技術內幕

通過前面這本書我們已經知道 mysql 怎麼用比較好了,現在我們來看看 mysql 的實現原理。

亮點:

  • mysql採用單程序多執行緒模式。邏輯上,採用了分層可插拔架構,上層是連線池管理模組,中層是語句解析、快取、優化模組,下層是可插拔的儲存引擎如myisam、Innodb(儲存引擎基於表而不是資料庫),下層依賴於作業系統層面的檔案系統
  • InnoDB支援事務、行鎖、外來鍵,主要面向線上事務處理應用(OLTP),通過MVVC提高併發性,預設可重複讀級別使用 next-key locking避免幻讀,此外還提供插入緩衝、二次寫、自適應hash索引、預讀等高效能與高可用功能;MyISAM不支援事務與行鎖,但是支援全文索引,主要面向OLAP應用;NDB是一個叢集引擎,資料全部在記憶體所以速度很快,但是連線操作不是在引擎層面解決的,效率較低;Memory也是記憶體儲存的引擎,適合臨時表儲存中間結果使用,預設使用hash索引,只支援表鎖;Archive只支援insert和select,支援壓縮適合儲存歸檔資料;還有太多不常用的,就不一一列舉了
  • innodb採用多執行緒模型,Master Thread負責將緩衝池中的資料非同步重新整理到磁碟,保證資料一致性,包括髒頁重新整理,合併插入緩衝,undo頁回收;IO thread負責寫操作AIO的回撥處理;Purge Thread 負責undo log的回收,減輕Master Thread的負擔;Page cleanner Thread 負責髒頁重新整理,減輕master thread 負擔
  • mysql通過寫緩衝WAL將隨機寫改成了順序寫提高了吞吐量,同時保證資料永續性,如果宕機就會用redoundo兩階段的日誌來將資料恢復到資料庫檔案中。但是mysql基本頁面一般是16KB,而作業系統基本頁面一般是4KB,如果在寫入到資料庫檔案時發生宕機,就可能引起partial write問題,亦即一個mysql頁面只寫入了一部分,這時就要依靠double write:同步緩衝區資料時,不是直接寫入檔案,而是寫入記憶體中的double write buffer再呼叫fsync寫到共享表空間檔案,然後馬上呼叫fsync同步至資料庫檔案,這樣如果發生了partial write,就可以去共享表空間找到該頁副本並複製到資料庫檔案中,然後再進行redo,undo。當然,如果檔案系統本身有防範機制就不必啟用double write
  • InnodB中頁是管理資料庫的最小磁碟單位,分為多個部分,其中Fileheader、Pageheader、FileTrailer(檢查完整性)是固定的,記錄一些該頁的資訊如checksum、頁所在B+樹層數。userrecords、freespace、pagedirectory(頁相對位置,B+索引只記錄一條資料所在頁,真正查詢還需把這一頁載入如記憶體,靠二分搜尋這一條記錄)是實際的行記錄儲存空間,因此大小是動態的。
  • Cardinality能幫助我們決策是否需要建立索引,可以通過show index來觀察,一般比如性別這個欄位Cardinality就很低,因為可能一個型別female就佔據了所有行的一半,根本沒必要建立索引,而名字這個欄位Cardinality就很高,適合建立索引(當然也得是應用常常按名字檢索,這樣才有必要建立索引)
  • Innodb1.2.x 開始支援全文檢索,並支援myisam引擎所有功能。全文檢索一般通過倒排索引實現,亦即儲存每一個詞彙及其對應文件,這樣關鍵詞檢索時就能迅速返回相應文件
  • 事務分為幾種:扁平事務,所有操作位於同一層次,要麼都提交要麼都回滾(也就是說不支援部分回滾),是應用程式成為原子操作的基本模組;帶有儲存點的扁平事務,在扁平事務基礎上支援部分回滾,用儲存點(volatile,易失的)實現;鏈事務,事務的連結;巢狀事務,頂層事務管理多個子事務;分散式事務,通常是在分散式場景下執行的扁平事務
  • 事務的實現主要依靠:

    • redo實現永續性,亦即commit操作必須將專門的redo日誌檔案fsync重新整理到磁碟才能返回成功,合併多條語句插入優於一句一句插入也是因為減少了日誌刷盤頻率,由於redolog塊與磁碟扇區塊大小一致,所以無需doublewrite
    • undo實現回滾操作和MVCC,是存在於資料庫內部共享表空間的一個特殊段(undo段)中的邏輯日誌,記錄了事務執行的反效果便於回滾,MVCC的實現是通過若某行記錄已被其他事物佔用,當前事務可以通過undo日誌得到改行之前版本的資訊,需注意undolog也會產生redolog,亦即undolog也需要永續性維護;
    • purge最終完成deleteupdate操作,因為Innodb支援MVCC,所以記錄不能在提交時立即處理,purge操作判斷合何時真正清理資料並回收undo page,具體來說,如果一行資料不被真正引用那就可清理deleteupdate資料了;
    • group commit將多個事務的資料一次性呼叫fsync重新整理到磁碟減少刷盤次數;
    • 需要注意的是binlog,用來實現Point-In-Time的恢復以及主從複製的實現,非常類似於redolog,但是兩者本質有很大不同:redolog產生於下層只針對Innodb引擎產生,是物理格式日誌,記錄針對每一頁的修改,事務過程中不斷被寫入;binlog產生於中層,針對任何引擎都會產生,是邏輯日誌,記錄針對每條SQL語句,事務提交後一次寫入
  • Mysql通過XA事務來保證分散式的一致性,同時內部也有XA的使用,如現在資料庫為了複製一般都是開啟binlog的,事務提交時既要寫binlog也要寫redolog,這就會涉及原子性問題,亦即兩個日誌必須同時寫入,通過先做prepare操作,將事務的xid寫入,然後寫入binlog,然後提交儲存引擎並寫入redolog

深入Linux核心架構

這是一本能把linux核心全貌展現給我們的大部頭,涵蓋了包括程序管理,記憶體管理、鎖與通訊、裝置驅動、檔案系統等等。要採取觀其大略,用時細讀的策略,去巨集觀的把握linux的設計哲學。

亮點:

  • 作業系統架構主要涉及兩種型別:微核心,最基本的功能由中央核心實現,其他功能委託給一些程序實現,通過通訊介面與中心核心通訊,這種模型研究活躍但是實用性進展甚微;巨集(單)核心,核心的全部程式碼打包到一個檔案中,核心中每個函式都可以訪問其他部分,目前效能強於微核心,linux也是使用這種模式,但是linux模組化做得很好(可以動態新增減少功能,核心檔案也不會膨脹),也消除了單核心的一個重要弱點
  • linux虛擬地址空間是每個程序管理自身資源與其他程序隔離的工具,與實體記憶體無直接關係(由CPU訪問匯流排數決定),分為核心空間(task-size~最高位)和使用者空間(0~task-size),這樣就為所有程序提供了一個統一的系統檢視,無需計較其他程序;Intel處理器有4中特權級別,但是linux只使用兩種不同的狀態:核心態和使用者態,使用者態程序禁止直接訪問核心空間(這是核心的專用領域)的資料和執行其程式碼,使用者態程序只能藉助於系統呼叫或者中斷來陷入核心態,由核心代為執行操作。實體記憶體基本單位是頁框頁幀),虛擬記憶體基本單位是,兩者大小一樣,對應關係由頁表(linux採用4級頁表)決定
  • 建立程序有兩種方式,fork(父子程序只有PID不同,使用COW使得fork更高效,亦即子程序只複製頁表,只有父子某一程序要向記憶體寫入資料時才真正複製實體記憶體,若只讀則可以共享而不必複製,這樣就延遲甚至消除了了大量的複製,節約了記憶體和CPU時間)和exec(將一個新程式載入到當前記憶體中執行,舊的程式記憶體頁將刷出)。linux使用clone建立執行緒(或者說輕量級程序),類似於fork,但是能精確檢查哪些資源與父程序共享哪些為執行緒獨立建立
  • Linux的程序排程分為兩大部分:排程策略和上下文切換。非實時程序的排程策略一般考慮公平性,linux所有可執行程序都在一個紅黑樹中排序,vruntime(一次排程間隔的虛擬執行時間實際執行時間*(NICE_0_LOAD/權重nice值))最小的程序位於左下方會被排程器優先考慮,休眠程序被放入等待佇列中,喚醒後會重新被加入紅黑樹。實時程序是指該程序必須在指定的時間內完成比如飛機飛行控制指令,因而一般都有相對非實時程序較高的優先順序,不同於非實時程序排程
  • 實體記憶體管理分為兩個層次:夥伴系統負責物理頁幀的管理,圍繞多頁組成的連續記憶體塊的拆分與合併,新進的核心引入了反碎片技術(可移動頁與可移動記憶體區)防止實體記憶體碎片發生;slab負責處理小塊記憶體的分配,並提供了使用者層malloc函式族的核心等價物。
  • Linux使用了SystemV(Unix的一個分支)引入的機制來支援使用者程序間通訊(IPC)與同步。主要通過訊號量、訊息佇列、共享記憶體三種方式實現。此外,linux還有其他IPC機制如訊號(kill -s)、管道(以虛擬檔案系統實現,適用於父子程序單向通訊)及命名管道(適用於無關係的程序間通訊)、socket(更通用,可多主機程序通訊)
  • Linux核心為了統一檔案訪問介面,在使用者程序(或者C標準庫)和檔案系統(Ext2/3,XFS,Reiserfs)之間引入抽象層:虛擬檔案系統(VFS)。檔案系統一般分為3種:基於磁碟的FS(如Ext2/3),無持久FS(如/proc),網路檔案系統。處理檔案時,使用者程式關注一個整型的檔案描述符fd(由核心分配,只在一個程序內有效,兩個程序即使有相同的fd也是指向不同的檔案),而核心關注的是inode,包含了一個檔案/目錄的元資料但不含檔名(檔名只是inode的別稱)。軟連線採用檔名指向的方式,獨立inode;硬連結採用引用計數,共享inode
  • 無持久FS有:proc標準掛載點是/proc,其資訊不能從塊裝置讀取,只有在讀取檔案內容時才動態生成相關資訊,也可以在不重新編譯核心原始碼的情況下修改核心行為,與核心通訊,包含網路資訊、記憶體管理、程序特徵資料、檔案系統、裝置驅動、電源、終端、系統控制引數;sysfs的標準掛載點是/sys,是一個向用戶空間匯出核心物件的檔案系統,提供了檢視和修改核心資料結構的能力;用於專門目的的小檔案系統
  • 應用程式看到的核心是一個執行多種系統功能(記憶體管理、系統資源請求、訪問外設、程序通訊、讀取檔案等)的大的例程集合,標準庫是一箇中間層,用於在不同的體系結構和系統間,標準化並簡化核心例程的管理

同學們,堪稱整個現代網際網路軟體基石的Linux核心原始碼就在這裡,有沒有勇氣去瞅瞅?
還有這本書更精簡,推薦給不想看太多原始碼和細節的同學: Linux核心設計與實現(原書第3版)

Kafka權威指南

Kafka是一個高吞吐量的分散式(支援分割槽partition、多副本replica、使用zookeeper協調)訊息系統,目前廣泛用來進行實時大資料處理如:hadoop批處理、storm/Spark流式處理引擎,web日誌、訊息佇列等等,所以作為一個後端開發者,很有必要了解一下。我想的是把kafka作為訊息佇列的代表進行學習,將來如果要用ActiveMQ、RabbitMQ、ZeroMQ、RocketMQ等或者要自己要開發一個MQ都可以舉一反三或者進行借鑑。

亮點:

  • Kafka是一個流平臺,允許釋出和訂閱資料流,可以看做傳統的MQ,但是有很多不同點:支援多生產者,多消費者,另外多個消費者還可以組成群組,共享一個訊息流並且特定訊息整個群組只消費一次;以叢集方式執行,可伸縮,還有MirrorMaker支援不統叢集之間的複製;資料可以持久化,類似於資料庫,保證訊息一定送達;流式處理使得你可以用很少的程式碼就能動態的處理派生流和資料集。也可以看做實時版的Hadoop(大資料中,Hadoop主要應用於批處理與資料分析,Kafka由於低延遲更適合業務應用,能夠在業務事件發生時及時作出響應,為業務運營提供支撐,比如商品瀏覽資料轉換為商品推薦)。也可以看做ETL,但是並非簡單移動資料而是面向實時資料流。這三個方面是Kafka的主要特性
  • 生產者產生一個ProducerRecord物件,包含Topicvalue,還可能有key或者partition,物件序列化後傳送到分割槽器上,如果有partition則不操作,若無則根據key來指定partition(partition的設計目的是取消訊息大小限制和提高並行度)。選好partition後發往對應的topic和partition的記錄批次,這一批記錄等待一次性的發往對應的broker(所以批的大小影響延遲和吞吐量),整個過程有失敗自動重試功能,超過一定次數返回客戶端異常。生產者通過設定ack=n,可以保證一個ProducerRecord有n個副本(一個Partition的所有副本中有一個Leader負責與客戶端互動,其餘副本作為Follower從Leader複製資料,提供容錯性)被寫入成功才會收到成功的ack訊息。整個topic的順序無法保證,1個分割槽的順序可以通過設定retries>0(重試次數),和max.in.flight.requests.per.connection=1(1次只發1條訊息避免後到的訊息先成功)來保證,不過會嚴重影響生產者效能,若非嚴格順序要求不建議使用
  • 一個消費者group總是能獲得一個topic的全部訊息不管還有沒有其他group訂閱該topic,group內通過新增消費者來伸縮讀取能力和處理能力,每個消費者只處理一部分資料,而且消費者數量不要超過partition數量,不然多餘的會閒置浪費。group內所有消費者向群組協調器傳送心跳保持活躍,新增和刪除消費者時的再均衡(分配分割槽與消費者對應關係)也是由群組協調器完成
  • Kafka使用ZooKeeper維護叢集成員資訊,所有broker都對應/brokers/ids的臨時節點並訂閱其變更事件,在broker加入或退出時得到訊息;叢集中有一個控制器對應/controller臨時節點(所有broker都可以嘗試建立節點成功則成為Controller,失敗則知道Controller已經存在,所有broker都會監聽這個節點的變更,通過遞增的controller epoch來忽略舊控制器訊息避免腦裂),除了完成broker功能之外還負責partition副本的leader選舉;持續得到來自leader最新訊息的副本叫做同步的副本,當leader失效時,只有同步的副本才可能被選為新的leader;不同partition的副本和leader都均勻的分佈在所有broker之間避免熱點問題,副本儘量放在不同機架提高容錯性
  • kafka處理常見幾種請求:元資料請求,可以傳送給任意broker,包含客戶端感興趣的主題列表,返回主題包含的分割槽、副本及其leader;生產請求,ack可以為0,1,all三個值;讀取請求,零拷貝返回客戶端指定的訊息(省掉了使用者緩衝區,直接從磁碟到核心),要注意的是,leader只有把資料安全複製到副本中才能給客戶端返回這個資料;其他請求,如偏移量(不再儲存在ZooKeeper中)
  • 因為大檔案的查詢和刪除都很費時易錯,所以每個partition都分成了許多segment,一個segment包含1GB或者1周的資料(較小為準),正在寫入資料的叫做活躍片段,永遠不會被刪除。每個分割槽還有一個索引提升查詢效率
  • 可靠性和速度(簡便性)通常是負相關的,Kafka在這兩者之間是可以高度配置的,適應從跟蹤使用者點選支付操作的不同可靠性級別要求。kafka的可靠性機制包括:分割槽訊息順序性、多副本確認實現容錯性、只要還有一個副本是活躍的已提交訊息就不會丟失、消費者只能消費已提交資料。這些基本機制加上一些配置引數(unclean.leader.election.enable、min.insync.replicas、生產者ack)的權衡就能構建不同級別的可靠系統,可靠級別可以是針對broker或者topic。總而言之、可靠性不僅是kafka單方面的事情,需要整個系統配合如系統架構、生產者消費者的使用方式與配置、topic和broker配置等等
  • 跨叢集的複製在kafka中叫做mirror功能,應用場景:將不同區域的叢集資料複製到中心叢集便於分析資料、冗餘實現災備、雲遷移。跨資料中心通訊有高延遲、有限頻寬、高成本等特點,常見架構有:Hub-Spoke架構(一中央多本地叢集)、雙活架構(兩個叢集都對外服務,就近原則)、主備架構(主叢集服務、備叢集只在主不可用時對外服務)
  • 事件流是無邊界的(持續增長)、有序的、可重播的、不可變的記錄。流式處理就是實時的處理一個或多個事件流,是一種程式設計正規化就像請求響應式批處理式,但是延遲吞吐量都位於兩者之間。

看完這本書基本就能把kafka的使用方法和基本原理搞清楚了,但是翻譯有時候有點點問題,我覺得普通的段落可以翻譯,但是專用名詞如segmentpartition等還是直接保留比較好,免得有歧義,當然整體上來來說配合官網文件讀起來還是沒問題的。
要想深入的話原始碼在

NoSQL精粹

本書先分析了傳統關係資料庫的不足然後引入NoSQL,講到了鍵值、文件、列族、圖等多種資料庫並分析了其優勢劣勢,可以讓我們對NoSQL有一個全面的瞭解,為我們進一步NoSQL的探索之路開一個好頭。

亮點:

  • NoSQL泛指最近誕生非關係型資料庫如Cassandra、MongoDB、Neo4j等,他們主張無模式(schemaless)的資料。傳統關係資料庫有可持久化模型標準便於共享和整合事務支援等優點,NoSQL有無模式便於程式物件與資料庫之間的對映適應叢集等優點。所以NoSQL和傳統關係資料庫各有所長,發展過程中也在互相借鑑優點,誰也不能取代誰,將來都會在各自的領域中發光發熱
  • 關係模型把資料分割成簡單的行,通常一個物件可能需要多行資料通過外來鍵連線在一起來表示(如果按照嚴格的關係資料庫正規化);聚合是領域驅動設計的術語,把一組相關聯的物件視為一個整體單元來操作,這個單元就叫做聚合(aggregate)。聚合的邊界劃分沒有標準答案,取決於你之後準備怎樣操作資料。因為以聚合為單位複製和分片比較自然所以叢集中操作資料庫還是聚合更簡單。若資料操作大多在同一聚合內執行則應使用面向聚合的資料庫(鍵值、文件、列族資料庫),若互動需要多種不同的格式則最好選聚合無知式aggregate-ignorant)資料庫(關係資料庫、圖資料庫),若待處理資料中有大量的關係最好就選關係資料庫,但是如果關係複雜、並行交錯最好選圖資料庫(插入費時、查詢比複雜的join快得多)
  • 寬泛的說資料分佈有兩條正交的路徑:複製,一份資料拷貝到多個節點,有主從式(主節點瓶頸問題)和對等式(一致性問題)兩種形式;分片,將不同的資料存放到不同的節點。這裡注意不要和另一個概念混淆:資料庫水平(一個表的不同行資料置於不同節點)、垂直(一個表的不同列置於不同節點或者拆為多個表)拆分。實際分散式系統中一般都是複製和分片結合的(如上面提到的kafka)
  • 版本戳可以檢測衝突用來實現CAS,可以用計數器、GUID、Hash、時間戳等方式實現也可以組合實現,分散式系統中,可以採用“由版本戳構成的陣列”來檢測不同節點是否發生了“相互衝突的更新操作”
  • 鍵值資料庫最簡單,只能按鍵檢索,適用於會話資訊、使用者配置資訊、購物車等;文件資料庫呈樹狀結構可以看做支援值檢索的鍵值資料庫,適用於CMS及部落格、電子商務等;列族資料庫將資料存在列族中,列族裡的行則把許多列資料與本行的行鍵聯絡起來,列族通常把需要一併訪問的相關資料分成組,提高訪問效率,適用於事件記錄、部落格、計數器等;圖資料庫把具有屬性的實體(節點)按關係(邊)聯絡起來,適用於網際網路資料如社交網路、安排運輸線、推薦引擎等
  • Neal Ford 2006年造出了多語言程式設計(polyglot programming):應該以多種語言混合編寫同一應用程式,以各自語言的優勢來解決其中不同的問題,同理資料庫也可以遵循類似的原則混合持久化(polyglot persistence),比如購物車可以用鍵值資料庫儲存,但是訂單要用關係型資料庫。所以除非是程式語言狂熱愛好者,我們大部分人都不應該去爭論什麼語言好,什麼資料庫好,不要想著一把刷子走遍世界,而應該廣泛瞭解,解決問題的時候選擇合適的工具,必要的時候也要自己改工具甚至造工具

本書的一個問題就是有點舊了,2013年初版,這5年來NoSQL發展很快,已經被廣泛用於企業級的系統之中了,尤其是Redis和MongoDB,所以有需求就大膽的用吧。

大型網站技術架構

把事情做大是我們許多人的追求,這本書就可以讓我們瞭解一下一個大型網站的架構應該要注意些什麼,無論是摸得著的tech還是比較摸不著的leadership,都值得借鑑。當然,書很薄,大多隻能泛泛而談,要想真正掌握,每一部分我們都得自己單獨深挖,所以本書可以看做一個大目錄,為接下來的學習指引方向。

亮點:

  • 講述了一個單機網站發展到大型分散式網站的完整歷程,處於業務快速發展期間的公司可以借鑑,提前做好規劃
  • 架構設計時要注意:不要盲目追隨大公司而是要考慮自己業務;要結合網站實際使用新技術,不要盲目追求新奇酷炫;不要想著用技術解決一切問題,業務模式等也需要考慮;
  • 模式:每一個模式描述了一個在我們周圍不斷重複的問題以及該問題解決方案的核心,這樣你就能1次又1次的使用該方案而不必重複勞動。模式的關鍵在於可重複性,包括問題和方案的可重複性。
  • 大型網站有一些常見的架構模式:分層,如應用層、服務層、資料層;分割,如購物、論壇、搜尋、廣告;分散式,包括服務、靜態資源、資料庫、計算、鎖、檔案、配置資訊等;叢集,相同業務通過叢集負載均衡提高服務能力;快取,如CDN、反向代理、本地快取、分散式快取;非同步,事務關係越少就越能獨立發展,非同步也是解耦的一種協作方式;冗餘,冷備、熱備、災備等;自動化,包括髮布、測試、部署、報警、失效轉移、恢復、降級、分配資源等,自動化能提高效率並減小人為出錯概率;安全,手機驗證碼、影象驗證碼、轉義、風控等。所有模式都是在效能(響應時間、qps等)、可用性(冗餘)、伸縮性(機器的增減)、擴充套件性(業務的增減)、安全性(惡意訪問、竊取、攻擊)這幾個維度之間進行平衡
  • 效能針對不同群體需要不同的定義(針對使用者的響應時間,針對系統的吞吐量、併發量等)來衡量,也需要測試(壓測等),同時要從架構的多個層面(前端、應用層、儲存層)來進行優化
  • 可用性一般用幾個9來形容(4個9就是99.99%,一年小於53分鐘不可用),實現高可用架構的主要手段是資料和服務的冗餘備份和失效轉移。應用層(文庫、貼吧、知道)和服務層(賬戶服務、session服務)因為一般無狀態所以很容易擴充套件(負載均衡),新節點上下線都很容易,但是服務層需要注意好分級管理、超時設定、非同步呼叫、服務降級、冪等設計等。資料層(資料庫、檔案、快取)為了保證資料不丟失需要複製並均勻分佈在叢集中
  • 叢集處理能力如果能和伺服器數量正相關,那麼網站就具有伸縮性架構,如果能成線性關係那更是高伸縮性,主要分為不同服務物理拆分和單一服務叢集伸縮(類比於資料庫垂直拆分和水平拆分)。應用叢集伸縮性一般通過負載均衡實現如http重定向、DNS輪詢、反向代理、IP負載均衡、資料鏈路層負載均衡等;快取叢集伸縮性僅靠負載均衡是不夠的,還需要一致性hash、虛擬槽分割槽等方法使得快取在節點發生變化時不會集體失效;儲存叢集伸縮性對永續性和可用性要求更高,但是這裡作者沒怎麼講具體做法
  • 擴充套件性是在對現有系統影響最小的情況下持續提升或增加服務的能力,表現在系統基礎設施穩定,應用之間耦合較少等,是系統架構層面的開閉原則體現。主要有:分散式訊息佇列解耦生產者與消費者、分散式服務打造可複用的業務平臺、利用開放平臺打造生態圈實現開放共贏等方式
  • 安全性一般主要注意XSS、注入、CSRF等攻擊,主要手段有:使用者輸入轉義、表單token、驗證碼、加密、過濾、風控等
  • 領導的真諦就是尋找一個值得共同奮鬥的目標,營造一個讓大家都能最大限度發揮自我價值的氛圍;發掘人的優秀比發掘優秀的人更有意義

讀完發現本書幾乎全程“黑友商”,有的甚至好幾遍(* ̄︶ ̄),有點不純粹啊。

深入理解 Java 記憶體模型

這本書是infoq上的系列部落格形成的一本很薄的書,對於瞭解JMM還是很有用的。
深入理解Java記憶體模型(二)——重排序: http://www.infoq.com/cn/artic...

亮點:

  • 重排序的目的是提高效能,分為三種:編譯器、處理器指令級並行技術、記憶體系統的重排序。JMM屬於語言級別的記憶體模型,通過禁止特定型別的編譯器重排序(直接可控)和處理器重排序(通過記憶體屏障實現)來在不同的平臺對為程式設計師提供一致的記憶體模型
  • 記憶體屏障有四種:StoreLoad的排列 ,其中StoreLoad Barriers是一個全能型的屏障,同時具有其他三個屏障的效果,它確保store資料對其他處理器的load可見
  • 處理器記憶體是硬體級別的記憶體模型,JMM是一個語言級別的記憶體模型,順序一致性記憶體是一個理論參考模型,效能依次降低,可程式設計性依次上升。所以JMM主要關注易程式設計性和效能,在兩者之間求得平衡

深入分散式快取:從原理到實踐

快取在如今的網際網路中已經幾乎是標配了,分散式快取更是大型網站不可或缺的,本書就讓我們從原理開源實現業務實踐一起打包瞭解了。

亮點:

  • 計算機世界中,無論是硬體還是軟體,無論是單機還是分散式,快取都被用於解決響應緩慢的問題,是提升效能的關鍵技術。其核心就是空間換時間,解耦高速部件與低速部件。快取由於不同硬體的訪問速度和價格不同而進行分級,每一級儲存對應熱度級別的資料,分層逐級減少流量的衝擊,保護使用者體驗與系統可用性。分散式環境下要關注其命中、更新、失效、一致性等
  • 快取是無所不在的,從架構的角度看可以分為三塊:客戶端快取 在客戶端減少網路請求,如頁面快取(檔案化)、瀏覽器通過HTTP協議實現的快取(etag,304 not-modified)、APP快取(sqlite)等;網路快取 位於客戶端與服務端之間,代理部分客戶端請求給服務端減負,如web代理快取(nginx)、邊緣快取(CDN);服務端快取 是快取的重點和難點,如資料庫快取(query_cache)、平臺級快取(大而全的Ehcache、極簡的Guava )、應用級快取(redis)
  • 書中澄清了許多分散式方面的概念和理論,如叢集是一組互相獨立的計算機通過網路連線構成一個組實現同一個功能,對客戶端表現為一個獨立伺服器,可以增加可用性與伸縮性,而分散式系統就是許多不同功能的叢集構成的一個實現特定業務的系統;冪等性是指呼叫1次和N次結果一樣,這個在分散式系統中很重要是因為網路抖動等因素常常導致重發;分散式系統的本質是一堆廉價的硬體攢在一起獲得更好的吞吐量和效能以及可用性,有幾個問題普遍關心:活性檢測(週期心跳、累計失效檢測)、高可用(主備、雙主、叢集)、容錯處理(冷熱切換、冗餘、備份)、重試機制(失敗重試、事務補償)、負載均衡(多機分擔請求),也有幾個實踐很重要:全域性ID(資料庫自增id如flicker、UUID、snowflake、預生成置於快取中)、雜湊取模分配一致性雜湊(單調性、分散性)、路由表資料拆分
  • 書中在JSR規範下帶領我們實現了一個快取框架CSCache,架構由上到下是客戶端、快取提供層、快取管理層、快取儲存層(基本儲存,LRU儲存,Weak儲存),設計思路值得借鑑
  • redis有瓶頸:單機資料量限制、單執行緒吞吐量限制(尤其是大塊資料讀取延遲明顯)、單點故障等,目前已有的解決方案有:水平拆分如Proxy和Twemproxy、自帶主從複製(含斷點續傳)、sentinel監控自動故障轉移(使用raft類似演算法選主)、redis3.0後出來的去中心化(採用Gossip協議在節點之間達成一致)的cluster提供了完整的sharding、replication、failover等解決方案
  • 本書覆蓋了廣告、社交、電商等多種業務的快取模式,能開開眼界,將來可能還真用得上,尤其是社交和電商這兩塊講得很細緻,幾乎可以作為一精簡而完整的解決方案了