Merge branch 'master' into add-DNS-reference

pull/276/head
RexHZhang 2020-05-23 15:29:30 -07:00 committed by GitHub
commit 34a6896497
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 6826 additions and 260 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
# Byte-compiled / optimized / DLL files
*.epub
__pycache__/
*.py[cod]

View File

@ -65,6 +65,7 @@ Translations to new languages are always welcome, especially if you can maintain
* Invite friends to review if possible. If desired, feel free to invite friends to help your original translation by letting them fork your repo, then merging their PRs.
* Add links to your translation at the top of every README*.md file. (For consistency, the link should be added in alphabetical order by ISO code, and the anchor text should be in the native language.)
* When done, indicate on the PR that it's ready to be merged into the main repo.
* Once accepted, your PR will be squashed into a single commit into the `master` branch.
### Translation template credits

1787
README-ja.md Normal file
View File

@ -0,0 +1,1787 @@
*[English](README.md) ∙ [日本語](README-ja.md) ∙ [简体中文](README-zh-Hans.md) ∙ [繁體中文](README-zh-TW.md) | [العَرَبِيَّة‎](https://github.com/donnemartin/system-design-primer/issues/170) ∙ [বাংলা](https://github.com/donnemartin/system-design-primer/issues/220) ∙ [Português do Brasil](https://github.com/donnemartin/system-design-primer/issues/40) ∙ [Deutsch](https://github.com/donnemartin/system-design-primer/issues/186) ∙ [ελληνικά](https://github.com/donnemartin/system-design-primer/issues/130) ∙ [עברית](https://github.com/donnemartin/system-design-primer/issues/272) ∙ [Italiano](https://github.com/donnemartin/system-design-primer/issues/104) ∙ [韓國語](https://github.com/donnemartin/system-design-primer/issues/102) ∙ [فارسی](https://github.com/donnemartin/system-design-primer/issues/110) ∙ [Polski](https://github.com/donnemartin/system-design-primer/issues/68) ∙ [русский язык](https://github.com/donnemartin/system-design-primer/issues/87) ∙ [Español](https://github.com/donnemartin/system-design-primer/issues/136) ∙ [ภาษาไทย](https://github.com/donnemartin/system-design-primer/issues/187) ∙ [Türkçe](https://github.com/donnemartin/system-design-primer/issues/39) ∙ [tiếng Việt](https://github.com/donnemartin/system-design-primer/issues/127) ∙ [Français](https://github.com/donnemartin/system-design-primer/issues/250) | [Add Translation](https://github.com/donnemartin/system-design-primer/issues/28)*
# システム設計入門
<p align="center">
<img src="http://i.imgur.com/jj3A5N8.png"/>
<br/>
</p>
## 動機・目的
> 大規模システムのシステム設計を学ぶ
>
> システム設計面接課題に備える
### 大規模システムの設計を学ぶ
スケーラブルなシステムのシステム設計を学ぶことは、より良いエンジニアになることに資するでしょう。
システム設計はとても広範なトピックを含みます。システム設計原理については **インターネット上には膨大な量の文献が散らばっています。**
このリポジトリは大規模システム構築に必要な知識を学ぶことができる **文献リストを体系的にまとめたもの** です。
### オープンソースコミュニティから学ぶ
このプロジェクトは、これからもずっと更新されていくオープンソースプロジェクトの初期段階にすぎません。
[Contributions](#contributing) は大歓迎です!
### システム設計面接課題に備える
コード技術面接に加えて、システム設計に関する知識は、多くのテック企業における **技術採用面接プロセス** で **必要不可欠な要素** です。
**システム設計面接での頻出質問に備え**、自分の解答と*模範解答*:ディスカッション、コードそして図表などを*比較*して学びましょう。
面接準備に役立つその他のトピック:
* [学習指針](#学習指針)
* [システム設計面接課題にどのように準備するか](#システム設計面接にどのようにして臨めばいいか)
* [システム設計課題例 **とその解答**](#システム設計課題例とその解答)
* [オブジェクト指向設計課題例、 **とその解答**](#オブジェクト指向設計問題と解答)
* [その他のシステム設計面接課題例](#他のシステム設計面接例題)
## 暗記カード
<p align="center">
<img src="http://i.imgur.com/zdCAkB3.png"/>
<br/>
</p>
この[Anki用フラッシュカードデッキ](https://apps.ankiweb.net/) は、間隔反復を活用して、システム設計のキーコンセプトの学習を支援します。
* [システム設計デッキ](resources/flash_cards/System%20Design.apkg)
* [システム設計練習課題デッキ](resources/flash_cards/System%20Design%20Exercises.apkg)
* [オブジェクト指向練習課題デッキ](resources/flash_cards/OO%20Design.apkg)
外出先や移動中の勉強に役立つでしょう。
### コーディング技術課題用の問題: 練習用インタラクティブアプリケーション
コード技術面接用の問題を探している場合は[**こちら**](https://github.com/donnemartin/interactive-coding-challenges)
<p align="center">
<img src="http://i.imgur.com/b4YtAEN.png"/>
<br/>
</p>
姉妹リポジトリの [**Interactive Coding Challenges**](https://github.com/donnemartin/interactive-coding-challenges)も見てみてください。追加の暗記デッキカードも入っています。
* [Coding deck](https://github.com/donnemartin/interactive-coding-challenges/tree/master/anki_cards/Coding.apkg)
## コントリビュート
> コミュニティから学ぶ
プルリクエスト等の貢献は積極的にお願いします:
* エラー修正
* セクション内容改善
* 新規セクション追加
* [翻訳する](https://github.com/donnemartin/system-design-primer/issues/28)
現在、内容の改善が必要な作業中のコンテンツは[こちら](#進行中の作業)です。
コントリビュートの前に[Contributing Guidelines](CONTRIBUTING.md)を読みましょう。
## システム設計目次
> 賛否も含めた様々なシステム設計の各トピックの概要。 **全てはトレードオフの関係にあります。**
>
> それぞれのセクションはより学びを深めるような他の文献へのリンクが貼られています。
<p align="center">
<img src="http://i.imgur.com/jrUBAF7.png"/>
<br/>
</p>
* [システム設計トピック: まずはここから](#システム設計トピックス-まずはここから)
* [Step 1: スケーラビリティに関する動画を見る](#ステップ-1-スケーラビリティに関する動画を観て復習する)
* [Step 2: スケーラビリティに関する記事を読む](#ステップ-2-スケーラビリティに関する資料を読んで復習する)
* [次のステップ](#次のステップ)
* [パフォーマンス vs スケーラビリティ](#パフォーマンス-vs-スケーラビリティ)
* [レイテンシー vs スループット](#レイテンシー-vs-スループット)
* [可用性 vs 一貫性](#可用性-vs-一貫性)
* [CAP理論](#cap-理論)
* [CP - 一貫性(consistency)と分割性(partition)耐性](#cp---一貫性と分断耐性consistency-and-partition-tolerance)
* [AP - 可用性(availability)と分割性(partition)耐性](#ap---可用性と分断耐性availability-and-partition-tolerance)
* [一貫性 パターン](#一貫性パターン)
* [弱い一貫性](#弱い一貫性)
* [結果整合性](#結果整合性)
* [強い一貫性](#強い一貫性)
* [可用性 パターン](#可用性パターン)
* [フェイルオーバー](#フェイルオーバー)
* [レプリケーション](#レプリケーション)
* [ドメインネームシステム(DNS)](#ドメインネームシステム)
* [コンテンツデリバリーネットワーク(CDN)](#コンテンツデリバリーネットワークcontent-delivery-network)
* [プッシュCDN](#プッシュcdn)
* [プルCDN](#プルcdn)
* [ロードバランサー](#ロードバランサー)
* [アクティブ/パッシブ構成](#アクティブパッシブ)
* [アクティブ/アクティブ構成](#アクティブアクティブ)
* [Layer 4 ロードバランシング](#layer-4-ロードバランシング)
* [Layer 7 ロードバランシング](#layer-7-ロードバランシング)
* [水平スケーリング](#水平スケーリング)
* [リバースプロキシ (WEBサーバー)](#リバースプロキシwebサーバー)
* [ロードバランサー vs リバースプロキシ](#ロードバランサー-vs-リバースプロキシ)
* [アプリケーションレイヤー](#アプリケーション層)
* [マイクロサービス](#マイクロサービス)
* [サービスディスカバリー](#service-discovery)
* [データベース](#データベース)
* [リレーショナルデータベースマネジメントシステム (RDBMS)](#リレーショナルデータベースマネジメントシステム-rdbms)
* [マスター/スレーブ レプリケーション](#マスタースレーブ-レプリケーション)
* [マスター/マスター レプリケーション](#マスターマスター-レプリケーション)
* [フェデレーション](#federation)
* [シャーディング](#シャーディング)
* [デノーマライゼーション](#非正規化)
* [SQL チューニング](#sqlチューニング)
* [NoSQL](#nosql)
* [キー/バリューストア](#キーバリューストア)
* [ドキュメントストア](#ドキュメントストア)
* [ワイドカラムストア](#ワイドカラムストア)
* [グラフ データベース](#グラフデータベース)
* [SQL or NoSQL](#sqlかnosqlか)
* [キャッシュ](#キャッシュ)
* [クライアントキャッシング](#クライアントキャッシング)
* [CDNキャッシング](#cdnキャッシング)
* [Webサーバーキャッシング](#webサーバーキャッシング)
* [データベースキャッシング](#データベースキャッシング)
* [アプリケーションキャッシング](#アプリケーションキャッシング)
* [データベースクエリレベルでキャッシングする](#データベースクエリレベルでのキャッシング)
* [オブジェクトレベルでキャッシングする](#オブジェクトレベルでのキャッシング)
* [いつキャッシュを更新するのか](#いつキャッシュを更新するか)
* [キャッシュアサイド](#キャッシュアサイド)
* [ライトスルー](#ライトスルー)
* [ライトビハインド (ライトバック)](#ライトビハインド-ライトバック)
* [リフレッシュアヘッド](#リフレッシュアヘッド)
* [非同期処理](#非同期処理)
* [メッセージキュー](#メッセージキュー)
* [タスクキュー](#タスクキュー)
* [バックプレッシャー](#バックプレッシャー)
* [通信](#通信)
* [伝送制御プロトコル (TCP)](#伝送制御プロトコル-tcp)
* [ユーザデータグラムプロトコル (UDP)](#ユーザデータグラムプロトコル-udp)
* [遠隔手続呼出 (RPC)](#遠隔手続呼出-rpc)
* [Representational state transfer (REST)](#representational-state-transfer-rest)
* [セキュリティ](#セキュリティ)
* [補遺](#補遺)
* [2の乗数表](#2の乗数表)
* [全てのプログラマーが知るべきレイテンシー値](#全てのプログラマーが知るべきレイテンシー値)
* [他のシステム設計面接例題](#他のシステム設計面接例題)
* [実世界でのアーキテクチャ](#実世界のアーキテクチャ)
* [各企業のアーキテクチャ](#各企業のアーキテクチャ)
* [企業のエンジニアブログ](#企業のエンジニアブログ)
* [作業中](#進行中の作業)
* [クレジット](#クレジット)
* [連絡情報](#contact-info)
* [ライセンス](#license)
## 学習指針
> 学習スパンに応じてみるべきトピックス (short, medium, long)
![Imgur](http://i.imgur.com/OfVllex.png)
**Q: 面接のためには、ここにあるものすべてをやらないといけないのでしょうか?**
**A: いえ、ここにあるすべてをやる必要はありません。**
面接で何を聞かれるかは以下の条件によって変わってきます:
* どれだけの技術経験があるか
* あなたの技術背景が何であるか
* どのポジションのために面接を受けているか
* どの企業の面接を受けているか
* 運
より経験のある候補者は一般的にシステム設計についてより深い知識を有していることを要求されるでしょう。システムアーキテクトやチームリーダーは各メンバーの持つような知識よりは深い見識を持っているべきでしょう。一流テック企業では複数回の設計面接を課されることが多いです。
まずは広く始めて、そこからいくつかの分野に絞って深めていくのがいいでしょう。様々なシステム設計のトピックについて少しずつ知っておくことはいいことです。以下の学習ガイドを自分の学習に当てられる時間、技術経験、どの職位、どの会社に応募しているかなどを加味して自分用に調整して使うといいでしょう。
* **短期間** - **幅広く** システム設計トピックを学ぶ。**いくつかの** 面接課題を解くことで対策する。
* **中期間** - **幅広く** そして **それなりに深く**システム設計トピックを学ぶ。**多くの** 面接課題を解くことで対策する。
* **長期間** - **幅広く** そして **もっと深く**システム設計トピックを学ぶ。**ほぼ全ての** 面接課題を解くことで対策する。
| | 短期間 | 中期間 | 長期間 |
|---|---|---|---|
| [システム設計トピック](#システム設計目次) を読み、システム動作機序について広く知る | :+1: | :+1: | :+1: |
| 次のリンク先のいくつかのページを読んで [各企業のエンジニアリングブログ](#企業のエンジニアブログ) 応募する会社について知る | :+1: | :+1: | :+1: |
| 次のリンク先のいくつかのページを読む [実世界でのアーキテクチャ](#実世界のアーキテクチャ) | :+1: | :+1: | :+1: |
| 復習する [システム設計面接課題にどのように準備するか](#システム設計面接にどのようにして臨めばいいか) | :+1: | :+1: | :+1: |
| とりあえず一周する [システム設計課題例](#システム設計課題例とその解答) | Some | Many | Most |
| とりあえず一周する [オブジェクト指向設計問題と解答](#オブジェクト指向設計問題と解答) | Some | Many | Most |
| 復習する [その他システム設計面接での質問例](#他のシステム設計面接例題) | Some | Many | Most |
## システム設計面接にどのようにして臨めばいいか
> システム設計面接試験問題にどのように取り組むか
システム設計面接は **open-ended conversation(Yes/Noでは答えられない口頭質問)です**。 自分で会話を組み立てることを求められます。
以下のステップに従って議論を組み立てることができるでしょう。この過程を確かなものにするために、次のセクション[システム設計課題例とその解答](#system-design-interview-questions-with-solutions) を以下の指針に従って読み込むといいでしょう。
### ステップ 1: そのシステム使用例の概要、制約、推計値等を聞き出し、まとめる
システム仕様の要求事項を聞き出し、問題箇所を特定しましょう。使用例と制約を明確にするための質問を投げかけましょう。要求する推計値についても議論しておきましょう。
* 誰がそのサービスを使うのか?
* どのように使うのか?
* 何人のユーザーがいるのか?
* システムはどのような機能を果たすのか?
* システムへの入力と出力は?
* どれだけの容量のデータを捌く必要があるのか?
* 一秒間に何リクエストの送信が想定されるか?
* 読み書き比率の推定値はいくら程度か?
### ステップ 2: より高レベルのシステム設計を組み立てる
重要なコンポーネントを全て考慮した高レベルのシステム設計概要を組み立てる。
* 主要なコンポーネントと接続をスケッチして書き出す
* 考えの裏付けをする
### ステップ 3: 核となるコンポーネントを設計する
それぞれの主要なコンポーネントについての詳細を学ぶ。例えば、[url短縮サービス](solutions/system_design/pastebin/README.md)の設計を問われた際には次のようにするといいでしょう:
* 元のURLのハッシュ化したものを作り、それを保存する
* [MD5](solutions/system_design/pastebin/README.md) と [Base62](solutions/system_design/pastebin/README.md)
* ハッシュ衝突
* SQL もしくは NoSQL
* データベーススキーマ
* ハッシュ化されたURLを元のURLに再翻訳する
* データベース参照
* API & オブジェクト指向の設計
### ステップ 4: システム設計のスケール
与えられた制約条件からボトルネックとなりそうなところを割り出し、明確化する。 例えば、スケーラビリティの問題解決のために以下の要素を考慮する必要があるだろうか?
* ロードバランサー
* 水平スケーリング
* キャッシング
* データベースシャーディング
取りうる解決策とそのトレードオフについて議論をしよう。全てのことはトレードオフの関係にある。ボトルネックについては[スケーラブルなシステム設計の原理](#システム設計目次)を読むといいでしょう。
### ちょっとした暗算問題
ちょっとした推計値を手計算ですることを求められることもあるかもしれません。[補遺](#補遺)の以下の項目が役に立つでしょう:
* [チラ裏計算でシステム設計する](http://highscalability.com/blog/2011/1/26/google-pro-tip-use-back-of-the-envelope-calculations-to-choo.html)
* [2の乗数表](#2の乗数表)
* [全てのプログラマーが知っておくべきレイテンシの参考値](#全てのプログラマーが知るべきレイテンシー値)
### 文献とその他の参考資料
以下のリンク先ページを見てどのような質問を投げかけられるか概要を頭に入れておきましょう:
* [システム設計面接で成功するには?](https://www.palantir.com/2011/10/how-to-rock-a-systems-design-interview/)
* [システム設計面接](http://www.hiredintech.com/system-design)
* [アーキテクチャ、システム設計面接への導入](https://www.youtube.com/watch?v=ZgdS0EUmn70)
## システム設計課題例とその解答
> 頻出のシステム設計面接課題と参考解答、コード及びダイアグラム
>
> 解答は `solutions/` フォルダ以下にリンクが貼られている
| 問題 | |
|---|---|
| Pastebin.com (もしくは Bit.ly) を設計する| [解答](solutions/system_design/pastebin/README.md) |
| Twitterタイムライン (もしくはFacebookフィード)を設計する<br/>Twitter検索(もしくはFacebook検索)機能を設計する | [解答](solutions/system_design/twitter/README.md) |
| ウェブクローラーを設計する | [解答](solutions/system_design/web_crawler/README.md) |
| Mint.comを設計する | [解答](solutions/system_design/mint/README.md) |
| SNSサービスのデータ構造を設計する | [解答](solutions/system_design/social_graph/README.md) |
| 検索エンジンのキー/バリュー構造を設計する | [解答](solutions/system_design/query_cache/README.md) |
| Amazonのカテゴリ毎の売り上げランキングを設計する | [解答](solutions/system_design/sales_rank/README.md) |
| AWS上で100万人規模のユーザーを捌くサービスを設計する | [解答](solutions/system_design/scaling_aws/README.md) |
| システム設計問題を追加する | [Contribute](#contributing) |
### Pastebin.com (もしくは Bit.ly) を設計する
[問題と解答を見る](solutions/system_design/pastebin/README.md)
![Imgur](http://i.imgur.com/4edXG0T.png)
### Twitterタイムライン&検索 (もしくはFacebookフィード&検索)を設計する
[問題と解答を見る](solutions/system_design/twitter/README.md)
![Imgur](http://i.imgur.com/jrUBAF7.png)
### ウェブクローラーの設計
[問題と解答を見る](solutions/system_design/web_crawler/README.md)
![Imgur](http://i.imgur.com/bWxPtQA.png)
### Mint.comの設計
[問題と解答を見る](solutions/system_design/mint/README.md)
![Imgur](http://i.imgur.com/V5q57vU.png)
### SNSサービスのデータ構造を設計する
[問題と解答を見る](solutions/system_design/social_graph/README.md)
![Imgur](http://i.imgur.com/cdCv5g7.png)
### 検索エンジンのキー/バリュー構造を設計する
[問題と解答を見る](solutions/system_design/query_cache/README.md)
![Imgur](http://i.imgur.com/4j99mhe.png)
### Amazonのカテゴリ毎の売り上げランキングを設計する
[問題と解答を見る](solutions/system_design/sales_rank/README.md)
![Imgur](http://i.imgur.com/MzExP06.png)
### AWS上で100万人規模のユーザーを捌くサービスを設計する
[問題と解答を見る](solutions/system_design/scaling_aws/README.md)
![Imgur](http://i.imgur.com/jj3A5N8.png)
## オブジェクト指向設計問題と解答
> 頻出のオブジェクト指向システム設計面接課題と参考解答、コード及びダイアグラム
>
> 解答は `solutions/` フォルダ以下にリンクが貼られている
>**備考: このセクションは作業中です**
| 問題 | |
|---|---|
| ハッシュマップの設計 | [解答](solutions/object_oriented_design/hash_table/hash_map.ipynb) |
| LRUキャッシュの設計 | [解答](solutions/object_oriented_design/lru_cache/lru_cache.ipynb) |
| コールセンターの設計 | [解答](solutions/object_oriented_design/call_center/call_center.ipynb) |
| カードのデッキの設計 | [解答](solutions/object_oriented_design/deck_of_cards/deck_of_cards.ipynb) |
| 駐車場の設計 | [解答](solutions/object_oriented_design/parking_lot/parking_lot.ipynb) |
| チャットサーバーの設計 | [解答](solutions/object_oriented_design/online_chat/online_chat.ipynb) |
| 円形配列の設計 | [Contribute](#contributing) |
| オブジェクト指向システム設計問題を追加する | [Contribute](#contributing) |
## システム設計トピックス: まずはここから
システム設計の勉強は初めて?
まず初めに、よく使われる設計原理について、それらが何であるか、どのように用いられるか、長所短所について基本的な知識を得る必要があります
### ステップ 1: スケーラビリティに関する動画を観て復習する
[Harvardでのスケーラビリティの講義](https://www.youtube.com/watch?v=-W9F__D3oY4)
* ここで触れられているトピックス:
* 垂直スケーリング
* 水平スケーリング
* キャッシング
* ロードバランシング
* データベースレプリケーション
* データベースパーティション
### ステップ 2: スケーラビリティに関する資料を読んで復習する
[スケーラビリティ](http://www.lecloud.net/tagged/scalability/chrono)
* ここで触れられているトピックス:
* [クローン](http://www.lecloud.net/post/7295452622/scalability-for-dummies-part-1-clones)
* [データベース](http://www.lecloud.net/post/7994751381/scalability-for-dummies-part-2-database)
* [キャッシュ](http://www.lecloud.net/post/9246290032/scalability-for-dummies-part-3-cache)
* [非同期](http://www.lecloud.net/post/9699762917/scalability-for-dummies-part-4-asynchronism)
### 次のステップ
次に、ハイレベルでのトレードオフについてみていく:
* **パフォーマンス** vs **スケーラビリティ**
* **レイテンシ** vs **スループット**
* **可用性** vs **一貫性**
**全てはトレードオフの関係にある**というのを肝に命じておきましょう。
それから、より深い内容、DNSやCDNそしてロードバランサーなどについて学習を進めていきましょう。
## パフォーマンス vs スケーラビリティ
リソースが追加されるのにつれて **パフォーマンス** が向上する場合そのサービスは **スケーラブル** であると言えるでしょう。一般的に、パフォーマンスを向上させるというのはすなわち計算処理を増やすことを意味しますが、データセットが増えた時などより大きな処理を捌けるようになることでもあります。<sup><a href=http://www.allthingsdistributed.com/2006/03/a_word_on_scalability.html>1</a></sup>
パフォーマンスvsスケーラビリティをとらえる他の考え方:
* **パフォーマンス** での問題を抱えている時、あなたのシステムは一人のユーザーにとって遅いと言えるでしょう。
* **スケーラビリティ** での問題を抱えているとき、一人のユーザーにとっては速いですが、多くのリクエストがある時には遅くなってしまうでしょう。
### その他の参考資料、ページ
* [スケーラビリティについて](http://www.allthingsdistributed.com/2006/03/a_word_on_scalability.html)
* [スケーラビリティ、可用性、安定性、パターン](http://www.slideshare.net/jboner/scalability-availability-stability-patterns/)
## レイテンシー vs スループット
**レイテンシー** とはなにがしかの動作を行う、もしくは結果を算出するのに要する時間
**スループット** とはそのような動作や結果算出が単位時間に行われる回数
一般的に、 **最大限のスループット** を **許容範囲内のレイテンシー** で実現することを目指すのが普通だ。
### その他の参考資料、ページ
* [レイテンシー vs スループットを理解する](https://community.cadence.com/cadence_blogs_8/b/sd/archive/2010/09/13/understanding-latency-vs-throughput)
## 可用性 vs 一貫性
### CAP 理論
<p align="center">
<img src="http://i.imgur.com/bgLMI2u.png"/>
<br/>
<i><a href=http://robertgreiner.com/2014/08/cap-theorem-revisited>Source: CAP theorem revisited</a></i>
</p>
分散型コンピュータシステムにおいては下の三つのうち二つまでしか同時に保証することはできない。:
* **一貫性** - 全ての読み込みは最新の書き込みもしくはエラーを受け取る
* **可用性** - 受け取る情報が最新のものだという保証はないが、全てのリクエストはレスポンスを必ず受け取る
* **分断耐性** - ネットワーク問題によって順不同の分断が起きてもシステムが動作を続ける
*ネットワークは信頼できないので、分断耐性は必ず保証しなければなりません。つまりソフトウェアシステムとしてのトレードオフは、一貫性を取るか、可用性を取るかを考えなければなりません。*
#### CP - 一貫性と分断耐性(consistency and partition tolerance)
分断されたードからのレスポンスを待ち続けているとタイムアウトエラーに陥る可能性があります。CPはあなたのサービスがアトミックな読み書き不可分操作を必要とする際にはいい選択肢でしょう。
#### AP - 可用性と分断耐性(availability and partition tolerance)
レスポンスはノード上にあるデータで最新のものを返します。つまり、最新版のデータが返されるとは限りません。分断が解消された後も、書き込みが反映されるのには時間がかかります。
[結果整合性](#結果整合性) を求めるサービスの際にはAPを採用するのがいいでしょう。もしくは、外部エラーに関わらずシステムが稼働する必要がある際にも同様です。
### その他の参考資料、ページ
* [CAP 理論を振り返る](http://robertgreiner.com/2014/08/cap-theorem-revisited/)
* [平易な英語でのCAP 理論のイントロ](http://ksat.me/a-plain-english-introduction-to-cap-theorem/)
* [CAP FAQ](https://github.com/henryr/cap-faq)
## 一貫性パターン
同じデータの複製が複数ある状態では、クライアントが一貫したデータ表示を受け取るために、どのようにそれらを同期すればいいのかという課題があります。 [CAP 理論](#cap-理論) における一貫性の定義を思い出してみましょう。全ての読み取りは最新の書き込みデータもしくはエラーを受け取るはずです。
### 弱い一貫性
書き込み後の読み取りでは、その最新の書き込みを読めたり読めなかったりする。ベストエフォート型のアプローチに基づく。
このアプローチはmemcachedなどのシステムに見られます。弱い一貫性はリアルタイム性が必要なユースケース、例えばVoIP、ビデオチャット、リアルタイムマルチプレイヤーゲームなどと相性がいいでしょう。例えば、電話に出ているときに数秒間音声が受け取れなくなったとしたら、その後に接続が回復してもその接続が切断されていた間に話されていたことは聞き取れないというような感じです。
### 結果整合性
書き込みの後、読み取りは最終的にはその結果を読み取ることができる(ミリ秒ほど遅れてというのが一般的です)。データは非同期的に複製されます。
このアプローチはDNSやメールシステムなどに採用されています。結果整合性は多くのリクエストを捌くサービスと相性がいいでしょう。
### 強い一貫性
書き込みの後、読み取りはそれを必ず読むことができます。データは同期的に複製されます。
このアプローチはファイルシステムやRDBMSなどで採用されています。トランザクションを扱うサービスでは強い一貫性が必要でしょう。
### その他の参考資料、ページ
* [データセンター間でのトランザクション](http://snarfed.org/transactions_across_datacenters_io.html)
## 可用性パターン
高い可用性を担保するには主に次の二つのパターンがあります: **フェイルオーバー** と **レプリケーション** です。
### フェイルオーバー
#### アクティブ・パッシブ
アクティブ・パッシブフェイルオーバーにおいては、周期信号はアクティブもしくはスタンバイ中のパッシブなサーバーに送られます。周期信号が中断された時には、パッシブだったサーバーがアクティブサーバーのIPアドレスを引き継いでサービスを再開します。
起動までのダウンタイムはパッシブサーバーが「ホット」なスタンバイ状態にあるか、「コールド」なスタンバイ状態にあるかで変わります。アクティブなサーバーのみがトラフィックを捌きます。
アクティブ・パッシブフェイルオーバーはマスター・スレーブフェイルオーバーと呼ばれることもあります。
#### アクティブ・アクティブ
アクティブアクティブ構成では両方のサーバーがトラフィックを捌くことで負荷を分散します。
これらのサーバーがパブリックなものの場合、DNSは両方のサーバーのパブリックIPを知っている必要があります。もし、プライベートなものな場合、アプリケーションロジックが両方のサーバーの情報について知っている必要があります。
アクティブ・アクティブなフェイルオーバーはマスター・マスターフェイルオーバーと呼ばれることもあります。
### 短所: フェイルオーバー
* フェイルオーバーではより多くのハードウェアを要し、複雑さが増します。
* 最新の書き込みがパッシブサーバーに複製される前にアクティブが落ちると、データ欠損が起きる潜在可能性があります。
### レプリケーション
#### マスター・スレーブ と マスター・マスター
このトピックは [データベース](#データベース) セクションにおいてより詳細に解説されています:
* [マスター・スレーブ レプリケーション](#マスタースレーブ-レプリケーション)
* [マスター・マスター レプリケーション](#マスターマスター-レプリケーション)
## ドメインネームシステム
<p align="center">
<img src="http://i.imgur.com/IOyLj4i.jpg"/>
<br/>
<i><a href=http://www.slideshare.net/srikrupa5/dns-security-presentation-issa>Source: DNS security presentation</a></i>
</p>
ドメインネームシステム (DNS) は www.example.com などのドメインネームをIPアドレスへと翻訳します。
DNSは少数のオーソライズされたサーバーが上位に位置する階層的構造です。あなたのルーターもしくはISPは検索をする際にどのDNSサーバーに接続するかという情報を提供します。低い階層のDNSサーバーはその経路マップをキャッシュします。ただ、この情報は伝搬遅延によって陳腐化する可能性があります。DNSの結果はあなたのブラウザもしくはOSに一定期間[time to live (TTL)](https://en.wikipedia.org/wiki/Time_to_live)に設定された期間)キャッシュされます。
* **NS record (name server)** - あなたのドメイン・サブドメインでのDNSサーバーを特定します。
* **MX record (mail exchange)** - メッセージを受け取るメールサーバーを特定します。
* **A record (address)** - IPアドレスに名前をつけます。
* **CNAME (canonical)** - 他の名前もしくは `CNAME` (example.com を www.example.com) もしくは `A` recordへと名前を指し示す。
[CloudFlare](https://www.cloudflare.com/dns/) や [Route 53](https://aws.amazon.com/route53/) などのサービスはマネージドDNSサービスを提供しています。いくつかのDNSサービスでは様々な手法を使ってトラフィックを捌くことができます:
* [加重ラウンドロビン](http://g33kinfo.com/info/archives/2657)
* トラフィックがメンテナンス中のサーバーに行くのを防ぎます
* 様々なクラスターサイズに応じて調整します
* A/B テスト
* レイテンシーベース
* 地理ベース
### 欠点: DNS
* 上記で示されているようなキャッシングによって緩和されているとはいえ、DNSサーバーへの接続には少し遅延が生じる。
* DNSサーバーは、[政府、ISP企業,そして大企業](http://superuser.com/questions/472695/who-controls-the-dns-servers/472729)に管理されているが、それらの管理は複雑である。
* DNSサービスは[DDoS attack](http://dyn.com/blog/dyn-analysis-summary-of-friday-october-21-attack/)の例で、IPアドレスなしにユーザーがTwitterなどにアクセスできなくなったように、攻撃を受ける可能性がある。
### その他の参考資料、ページ
* [DNS アーキテクチャ](https://technet.microsoft.com/en-us/library/dd197427(v=ws.10).aspx)
* [Wikipedia](https://en.wikipedia.org/wiki/Domain_Name_System)
* [DNS 記事](https://support.dnsimple.com/categories/dns/)
## コンテンツデリバリーネットワーク(Content delivery network)
<p align="center">
<img src="http://i.imgur.com/h9TAuGI.jpg"/>
<br/>
<i><a href=https://www.creative-artworks.eu/why-use-a-content-delivery-network-cdn/>Source: Why use a CDN</a></i>
</p>
コンテンツデリバリーネットワーク(CDN)は世界中に配置されたプロキシサーバーのネットワークがユーザーに一番地理的に近いサーバーからコンテンツを配信するシステムのことです。AmazonのCloudFrontなどは例外的にダイナミックなコンテンツも配信しますが、一般的に、HTML/CSS/JS、写真、そして動画などの静的ファイルがCDNを通じて配信されます。そのサイトのDNSがクライアントにどのサーバーと交信するかという情報を伝えます。
CDNを用いてコンテンツを配信することで以下の二つの理由でパフォーマンスが劇的に向上します:
* ユーザーは近くにあるデータセンターから受信できる
* バックエンドサーバーはCDNが処理してくれるリクエストに関しては処理する必要がなくなります
### プッシュCDN
プッシュCDNではサーバーデータに更新があった時には必ず、新しいコンテンツを受け取る方式です。コンテンツを用意し、CDNに直接アップロードし、URLをCDNを指すように指定するところまで、全て自分で責任を負う形です。コンテンツがいつ期限切れになるのか更新されるのかを設定することができます。コンテンツは新規作成時、更新時のみアップロードされることでトラフィックは最小化される一方、ストレージは最大限消費されてしまいます。
トラフィックの少ない、もしくは頻繁にはコンテンツが更新されないサイトの場合にはプッシュCDNと相性がいいでしょう。コンテンツは定期的に再びプルされるのではなく、CDNに一度のみ配置されます。
### プルCDN
プルCDNでは一人目のユーザーがリクエストした時に、新しいコンテンツをサービスのサーバーから取得します。コンテンツは自分のサーバーに保存して、CDNを指すURLを書き換えます。結果として、CDNにコンテンツがキャッシュされるまではリクエスト処理が遅くなります。
[time-to-live (TTL)](https://en.wikipedia.org/wiki/Time_to_live) はコンテンツがどれだけの期間キャッシュされるかを規定します。プルCDNはCDN 上でのストレージスペースを最小化しますが、有効期限が切れたファイルが更新前にプルされてしまうことで冗長なトラフィックに繋がってしまう可能性があります。
大規模なトラフィックのあるサイトではプルCDNが相性がいいでしょう。というのも、トラフィックの大部分は最近リクエストされ、CDNに残っているコンテンツにアクセスするものであることが多いからです。
### 欠点: CDN
* CDNのコストはトラフィック量によって変わります。もちろん、CDNを使わない場合のコストと比較するべきでしょう。
* TTLが切れる前にコンテンツが更新されると陳腐化する恐れがあります。
* CDNでは静的コンテンツがCDNを指すようにURLを更新する必要があります。
### その他の参考資料、ページ
* [グローバルに分散されたコンテンツデリバリーネットワーク](http://repository.cmu.edu/cgi/viewcontent.cgi?article=2112&context=compsci)
* [プッシュCDNとプルCDNの違い](http://www.travelblogadvice.com/technical/the-differences-between-push-and-pull-cdns/)
* [Wikipedia](https://en.wikipedia.org/wiki/Content_delivery_network)
## ロードバランサー
<p align="center">
<img src="http://i.imgur.com/h81n9iK.png"/>
<br/>
<i><a href=http://horicky.blogspot.com/2010/10/scalable-system-design-patterns.html>Source: Scalable system design patterns</a></i>
</p>
ロードバランサーは入力されるクライアントのリクエストをアプリケーションサーバーやデータベースへと分散させる。どのケースでもロードバランサーはサーバー等計算リソースからのレスポンスを適切なクライアントに返す。ロードバランサーは以下のことに効果的です:
* リクエストが状態の良くないサーバーに行くのを防ぐ
* リクエストを過剰に送るのを防ぐ
* 特定箇所の欠陥でサービスが落ちることを防ぐ
ロードバランサーは (費用の高い) ハードウェアもしくはHAProxyなどのソフトウェアで実現できる。
他の利点としては:
* **SSL termination** - 入力されるリクエストを解読する、また、サーバーレスポンスを暗号化することでバックエンドのサーバーがこのコストが高くつきがちな処理を請け負わなくていいように肩代わりします。
* [X.509 certificates](https://en.wikipedia.org/wiki/X.509) をそれぞれのサーバーにインストールする必要をなくします
* **セッション管理** - クッキーを取り扱うウェブアプリがセッション情報を保持していない時などに、特定のクライアントのリクエストを同じインスタンスへと流します。
障害に対応するために、[アクティブ・パッシブ](#アクティブパッシブ) もしくは [アクティブ・アクティブ](#アクティブアクティブ) モードのどちらにおいても、複数のロードバランサーを配置するのが一般的です。
ロードバランサーは以下のような種々のメトリックを用いてトラフィックルーティングを行うことができます:
* ランダム
* Least loaded
* セッション/クッキー
* [ラウンドロビンもしくは加重ラウンドロビン](http://g33kinfo.com/info/archives/2657)
* [Layer 4](#layer-4-ロードバランシング)
* [Layer 7](#layer-7-ロードバランシング)
### Layer 4 ロードバランシング
Layer 4 ロードバランサーは [トランスポートレイヤー](#通信) を参照してどのようにリクエストを配分するか判断します。一般的に、トランスポートレイヤーとしては、ソース、送信先IPアドレス、ヘッダーに記述されたポート番号が含まれますが、パケットの中身のコンテンツは含みません。 Layer 4 ロードバランサーはネットワークパケットを上流サーバーへ届け、上流サーバーから配信することでネットワークアドレス変換 [Network Address Translation (NAT)](https://www.nginx.com/resources/glossary/layer-4-load-balancing/) を実現します。
### Layer 7 ロードバランシング
Layer 7 ロードバランサーは [アプリケーションレイヤー](#通信) を参照してどのようにリクエストを配分するか判断します。ヘッダー、メッセージ、クッキーなどのコンテンツのことです。Layer 7 ロードバランサーはネットワークトラフィックの終端を受け持ち メッセージを読み込み、ロードバランシングの判断をし、選択したサーバーとの接続を繋ぎます。例えば layer 7 ロードバランサーは動画のトラフィックを直接、そのデータをホストしているサーバーにつなぐと同時に、決済処理などのより繊細なトラフィックをセキュリティ強化されたサーバーに流すということもできる。
柔軟性とのトレードオフになりますが、 layer 4 ロードバランサーではLayer 7ロードバランサーよりも所要時間、計算リソースを少なく済ませることができます。ただし、昨今の汎用ハードウェアではパフォーマンスは最小限のみしか発揮できないでしょう。
### 水平スケーリング
ロードバランサーでは水平スケーリングによってパフォーマンスと可用性を向上させることができます。手頃な汎用マシンを追加することによってスケールアウトさせる方が、一つのサーバーをより高価なマシンにスケールアップする(**垂直スケーリング**)より費用対効果も高くなり、結果的に可用性も高くなります。また、汎用ハードウェアを扱える人材を雇う方が、特化型の商用ハードウェアを扱える人材を雇うよりも簡単でしょう。
#### 欠点: 水平スケーリング
* 水平的にスケーリングしていくと、複雑さが増す上に、サーバーのクローニングが必要になる。
* サーバーはステートレスである必要がある: ユーザーに関連するセッションや、プロフィール写真などのデータを持ってはいけない
* セッションは一元的な[データベース](#データベース) (SQL、 NoSQL)などのデータストアにストアされるか [キャッシュ](#キャッシュ) (Redis、 Memcached)に残す必要があります。
* キャッシュやデータベースなどの下流サーバーは上流サーバーがスケールアウトするにつれてより多くの同時接続を保たなければなりません。
### 欠点: ロードバランサー
* ロードバランサーはリソースが不足していたり、設定が適切でない場合、システム全体のボトルネックになる可能性があります。
* 単一障害点を除こうとしてロードバランサーを導入した結果、複雑さが増してしまうことになります。
* ロードバランサーが一つだけだとそこが単一障害点になってしまいます。一方で、ロードバランサーを複数にすると、さらに複雑さが増してしまいます。
### その他の参考資料、ページ
* [NGINX アーキテクチャ](https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/)
* [HAProxy アーキテクチャガイド](http://www.haproxy.org/download/1.2/doc/architecture.txt)
* [スケーラビリティ](http://www.lecloud.net/post/7295452622/scalability-for-dummies-part-1-clones)
* [Wikipedia](https://en.wikipedia.org/wiki/Load_balancing_(computing))
* [Layer 4 ロードバランシング](https://www.nginx.com/resources/glossary/layer-4-load-balancing/)
* [Layer 7 ロードバランシング](https://www.nginx.com/resources/glossary/layer-7-load-balancing/)
* [ELB listener config](http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-listener-config.html)
## リバースプロキシ(webサーバー)
<p align="center">
<img src="http://i.imgur.com/n41Azff.png"/>
<br/>
<i><a href=https://upload.wikimedia.org/wikipedia/commons/6/67/Reverse_proxy_h2g2bob.svg>Source: Wikipedia</a></i>
<br/>
</p>
リバースプロキシサーバーは内部サービスをまとめて外部に統一されたインターフェースを提供するウェブサーバーです。クライアントからのリクエストはそれに対応するサーバーに送られて、その後レスポンスをリバースプロキシがクライアントに返します。
他には以下のような利点があります:
* **より堅牢なセキュリティ** - バックエンドサーバーの情報を隠したり、IPアドレスをブラックリスト化したり、クライアントごとの接続数を制限したりできます。
* **スケーラビリティや柔軟性が増します** - クライアントはリバースプロキシのIPしか見ないので、裏でサーバーをスケールしたり、設定を変えやすくなります。
* **SSL termination** - 入力されるリクエストを解読し、サーバーのレスポンスを暗号化することでサーバーがこのコストのかかりうる処理をしなくて済むようになります。
* [X.509 証明書](https://en.wikipedia.org/wiki/X.509) を各サーバーにインストールする必要がなくなります。
* **圧縮** - サーバーレスポンスを圧縮できます
* **キャッシング** - キャッシュされたリクエストに対して、レスポンスを返します
* **静的コンテンツ** - 静的コンテンツを直接送信することができます。
* HTML/CSS/JS
* 写真
* 動画
* などなど
### ロードバランサー vs リバースプロキシ
* 複数のサーバーがある時にはロードバランサーをデプロイすると役に立つでしょう。 しばしば、ロードバランサーは同じ機能を果たすサーバー群へのトラフィックを捌きます。
* リバースプロキシでは、上記に述べたような利点を、単一のウェブサーバーやアプリケーションレイヤーに対しても示すことができます。
* NGINX や HAProxy などの技術はlayer 7 リバースプロキシとロードバランサーの両方をサポートします。
### 欠点: リバースプロキシ
* リバースプロキシを導入するとシステムの複雑性が増します。
* 単一のリバースプロキシは単一障害点になりえます。一方で、複数のリバースプロキシを導入すると(例: [フェイルオーバー](https://en.wikipedia.org/wiki/Failover)) 複雑性はより増します。
### その他の参考資料、ページ
* [リバースプロキシ vs ロードバランサー](https://www.nginx.com/resources/glossary/reverse-proxy-vs-load-balancer/)
* [NGINX アーキテクチャ](https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/)
* [HAProxy アーキテクチャ ガイド](http://www.haproxy.org/download/1.2/doc/architecture.txt)
* [Wikipedia](https://en.wikipedia.org/wiki/Reverse_proxy)
## アプリケーション層
<p align="center">
<img src="http://i.imgur.com/yB5SYwm.png"/>
<br/>
<i><a href=http://lethain.com/introduction-to-architecting-systems-for-scale/#platform_layer>Source: Intro to architecting systems for scale</a></i>
</p>
ウェブレイヤーをアプリケーション層 (プラットフォーム層とも言われる) と分離することでそれぞれの層を独立にスケール、設定することができるようになります。新しいAPIをアプリケーション層に追加する際に、不必要にウェブサーバーを追加する必要がなくなります。
**単一責任の原則** では、小さい自律的なサービスが協調して動くように提唱しています。小さいサービスの小さいチームが急成長のためにより積極的な計画を立てられるようにするためです。
アプリケーション層は[非同期処理](#非同期処理)もサポートします。
### マイクロサービス
独立してデプロイできる、小規模なモジュール様式である[マイクロサービス](https://en.wikipedia.org/wiki/Microservices)もこの議論に関係してくる技術でしょう。それぞれのサービスは独自のプロセスを処理し、明確で軽量なメカニズムで通信して、その目的とする機能を実現します。<sup><a href=https://smartbear.com/learn/api-design/what-are-microservices>1</a></sup>
例えばPinterestでは以下のようなマイクロサービスに分かれています。ユーザープロフィール、フォロワー、フィード、検索、写真アップロードなどです。
### サービスディスカバリー
[Consul](https://www.consul.io/docs/index.html)、 [Etcd](https://coreos.com/etcd/docs/latest)、 [Zookeeper](http://www.slideshare.net/sauravhaloi/introduction-to-apache-zookeeper) などのシステムでは、登録されているサービスの名前、アドレス、ポートの情報を監視することで、サービス同士が互いを見つけやすくしています。サービスの完全性の確認には [Health checks](https://www.consul.io/intro/getting-started/checks.html) が便利で、これには [HTTP](#hypertext-transfer-protocol-http) エンドポイントがよく使われます。 Consul と Etcd のいずれも組み込みの [key-value store](#キーバリューストア) を持っており、設定データや共有データなどのデータを保存しておくことに使われます。
### 欠点: アプリケーション層
* アーキテクチャ、運用、そしてプロセスを考慮すると、緩く結び付けられたアプリケーション層を追加するには、モノリシックなシステムとは異なるアプローチが必要です。
* マイクロサービスはデプロイと運用の点から見ると複雑性が増すことになります。
### その他の参考資料、ページ
* [スケールするシステムアーキテクチャを設計するためのイントロ](http://lethain.com/introduction-to-architecting-systems-for-scale)
* [システム設計インタビューを紐解く](http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview)
* [サービス指向アーキテクチャ](https://en.wikipedia.org/wiki/Service-oriented_architecture)
* [Zookeeperのイントロダクション](http://www.slideshare.net/sauravhaloi/introduction-to-apache-zookeeper)
* [マイクロサービスを作るために知っておきたいこと](https://cloudncode.wordpress.com/2016/07/22/msa-getting-started/)
## データベース
<p align="center">
<img src="http://i.imgur.com/Xkm5CXz.png"/>
<br/>
<i><a href=https://www.youtube.com/watch?v=w95murBkYmU>Source: Scaling up to your first 10 million users</a></i>
</p>
### リレーショナルデータベースマネジメントシステム (RDBMS)
SQLなどのリレーショナルデータベースはテーブルに整理されたデータの集合である。
**ACID** はリレーショナルデータベースにおける[トランザクション](https://en.wikipedia.org/wiki/Database_transaction)のプロパティの集合である
* **不可分性** - それぞれのトランザクションはあるかないかのいずれかである
* **一貫性** - どんなトランザクションもデータベースをある確かな状態から次の状態に遷移させる。
* **独立性** - 同時にトランザクションを処理することは、連続的にトランザクションを処理するのと同じ結果をもたらす。
* **永続性** - トランザクションが処理されたら、そのように保存される
リレーショナルデータベースをスケールさせるためにはたくさんの技術がある: **マスター・スレーブ レプリケーション**、 **マスター・マスター レプリケーション**、 **federation****シャーディング**、 **非正規化**、 そして **SQL チューニング**
#### マスタースレーブ レプリケーション
マスターデータベースが読み取りと書き込みを処理し、書き込みを一つ以上のスレーブデータベースに複製します。スレーブデータベースは読み取りのみを処理します。スレーブデータベースは木構造のように追加のスレーブにデータを複製することもできます。マスターデータベースがオフラインになった場合には、いずれかのスレーブがマスターに昇格するか、新しいマスターデータベースが追加されるまでは読み取り専用モードで稼働します。
<p align="center">
<img src="http://i.imgur.com/C9ioGtn.png"/>
<br/>
<i><a href=http://www.slideshare.net/jboner/scalability-availability-stability-patterns/>Source: Scalability, availability, stability, patterns</a></i>
</p>
##### 欠点: マスタースレーブ レプリケーション
* スレーブをマスターに昇格させるには追加のロジックが必要になる。
* マスタースレーブ レプリケーション、マスターマスター レプリケーションの **両方** の欠点は[欠点: レプリケーション](#欠点-マスタースレーブ-レプリケーション)を参照
#### マスターマスター レプリケーション
いずれのマスターも読み取り書き込みの両方に対応する。書き込みに関してはそれぞれ協調する。いずれかのマスターが落ちても、システム全体としては読み書き両方に対応したまま運用できる。
<p align="center">
<img src="http://i.imgur.com/krAHLGg.png"/>
<br/>
<i><a href=http://www.slideshare.net/jboner/scalability-availability-stability-patterns/>Source: Scalability, availability, stability, patterns</a></i>
</p>
##### 欠点: マスターマスター レプリケーション
* ロードバランサーを導入するか、アプリケーションロジックを変更することでどこに書き込むかを指定しなければならない。
* 大体のマスターマスターシステムは、一貫性が緩いACID原理を守っていないもしくは、同期する時間がかかるために書き込みのレイテンシーが増加してしまっている。
* 書き込みノードが追加され、レイテンシーが増加するにつれ書き込みの衝突の可能性が増える。
* マスタースレーブ レプリケーション、マスターマスター レプリケーションの **両方** の欠点は[欠点: レプリケーション](#欠点-マスタースレーブ-レプリケーション) を参照
##### 欠点: レプリケーション
* 新しいデータ書き込みを複製する前にマスターが落ちた場合にはそのデータが失われてしまう可能性がある。
* 書き込みは読み取りレプリカにおいてリプレイされる。書き込みが多い場合、複製ノードが書き込みの処理のみで行き詰まって、読み取りの処理を満足に行えない可能性がある。
* 読み取りスレーブノードの数が多ければ多いほど、複製しなければならない数も増え、複製時間が伸びてしまいます。
* システムによっては、マスターへの書き込みはマルチスレッドで並列処理できる一方、スレーブへの複製は単一スレッドで連続的に処理しなければならない場合があります。
* レプリケーションでは追加のハードウェアが必要になり、複雑性も増します。
##### その他の参考資料、ページ: レプリケーション
* [スケーラビリティ、 可用性、 スタビリティ パターン](http://www.slideshare.net/jboner/scalability-availability-stability-patterns/)
* [マルチマスター レプリケーション](https://en.wikipedia.org/wiki/Multi-master_replication)
#### Federation
<p align="center">
<img src="http://i.imgur.com/U3qV33e.png"/>
<br/>
<i><a href=https://www.youtube.com/watch?v=w95murBkYmU>Source: Scaling up to your first 10 million users</a></i>
</p>
フェデレーション (もしくは機能分割化とも言う) はデータベースを機能ごとに分割する。例えば、モノリシックな単一データベースの代わりに、データベースを **フォーラム**、 **ユーザー**、 **プロダクト** のように三つにすることで、データベース一つあたりの書き込み・読み取りのトラフィックが減り、その結果レプリケーションのラグも短くなります。データベースが小さくなることで、メモリーに収まるデータが増えます。キャッシュの局所性が高まるため、キャッシュヒット率も上がります。単一の中央マスターで書き込みを直列化したりしないため、並列で書き込みを処理することができ、スループットの向上が期待できます。
##### 欠点: federation
* 大規模な処理やテーブルを要するスキーマの場合、フェデレーションは効果的とは言えないでしょう。
* どのデータベースに読み書きをするのかを指定するアプリケーションロジックを更新しなければなりません。
* [server link](http://stackoverflow.com/questions/5145637/querying-data-by-joining-two-tables-in-two-database-on-different-servers)で二つのデータベースからのデータを連結するのはより複雑になるでしょう。
* フェデレーションでは追加のハードウェアが必要になり、複雑性も増します。
##### その他の参考資料、ページ: federation
* [Scaling up to your first 10 million users](https://www.youtube.com/watch?v=w95murBkYmU)
#### シャーディング
<p align="center">
<img src="http://i.imgur.com/wU8x5Id.png"/>
<br/>
<i><a href=http://www.slideshare.net/jboner/scalability-availability-stability-patterns/>Source: Scalability, availability, stability, patterns</a></i>
</p>
シャーディングでは異なるデータベースにそれぞれがデータのサブセット断片のみを持つようにデータを分割します。ユーザーデータベースを例にとると、ユーザー数が増えるにつれてクラスターにはより多くの断片が加えられることになります。
[federation](#federation)の利点に似ていて、シャーディングでは読み書きのトラフィックを減らし、レプリケーションを減らし、キャッシュヒットを増やすことができます。インデックスサイズも減らすことができます。一般的にはインデックスサイズを減らすと、パフォーマンスが向上しクエリ速度が速くなります。なにがしかのデータを複製する機能がなければデータロスにつながりますが、もし、一つのシャードが落ちても、他のシャードが動いていることになります。フェデレーションと同じく、単一の中央マスターが書き込みの処理をしなくても、並列で書き込みを処理することができ、スループットの向上が期待できます。
ユーザーテーブルをシャードする一般的な方法は、ユーザーのラストネームイニシャルでシャードするか、ユーザーの地理的配置でシャードするなどです。
##### 欠点: シャーディング
* シャードに対応するようにアプリケーションロジックを変更しなければなりません。結果としてSQLクエリが複雑になります。
* シャードではデータ配分がいびつになってしまう可能性があります。例えば、標準ユーザーの集合を持つシャードがある場合、そのシャードが他のシャードよりも重い負荷を負うことになります。
* リバランシングをすると複雑性がより増します。[consistent hashing](http://www.paperplanes.de/2011/12/9/the-magic-of-consistent-hashing.html) に基づいたシャーディングでは、通信データを削減することもできます。
* 複数のシャードからのデータを連結するのはより複雑です。
* シャーディングでは追加のハードウェアが必要になり、複雑性も増します。
##### その他の参考資料、ページ: シャーディング
* [シャードの登場](http://highscalability.com/blog/2009/8/6/an-unorthodox-approach-to-database-design-the-coming-of-the.html)
* [シャードデータベースアーキテクチャ](https://en.wikipedia.org/wiki/Shard_(database_architecture))
* [Consistent hashing](http://www.paperplanes.de/2011/12/9/the-magic-of-consistent-hashing.html)
#### 非正規化
非正規化では、書き込みのパフォーマンスをいくらか犠牲にして読み込みのパフォーマンスを向上させようとします。計算的に重いテーブルの結合などをせずに、複数のテーブルに冗長なデータのコピーが書き込まれるのを許容します。いくつかのRDBMS例えば、[PostgreSQL](https://en.wikipedia.org/wiki/PostgreSQL) やOracleはこの冗長な情報を取り扱い、一貫性を保つための[materialized views](https://en.wikipedia.org/wiki/Materialized_view) という機能をサポートしています。
[フェデレーション](#federation) や [シャーディング](#シャーディング)などのテクニックによってそれぞれのデータセンターに分配されたデータを合一させることはとても複雑な作業です。非正規化によってそのような複雑な処理をしなくて済むようになります。
多くのシステムで、100対1あるいは1000対1くらいになるくらい読み取りの方が、書き込みのトラフィックよりも多いことでしょう。読み込みを行うために、複雑なデータベースのジョイン処理が含まれるものは計算的に高価につきますし、ディスクの処理時間で膨大な時間を費消してしまうことになります。
##### 欠点: 非正規化
* データが複製される。
* 冗長なデータの複製が同期されるように制約が存在し、そのことでデータベース全体の設計が複雑化する。
* 非正規化されたデータベースは過大な書き込みを処理しなければならない場合、正規化されているそれよりもパフォーマンスにおいて劣る可能性がある。
###### その他の参考資料、ページ: 非正規化
* [Denormalization](https://en.wikipedia.org/wiki/Denormalization)
#### SQLチューニング
SQLチューニングは広範な知識を必要とする分野で多くの [](https://www.amazon.com/s/ref=nb_sb_noss_2?url=search-alias%3Daps&field-keywords=sql+tuning) が書かれています。
ボトルネックを明らかにし、シミュレートする上で、 **ベンチマーク** を定め、 **プロファイル** することはとても重要です。
* **ベンチマーク** - [ab](http://httpd.apache.org/docs/2.2/programs/ab.html)などのツールを用いて、高負荷の状況をシミュレーションしてみましょう。
* **プロファイル** - [slow query log](http://dev.mysql.com/doc/refman/5.7/en/slow-query-log.html) などのツールを用いて、パフォーマンス状況の確認をしましょう。
ベンチマークとプロファイルをとることで以下のような効率化の選択肢をとることになるでしょう。
##### スキーマを絞る
* MySQLはアクセス速度向上のため、ディスク上の連続したブロックへデータを格納しています。
* 長さの決まったフィールドに対しては `VARCHAR` よりも `CHAR` を使うようにしましょう。
* `CHAR` の方が効率的に速くランダムにデータにアクセスできます。 一方、 `VARCHAR` では次のデータに移る前にデータの末尾を検知しなければならないために速度が犠牲になります。
* ブログの投稿など、大きなテキストには TEXT を使いましょう。 TEXT ではブーリアン型の検索も可能です。 TEXT フィールドには、テキストブロックが配置されている、ディスク上の場所へのポインターが保存されます。
* 2の32乗や40億以下を超えない程度の大きな数には INT を使いましょう。
* 通貨に関しては小数点表示上のエラーを避けるために `DECIMAL` を使いましょう。
* 大きな `BLOBS` を保存するのは避けましょう。どこからそのオブジェクトを取ってくることができるかの情報を保存しましょう。
* `VARCHAR(255)` は8ビットで数えられる最大の文字数です。一部のDBMSでは、1バイトの利用効率を最大化するためにこの文字数がよく使われます。
* [検索性能向上のため](http://stackoverflow.com/questions/1017239/how-do-null-values-affect-performance-in-a-database-search) 、可能であれば `NOT NULL` 制約を設定しましょう。
##### インデックスを効果的に用いる
* クエリ(`SELECT`、 `GROUP BY``ORDER BY``JOIN`) の対象となる列にインデックスを使うことで速度を向上できるかもしれません。
* インデックスは通常、平衡探索木である[B木](https://en.wikipedia.org/wiki/B-tree)の形で表されます。B木によりデータは常にソートされた状態になります。また検索、順次アクセス、挿入、削除を対数時間で行えます。
* インデックスを配置することはデータをメモリーに残すことにつながりより容量を必要とします。
* インデックスの更新も必要になるため書き込みも遅くなります。
* 大量のデータをロードする際には、インデックスを切ってからデータをロードして再びインデックスをビルドした方が速いことがあります。
##### 高負荷なジョインを避ける
* パフォーマンス上必要なところには[非正規化](#非正規化)を適用する
##### テーブルのパーティション
* テーブルを分割し、ホットスポットを独立したテーブルに分離してメモリーに乗せられるようにする。
##### クエリキャッシュを調整する
* 場合によっては[クエリキャッシュ](http://dev.mysql.com/doc/refman/5.7/en/query-cache) が[パフォーマンス問題](https://www.percona.com/blog/2014/01/28/10-mysql-performance-tuning-settings-after-installation/) を引き起こす可能性がある
##### その他の参考資料、ページ: SQLチューニング
* [MySQLクエリを最適化するためのTips](http://20bits.com/article/10-tips-for-optimizing-mysql-queries-that-dont-suck)
* [VARCHAR(255)をやたらよく見かけるのはなんで?](http://stackoverflow.com/questions/1217466/is-there-a-good-reason-i-see-varchar255-used-so-often-as-opposed-to-another-l)
* [null値はどのようにパフォーマンスに影響するのか](http://stackoverflow.com/questions/1017239/how-do-null-values-affect-performance-in-a-database-search)
* [Slow query log](http://dev.mysql.com/doc/refman/5.7/en/slow-query-log.html)
### NoSQL
NoSQL は **key-value store****document-store****wide column store**、 もしくは **graph database**によって表現されるデータアイテムの集合です。データは一般的に正規化されておらず、アプリケーション側でジョインが行われます。大部分のNoSQLは真のACIDトランザクションを持たず、 [結果整合性](#結果整合性) 的な振る舞いの方を好みます。
**BASE** はしばしばNoSQLデータベースのプロパティを説明するために用いられます。[CAP Theorem](#cap-理論) と対照的に、BASEは一貫性よりも可用性を優先します。
* **Basically available** - システムは可用性を保証します。
* **Soft state** - システムの状態は入力がなくても時間経過とともに変化する可能性があります。
* **結果整合性** - システム全体は時間経過とともにその間に入力がないという前提のもと、一貫性が達成されます。
[SQLかNoSQLか](#sqlかnosqlか) を選択するのに加えて、どのタイプのNoSQLがどの使用例に最も適するかを理解するのはとても有益です。このセクションでは **キーバリューストア**、 **ドキュメントストア**、 **ワイドカラムストア**、 と **グラフデータベース** について触れていきます。
#### キーバリューストア
> 概要: ハッシュテーブル
キーバリューストアでは一般的にO(1)の読み書きができ、それらはメモリないしSSDで裏付けられています。データストアはキーを [辞書的順序](https://en.wikipedia.org/wiki/Lexicographical_order) で保持することでキーの効率的な取得を可能にしています。キーバリューストアではメタデータを値とともに保持することが可能です。
キーバリューストアはハイパフォーマンスな挙動が可能で、単純なデータモデルやインメモリーキャッシュレイヤーなどのデータが急速に変わる場合などに使われます。単純な処理のみに機能が制限されているので、追加の処理機能が必要な場合にはその複雑性はアプリケーション層に載せることになります。
キーバリューストアはもっと複雑なドキュメントストアや、グラフデータベースなどの基本です。
##### その他の参考資料、ページ: キーバリューストア
* [キーバリューデータベース](https://en.wikipedia.org/wiki/Key-value_database)
* [キーバリューストアの欠点](http://stackoverflow.com/questions/4056093/what-are-the-disadvantages-of-using-a-key-value-table-over-nullable-columns-or)
* [Redisアーキテクチャ](http://qnimate.com/overview-of-redis-architecture/)
* [メムキャッシュアーキテクチャ](https://www.adayinthelifeof.nl/2011/02/06/memcache-internals/)
#### ドキュメントストア
> 概要: ドキュメントがバリューとして保存されたキーバリューストア
ドキュメントストアはオブジェクトに関する全ての情報を持つドキュメント(XML、 JSON、 binaryなど)を中心に据えたシステムです。ドキュメントストアでは、ドキュメント自身の内部構造に基づいた、APIもしくはクエリ言語を提供します。 *メモ:多くのキーバリューストアでは、値のメタデータを扱う機能を含んでいますが、そのことによって二つドキュメントストアとの境界線が曖昧になってしまっています。*
以上のことを実現するために、ドキュメントはコレクション、タグ、メタデータやディレクトリなどとして整理されています。ドキュメント同士はまとめてグループにできるものの、それぞれで全く異なるフィールドを持つ可能性があります。
[MongoDB](https://www.mongodb.com/mongodb-architecture) や [CouchDB](https://blog.couchdb.org/2016/08/01/couchdb-2-0-architecture/) などのドキュメントストアも、複雑なクエリを処理するためのSQLのような言語を提供しています。[DynamoDB](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/decandia07dynamo.pdf) はキーバリューとドキュメントの両方をサポートしています。
ドキュメントストアは高い柔軟性を担保するので、頻繁に変化するデータを扱う時に用いられます。
##### その他の参考資料、ページ: ドキュメントストア
* [ドキュメント指向 データベース](https://en.wikipedia.org/wiki/Document-oriented_database)
* [MongoDB アーキテクチャ](https://www.mongodb.com/mongodb-architecture)
* [CouchDB アーキテクチャ](https://blog.couchdb.org/2016/08/01/couchdb-2-0-architecture/)
* [Elasticsearch アーキテクチャ](https://www.elastic.co/blog/found-elasticsearch-from-the-bottom-up)
#### ワイドカラムストア
<p align="center">
<img src="http://i.imgur.com/n16iOGk.png"/>
<br/>
<i><a href=http://blog.grio.com/2015/11/sql-nosql-a-brief-history.html>Source: SQL & NoSQL, a brief history</a></i>
</p>
> 概要: ネストされたマップ `カラムファミリー<行キー、 カラム<ColKey、 Value、 Timestamp>>`
ワイドカラムストアのデータの基本単位はカラムネーム・バリューのペアです。それぞれのカラムはカラムファミリーとしてSQLテーブルのようにグループ化することができます。スーパーカラムファミリーはカラムファミリーの集合です。それぞれのカラムには行キーでアクセスすることができます。同じ行キーを持つカラムは同じ行として認識されます。それぞれの値は、バージョン管理とコンフリクトが起きた時のために、タイムスタンプを含みます。
Googleは[Bigtable](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/chang06bigtable.pdf)を初のワイドカラムストアとして発表しました。それがオープンソースでHadoopなどでよく使われる[HBase](https://www.mapr.com/blog/in-depth-look-hbase-architecture) やFacebookによる[Cassandra](http://docs.datastax.com/en/archived/cassandra/2.0/cassandra/architecture/architectureIntro_c.html) などのプロジェクトに影響を与えました。BigTable、HBaseやCassandraなどのストアはキーを辞書形式で保持することで選択したキーレンジでのデータ取得を効率的にします。
ワイドカラムストアは高い可用性とスケーラビリティを担保します。これらはとても大規模なデータセットを扱うことによく使われます。
##### その他の参考資料、ページ: ワイドカラムストア
* [SQL & NoSQL簡単に歴史をさらう](http://blog.grio.com/2015/11/sql-nosql-a-brief-history.html)
* [Bigtable アーキテクチャ](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/chang06bigtable.pdf)
* [HBase アーキテクチャ](https://www.mapr.com/blog/in-depth-look-hbase-architecture)
* [Cassandra アーキテクチャ](http://docs.datastax.com/en/archived/cassandra/2.0/cassandra/architecture/architectureIntro_c.html)
#### グラフデータベース
<p align="center">
<img src="http://i.imgur.com/fNcl65g.png"/>
<br/>
<i><a href=https://en.wikipedia.org/wiki/File:GraphDatabase_PropertyGraph.png>Source: Graph database</a></i>
</p>
> 概要: グラフ
グラフデータベースでは、それぞれのノードがレコードで、それぞれのアークは二つのノードを繋ぐ関係性として定義されます。グラフデータベースは多数の外部キーや多対多などの複雑な関係性を表すのに最適です。
グラフデータベースはSNSなどのサービスの複雑な関係性モデルなどについて高いパフォーマンスを発揮します。比較的新しく、まだ一般的には用いられていないので、開発ツールやリソースを探すのが他の方法に比べて難しいかもしれません。多くのグラフは[REST APIs](#representational-state-transfer-rest)を通じてのみアクセスできます。
##### その他の参考資料、ページ: グラフ
* [Graphデータベース](https://en.wikipedia.org/wiki/Graph_database)
* [Neo4j](https://neo4j.com/)
* [FlockDB](https://blog.twitter.com/2010/introducing-flockdb)
#### その他の参考資料、ページ: NoSQL
* [基本用語の説明](http://stackoverflow.com/questions/3342497/explanation-of-base-terminology)
* [NoSQLデータベースについて調査と選択ガイド](https://medium.com/baqend-blog/nosql-databases-a-survey-and-decision-guidance-ea7823a822d#.wskogqenq)
* [スケーラビリティ](http://www.lecloud.net/post/7994751381/scalability-for-dummies-part-2-database)
* [NoSQLのイントロダクション](https://www.youtube.com/watch?v=qI_g07C_Q5I)
* [NoSQLパターン](http://horicky.blogspot.com/2009/11/nosql-patterns.html)
### SQLかNoSQLか
<p align="center">
<img src="http://i.imgur.com/wXGqG5f.png"/>
<br/>
<i><a href=https://www.infoq.com/articles/Transition-RDBMS-NoSQL/>Source: Transitioning from RDBMS to NoSQL</a></i>
</p>
**SQL** を選ぶ理由:
* 構造化されたデータ
* 厳格なスキーマ
* リレーショナルデータ
* 複雑なジョインをする必要性
* トランザクション
* スケールする際のパターンが明確なとき
* 開発者の数、コミュニティ、コード等がより充実している
* インデックスによるデータ探索はとても速い
**NoSQL** を選ぶ理由:
* 準構造化されたデータ
* ダイナミックないし、フレキシブルなスキーマ
* ノンリレーショナルなデータ
* 複雑なジョインをする必要がない
* データの多くのTB (もしくは PB) を保存する
* 集中的、大規模なデータ負荷に耐えられる
* IOPSについては極めて高いスループットを示す
NoSQLに適するサンプルデータ:
* 急激なクリックストリームやログデータの収集
* リーダーボードやスコアリングデータ
* ショッピングカートなどの一時的情報
* 頻繁にアクセスされる ('ホットな') テーブル
* メタデータやルックアップテーブル
##### その他の参考資料、ページ:  SQLもしくはNoSQL
* [最初の1000万ユーザーにスケールアップするために](https://www.youtube.com/watch?v=w95murBkYmU)
* [SQLとNoSQLの違い](https://www.sitepoint.com/sql-vs-nosql-differences/)
## キャッシュ
<p align="center">
<img src="http://i.imgur.com/Q6z24La.png"/>
<br/>
<i><a href=http://horicky.blogspot.com/2010/10/scalable-system-design-patterns.html>Source: Scalable system design patterns</a></i>
</p>
キャッシュはページの読み込み時間を削減し、サーバーやデータベースへの負荷を低減することができます。このモデルでは、実際の処理を保存するために、ディスパッチャーがまず以前にリクエストが送信されたかどうかを確認し、直前の結果を受け取ります。
データベースはそのパーティションに渡って統合された読み取り書き込みの分配を要求しますが、人気アイテムはその分配を歪めてシステム全体のボトルネックになってしまうことがあります。データベースの前にキャッシュを差し込むことでこのように、均一でない負荷やトラフィックの急激な増加を吸収することができます。
### クライアントキャッシング
キャッシュはOSやブラウザーなどのクライアントサイド、[サーバーサイド](#リバースプロキシwebサーバー) もしくは独立のキャッシュレイヤーに設置することができます。
### CDNキャッシング
[CDN](#コンテンツデリバリーネットワークcontent-delivery-network) もキャッシュの一つとして考えることができます。
### Webサーバーキャッシング
[リバースプロキシ](#リバースプロキシwebサーバー) や [Varnish](https://www.varnish-cache.org/) などのキャッシュは静的そして動的なコンテンツを直接配信することができます。 webサーバーもリクエストをキャッシュしてアプリケーションサーバーに接続することなしにレスポンスを返すことができます。
### データベースキャッシング
データベースは普通、一般的な使用状況に適するようなキャッシングの設定を初期状態で持っています。この設定を特定の仕様に合わせて調整することでパフォーマンスを向上させることができます。
### アプリケーションキャッシング
メムキャッシュなどのIn-memoryキャッシュやRedisはアプリケーションとデータストレージの間のキーバリューストアです。データはRAMで保持されるため、データがディスクで保存される一般的なデータベースよりもだいぶ速いです。RAM容量はディスクよりも限られているので、[least recently used (LRU)](https://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used)などの[cache invalidation](https://en.wikipedia.org/wiki/Cache_algorithms) アルゴリズムが 'コールド' なエントリを弾き、'ホット' なデータをRAMに保存します。
Redisはさらに以下のような機能を備えています:
* パージステンス設定
* ソート済みセット、リストなどの組み込みデータ構造
キャッシュには様々なレベルのものがありますが、いずれも大きく二つのカテゴリーのいずれかに分類することができます: **データベースクエリ** と **オブジェクト** です:
* 行レベル
* クエリレベル
* Fully-formed serializable objects
* Fully-rendered HTML
一般的に、ファイルベースキャッシングはクローンを作り出してオートスケーリングを難しくしてしまうので避けるべきです。
### データベースクエリレベルでのキャッシング
データベースをクエリする際には必ずクエリをキーとしてハッシュして結果をキャッシュに保存しましょう。この手法はキャッシュ期限切れ問題に悩むことになります:
* 複雑なクエリによりキャッシュされた結果を削除することが困難
* テーブルセルなどのデータ断片が変化した時に、その変化したセルを含むかもしれない全てのキャッシュされたクエリを削除する必要がある。
### オブジェクトレベルでのキャッシング
データをアプリケーションコードでそうするように、オブジェクトとして捉えてみましょう。アプリケーションに、データベースからのデータセットをクラスインスタンスやデータ構造として組み立てさせます。:
* そのデータが変更されたら、オブジェクトをキャッシュから削除すること
* 非同期処理を許容します: ワーカーがキャッシュされたオブジェクトの中で最新のものを集めてきます
何をキャッシュするか:
* ユーザーのセッション
* 完全にレンダーされたウェブページ
* アクテビティストリーム
* ユーザーグラフデータ
### いつキャッシュを更新するか
キャッシュに保存できる容量は限られているため、自分のケースではどのキャッシュ手法が一番いいかは検討する必要があります。
#### キャッシュアサイド
<p align="center">
<img src="http://i.imgur.com/ONjORqk.png"/>
<br/>
<i><a href=http://www.slideshare.net/tmatyashovsky/from-cache-to-in-memory-data-grid-introduction-to-hazelcast>Source: From cache to in-memory data grid</a></i>
</p>
アプリケーションはストレージへの読み書きの処理をします。キャッシュはストレージとは直接やりとりをしません。アプリケーションは以下のことをします:
* キャッシュの中のエントリを参照しますが、結果としてキャッシュミスになります
* データベースからエントリを取得します
* エントリをキャッシュに追加します
* エントリを返します
```python
def get_user(self, user_id):
user = cache.get("user.{0}", user_id)
if user is None:
user = db.query("SELECT * FROM users WHERE user_id = {0}", user_id)
if user is not None:
key = "user.{0}".format(user_id)
cache.set(key, json.dumps(user))
return user
```
[Memcached](https://memcached.org/) は通常このように使われる。
その後のキャッシュデータ読み込みは速いです。キャッシュアサイドはレージーローディングであるとも言われます。リクエストされたデータのみがキャッシュされ、リクエストされていないデータでキャッシュが溢れるのを防止します。
##### 欠点: キャッシュアサイド
* 各キャッシュミスは三つのトリップを呼び出すことになり、体感できるほどの遅延が起きてしまいます。
* データベースのデータが更新されるとキャッシュデータは古いものになってしまいます。time-to-live (TTL)を設定することでキャッシュエントリの更新を強制的に行う、もしくはライトスルーを採用することでこの問題は緩和できます。
* ノードが落ちると、新規の空のノードで代替されることでレイテンシーが増加することになります。
#### ライトスルー
<p align="center">
<img src="http://i.imgur.com/0vBc0hN.png"/>
<br/>
<i><a href=http://www.slideshare.net/jboner/scalability-availability-stability-patterns/>Source: Scalability, availability, stability, patterns</a></i>
</p>
アプリケーションはキャッシュをメインのデータストアとして使い、そこにデータの読み書きを行います。一方、キャッシュはデータベースへの読み書きを担当します。
* アプリケーションはキャッシュにあるエントリを追加・更新します
* キャッシュは同期的にデータストアに書き込みを行います
* エントリを返します
アプリケーションコード:
```
set_user(12345, {"foo":"bar"})
```
キャッシュコード:
```python
def set_user(user_id, values):
user = db.query("UPDATE Users WHERE id = {0}", user_id, values)
cache.set(user_id, user)
```
ライトスルーは書き込み処理のせいで全体としては遅いオペレーションですが、書き込まれたばかりのデータに関する読み込みは速いです。ユーザー側は一般的にデータ更新時の方が読み込み時よりもレイテンシーに許容的です。キャッシュ内のデータは最新版で保たれます。
##### 欠点: ライトスルー
* ノードが落ちたこと、もしくはスケーリングによって新しいノードが作成された時に、新しいノードはデータベース内のエントリーが更新されるまではエントリーをキャッシュしません。キャッシュアサイドとライトスルーを併用することでこの問題を緩和できます。
* 書き込まれたデータの大部分は一度も読み込まれることはありません。このデータはTTLによって圧縮することができます。
#### ライトビハインド (ライトバック)
<p align="center">
<img src="http://i.imgur.com/rgSrvjG.png"/>
<br/>
<i><a href=http://www.slideshare.net/jboner/scalability-availability-stability-patterns/>Source: Scalability, availability, stability, patterns</a></i>
</p>
ライトビハインドではアプリケーションは以下のことをします:
* キャッシュのエントリーを追加・更新します
* データストアへの書き込みを非同期的に行うことで、書き込みパフォーマンスを向上させます。
##### 欠点: ライトビハインド
* キャッシュがデータストア内のコンテンツにヒットする前にキャッシュが落ちるとデータ欠損が起きる可能性があります。
* キャッシュアサイドやライトスルーよりも実装が複雑になります。
#### リフレッシュアヘッド
<p align="center">
<img src="http://i.imgur.com/kxtjqgE.png"/>
<br/>
<i><a href=http://www.slideshare.net/tmatyashovsky/from-cache-to-in-memory-data-grid-introduction-to-hazelcast>Source: From cache to in-memory data grid</a></i>
</p>
期限切れよりも前に、直近でアクセスされた全てのキャッシュエントリを自動的に更新するように設定することができます。
もしどのアイテムが将来必要になるのかを正確に予測することができるのならば、リードスルーよりもレイテンシーを削減することができます。
##### 欠点: リフレッシュアヘッド
* どのアイテムが必要になるかの予測が正確でない場合にはリフレッシュアヘッドがない方がレイテンシーは良いという結果になってしまいます。
### 欠点: キャッシュ
* [cache invalidation](https://en.wikipedia.org/wiki/Cache_algorithms)などを用いて、データベースなどの真のデータとキャッシュの間の一貫性を保つ必要があります。
* Redisやmemcachedを追加することでアプリケーション構成を変更する必要があります。
* Cache invalidationも難しいですがそれに加えて、いつキャッシュを更新するかという複雑な問題にも悩まされることになります。
### その他の参考資料、ページ
* [From cache to in-memory data grid](http://www.slideshare.net/tmatyashovsky/from-cache-to-in-memory-data-grid-introduction-to-hazelcast)
* [スケーラブルなシステムデザインパターン](http://horicky.blogspot.com/2010/10/scalable-system-design-patterns.html)
* [スケールできるシステムを設計するためのイントロダクション](http://lethain.com/introduction-to-architecting-systems-for-scale/)
* [スケーラビリティ、可用性、安定性、パターン](http://www.slideshare.net/jboner/scalability-availability-stability-patterns/)
* [スケーラビリティ](http://www.lecloud.net/post/9246290032/scalability-for-dummies-part-3-cache)
* [AWS ElastiCacheのストラテジー](http://docs.aws.amazon.com/AmazonElastiCache/latest/UserGuide/Strategies.html)
* [Wikipedia](https://en.wikipedia.org/wiki/Cache_(computing))
## 非同期処理
<p align="center">
<img src="http://i.imgur.com/54GYsSx.png"/>
<br/>
<i><a href=http://lethain.com/introduction-to-architecting-systems-for-scale/#platform_layer>Source: Intro to architecting systems for scale</a></i>
</p>
非同期のワークフローはもし、連続的に行われるとリクエスト時間を圧迫してしまうような重い処理を別で処理する手法です。また、定期的にデータを集合させるなどの時間がかかるような処理を前もって処理しておくことにも役立ちます。
### メッセージキュー
メッセージキューはメッセージを受け取り、保存し、配信します。もし、処理がインラインで行うには遅すぎる場合、以下のようなワークフローでメッセージキューを用いるといいでしょう:
* アプリケーションはジョブをキューに配信し、ユーザーにジョブステータスを伝えます。
* ワーカーがジョブキューから受け取って、処理を行い、終了したらそのシグナルを返します。
ユーザーの処理が止まることはなく、ジョブはバックグラウンドで処理されます。この間に、クライアントはオプションとして、タスクが完了したかのように見せるために小規模の処理を行います。例えば、ツイートを投稿するときに、ツイートはすぐにあなたのタイムラインに反映されたように見えますが、そのツイートが実際に全てのフォロワーに配信されるまでにはもう少し時間がかかっているでしょう。
**Redis** はシンプルなメッセージ仲介としてはいいですが、メッセージが失われてしまう可能性があります。
**RabbitMQ** はよく使われていますが、'AMQP'プロトコルに対応して、自前のノードを立てる必要があります。
**Amazon SQS** という選択肢もありますが、レイテンシーが高く、メッセージが重複して配信されてしまう可能性があります。
### タスクキュー
タスクキューはタスクとその関連するデータを受け取り、処理した上でその結果を返します。スケジュール管理をできるほか、バックグラウンドでとても重いジョブをこなすこともできます。
**Celery** はスケジューリングとpythonのサポートがあります。
### バックプレッシャー
もし、キューが拡大しすぎると、メモリーよりもキューの方が大きくなりキャッシュミスが起こり、ディスク読み出しにつながり、パフォーマンスが低下することにつながります。[バックプレッシャー](http://mechanical-sympathy.blogspot.com/2012/05/apply-back-pressure-when-overloaded.html)はキューサイズを制限することで回避することができ、高いスループットを確保しキューにすでにあるジョブについてのレスポンス時間を短縮できます。キューがいっぱいになると、クライアントはサーバービジーもしくはHTTP 503をレスポンスとして受け取りまた後で時間をおいてアクセスするようにメッセージを受け取ります。クライアントは[exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff)などによって後ほど再度時間を置いてリクエストすることができます。
### 欠点: 非同期処理
* キューを用いることで遅延が起こり、複雑さも増すため、あまり重くない計算処理やリアルタイムワークフローにおいては同期処理の方がいいでしょう。
### その他の参考資料、ページ
* [It's all a numbers game](https://www.youtube.com/watch?v=1KRYH75wgy4)
* [オーバーロードした時にバックプレッシャーを適用する](http://mechanical-sympathy.blogspot.com/2012/05/apply-back-pressure-when-overloaded.html)
* [Little's law](https://en.wikipedia.org/wiki/Little%27s_law)
* [メッセージキューとタスクキューの違いとは?](https://www.quora.com/What-is-the-difference-between-a-message-queue-and-a-task-queue-Why-would-a-task-queue-require-a-message-broker-like-RabbitMQ-Redis-Celery-or-IronMQ-to-function)
## 通信
<p align="center">
<img src="http://i.imgur.com/5KeocQs.jpg"/>
<br/>
<i><a href=http://www.escotal.com/osilayer.html>Source: OSI 7 layer model</a></i>
</p>
### Hypertext transfer protocol (HTTP)
HTTP はクライアントとサーバー間でのデータをエンコードして転送するための手法です。リクエスト・レスポンスに関わるプロトコルです。クライアントがリクエストをサーバーに投げ、サーバーがリクエストに関係するコンテンツと完了ステータス情報をレスポンスとして返します。HTTPは自己完結するので、間にロードバランサー、キャッシュ、エンクリプション、圧縮などのどんな中間ルーターが入っても動くようにできています。
基本的なHTTPリクエストはHTTP動詞(メソッド)とリソース(エンドポイント)で成り立っています。以下がよくあるHTTP動詞です。:
| 動詞 | 詳細 | 冪等性* | セーフ | キャッシュできるか |
|---|---|---|---|---|
| GET | リソースを読み取る | Yes | Yes | Yes |
| POST | リソースを作成するもしくはデータを処理するトリガー | No | No | Yes レスポンスが新しい情報を含む場合 |
| PUT | リソースを作成もしくは入れ替える | Yes | No | No |
| PATCH | リソースを部分的に更新する | No | No | Yes レスポンスが新しい情報を含む場合 |
| DELETE | リソースを削除する | Yes | No | No |
*何度呼んでも同じ結果が返ってくること*
HTTPは**TCP** や **UDP** などの低級プロトコルに依存しているアプリケーションレイヤーのプロトコルである。
#### その他の参考資料、ページ: HTTP
* [HTTPってなに?](https://www.nginx.com/resources/glossary/http/)
* [HTTP と TCPの違い](https://www.quora.com/What-is-the-difference-between-HTTP-protocol-and-TCP-protocol)
* [PUT と PATCHの違い](https://laracasts.com/discuss/channels/general-discussion/whats-the-differences-between-put-and-patch?page=1)
### 伝送制御プロトコル (TCP)
<p align="center">
<img src="http://i.imgur.com/JdAsdvG.jpg"/>
<br/>
<i><a href=http://www.wildbunny.co.uk/blog/2012/10/09/how-to-make-a-multi-player-game-part-1/>Source: How to make a multiplayer game</a></i>
</p>
TCPは[IP network](https://en.wikipedia.org/wiki/Internet_Protocol)の上で成り立つ接続プロトコルです。接続は[handshake](https://en.wikipedia.org/wiki/Handshaking)によって開始、解除されます。全ての送信されたパケットは欠損なしで送信先に送信された順番で到達するように以下の方法で保証されています:
* シーケンス番号と[checksum fields](https://en.wikipedia.org/wiki/Transmission_Control_Protocol#Checksum_computation)が全てのパケットに用意されている
* [Acknowledgement](https://en.wikipedia.org/wiki/Acknowledgement_(data_networks))パケットと自動再送信
もし送信者が正しいレスポンスを受け取らなかったとき、パケットを再送信します。複数のタイムアウトがあったとき、接続は解除されます。TCP は[フロー制御](https://en.wikipedia.org/wiki/Flow_control_(data)) と [輻輳制御](https://en.wikipedia.org/wiki/Network_congestion#Congestion_control)も実装しています。これらの機能によって速度は低下し、一般的にUDPよりも非効率な転送手段になっています。
ハイスループットを実現するために、ウェブサーバーはかなり大きな数のTCP接続を開いておくことがあり、そのことでメモリー使用が圧迫されます。ウェブサーバスレッドと例えば[memcached](#memcached) サーバーの間で多数のコネクションを保っておくことは高くつくかもしれません。可能なところではUDPに切り替えるだけでなく[コネクションプーリング](https://en.wikipedia.org/wiki/Connection_pool)なども役立つかもしれません。
TCPは高い依存性を要し、時間制約が厳しくないものに適しているでしょう。ウェブサーバー、データベース情報、SMTP、FTPやSSHなどの例に適用されます。
以下の時にUDPよりもTCPを使うといいでしょう:
* 全てのデータが欠損することなしに届いてほしい
* ネットワークスループットの最適な自動推測をしてオペレーションしたい
### ユーザデータグラムプロトコル (UDP)
<p align="center">
<img src="http://i.imgur.com/yzDrJtA.jpg"/>
<br/>
<i><a href=http://www.wildbunny.co.uk/blog/2012/10/09/how-to-make-a-multi-player-game-part-1/>Source: How to make a multiplayer game</a></i>
</p>
UDPはコネクションレスです。データグラムパケットのようなものはデータグラムレベルでの保証しかされません。データグラムは順不同で受け取り先に到着したりそもそも着かなかったりします。UDPは輻輳制御をサポートしません。TCPにおいてはサポートされているこれらの保証がないため、UDPは一般的に、TCPよりも効率的です。
UDPはサブネット上のすべての機器にデータグラムを送信することができます。これは[DHCP](https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol) において役に立ちます。というのも、クライアントはまだIPアドレスを取得していないので、IPアドレスを必要とするTCPによるストリームができないからです。
UDPは信頼性の面では劣りますが、VoIP、ビデオチャット、ストリーミングや同時通信マルチプレイヤーゲームなどのリアルタイム性が重視される時にはとても効果的です。
TCPよりもUDPを使うのは:
* レイテンシーを最低限に抑えたい時
* データ欠損よりも、データ遅延を重視するとき
* エラー修正を自前で実装したいとき
#### その他の参考資料、ページ: TCP と UDP
* [ゲームプログラミングのためのネットワーク](http://gafferongames.com/networking-for-game-programmers/udp-vs-tcp/)
* [TCP と UDP プロトコルの主な違い](http://www.cyberciti.biz/faq/key-differences-between-tcp-and-udp-protocols/)
* [TCP と UDPの違い](http://stackoverflow.com/questions/5970383/difference-between-tcp-and-udp)
* [Transmission control protocol](https://en.wikipedia.org/wiki/Transmission_Control_Protocol)
* [User datagram protocol](https://en.wikipedia.org/wiki/User_Datagram_Protocol)
* [Facebookのメムキャッシュスケーリング](http://www.cs.bu.edu/~jappavoo/jappavoo.github.com/451/papers/memcache-fb.pdf)
### 遠隔手続呼出 (RPC)
<p align="center">
<img src="http://i.imgur.com/iF4Mkb5.png"/>
<br/>
<i><a href=http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview>Source: Crack the system design interview</a></i>
</p>
RPCではクライアントがリモートサーバーなどの異なるアドレス空間でプロシージャーが処理されるようにします。プロシージャーはローカルでのコールのように、クライアントからサーバーにどのように通信するかという詳細を省いた状態でコードが書かれます。リモートのコールは普通、ローカルのコールよりも遅く、信頼性に欠けるため、RPCコールをローカルコールと区別させておくことが好ましいでしょう。人気のRPCフレームワークは以下です。[Protobuf](https://developers.google.com/protocol-buffers/)、 [Thrift](https://thrift.apache.org/)、[Avro](https://avro.apache.org/docs/current/)
RPC は リクエストレスポンスプロトコル:
* **クライアントプログラム** - クライアントスタブプロシージャーを呼び出します。パラメータはローカルでのプロシージャーコールのようにスタックへとプッシュされていきます。
* **クライアントスタブプロシージャー** - プロシージャIDとアーギュメントをパックしてリクエストメッセージにします。
* **クライアント通信モジュール** - OSがクライアントからサーバーへとメッセージを送ります。
* **サーバー通信モジュール** - OSが受け取ったパケットをサーバースタブプロシージャーに受け渡します。
* **サーバースタブプロシージャー** - 結果を展開し、プロシージャーIDにマッチするサーバープロシージャーを呼び出し、結果を返します。
* サーバーレスポンスは上記のステップを逆順で繰り返します。
Sample RPC calls:
```
GET /someoperation?data=anId
POST /anotheroperation
{
"data":"anId";
"anotherdata": "another value"
}
```
RPCは振る舞いを公開することに焦点を当てています。RPCは内部通信パフォーマンスを理由として使われることが多いです。というのも、使用する状況に合わせてネイティブコールを自作することができるからです。
ネイティブライブラリー (aka SDK) を呼ぶのは以下の時:
* ターゲットのプラットフォームを知っている時
* ロジックがどのようにアクセスされるのかを管理したいとき
* ライブラリー外でエラーがどのようにコントロールされるかを管理したい時
* パフォーマンスとエンドユーザーエクスペリエンスが最優先の時
**REST** プロトコルに従うHTTP APIはパブリックAPIにおいてよく用いられます。
#### 欠点: RPC
* RPCクライアントとはサービス実装により厳密に左右されることになります。
* 新しいオペレーション、使用例があるたびに新しくAPIが定義されなければなりません。
* RPCをデバッグするのは難しい可能性があります。
* 既存のテクノロジーをそのまま使ってサービスを構築することはできないかもしれません。例えば、[Squid](http://www.squid-cache.org/)などのサーバーに[RPCコールが正しくキャッシュ](http://etherealbits.com/2012/12/debunking-the-myths-of-rpc-rest/) されるように追加で骨を折る必要があるかもしれません。
### Representational state transfer (REST)
RESTは、クライアントがサーバーによってマネージされるリソースに対して処理を行うクライアント・サーバーモデルを支持するアーキテキチャスタイルです。サーバーは操作できるもしくは新しいリソースレプレゼンテーションを受け取ることができるようなリソースやアクションのレプレゼンテーションを提供します。すべての通信はステートレスでキャッシュ可能でなければなりません。
RESTful なインターフェースには次の四つの特徴があります:
* **特徴的なリソース (URI in HTTP)** - どのオペレーションであっても同じURIを使う。
* **HTTP動詞によって変わる (Verbs in HTTP)** - 動詞、ヘッダー、ボディを使う
* **自己説明的なエラーメッセージ (status response in HTTP)** - ステータスコードを使い、新しく作ったりしないこと。
* **[HATEOAS](http://restcookbook.com/Basics/hateoas/) (HTML interface for HTTP)** - 自分のwebサービスがブラウザで完全にアクセスできること。
サンプル REST コール:
```
GET /someresources/anId
PUT /someresources/anId
{"anotherdata": "another value"}
```
RESTはデータを公開することに焦点を当てています。クライアントとサーバーのカップリングを最小限にするもので、パブリックAPIなどによく用いられます。RESTはURI、 [representation through headers](https://github.com/for-GET/know-your-http-well/blob/master/headers.md)、そして、GET、POST、PUT、 DELETE、PATCHなどのHTTP動詞等のよりジェネリックで統一されたメソッドを用います。ステートレスであるのでRESTは水平スケーリングやパーティショニングに最適です。
#### 欠点: REST
* RESTはデータ公開に焦点を当てているので、リソースが自然に整理されていなかったり、シンプルなヒエラルキーで表せられない時にはよい選択肢とは言えないかもしれません。例えば、とあるイベントのセットにマッチするすべての更新情報を返すと言った処理は簡単にはパスで表現することができません。RESTでは、URIパス、クエリパラメータ、そして場合によってはリクエストボディなどによって実装されることが多いでしょう。
* RESTは少数の動詞に依存しています(GET、POST、PUT、DELETE、そして PATCH) が時には使いたい事例に合わないことがあります。例えば、期限の切れたドキュメントをアーカイブに移したい場合などはこれらの動詞の中には綺麗にはフィットしません。
* ネストされたヒエラルキーの中にあるリソースをとってくるのはシングルビューを描画するのにクライアントとサーバー間で数回やりとりしなければなりません。例として、ブログエントリーのコンテンツとそれに対するコメントを表示する場合などです。様々なネットワーク環境で動作する可能性が考えられるモバイルアプリケーションにおいてはこのような複数のやり取りは好ましくありません。
* 時が経つにつれて、APIレスポンスにより多くのフィールドが与えられて、古いクライアントはすでにいらないものも含めてすべてのデータフィールドを受け取ることになります。そのことで、ペイロードが大きくなりすぎて、レイテンシーも拡大することになります。
### RPCとREST比較
| Operation | RPC | REST |
|---|---|---|
| サインアップ | **POST** /signup | **POST** /persons |
| リザイン | **POST** /resign<br/>{<br/>"personid": "1234"<br/>} | **DELETE** /persons/1234 |
| Person読み込み | **GET** /readPerson?personid=1234 | **GET** /persons/1234 |
| Personのアイテムリスト読み込み | **GET** /readUsersItemsList?personid=1234 | **GET** /persons/1234/items |
| Personのアイテムへのアイテム追加 | **POST** /addItemToUsersItemsList<br/>{<br/>"personid": "1234";<br/>"itemid": "456"<br/>} | **POST** /persons/1234/items<br/>{<br/>"itemid": "456"<br/>} |
| アイテム更新 | **POST** /modifyItem<br/>{<br/>"itemid": "456";<br/>"key": "value"<br/>} | **PUT** /items/456<br/>{<br/>"key": "value"<br/>} |
| アイテム削除 | **POST** /removeItem<br/>{<br/>"itemid": "456"<br/>} | **DELETE** /items/456 |
<p align="center">
<i><a href=https://apihandyman.io/do-you-really-know-why-you-prefer-rest-over-rpc/>Source: Do you really know why you prefer REST over RPC</a></i>
</p>
#### その他の参考資料、ページ: REST と RPC
* [Do you really know why you prefer REST over RPC](https://apihandyman.io/do-you-really-know-why-you-prefer-rest-over-rpc/)
* [When are RPC-ish approaches more appropriate than REST?](http://programmers.stackexchange.com/a/181186)
* [REST vs JSON-RPC](http://stackoverflow.com/questions/15056878/rest-vs-json-rpc)
* [Debunking the myths of RPC and REST](http://etherealbits.com/2012/12/debunking-the-myths-of-rpc-rest/)
* [What are the drawbacks of using REST](https://www.quora.com/What-are-the-drawbacks-of-using-RESTful-APIs)
* [Crack the system design interview](http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview)
* [Thrift](https://code.facebook.com/posts/1468950976659943/)
* [Why REST for internal use and not RPC](http://arstechnica.com/civis/viewtopic.php?t=1190508)
## セキュリティ
このセクションは更新が必要です。[contributing](#contributing)してください!
セキュリティは幅広いトピックです。十分な経験、セキュリティ分野のバックグラウンドがなくても、セキュリティの知識を要する職に応募するのでない限り、基本以上のことを知る必要はないでしょう。
* 情報伝達、保存における暗号化
* [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) や [SQL injection](https://en.wikipedia.org/wiki/SQL_injection)を防ぐために、全てのユーザー入力もしくはユーザーに露出される入力パラメーターをサニタイズする
* SQL injectionを防ぐためにパラメータ化されたクエリを用いる。
* [least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege)の原理を用いる
### その他の参考資料、ページ:
* [開発者のためのセキュリティガイド](https://github.com/FallibleInc/security-guide-for-developers)
* [OWASP top ten](https://www.owasp.org/index.php/OWASP_Top_Ten_Cheat_Sheet)
## 補遺
暗算で、推計値を求める必要があることも時にはあります。例えば、ディスクから100枚イメージ分のサムネイルを作る時間を求めたり、その時にどれだけディスクメモリーが消費されるかなどの値です。**2の乗数表** と **全てのプログラマーが知るべきレイテンシー値** は良い参考になるでしょう。
### 2の乗数表
```
乗数 厳密な値 約 Bytes
---------------------------------------------------------------
7 128
8 256
10 1024 1 thousand 1 KB
16 65,536 64 KB
20 1,048,576 1 million 1 MB
30 1,073,741,824 1 billion 1 GB
32 4,294,967,296 4 GB
40 1,099,511,627,776 1 trillion 1 TB
```
#### その他の参考資料、ページ:
* [2の乗数表](https://en.wikipedia.org/wiki/Power_of_two)
### 全てのプログラマーが知るべきレイテンシー値
```
Latency Comparison Numbers
--------------------------
L1 cache reference 0.5 ns
Branch mispredict 5 ns
L2 cache reference 7 ns 14x L1 cache
Mutex lock/unlock 25 ns
Main memory reference 100 ns 20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy 10,000 ns 10 us
Send 1 KB bytes over 1 Gbps network 10,000 ns 10 us
Read 4 KB randomly from SSD* 150,000 ns 150 us ~1GB/sec SSD
Read 1 MB sequentially from memory 250,000 ns 250 us
Round trip within same datacenter 500,000 ns 500 us
Read 1 MB sequentially from SSD* 1,000,000 ns 1,000 us 1 ms ~1GB/sec SSD, 4X memory
Disk seek 10,000,000 ns 10,000 us 10 ms 20x datacenter roundtrip
Read 1 MB sequentially from 1 Gbps 10,000,000 ns 10,000 us 10 ms 40x memory, 10X SSD
Read 1 MB sequentially from disk 30,000,000 ns 30,000 us 30 ms 120x memory, 30X SSD
Send packet CA->Netherlands->CA 150,000,000 ns 150,000 us 150 ms
Notes
-----
1 ns = 10^-9 seconds
1 us = 10^-6 seconds = 1,000 ns
1 ms = 10^-3 seconds = 1,000 us = 1,000,000 ns
```
上記表に基づいた役に立つ数値:
* ディスクからの連続読み取り速度 30 MB/s
* 1 Gbps Ethernetからの連続読み取り速度 100 MB/s
* SSDからの連続読み取り速度 1 GB/s
* main memoryからの連続読み取り速度 4 GB/s
* 1秒で地球6-7周できる
* 1秒でデータセンターと2000周やりとりできる
#### レイテンシーの視覚的表
![](https://camo.githubusercontent.com/77f72259e1eb58596b564d1ad823af1853bc60a3/687474703a2f2f692e696d6775722e636f6d2f6b307431652e706e67)
#### その他の参考資料、ページ:
* [全てのプログラマーが知るべきレイテンシー値 - 1](https://gist.github.com/jboner/2841832)
* [全てのプログラマーが知るべきレイテンシー値 - 2](https://gist.github.com/hellerbarde/2843375)
* [Designs, lessons, and advice from building large distributed systems](http://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf)
* [Software Engineering Advice from Building Large-Scale Distributed Systems](https://static.googleusercontent.com/media/research.google.com/en//people/jeff/stanford-295-talk.pdf)
### 他のシステム設計面接例題
> 頻出のシステム設計面接課題とその解答へのリンク
| 質問 | 解答 |
|---|---|
| Dropboxのようなファイル同期サービスを設計する | [youtube.com](https://www.youtube.com/watch?v=PE4gwstWhmc) |
| Googleのような検索エンジンの設計 | [queue.acm.org](http://queue.acm.org/detail.cfm?id=988407)<br/>[stackexchange.com](http://programmers.stackexchange.com/questions/38324/interview-question-how-would-you-implement-google-search)<br/>[ardendertat.com](http://www.ardendertat.com/2012/01/11/implementing-search-engines/)<br/>[stanford.edu](http://infolab.stanford.edu/~backrub/google.html) |
| Googleのようなスケーラブルなwebクローラーの設計 | [quora.com](https://www.quora.com/How-can-I-build-a-web-crawler-from-scratch) |
| Google docsの設計 | [code.google.com](https://code.google.com/p/google-mobwrite/)<br/>[neil.fraser.name](https://neil.fraser.name/writing/sync/) |
| Redisのようなキーバリューストアの設計 | [slideshare.net](http://www.slideshare.net/dvirsky/introduction-to-redis) |
| Memcachedのようなキャッシュシステムの設計 | [slideshare.net](http://www.slideshare.net/oemebamo/introduction-to-memcached) |
| Amazonのようなレコメンデーションシステムの設計 | [hulu.com](http://tech.hulu.com/blog/2011/09/19/recommendation-system.html)<br/>[ijcai13.org](http://ijcai13.org/files/tutorial_slides/td3.pdf) |
| BitlyのようなURL短縮サービスの設計 | [n00tc0d3r.blogspot.com](http://n00tc0d3r.blogspot.com/) |
| WhatsAppのようなチャットアプリの設計 | [highscalability.com](http://highscalability.com/blog/2014/2/26/the-whatsapp-architecture-facebook-bought-for-19-billion.html)
| Instagramのような写真共有サービスの設計 | [highscalability.com](http://highscalability.com/flickr-architecture)<br/>[highscalability.com](http://highscalability.com/blog/2011/12/6/instagram-architecture-14-million-users-terabytes-of-photos.html) |
| Facebookニュースフィードの設計 | [quora.com](http://www.quora.com/What-are-best-practices-for-building-something-like-a-News-Feed)<br/>[quora.com](http://www.quora.com/Activity-Streams/What-are-the-scaling-issues-to-keep-in-mind-while-developing-a-social-network-feed)<br/>[slideshare.net](http://www.slideshare.net/danmckinley/etsy-activity-feeds-architecture) |
| Facebookタイムラインの設計 | [facebook.com](https://www.facebook.com/note.php?note_id=10150468255628920)<br/>[highscalability.com](http://highscalability.com/blog/2012/1/23/facebook-timeline-brought-to-you-by-the-power-of-denormaliza.html) |
| Facebookチャットの設計 | [erlang-factory.com](http://www.erlang-factory.com/upload/presentations/31/EugeneLetuchy-ErlangatFacebook.pdf)<br/>[facebook.com](https://www.facebook.com/note.php?note_id=14218138919&id=9445547199&index=0) |
| Facebookのようなgraph検索の設計 | [facebook.com](https://www.facebook.com/notes/facebook-engineering/under-the-hood-building-out-the-infrastructure-for-graph-search/10151347573598920)<br/>[facebook.com](https://www.facebook.com/notes/facebook-engineering/under-the-hood-indexing-and-ranking-in-graph-search/10151361720763920)<br/>[facebook.com](https://www.facebook.com/notes/facebook-engineering/under-the-hood-the-natural-language-interface-of-graph-search/10151432733048920) |
| CloudFlareのようなCDNの設計 | [cmu.edu](http://repository.cmu.edu/cgi/viewcontent.cgi?article=2112&context=compsci) |
| Twitterのトレンド機能の設計 | [michael-noll.com](http://www.michael-noll.com/blog/2013/01/18/implementing-real-time-trending-topics-in-storm/)<br/>[snikolov .wordpress.com](http://snikolov.wordpress.com/2012/11/14/early-detection-of-twitter-trends/) |
| ランダムID発行システムの設計 | [blog.twitter.com](https://blog.twitter.com/2010/announcing-snowflake)<br/>[github.com](https://github.com/twitter/snowflake/) |
| 一定のインターバル時間での上位k件を返す | [ucsb.edu](https://icmi.cs.ucsb.edu/research/tech_reports/reports/2005-23.pdf)<br/>[wpi.edu](http://davis.wpi.edu/xmdv/docs/EDBT11-diyang.pdf) |
| 複数のデータセンターからデータを配信するサービスの設計 | [highscalability.com](http://highscalability.com/blog/2009/8/24/how-google-serves-data-from-multiple-datacenters.html) |
| オンラインの複数プレイヤーカードゲームの設計 | [indieflashblog.com](http://www.indieflashblog.com/how-to-create-an-asynchronous-multiplayer-game.html)<br/>[buildnewgames.com](http://buildnewgames.com/real-time-multiplayer/) |
| ガーベッジコレクションシステムの設計 | [stuffwithstuff.com](http://journal.stuffwithstuff.com/2013/12/08/babys-first-garbage-collector/)<br/>[washington.edu](http://courses.cs.washington.edu/courses/csep521/07wi/prj/rick.pdf) |
| システム設計例題を追加する | [Contribute](#contributing) |
### 実世界のアーキテクチャ
> 世の中のシステムがどのように設計されているかについての記事
<p align="center">
<img src="http://i.imgur.com/TcUo2fw.png"/>
<br/>
<i><a href=https://www.infoq.com/presentations/Twitter-Timeline-Scalability>Source: Twitter timelines at scale</a></i>
</p>
**以下の記事の重箱の隅をつつくような細かい詳細にこだわらないこと。むしろ**
* 共通の原理、技術、パターンを探ること
* それぞれのコンポーネントでどんな問題が解決され、コンポーネントはどこでうまく使えもしくは使えないかを知ること
* 学んだことを復習すること
|種類 | システム | 参考ページ |
|---|---|---|
| データ処理 | **MapReduce** - Googleの分散データ処理システム | [research.google.com](http://static.googleusercontent.com/media/research.google.com/zh-CN/us/archive/mapreduce-osdi04.pdf) |
| データ処理 | **Spark** - Databricksの分散データ処理システム | [slideshare.net](http://www.slideshare.net/AGrishchenko/apache-spark-architecture) |
| データ処理 | **Storm** - Twitterの分散データ処理システム | [slideshare.net](http://www.slideshare.net/previa/storm-16094009) |
| | | |
| データストア | **Bigtable** - Googleのカラム指向分散データベース | [harvard.edu](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/chang06bigtable.pdf) |
| データストア | **HBase** - Bigtableのオープンソース実装 | [slideshare.net](http://www.slideshare.net/alexbaranau/intro-to-hbase) |
| データストア | **Cassandra** - Facebookのカラム指向分散データベース | [slideshare.net](http://www.slideshare.net/planetcassandra/cassandra-introduction-features-30103666)
| データストア | **DynamoDB** - Amazonのドキュメント指向分散データベース | [harvard.edu](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/decandia07dynamo.pdf) |
| データストア | **MongoDB** - ドキュメント指向分散データベース | [slideshare.net](http://www.slideshare.net/mdirolf/introduction-to-mongodb) |
| データストア | **Spanner** - Googleのグローバル分散データベース | [research.google.com](http://research.google.com/archive/spanner-osdi2012.pdf) |
| データストア | **Memcached** - 分散メモリーキャッシングシステム | [slideshare.net](http://www.slideshare.net/oemebamo/introduction-to-memcached) |
| データストア | **Redis** - 永続性とバリュータイプを兼ね備えた分散メモリーキャッシングシステム | [slideshare.net](http://www.slideshare.net/dvirsky/introduction-to-redis) |
| | | |
| ファイルシステム | **Google File System (GFS)** - 分散ファイルシステム | [research.google.com](http://static.googleusercontent.com/media/research.google.com/zh-CN/us/archive/gfs-sosp2003.pdf) |
| ファイルシステム | **Hadoop File System (HDFS)** - GFSのオープンソース実装 | [apache.org](https://hadoop.apache.org/docs/r1.2.1/hdfs_design.html) |
| | | |
| Misc | **Chubby** - 疎結合の分散システムをロックするGoogleのサービス | [research.google.com](http://static.googleusercontent.com/external_content/untrusted_dlcp/research.google.com/en/us/archive/chubby-osdi06.pdf) |
| Misc | **Dapper** - 分散システムを追跡するインフラ | [research.google.com](http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36356.pdf)
| Misc | **Kafka** - LinkedInによるPub/subメッセージキュー | [slideshare.net](http://www.slideshare.net/mumrah/kafka-talk-tri-hug) |
| Misc | **Zookeeper** - 同期を可能にする中央集権インフラとサービス | [slideshare.net](http://www.slideshare.net/sauravhaloi/introduction-to-apache-zookeeper) |
| | アーキテクチャを追加する | [Contribute](#contributing) |
### 各企業のアーキテクチャ
| 企業 | 参考ページ |
|---|---|
| Amazon | [Amazon architecture](http://highscalability.com/amazon-architecture) |
| Cinchcast | [Producing 1,500 hours of audio every day](http://highscalability.com/blog/2012/7/16/cinchcast-architecture-producing-1500-hours-of-audio-every-d.html) |
| DataSift | [Realtime datamining At 120,000 tweets per second](http://highscalability.com/blog/2011/11/29/datasift-architecture-realtime-datamining-at-120000-tweets-p.html) |
| DropBox | [How we've scaled Dropbox](https://www.youtube.com/watch?v=PE4gwstWhmc) |
| ESPN | [Operating At 100,000 duh nuh nuhs per second](http://highscalability.com/blog/2013/11/4/espns-architecture-at-scale-operating-at-100000-duh-nuh-nuhs.html) |
| Google | [Google architecture](http://highscalability.com/google-architecture) |
| Instagram | [14 million users, terabytes of photos](http://highscalability.com/blog/2011/12/6/instagram-architecture-14-million-users-terabytes-of-photos.html)<br/>[What powers Instagram](http://instagram-engineering.tumblr.com/post/13649370142/what-powers-instagram-hundreds-of-instances) |
| Justin.tv | [Justin.Tv's live video broadcasting architecture](http://highscalability.com/blog/2010/3/16/justintvs-live-video-broadcasting-architecture.html) |
| Facebook | [Scaling memcached at Facebook](https://cs.uwaterloo.ca/~brecht/courses/854-Emerging-2014/readings/key-value/fb-memcached-nsdi-2013.pdf)<br/>[TAO: Facebooks distributed data store for the social graph](https://cs.uwaterloo.ca/~brecht/courses/854-Emerging-2014/readings/data-store/tao-facebook-distributed-datastore-atc-2013.pdf)<br/>[Facebooks photo storage](https://www.usenix.org/legacy/event/osdi10/tech/full_papers/Beaver.pdf) |
| Flickr | [Flickr architecture](http://highscalability.com/flickr-architecture) |
| Mailbox | [From 0 to one million users in 6 weeks](http://highscalability.com/blog/2013/6/18/scaling-mailbox-from-0-to-one-million-users-in-6-weeks-and-1.html) |
| Pinterest | [From 0 To 10s of billions of page views a month](http://highscalability.com/blog/2013/4/15/scaling-pinterest-from-0-to-10s-of-billions-of-page-views-a.html)<br/>[18 million visitors, 10x growth, 12 employees](http://highscalability.com/blog/2012/5/21/pinterest-architecture-update-18-million-visitors-10x-growth.html) |
| Playfish | [50 million monthly users and growing](http://highscalability.com/blog/2010/9/21/playfishs-social-gaming-architecture-50-million-monthly-user.html) |
| PlentyOfFish | [PlentyOfFish architecture](http://highscalability.com/plentyoffish-architecture) |
| Salesforce | [How they handle 1.3 billion transactions a day](http://highscalability.com/blog/2013/9/23/salesforce-architecture-how-they-handle-13-billion-transacti.html) |
| Stack Overflow | [Stack Overflow architecture](http://highscalability.com/blog/2009/8/5/stack-overflow-architecture.html) |
| TripAdvisor | [40M visitors, 200M dynamic page views, 30TB data](http://highscalability.com/blog/2011/6/27/tripadvisor-architecture-40m-visitors-200m-dynamic-page-view.html) |
| Tumblr | [15 billion page views a month](http://highscalability.com/blog/2012/2/13/tumblr-architecture-15-billion-page-views-a-month-and-harder.html) |
| Twitter | [Making Twitter 10000 percent faster](http://highscalability.com/scaling-twitter-making-twitter-10000-percent-faster)<br/>[Storing 250 million tweets a day using MySQL](http://highscalability.com/blog/2011/12/19/how-twitter-stores-250-million-tweets-a-day-using-mysql.html)<br/>[150M active users, 300K QPS, a 22 MB/S firehose](http://highscalability.com/blog/2013/7/8/the-architecture-twitter-uses-to-deal-with-150m-active-users.html)<br/>[Timelines at scale](https://www.infoq.com/presentations/Twitter-Timeline-Scalability)<br/>[Big and small data at Twitter](https://www.youtube.com/watch?v=5cKTP36HVgI)<br/>[Operations at Twitter: scaling beyond 100 million users](https://www.youtube.com/watch?v=z8LU0Cj6BOU) |
| Uber | [How Uber scales their real-time market platform](http://highscalability.com/blog/2015/9/14/how-uber-scales-their-real-time-market-platform.html) |
| WhatsApp | [The WhatsApp architecture Facebook bought for $19 billion](http://highscalability.com/blog/2014/2/26/the-whatsapp-architecture-facebook-bought-for-19-billion.html) |
| YouTube | [YouTube scalability](https://www.youtube.com/watch?v=w5WVu624fY8)<br/>[YouTube architecture](http://highscalability.com/youtube-architecture) |
### 企業のエンジニアブログ
> 面接を受ける企業のアーキテクチャ
>
> 投げられる質問は同じ分野から来ることもあるでしょう
* [Airbnb Engineering](http://nerds.airbnb.com/)
* [Atlassian Developers](https://developer.atlassian.com/blog/)
* [Autodesk Engineering](http://cloudengineering.autodesk.com/blog/)
* [AWS Blog](https://aws.amazon.com/blogs/aws/)
* [Bitly Engineering Blog](http://word.bitly.com/)
* [Box Blogs](https://www.box.com/blog/engineering/)
* [Cloudera Developer Blog](http://blog.cloudera.com/blog/)
* [Dropbox Tech Blog](https://tech.dropbox.com/)
* [Engineering at Quora](http://engineering.quora.com/)
* [Ebay Tech Blog](http://www.ebaytechblog.com/)
* [Evernote Tech Blog](https://blog.evernote.com/tech/)
* [Etsy Code as Craft](http://codeascraft.com/)
* [Facebook Engineering](https://www.facebook.com/Engineering)
* [Flickr Code](http://code.flickr.net/)
* [Foursquare Engineering Blog](http://engineering.foursquare.com/)
* [GitHub Engineering Blog](http://githubengineering.com/)
* [Google Research Blog](http://googleresearch.blogspot.com/)
* [Groupon Engineering Blog](https://engineering.groupon.com/)
* [Heroku Engineering Blog](https://engineering.heroku.com/)
* [Hubspot Engineering Blog](http://product.hubspot.com/blog/topic/engineering)
* [High Scalability](http://highscalability.com/)
* [Instagram Engineering](http://instagram-engineering.tumblr.com/)
* [Intel Software Blog](https://software.intel.com/en-us/blogs/)
* [Jane Street Tech Blog](https://blogs.janestreet.com/category/ocaml/)
* [LinkedIn Engineering](http://engineering.linkedin.com/blog)
* [Microsoft Engineering](https://engineering.microsoft.com/)
* [Microsoft Python Engineering](https://blogs.msdn.microsoft.com/pythonengineering/)
* [Netflix Tech Blog](http://techblog.netflix.com/)
* [Paypal Developer Blog](https://devblog.paypal.com/category/engineering/)
* [Pinterest Engineering Blog](http://engineering.pinterest.com/)
* [Quora Engineering](https://engineering.quora.com/)
* [Reddit Blog](http://www.redditblog.com/)
* [Salesforce Engineering Blog](https://developer.salesforce.com/blogs/engineering/)
* [Slack Engineering Blog](https://slack.engineering/)
* [Spotify Labs](https://labs.spotify.com/)
* [Twilio Engineering Blog](http://www.twilio.com/engineering)
* [Twitter Engineering](https://engineering.twitter.com/)
* [Uber Engineering Blog](http://eng.uber.com/)
* [Yahoo Engineering Blog](http://yahooeng.tumblr.com/)
* [Yelp Engineering Blog](http://engineeringblog.yelp.com/)
* [Zynga Engineering Blog](https://www.zynga.com/blogs/engineering)
#### その他の参考資料、ページ:
* [kilimchoi/engineering-blogs](https://github.com/kilimchoi/engineering-blogs)
ここにあるリストは比較的小規模なものにとどめ、[kilimchoi/engineering-blogs](https://github.com/kilimchoi/engineering-blogs)により詳細に記すことで重複しないようにしておくことにする。エンジニアブログへのリンクを追加する場合はここではなく、engineering-blogsレボジトリに追加することを検討してください。
## 進行中の作業
セクションの追加や、進行中の作業を手伝っていただける場合は[こちら](#contributing)!
* MapReduceによる分散コンピューティング
* Consistent hashing
* Scatter gather
* [Contribute](#contributing)
## クレジット
クレジット及び、参照ページは適時このリポジトリ内に記載してあります
Special thanks to:
* [Hired in tech](http://www.hiredintech.com/system-design/the-system-design-process/)
* [Cracking the coding interview](https://www.amazon.com/dp/0984782850/)
* [High scalability](http://highscalability.com/)
* [checkcheckzz/system-design-interview](https://github.com/checkcheckzz/system-design-interview)
* [shashank88/system_design](https://github.com/shashank88/system_design)
* [mmcgrana/services-engineering](https://github.com/mmcgrana/services-engineering)
* [System design cheat sheet](https://gist.github.com/vasanthk/485d1c25737e8e72759f)
* [A distributed systems reading list](http://dancres.github.io/Pages/)
* [Cracking the system design interview](http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview)
## Contact info
Feel free to contact me to discuss any issues, questions, or comments.
My contact info can be found on my [GitHub page](https://github.com/donnemartin).
## License
*I am providing code and resources in this repository to you under an open source license. Because this is my personal repository, the license you receive to my code and resources is from me and not my employer (Facebook).*
Copyright 2017 Donne Martin
Creative Commons Attribution 4.0 International License (CC BY 4.0)
http://creativecommons.org/licenses/by/4.0/

View File

@ -1,23 +1,17 @@
> * 原文地址:[github.com/donnemartin/system-design-primer](https://github.com/donnemartin/system-design-primer)
> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner)
> * 译者:[XatMassacrE](https://github.com/XatMassacrE)、[L9m](https://github.com/L9m)、[Airmacho](https://github.com/Airmacho)、[xiaoyusilen](https://github.com/xiaoyusilen)、[jifaxu](https://github.com/jifaxu)
> * 译者:[XatMassacrE](https://github.com/XatMassacrE)、[L9m](https://github.com/L9m)、[Airmacho](https://github.com/Airmacho)、[xiaoyusilen](https://github.com/xiaoyusilen)、[jifaxu](https://github.com/jifaxu)、[根号三](https://github.com/sqrthree)
> * 这个 [链接](https://github.com/xitu/system-design-primer/compare/master...donnemartin:master) 用来查看本翻译与英文版是否有差别(如果你没有看到 README.md 发生变化,那就意味着这份翻译文档是最新的)。
*[English](README.md) ∙ [日本語](README-ja.md) ∙ [简体中文](README-zh-Hans.md) ∙ [繁體中文](README-zh-TW.md) | [العَرَبِيَّة‎](https://github.com/donnemartin/system-design-primer/issues/170) ∙ [বাংলা](https://github.com/donnemartin/system-design-primer/issues/220) ∙ [Português do Brasil](https://github.com/donnemartin/system-design-primer/issues/40) ∙ [Deutsch](https://github.com/donnemartin/system-design-primer/issues/186) ∙ [ελληνικά](https://github.com/donnemartin/system-design-primer/issues/130) ∙ [עברית](https://github.com/donnemartin/system-design-primer/issues/272) ∙ [Italiano](https://github.com/donnemartin/system-design-primer/issues/104) ∙ [韓國語](https://github.com/donnemartin/system-design-primer/issues/102) ∙ [فارسی](https://github.com/donnemartin/system-design-primer/issues/110) ∙ [Polski](https://github.com/donnemartin/system-design-primer/issues/68) ∙ [русский язык](https://github.com/donnemartin/system-design-primer/issues/87) ∙ [Español](https://github.com/donnemartin/system-design-primer/issues/136) ∙ [ภาษาไทย](https://github.com/donnemartin/system-design-primer/issues/187) ∙ [Türkçe](https://github.com/donnemartin/system-design-primer/issues/39) ∙ [tiếng Việt](https://github.com/donnemartin/system-design-primer/issues/127) ∙ [Français](https://github.com/donnemartin/system-design-primer/issues/250) | [Add Translation](https://github.com/donnemartin/system-design-primer/issues/28)*
# 系统设计入门
<p align="center">
<img src="http://i.imgur.com/jj3A5N8.png">
<img src="http://i.imgur.com/jj3A5N8.png"/>
<br/>
</p>
## 翻译
有兴趣参与[翻译](https://github.com/donnemartin/system-design-primer/issues/28)? 以下是正在进行中的翻译:
* [巴西葡萄牙语](https://github.com/donnemartin/system-design-primer/issues/40)
* [简体中文](https://github.com/donnemartin/system-design-primer/issues/38)
* [土耳其语](https://github.com/donnemartin/system-design-primer/issues/39)
## 目的
> 学习如何设计大型系统。
@ -55,7 +49,7 @@
## 抽认卡
<p align="center">
<img src="http://i.imgur.com/zdCAkB3.png">
<img src="http://i.imgur.com/zdCAkB3.png"/>
<br/>
</p>
@ -72,7 +66,7 @@
你正在寻找资源以准备[**编程面试**](https://github.com/donnemartin/interactive-coding-challenges)吗?
<p align="center">
<img src="http://i.imgur.com/b4YtAEN.png">
<img src="http://i.imgur.com/b4YtAEN.png"/>
<br/>
</p>
@ -89,6 +83,7 @@
* 修复错误
* 完善章节
* 添加章节
* [帮助翻译](https://github.com/donnemartin/system-design-primer/issues/28)
一些还需要完善的内容放在了[正在完善中](#正在完善中)。
@ -98,17 +93,17 @@
> 各种系统设计主题的摘要,包括优点和缺点。**每一个主题都面临着取舍和权衡**。
>
> 每个章节都包含着更的资源的链接。
> 每个章节都包含着更的资源的链接。
<p align="center">
<img src="http://i.imgur.com/jrUBAF7.png">
<img src="http://i.imgur.com/jrUBAF7.png"/>
<br/>
</p>
* [系统设计主题:从这里开始](#系统设计主题从这里开始)
* [第一步:回顾可扩展性的视频讲座](#第一步回顾可扩展性scalability的视频讲座)
* [第二步: 回顾可扩展性的文章](#第二步回顾可扩展性文章)
* [第二步:回顾可扩展性的文章](#第二步回顾可扩展性文章)
* [接下来的步骤](#接下来的步骤)
* [性能与拓展性](#性能与可扩展性)
* [延迟与吞吐量](#延迟与吞吐量)
@ -207,11 +202,11 @@
那些有经验的候选人通常会被期望了解更多的系统设计的知识。架构师或者团队负责人则会被期望了解更多除了个人贡献之外的知识。顶级的科技公司通常也会有一次或者更多的系统设计面试。
面试会很宽泛的展开并在几个领域深入。这帮助你了解一些关于系统设计的不同的主题。基于你的时间线,经验,面试的职位和面试的公司对下面的指导做出适当的调整。
面试会很宽泛的展开并在几个领域深入。这帮助你了解一些关于系统设计的不同的主题。基于你的时间线,经验,面试的职位和面试的公司对下面的指导做出适当的调整。
* **短期** - 以系统设计主题的**广度**为目标。通过解决**一些**面试题来练习。
* **中期** - 以系统设计主题的**广度**和**初级深度**为目标。通过解决**很多**面试题来练习。
* **长期** - 以系统设计主题的**广度**和**高级深度**为目标。通过解决**大部分**面试题来联系
* **长期** - 以系统设计主题的**广度**和**高级深度**为目标。通过解决**大部分**面试题来练习
| | 短期 | 中期 | 长期 |
| ---------------------------------------- | ---- | ---- | ---- |
@ -242,9 +237,9 @@
* 我们希望每秒钟处理多少请求?
* 我们希望的读写比率?
### 第二步:创造一个高级的设计
### 第二步:创造一个高级的设计
使用所有重要的组件来描绘出一个高级的设计。
使用所有重要的组件来描绘出一个高级的设计。
* 画出主要的组件和连接
* 证明你的想法
@ -262,22 +257,22 @@
* 数据库查找
* API 和面向对象设计
### 第四步:度量设计
### 第四步:扩展设计
确认和处理瓶颈以及一些限制。举例来说就是你需要下面的这些来完成展性的议题吗?
确认和处理瓶颈以及一些限制。举例来说就是你需要下面的这些来完成展性的议题吗?
* 负载均衡
* 水平
* 水平
* 缓存
* 数据库分片
论述可能的解决办法和代价。每件事情需要取舍。可以使用[可展系统的设计原则](#系统设计主题的索引)来处理瓶颈。
论述可能的解决办法和代价。每件事情需要取舍。可以使用[可展系统的设计原则](#系统设计主题的索引)来处理瓶颈。
### 信封背面的计算
### 预估计算量
你或许会被要求通过手算进行一些估算。涉及到的[附录](#附录)涉及到的是下面的这些资源:
你或许会被要求通过手算进行一些估算。[附录](#附录)涉及到的是下面的这些资源:
* [使用信封的背面做计算](http://highscalability.com/blog/2011/1/26/google-pro-tip-use-back-of-the-envelope-calculations-to-choo.html)
* [使用预估计算量](http://highscalability.com/blog/2011/1/26/google-pro-tip-use-back-of-the-envelope-calculations-to-choo.html)
* [2 的次方表](#2-的次方表)
* [每个程序员都应该知道的延迟数](#每个程序员都应该知道的延迟数)
@ -298,7 +293,7 @@
| 问题 | |
| ---------------------------------------- | ---------------------------------------- |
| 设计 Pastebin.com (或者 Bit.ly) | [解答](solutions/system_design/pastebin/README.md) |
| 设计 Pastebin.com (或者 Bit.ly) | [解答](solutions/system_design/pastebin/README-zh-Hans.md) |
| 设计 Twitter 时间线和搜索 (或者 Facebook feed 和搜索) | [解答](solutions/system_design/twitter/README.md) |
| 设计一个网页爬虫 | [解答](solutions/system_design/web_crawler/README.md) |
| 设计 Mint.com | [解答](solutions/system_design/mint/README.md) |
@ -395,7 +390,7 @@
### 第二步:回顾可扩展性文章
[可扩展性](http://www.lecloud.net/tagged/scalability)
[可扩展性](http://www.lecloud.net/tagged/scalability/chrono)
* 主题涵盖:
* [Clones](http://www.lecloud.net/post/7295452622/scalability-for-dummies-part-1-clones)
@ -446,7 +441,7 @@
### CAP 理论
<p align="center">
<img src="http://i.imgur.com/bgLMI2u.png">
<img src="http://i.imgur.com/bgLMI2u.png"/>
<br/>
<strong><a href="http://robertgreiner.com/2014/08/cap-theorem-revisited">来源:再看 CAP 理论</a></strong>
</p>
@ -541,7 +536,7 @@ DNS 和 email 等系统使用的是此种方式。最终一致性在高可用性
## 域名系统
<p align="center">
<img src="http://i.imgur.com/IOyLj4i.jpg">
<img src="http://i.imgur.com/IOyLj4i.jpg"/>
<br/>
<strong><a href="http://www.slideshare.net/srikrupa5/dns-security-presentation-issa">来源DNS 安全介绍</a></strong>
</p>
@ -568,7 +563,7 @@ DNS 和 email 等系统使用的是此种方式。最终一致性在高可用性
* 虽说缓存可以减轻 DNS 延迟,但连接 DNS 服务器还是带来了轻微的延迟。
* 虽然它们通常由[政府,网络服务提供商和大公司](http://superuser.com/questions/472695/who-controls-the-dns-servers/472729)管理,但 DNS 服务管理仍可能是复杂的。
* DNS 服务最近遭受 [DDoS 攻击](http://dyn.com/blog/dyn-analysis-summary-of-friday-october-21-attack/),阻止不知道 Twtter IP 地址的用户访问 Twiiter。
* DNS 服务最近遭受 [DDoS 攻击](http://dyn.com/blog/dyn-analysis-summary-of-friday-october-21-attack/),阻止不知道 Twitter IP 地址的用户访问 Twitter。
### 来源及延伸阅读
@ -579,7 +574,7 @@ DNS 和 email 等系统使用的是此种方式。最终一致性在高可用性
## 内容分发网络CDN
<p align="center">
<img src="http://i.imgur.com/h9TAuGI.jpg">
<img src="http://i.imgur.com/h9TAuGI.jpg"/>
<br/>
<strong><a href="https://www.creative-artworks.eu/why-use-a-content-delivery-network-cdn/">来源:为什么使用 CDN</a></strong>
</p>
@ -618,7 +613,7 @@ CDN 拉取是当第一个用户请求该资源时,从服务器上拉取资源
## 负载均衡器
<p align="center">
<img src="http://i.imgur.com/h81n9iK.png">
<img src="http://i.imgur.com/h81n9iK.png"/>
<br/>
<strong><a href="http://horicky.blogspot.com/2010/10/scalable-system-design-patterns.html">来源:可扩展的系统设计模式</a></strong>
</p>
@ -687,7 +682,7 @@ CDN 拉取是当第一个用户请求该资源时,从服务器上拉取资源
## 反向代理web 服务器)
<p align="center">
<img src="http://i.imgur.com/n41Azff.png">
<img src="http://i.imgur.com/n41Azff.png"/>
<br/>
<strong><a href="https://upload.wikimedia.org/wikipedia/commons/6/67/Reverse_proxy_h2g2bob.svg">资料来源:维基百科</a></strong>
<br/>
@ -731,7 +726,7 @@ CDN 拉取是当第一个用户请求该资源时,从服务器上拉取资源
## 应用层
<p align="center">
<img src="http://i.imgur.com/yB5SYwm.png">
<img src="http://i.imgur.com/yB5SYwm.png"/>
<br/>
<strong><a href="http://lethain.com/introduction-to-architecting-systems-for-scale/#platform_layer">资料来源:可缩放系统构架介绍</a></strong>
</p>
@ -761,7 +756,7 @@ CDN 拉取是当第一个用户请求该资源时,从服务器上拉取资源
### 来源及延伸阅读
- [可缩放系统构架介绍](http://lethain.com/introduction-to-architecting-systems-for-scale)
- [破解系统设计面试](http://www.puncsky.com/blog/2016/02/14/crack-the-system-design-interview/)
- [破解系统设计面试](http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview)
- [面向服务架构](https://en.wikipedia.org/wiki/Service-oriented_architecture)
- [Zookeeper 介绍](http://www.slideshare.net/sauravhaloi/introduction-to-apache-zookeeper)
- [构建微服务,你所需要知道的一切](https://cloudncode.wordpress.com/2016/07/22/msa-getting-started/)
@ -769,9 +764,9 @@ CDN 拉取是当第一个用户请求该资源时,从服务器上拉取资源
## 数据库
<p align="center">
<img src="http://i.imgur.com/Xkm5CXz.png">
<img src="http://i.imgur.com/Xkm5CXz.png"/>
<br/>
<strong><a href="https://www.youtube.com/watch?v=vg5onp8TU6Q">资料来源:扩展你的用户数到第一个一千万</a></strong>
<strong><a href="https://www.youtube.com/watch?v=w95murBkYmU">资料来源:扩展你的用户数到第一个一千万</a></strong>
</p>
### 关系型数据库管理系统RDBMS
@ -790,7 +785,7 @@ CDN 拉取是当第一个用户请求该资源时,从服务器上拉取资源
关系型数据库扩展包括许多技术:**主从复制**、**主主复制**、**联合**、**分片**、**非规范化**和 **SQL调优**
<p align="center">
<img src="http://i.imgur.com/C9ioGtn.png">
<img src="http://i.imgur.com/C9ioGtn.png"/>
<br/>
<strong><a href="http://www.slideshare.net/jboner/scalability-availability-stability-patterns/">资料来源:可扩展性、可用性、稳定性、模式</a></strong>
</p>
@ -805,7 +800,7 @@ CDN 拉取是当第一个用户请求该资源时,从服务器上拉取资源
- 参考[不利之处:复制](#不利之处复制)中,主从复制和主主复制**共同**的问题。
<p align="center">
<img src="http://i.imgur.com/krAHLGg.png">
<img src="http://i.imgur.com/krAHLGg.png"/>
<br/>
<strong><a href="http://www.slideshare.net/jboner/scalability-availability-stability-patterns/">资料来源:可扩展性、可用性、稳定性、模式</a></strong>
</p>
@ -840,9 +835,9 @@ CDN 拉取是当第一个用户请求该资源时,从服务器上拉取资源
#### 联合
<p align="center">
<img src="http://i.imgur.com/U3qV33e.png">
<img src="http://i.imgur.com/U3qV33e.png"/>
<br/>
<strong><a href="https://www.youtube.com/watch?v=vg5onp8TU6Q">资料来源:扩展你的用户数到第一个一千万</a></strong>
<strong><a href="https://www.youtube.com/watch?v=w95murBkYmU">资料来源:扩展你的用户数到第一个一千万</a></strong>
</p>
联合(或按功能划分)将数据库按对应功能分割。例如,你可以有三个数据库:**论坛**、**用户**和**产品**,而不仅是一个单体数据库,从而减少每个数据库的读取和写入流量,减少复制延迟。较小的数据库意味着更多适合放入内存的数据,进而意味着更高的缓存命中几率。没有只能串行写入的中心化主库,你可以并行写入,提高负载能力。
@ -857,12 +852,12 @@ CDN 拉取是当第一个用户请求该资源时,从服务器上拉取资源
##### 来源及延伸阅读:联合
- [扩展你的用户数到第一个一千万](https://www.youtube.com/watch?v=vg5onp8TU6Q)
- [扩展你的用户数到第一个一千万](https://www.youtube.com/watch?v=w95murBkYmU)
#### 分片
<p align="center">
<img src="http://i.imgur.com/wU8x5Id.png">
<img src="http://i.imgur.com/wU8x5Id.png"/>
<br/>
<strong><a href="http://www.slideshare.net/jboner/scalability-availability-stability-patterns/">资料来源:可扩展性、可用性、稳定性、模式</a></strong>
</p>
@ -889,7 +884,7 @@ CDN 拉取是当第一个用户请求该资源时,从服务器上拉取资源
#### 非规范化
非规范化试图以写入性能为代价来换取读取性能。在多个表中冗余数据副本,以避免高成本的联结操作。一些关系型数据库,比如 [PostgreSQl](https://en.wikipedia.org/wiki/PostgreSQL) 和 Oracle 支持[物化视图](https://en.wikipedia.org/wiki/Materialized_view),可以处理冗余信息存储和保证冗余副本一致。
非规范化试图以写入性能为代价来换取读取性能。在多个表中冗余数据副本,以避免高成本的联结操作。一些关系型数据库,比如 [PostgreSQL](https://en.wikipedia.org/wiki/PostgreSQL) 和 Oracle 支持[物化视图](https://en.wikipedia.org/wiki/Materialized_view),可以处理冗余信息存储和保证冗余副本一致。
当数据使用诸如[联合](#联合)和[分片](#分片)等技术被分割,进一步提高了处理跨数据中心的联结操作复杂度。非规范化可以规避这种复杂的联结操作。
@ -924,7 +919,7 @@ SQL 调优是一个范围很广的话题,有很多相关的[书](https://www.a
- 使用 `TEXT` 类型存储大块的文本,例如博客正文。`TEXT` 还允许布尔搜索。使用 `TEXT` 字段需要在磁盘上存储一个用于定位文本块的指针。
- 使用 `INT` 类型存储高达 2^32 或 40 亿的较大数字。
- 使用 `DECIMAL` 类型存储货币可以避免浮点数表示错误。
- 避免使用 `BLOBS` 存储对象,存储存放对象的位置。
- 避免使用 `BLOBS` 存储实际对象,而是用来存储存放对象的位置。
- `VARCHAR(255)` 是以 8 位数字存储的最大字符数,在某些关系型数据库中,最大限度地利用字节。
- 在适用场景中设置 `NOT NULL` 约束来[提高搜索性能](http://stackoverflow.com/questions/1017239/how-do-null-values-affect-performance-in-a-database-search)。
@ -1006,7 +1001,7 @@ MongoDB 和 CouchDB 等一些文档类型存储还提供了类似 SQL 语言的
#### 列型存储
<p align="center">
<img src="http://i.imgur.com/n16iOGk.png">
<img src="http://i.imgur.com/n16iOGk.png"/>
<br/>
<strong><a href="http://blog.grio.com/2015/11/sql-nosql-a-brief-history.html">资料来源: SQL 和 NoSQL一个简短的历史</a></strong>
</p>
@ -1029,9 +1024,9 @@ Google 发布了第一个列型存储数据库 [Bigtable](http://www.read.seas.h
#### 图数据库
<p align="center">
<img src="http://i.imgur.com/fNcl65g.png">
<img src="http://i.imgur.com/fNcl65g.png"/>
<br/>
<strong><a href="https://en.wikipedia.org/wiki/File:GraphDatabase_PropertyGraph.png">资料来源:图数据库</a></strong>
<strong><a href="https://en.wikipedia.org/wiki/File:GraphDatabase_PropertyGraph.png"/>资料来源:图数据库</a></strong>
</p>
> 抽象模型: 图
@ -1056,7 +1051,7 @@ Google 发布了第一个列型存储数据库 [Bigtable](http://www.read.seas.h
### SQL 还是 NoSQL
<p align="center">
<img src="http://i.imgur.com/wXGqG5f.png">
<img src="http://i.imgur.com/wXGqG5f.png"/>
<br/>
<strong><a href="https://www.infoq.com/articles/Transition-RDBMS-NoSQL/">资料来源:从 RDBMS 转换到 NoSQL</a></strong>
</p>
@ -1092,12 +1087,12 @@ Google 发布了第一个列型存储数据库 [Bigtable](http://www.read.seas.h
##### 来源及延伸阅读SQL 或 NoSQL
- [扩展你的用户数到第一个千万](https://www.youtube.com/watch?v=vg5onp8TU6Q)
- [扩展你的用户数到第一个千万](https://www.youtube.com/watch?v=w95murBkYmU)
- [SQL 和 NoSQL 的不同](https://www.sitepoint.com/sql-vs-nosql-differences/)
## 缓存
<p align="center">
<img src="http://i.imgur.com/Q6z24La.png">
<img src="http://i.imgur.com/Q6z24La.png"/>
<br/>
<strong><a href="http://horicky.blogspot.com/2010/10/scalable-system-design-patterns.html">资料来源:可扩展的系统设计模式</a></strong>
</p>
@ -1168,7 +1163,7 @@ Redis 有下列附加功能:
#### 缓存模式
<p align="center">
<img src="http://i.imgur.com/ONjORqk.png">
<img src="http://i.imgur.com/ONjORqk.png"/>
<br/>
<strong><a href="http://www.slideshare.net/tmatyashovsky/from-cache-to-in-memory-data-grid-introduction-to-hazelcast">资料来源:从缓存到内存数据网格</a></strong>
</p>
@ -1180,7 +1175,7 @@ Redis 有下列附加功能:
- 将查找到的结果存储到缓存中
- 返回所需内容
```
```python
def get_user(self, user_id):
user = cache.get("user.{0}", user_id)
if user is None:
@ -1204,7 +1199,7 @@ def get_user(self, user_id):
#### 直写模式
<p align="center">
<img src="http://i.imgur.com/0vBc0hN.png">
<img src="http://i.imgur.com/0vBc0hN.png"/>
<br/>
<strong><a href="http://www.slideshare.net/jboner/scalability-availability-stability-patterns/">资料来源:可扩展性、可用性、稳定性、模式</a></strong>
</p>
@ -1223,7 +1218,7 @@ set_user(12345, {"foo":"bar"})
缓存代码:
```
```python
def set_user(user_id, values):
user = db.query("UPDATE Users WHERE id = {0}", user_id, values)
cache.set(user_id, user)
@ -1239,7 +1234,7 @@ def set_user(user_id, values):
#### 回写模式
<p align="center">
<img src="http://i.imgur.com/rgSrvjG.png">
<img src="http://i.imgur.com/rgSrvjG.png"/>
<br/>
<strong><a href="http://www.slideshare.net/jboner/scalability-availability-stability-patterns/">资料来源:可扩展性、可用性、稳定性、模式</a></strong>
</p>
@ -1257,7 +1252,7 @@ def set_user(user_id, values):
#### 刷新
<p align="center">
<img src="http://i.imgur.com/kxtjqgE.png">
<img src="http://i.imgur.com/kxtjqgE.png"/>
<br/>
<strong><a href=http://www.slideshare.net/tmatyashovsky/from-cache-to-in-memory-data-grid-introduction-to-hazelcast>资料来源:从缓存到内存数据网格</a></strong>
</p>
@ -1289,7 +1284,7 @@ def set_user(user_id, values):
## 异步
<p align="center">
<img src="http://i.imgur.com/54GYsSx.png">
<img src="http://i.imgur.com/54GYsSx.png"/>
<br/>
<strong><a href=http://lethain.com/introduction-to-architecting-systems-for-scale/#platform_layer>资料来源:可缩放系统构架介绍</a></strong>
</p>
@ -1319,7 +1314,7 @@ def set_user(user_id, values):
### 背压
如果队列开始明显增长,那么队列大小可能会超过内存大小,导致高速缓存未命中,磁盘读取,甚至性能更慢。[背压](http://mechanical-sympathy.blogspot.com/2012/05/apply-back-pressure-when-overloaded.html)可以通过限制队列大小来帮助我们,从而为队列中的作业保持高吞吐率和良好的响应时间。一旦队列填满,客户端将得到服务器忙活着 HTTP 503 状态码,以便稍后重试。客户端可以在稍后时间重试该请求,也许是[指数退避](https://en.wikipedia.org/wiki/Exponential_backoff)。
如果队列开始明显增长,那么队列大小可能会超过内存大小,导致高速缓存未命中,磁盘读取,甚至性能更慢。[背压](http://mechanical-sympathy.blogspot.com/2012/05/apply-back-pressure-when-overloaded.html)可以通过限制队列大小来帮助我们,从而为队列中的作业保持高吞吐率和良好的响应时间。一旦队列填满,客户端将得到服务器忙或者 HTTP 503 状态码,以便稍后重试。客户端可以在稍后时间重试该请求,也许是[指数退避](https://en.wikipedia.org/wiki/Exponential_backoff)。
### 异步的缺点:
@ -1335,7 +1330,7 @@ def set_user(user_id, values):
## 通讯
<p align="center">
<img src="http://i.imgur.com/5KeocQs.jpg">
<img src="http://i.imgur.com/5KeocQs.jpg"/>
<br/>
<strong><a href=http://www.escotal.com/osilayer.html>资料来源OSI 7层模型</a></strong>
</p>
@ -1370,7 +1365,7 @@ HTTP 是依赖于较低级协议(如 **TCP** 和 **UDP**)的应用层协议
### 传输控制协议TCP
<p align="center">
<img src="http://i.imgur.com/JdAsdvG.jpg">
<img src="http://i.imgur.com/JdAsdvG.jpg"/>
<br/>
<strong><a href="http://www.wildbunny.co.uk/blog/2012/10/09/how-to-make-a-multi-player-game-part-1/">资料来源:如何制作多人游戏</a></strong>
</p>
@ -1394,7 +1389,7 @@ TCP 对于需要高可靠性但时间紧迫的应用程序很有用。比如包
### 用户数据报协议UDP
<p align="center">
<img src="http://i.imgur.com/yzDrJtA.jpg">
<img src="http://i.imgur.com/yzDrJtA.jpg"/>
<br/>
<strong><a href="http://www.wildbunny.co.uk/blog/2012/10/09/how-to-make-a-multi-player-game-part-1">资料来源:如何制作多人游戏</a></strong>
</p>
@ -1423,7 +1418,7 @@ UDP 可靠性更低但适合用在网络电话、视频聊天,流媒体和实
### 远程过程调用协议RPC
<p align="center">
<img src="http://i.imgur.com/iF4Mkb5.png">
<img src="http://i.imgur.com/iF4Mkb5.png"/>
<br/>
<strong><a href="http://www.puncsky.com/blog/2016/02/14/crack-the-system-design-interview">Source: Crack the system design interview</a></strong>
</p>
@ -1521,7 +1516,7 @@ REST 关注于暴露数据。它减少了客户端/服务端的耦合程度,
* [REST vs JSON-RPC](http://stackoverflow.com/questions/15056878/rest-vs-json-rpc)
* [揭开 RPC 和 REST 的神秘面纱](http://etherealbits.com/2012/12/debunking-the-myths-of-rpc-rest/)
* [使用 REST 的缺点是什么](https://www.quora.com/What-are-the-drawbacks-of-using-RESTful-APIs)
* [破解系统设计面试](http://www.puncsky.com/blog/2016/02/14/crack-the-system-design-interview/)
* [破解系统设计面试](http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview)
* [Thrift](https://code.facebook.com/posts/1468950976659943/)
* [为什么在内部使用 REST 而不是 RPC](http://arstechnica.com/civis/viewtopic.php?t=1190508)
@ -1572,7 +1567,7 @@ Latency Comparison Numbers
L1 cache reference 0.5 ns
Branch mispredict 5 ns
L2 cache reference 7 ns 14x L1 cache
Mutex lock/unlock 100 ns
Mutex lock/unlock 25 ns
Main memory reference 100 ns 20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy 10,000 ns 10 us
Send 1 KB bytes over 1 Gbps network 10,000 ns 10 us
@ -1618,10 +1613,10 @@ Notes
| 问题 | 引用 |
| ----------------------- | ---------------------------------------- |
| 设计类似于 Dropbox 的文件同步服务 | [youtube.com](https://www.youtube.com/watch?v=PE4gwstWhmc) |
| 设计类似于 Google 的搜索引擎 | [queue.acm.org](http://queue.acm.org/detail.cfm?id=988407)<br/>[stackexchange.com](http://programmers.stackexchange.com/questions/38324/interview-question-how-would-you-implement-google-search)<br/>[ardendertat.com](http://www.ardendertat.com/2012/01/11/implementing-search-engines/)<br>[stanford.edu](http://infolab.stanford.edu/~backrub/google.html) |
| 设计类似于 Google 的搜索引擎 | [queue.acm.org](http://queue.acm.org/detail.cfm?id=988407)<br/>[stackexchange.com](http://programmers.stackexchange.com/questions/38324/interview-question-how-would-you-implement-google-search)<br/>[ardendertat.com](http://www.ardendertat.com/2012/01/11/implementing-search-engines/)<br/>[stanford.edu](http://infolab.stanford.edu/~backrub/google.html) |
| 设计类似于 Google 的可扩展网络爬虫 | [quora.com](https://www.quora.com/How-can-I-build-a-web-crawler-from-scratch) |
| 设计 Google 文档 | [code.google.com](https://code.google.com/p/google-mobwrite/)<br/>[neil.fraser.name](https://neil.fraser.name/writing/sync/) |
| 设计类似 Redis 的值存储 | [slideshare.net](http://www.slideshare.net/dvirsky/introduction-to-redis) |
| 设计类似 Redis 的值存储 | [slideshare.net](http://www.slideshare.net/dvirsky/introduction-to-redis) |
| 设计类似 Memcached 的缓存系统 | [slideshare.net](http://www.slideshare.net/oemebamo/introduction-to-memcached) |
| 设计类似亚马逊的推荐系统 | [hulu.com](http://tech.hulu.com/blog/2011/09/19/recommendation-system.html)<br/>[ijcai13.org](http://ijcai13.org/files/tutorial_slides/td3.pdf) |
| 设计类似 Bitly 的短链接系统 | [n00tc0d3r.blogspot.com](http://n00tc0d3r.blogspot.com/) |
@ -1645,7 +1640,7 @@ Notes
> 关于现实中真实的系统是怎么设计的文章。
<p align="center">
<img src="http://i.imgur.com/TcUo2fw.png">
<img src="http://i.imgur.com/TcUo2fw.png"/>
<br/>
<strong><a href="https://www.infoq.com/presentations/Twitter-Timeline-Scalability">Source: Twitter timelines at scale</a></strong>
</p>
@ -1782,7 +1777,7 @@ Notes
* [mmcgrana/services-engineering](https://github.com/mmcgrana/services-engineering)
* [System design cheat sheet](https://gist.github.com/vasanthk/485d1c25737e8e72759f)
* [A distributed systems reading list](http://dancres.github.io/Pages/)
* [Cracking the system design interview](http://www.puncsky.com/blog/2016/02/14/crack-the-system-design-interview/)
* [Cracking the system design interview](http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview)
## 联系方式

1786
README-zh-TW.md Normal file
View File

@ -0,0 +1,1786 @@
*[English](README.md) ∙ [日本語](README-ja.md) ∙ [简体中文](README-zh-Hans.md) ∙ [繁體中文](README-zh-TW.md) | [العَرَبِيَّة‎](https://github.com/donnemartin/system-design-primer/issues/170) ∙ [বাংলা](https://github.com/donnemartin/system-design-primer/issues/220) ∙ [Português do Brasil](https://github.com/donnemartin/system-design-primer/issues/40) ∙ [Deutsch](https://github.com/donnemartin/system-design-primer/issues/186) ∙ [ελληνικά](https://github.com/donnemartin/system-design-primer/issues/130) ∙ [עברית](https://github.com/donnemartin/system-design-primer/issues/272) ∙ [Italiano](https://github.com/donnemartin/system-design-primer/issues/104) ∙ [韓國語](https://github.com/donnemartin/system-design-primer/issues/102) ∙ [فارسی](https://github.com/donnemartin/system-design-primer/issues/110) ∙ [Polski](https://github.com/donnemartin/system-design-primer/issues/68) ∙ [русский язык](https://github.com/donnemartin/system-design-primer/issues/87) ∙ [Español](https://github.com/donnemartin/system-design-primer/issues/136) ∙ [ภาษาไทย](https://github.com/donnemartin/system-design-primer/issues/187) ∙ [Türkçe](https://github.com/donnemartin/system-design-primer/issues/39) ∙ [tiếng Việt](https://github.com/donnemartin/system-design-primer/issues/127) ∙ [Français](https://github.com/donnemartin/system-design-primer/issues/250) | [Add Translation](https://github.com/donnemartin/system-design-primer/issues/28)*
# 系統設計入門
<p align="center">
<img src="http://i.imgur.com/jj3A5N8.png"/>
<br/>
</p>
## 動機
> 學習如何設計大型系統。
>
> 準備系統設計的面試。
### 學習如何設計大型系統
學習如何設計可擴展的系統會幫助你成為一個更好的工程師。
系統設計是一個廣泛的主題。在網路上,關於**系統設計的資源也是不計其數**。
本專案將**許多資源進行分門別類**,幫助你學習如何建構可擴展的系統。
### 從開放原始碼社群中學習
這是一個早期、持續不斷更新的開放原始碼專案。
[任何貢獻](#如何貢獻) 都相當歡迎!
### 準備系統設計的面試
除了程式的面試外,系統設計在許多科技公司的**面試流程**中都是**必要**的一環。
**練習常見的系統設計問題**,並且將你的設計結果和**參考答案**進行**比對**:討論、程式碼和圖表。
關於面試的其他主題:
* [學習指南](#學習指南)
* [如何解決一個系統設計的面試題目](#如何解決一個系統設計的面試題目)
* [系統設計面試問題與**解答**](#系統設計面試問題與解答)
* [物件導向設計問題與**解答**](#物件導向設計面試問題與解答)
* [其他的系統設計面試問題](#其他的系統設計面試問題)
## 學習單字卡
<p align="center">
<img src="http://i.imgur.com/zdCAkB3.png"/>
<br/>
</p>
底下提供的[學習單字卡](https://apps.ankiweb.net/)以每隔一段時間間隔出現的方式,幫助你學習系統設計的概念。
* [系統設計單字卡](resources/flash_cards/System%20Design.apkg)
* [系統設計練習單字卡](resources/flash_cards/System%20Design%20Exercises.apkg)
* [物件導向設計練習單字卡](resources/flash_cards/OO%20Design.apkg)
這些是非常棒的學習資源,隨時都可以使用。
### 程式設計學習資源:互動式程式學習設計
你正在尋找資源來面對[**程式語言面試**](https://github.com/donnemartin/interactive-coding-challenges)嗎?
<p align="center">
<img src="http://i.imgur.com/b4YtAEN.png"/>
<br/>
</p>
請參考 [**互動程式語言學習挑戰**](https://github.com/donnemartin/interactive-coding-challenges),當中還包含了底下的學習單字卡:
* [程式語言學習單卡](https://github.com/donnemartin/interactive-coding-challenges/tree/master/anki_cards/Coding.apkg)
## 如何貢獻
> 從社群中學習
隨時歡迎提交 Pull Request
* 修正錯誤
* 改善章節內容
* 增加新的章節
* [翻譯](https://github.com/donnemartin/system-design-primer/issues/28)
某些還需要再完善的章節放在 [修正中](#仍在進行中)。
請參考 [貢獻指南](CONTRIBUTING.md)。
## 系統設計主題的索引
> 底下是數個系統設計的主題,包含了優點及缺點。**要記得,每一個系統設計的考量都包含著某種取捨**。
>
> 每一章節都包含更深入資源的連結。
<p align="center">
<img src="http://i.imgur.com/jrUBAF7.png"/>
<br/>
</p>
* [系統設計主題:從這裡開始](#系統設計主題從這裡開始)
* [第一步:複習關於可擴展性的影片講座](#第一步複習關於可擴展性的影片講座)
* [第二步:複習關於可擴展性的文章](#第二步複習關於可擴展性的文章)
* [下一步](#下一步)
* [效能與可擴展性](#效能與可擴展性)
* [延遲與吞吐量](#延遲與吞吐量)
* [可用性與一致性](#可用性與一致性)
* [CAP 理論](#cap-理論)
* [CP-一致性與部分容錯性](#cp-一致性與部分容錯性)
* [AP-可用性與部分容錯性](#ap-可用性與部分容錯性)
* [一致性模式](#一致性模式)
* [弱一致性](#弱一致性)
* [最終一致性](#最終一致性)
* [強一致性](#強一致性)
* [可用性模式](#可用性模式)
* [容錯轉移](#容錯轉移)
* [複寫機制](#複寫機制)
* [域名系統](#域名系統)
* [內容傳遞網路(CDN)](#內容傳遞網路cdn)
* [推送式 CDNs](#推送式-cdns)
* [拉取式 CDNs](#拉取式-cdns)
* [負載平衡器](#負載平衡器)
* [主動到備用切換模式(AP Mode)](#主動到備用切換模式ap-mode)
* [雙主動切換模式(AA Mode)](#雙主動切換模式aa-mode)
* [第四層負載平衡](#第四層負載平衡)
* [第七層負載平衡](#第七層負載平衡)
* [水平擴展](#水平擴展)
* [反向代理(網頁伺服器)](#反向代理網頁伺服器)
* [負載平衡器與反向代理伺服器](#負載平衡器與反向代理伺服器)
* [應用層](#應用層)
* [微服務](#微服務)
* [服務發現](#服務發現)
* [資料庫](#資料庫)
* [關連式資料庫管理系統(RDBMS)](#關連式資料庫管理系統rdbms)
* [主從複寫](#主從複寫)
* [主動模式複寫](#主動模式複寫)
* [聯邦式資料庫](#聯邦式資料庫)
* [分片](#分片)
* [反正規化](#反正規化)
* [SQL 優化](#sql-優化)
* [NoSQL](#nosql)
* [鍵-值對的資料庫](#鍵-值對的資料庫)
* [文件類型資料庫](#文件類型資料庫)
* [列儲存型資料庫](#列儲存型資料庫)
* [圖形資料庫](#圖形資料庫)
* [SQL 或 NoSQL](#sql-或-nosql)
* [快取](#快取)
* [客戶端快取](#客戶端快取)
* [CDN 快取](#cdn-快取)
* [網站伺服器快取](#網站伺服器快取)
* [資料庫快取](#資料庫快取)
* [應用程式快取](#應用程式快取)
* [資料庫查詢級別的快取](#資料庫查詢級別的快取)
* [物件級別的快取](#物件級別的快取)
* [什麼時候要更新快取](#什麼時候要更新快取)
* [快取模式](#快取模式)
* [寫入模式](#寫入模式)
* [事後寫入(回寫)](#事後寫入回寫)
* [更新式快取](#更新式快取)
* [非同步機制](#非同步機制)
* [訊息佇列](#訊息佇列)
* [工作佇列](#工作佇列)
* [背壓機制](#背壓機制)
* [通訊](#通訊)
* [傳輸控制通訊協定(TCP)](#傳輸控制通訊協定tcp)
* [使用者資料流通訊協定 (UDP)](#使用者資料流通訊協定-udp)
* [遠端程式呼叫 (RPC)](#遠端程式呼叫-rpc)
* [具象狀態轉移 (REST)](#具象狀態轉移-rest)
* [資訊安全](#資訊安全)
* [附錄](#附錄)
* [2 的次方表](#2-的次方表)
* [每個開發者都應該知道的延遲數量級](#每個開發者都應該知道的延遲數量級)
* [其他的系統設計面試問題](#其他的系統設計面試問題)
* [真實世界的架構](#真實世界的架構)
* [公司的系統架構](#公司的系統架構)
* [公司的工程部落格](#公司的工程部落格)
* [仍在進行中](#仍在進行中)
* [致謝](#致謝)
* [聯絡資訊](#聯絡資訊)
* [授權](#授權)
## 學習指南
> 基於你面試的時間 (短、中、長) 來複習這些建議的主題。
![Imgur](http://i.imgur.com/OfVllex.png)
**Q: 對於面試者來說,我需要知道這裡所有的知識嗎?**
**A: 不,如果是為了面試,你不需要知道這裡所有的知識**。
在一場面試中,你會被問到什麼問題取決於以下幾點:
* 你有多少經驗
* 你的技術背景
* 你面試什麼職位
* 你面試什麼公司
* 你的幸運程度
越有經驗的面試者通常被期望瞭解更多系統設計的知識。如果是架構師或團隊負責人則被預期瞭解更多除了個人貢獻以外的知識。頂尖的科技公司通常也會有一次或多次的系統設計面試。
面試時,會很廣泛地展開,並在幾個特定領域深入探討。這裡會幫助你了解一些系統設計不同的主題,基於你面試的時間、經驗、職位和公司來調整你所需要涉獵的知識內容。
* **短期** - 以系統設計的**廣度**為目標。通過解決**一些**面試題目來練習。
* **中期** - 以系統設計的**廣度**和**初級深度**為目標。通過解決**很多**面試題目來練習。
* **長期** - 以系統設計主題的**廣度**和**高級深度**為目標。通過解決**大部分**面試題目來練習。
| | 短期 | 中期 | 長期 |
|---------------------------------------------------------------------------------|------|------|--------|
| 閱讀 [系統設計主題的索引](#系統設計主題的索引) 來取得關於系統如何運作的廣泛知識 | :+1: | :+1: | :+1: |
| 閱讀一些你要面試的 [公司的工程部落格](#公司的工程部落格) 文章 | :+1: | :+1: | :+1: |
| 閱讀關於 [真實世界的架構](#真實世界的架構) | :+1: | :+1: | :+1: |
| 複習 [如何解決一個系統設計的面試題目](#如何解決一個系統設計的面試題目) | :+1: | :+1: | :+1: |
| 完成 [系統設計面試題目與解答](#系統設計面試問題與解答) | 一些 | 很多 | 大部分 |
| 完成 [物件導向設計與解答](#物件導向設計面試問題與解答) | 一些 | 很多 | 大部分 |
| 複習 [其他的系統設計面試問題](#其他的系統設計面試問題) | 一些 | 很多 | 大部分 |
## 如何解決一個系統設計的面試題目
> 如何面對一個系統設計的面試題目
系統設計是一個**開放式的對話過程**,面試官會期望由你來主導這個對話。
你可以使用下面的步驟來引導整個討論過程。為了鞏固這個流程,請使用下面的步驟完成 [系統設計面試題目與解答](#系統設計面試問題與解答) 這個章節。
### 第一步:描述使用的場景、限制及假設
把問題的需求和範圍等資訊搜集起來。詢問以下問題,讓我們可以明確的定義使用場景和限制,對於提出的假設進行討論:
* 誰會使用這個系統?
* 他們怎麼使用系統?
* 有多少使用者?
* 系統的作用是什麼?
* 系統的輸入和輸出是什麼?
* 我們預期希望處理多少資料?
* 我們希望每秒處理多少請求?
* 預期的讀、寫比例為何?
### 第二步:建立一個高階的設計
使用重要的元件來建立一個高階的設計。
* 畫出主要的元件與其相互連接情況
* 證明你的想法
### 第三步: 設計核心的元件
對每一個核心元件進行深入的分析。舉例來說, 如果你被問到 [設計一個短網址的服務](solutions/system_design/pastebin/README.md) ,可以開始討論以下內容:
* 產生並儲存一個完整網址的 Hash
* [MD5](solutions/system_design/pastebin/README.md) 和 [Base62](solutions/system_design/pastebin/README.md)
* Hash 碰撞
* SQL 或 NoSQL
* 資料庫的模型
* 將一個 hash 過後的網址轉換為完整的網址
* 資料庫查詢
* API 和物件導向設計
### 第四步:評估你的設計
確認及指出你的設計的瓶頸與限制,舉例來說,你需要底下幾個元件來擴展你的設計嗎?
* 負載平衡
* 水平擴展
* 快取
* 資料庫切片
針對你的設計討論可能的解決方法與代價。每個設計都有取捨。使用 [可擴展的設計原則](#系統設計主題:從這裡開始) 來處理系統瓶頸。
### 快速有效的進行估算
你可能被要求針對你的設計進行一些估算,可以參考 [附錄](#附錄) 的一些資源:
* [使用快速估算法](http://highscalability.com/blog/2011/1/26/google-pro-tip-use-back-of-the-envelope-calculations-to-choo.html)
* [2 的次方表](#2-的次方表)
* [每個開發者都應該知道的延遲數量級](#每個開發者都應該知道的延遲數量級)
### 相關資源與延伸閱讀
查看以下的連結獲得更好的做法:
* [如何在系統設計的面試中勝出](https://www.palantir.com/2011/10/how-to-rock-a-systems-design-interview/)
* [系統設計的面試](http://www.hiredintech.com/system-design)
* [系統架構與設計的面試介紹](https://www.youtube.com/watch?v=ZgdS0EUmn70)
## 系統設計面試問題與解答
> 常見的系統設計面試問題與相關範例的討論、程式碼以及圖表。
>
> 相關的解答位於 `解答/` 的資料夾中。
| 問題 | |
|-----------------------------------------------------------------------------------------------------|--------------------------------------------------------|
| 設計 Pastebin.com (或 Bit.ly) | [解答](solutions/system_design/pastebin/README.md) |
| 設計一個像是 Twitter 的 timeline (或 Facebook feed)設計一個 Twitter 搜尋功能 (or Facebook 搜尋功能) | [解答](solutions/system_design/twitter/README.md) |
| 設計一個爬蟲系統 | [解答](solutions/system_design/web_crawler/README.md) |
| 設計 Mint.com 網站 | [解答](solutions/system_design/mint/README.md) |
| 設計一個社交網站的資料結構 | [解答](solutions/system_design/social_graph/README.md) |
| 設計一個搜尋引擎使用的鍵值儲存資料結構 | [解答](solutions/system_design/query_cache/README.md) |
| 設計一個根據產品分類的亞馬遜銷售排名 | [解答](solutions/system_design/sales_rank/README.md) |
| 在 AWS 上設計一個百萬用戶等級的系統 | [解答](solutions/system_design/scaling_aws/README.md) |
| 增加一個系統設計的問題 | [貢獻](#如何貢獻) |
### 設計 Pastebin.com (或 Bit.ly)
[閱讀練習與解答](solutions/system_design/pastebin/README.md)
![Imgur](http://i.imgur.com/4edXG0T.png)
### 設計一個像是 Twitter 的 timeline (或 Facebook feed)設計一個 Twitter 搜尋功能 (or Facebook 搜尋功能)
[閱讀練習與解答](solutions/system_design/twitter/README.md)
![Imgur](http://i.imgur.com/jrUBAF7.png)
### 設計一個爬蟲系統
[閱讀練習與解答](solutions/system_design/web_crawler/README.md)
![Imgur](http://i.imgur.com/bWxPtQA.png)
### 設計 Mint.com 網站
[閱讀練習與解答](solutions/system_design/mint/README.md)
![Imgur](http://i.imgur.com/V5q57vU.png)
### 設計一個社交網站的資料結構
[閱讀練習與解答](solutions/system_design/social_graph/README.md)
![Imgur](http://i.imgur.com/cdCv5g7.png)
### 設計一個搜尋引擎使用的鍵值儲存資料結構
[閱讀練習與解答](solutions/system_design/query_cache/README.md)
![Imgur](http://i.imgur.com/4j99mhe.png)
### 設計一個根據產品分類的亞馬遜銷售排名
[閱讀練習與解答](solutions/system_design/sales_rank/README.md)
![Imgur](http://i.imgur.com/MzExP06.png)
### 在 AWS 上設計一個百萬用戶等級的系統
[閱讀練習與解答](solutions/system_design/scaling_aws/README.md)
![Imgur](http://i.imgur.com/jj3A5N8.png)
## 物件導向設計面試問題與解答
> 常見的物件導向面試問題與案例探討、程式碼與圖表。
>
> 相關的答案為在 `解答/` 目錄中。
>**注意: 本章節仍在完善內容中**
| 問題 | |
|--------------------------|----------------------------------------------------------------------------|
| 設計一個 hash map | [解答](solutions/object_oriented_design/hash_table/hash_map.ipynb) |
| 設計一個 LRU 快取 | [解答](solutions/object_oriented_design/lru_cache/lru_cache.ipynb) |
| 設計一個客服系統 | [解答](solutions/object_oriented_design/call_center/call_center.ipynb) |
| 設計一副牌 | [解答](solutions/object_oriented_design/deck_of_cards/deck_of_cards.ipynb) |
| 設計一個停車場 | [解答](solutions/object_oriented_design/online_chat/online_chat.ipynb) |
| 設計一個環形陣列 | [如何貢獻](#如何貢獻) |
| 增加一個物件導向設計問題 | [如何貢獻](#如何貢獻) |
## 系統設計主題:從這裡開始
你是系統設計的新手嗎?
首先,你需要對於基本的原則有一定的認識,知道他們是什麼,如何使用,以及他們的優缺點。
### 第一步:複習關於可擴展性的影片講座
[哈佛大學可擴展性的影片](https://www.youtube.com/watch?v=-W9F__D3oY4)
* 包含以下主題:
* 垂直擴展
* 水平擴展
* 快取
* 負載平衡
* 資料庫複寫
* 資料庫分割
### 第二步:複習關於可擴展性的文章
[可擴展性](http://www.lecloud.net/tagged/scalability/chrono)
* 包含以下主題:
* [複製](http://www.lecloud.net/post/7295452622/scalability-for-dummies-part-1-clones)
* [資料庫](http://www.lecloud.net/post/7994751381/scalability-for-dummies-part-2-database)
* [快取](http://www.lecloud.net/post/9246290032/scalability-for-dummies-part-3-cache)
* [非同步](http://www.lecloud.net/post/9699762917/scalability-for-dummies-part-4-asynchronism)
### 下一步
接下來,我們需要看看某些取捨:
* **效能** vs **可擴展性**
* **延遲** vs **吞吐量**
* **可用性** vs **一致性**
記住,任何設計都是取捨。
接著,我們將會深入更具體的主題,包含 DNS、CDN 和負載平衡。
## 效能與可擴展性
如果服務**性能**的增加和資源的投入是成正比時,代表服務是可擴展的。一般來說,增加性能代表服務更多的工作單元,也可以處理更多的資料。<sup><a href="http://www.allthingsdistributed.com/2006/03/a_word_on_scalability.html">1</a></sup>
從另一方面來看看性能與可擴展性:
* 如果你的系統存在**性能**問題時,對單一使用者來說的感覺是慢的。
* 如果你的系統存在**可擴展性**問題時,對於單一使用者來說感覺較快,但在高負載的時候就會變慢。
### 來源及延伸閱讀
* [簡談可擴展性](http://www.allthingsdistributed.com/2006/03/a_word_on_scalability.html)
* [可擴展性、可用性、穩定性與相關模式](http://www.slideshare.net/jboner/scalability-availability-stability-patterns/)
## 延遲與吞吐量
**延遲** 指執行一個操作或運算結果所花費的時間。
**吞吐量** 指單位時間內執行此類型操作或運算的數量。
一般來說,你應該以**可接受的延遲**數量下的**最大化吞吐量**為設計目標。
### 來源及延伸閱讀
* [了解延遲與吞吐量](https://community.cadence.com/cadence_blogs_8/b/sd/archive/2010/09/13/understanding-latency-vs-throughput)
## 可用性與一致性
### CAP 理論
<p align="center">
<img src="http://i.imgur.com/bgLMI2u.png"/>
<br/>
<i><a href=http://robertgreiner.com/2014/08/cap-theorem-revisited>來源:再看 CAP 理論</a></i>
</p>
在一個分散式系統中,只能滿足以下三個項目的任兩項:
* **一致性** - 每次讀取都可以得到最新的資料,但偶爾會拿到錯誤
* **可用性** - 每次讀取都可以得到非錯誤的回應,但不能保證可以得到最新的資料
* **部分容錯性** - 在任意分區的網路故障情況下,系統仍然能夠持續運行
*網路是不可靠的,你的設計必須要確保部分容錯性,所以你只能夠在一致性與可用性中做出取捨。*
#### CP-一致性與部分容錯性
等待分區的節點回覆可能會導致超時錯誤如果你的系統的需求是需要保證原子讀寫時CP 是一個不錯的選擇。
#### AP-可用性與部分容錯性
每個進行回覆的節點中的最新版本可能不是最新的,當分區節點解析完畢後,寫入的操作可能需要一些時間來傳播資料。
當你的系統需求需要保證 [最終一致性](#最終一致性) 或當外部系統故障時系統要能夠繼續運作時AP 是一個不錯的選擇。
### 來源及延伸閱讀
* [複習 CAP 理論](http://robertgreiner.com/2014/08/cap-theorem-revisited/)
* [簡單的介紹 CAP 理論](http://ksat.me/a-plain-english-introduction-to-cap-theorem/)
* [CAP 問與答](https://github.com/henryr/cap-faq)
## 一致性模式
當你的資料有多個副本時,要考慮怎麼同步他們,以便讓使用者有一致的資料顯示。想想 [CAP 理論](#cap-理論) 中的一致性定律 - 每次的訪問都可以得到最新的資料,但可能也會收到錯誤的回應。
### 弱一致性
在寫入之後,任何的存取不一定可以拿到資料,弱一致性將盡力確保能存取到最新的資料。
這種方式可以在 memcached 等系統中看到。弱一致性可以在 VoIP、視訊聊天或其他即時多人線上遊戲中看到相關的使用案例。比方說如果你在通話中遺失幾秒鐘時間的資料再重新連接後你是無法聽到這幾秒鐘的內容。
### 最終一致性
在寫入後的讀取操作最終可以看到被寫入的資料(通常在數毫秒內)。資料透過非同步的方式被複製。
DNS 或是電子郵件系統使用的就是這種方式,最終一致性在高可用的系統中效果很好。
### 強一致性
在寫入後,讀取將立刻取得資料,資料是透過同步的方式寫入。
檔案系統或資料庫系統就是使用這種方式,強一致性在需要紀錄的系統中表現很好。
### 來源及延伸閱讀
* [資料中心的記錄行為](http://snarfed.org/transactions_across_datacenters_io.html)
## 可用性模式
關於可用性有兩種模式:**容錯轉移** 和 **複寫**。
### 容錯轉移
#### 主動到備用切換模式(AP Mode)
在這個模式下heartbeat 訊號會在主動和備用的機器中發送,當 heartbeat 中斷時,備用的機器就會切換為主動機器的 IP 位置接替服務。
當機的時間取決於備用的機器是在「熱」待機狀態還是「冷」待機狀態。只有處於主動的機器會處理使用者來的流量。
這個模式的切換也被稱為主從的切換模式。
#### 雙主動切換模式(AA Mode)
在此模式下,兩台伺服器都會負責處理流量,流量會在他們之間進行分散負載。
如果是外部網路的伺服器DNS 需要知道兩台機器的 IP 位置,如果是內部網路的伺服器,應用程式邏輯需要知道這兩台機器。
雙主動切換模式也被稱為 master-master 切換。
### 缺點:容錯轉移
* 容錯轉移會需要增加額外的硬體與複雜度。
* 如果在新寫入的資料被複製到備用的機器前系統就發生故障,那有可能會遺失資料。
### 複寫機制
#### 主動到備用複寫與雙主動複寫
這一個主題進一步討論了 [資料庫](#資料庫) 部分:
* [主動到備用複寫](#主動到備用複寫)
* [雙主動複寫](#雙主動複寫)
## 域名系統
<p align="center">
<img src="http://i.imgur.com/IOyLj4i.jpg"/>
<br/>
<i><a href=http://www.slideshare.net/srikrupa5/dns-security-presentation-issa>資料來源DNS 安全介紹</a></i>
</p>
DNS 是將域名轉換為 IP 地址的系統。
DNS 是階層式的架構,一部分的 DNS 伺服器位於頂層,當查詢域名時,你的路由器或 ISP 業者會提供連接到 DNS 伺服器的資訊。較底層的 DNS 伺服器會快取查詢的結果,而這些快取資訊會因為 DNS 的傳遞而逐漸更新。DNS 的結果可以暫存在瀏覽器或操作系統中一段時間,時間的長短取決於 [存活時間(TTL)](https://en.wikipedia.org/wiki/Time_to_live) 的設定。
* **NS 記錄 (域名伺服器)** - 指定解析域名或子域名的 DNS 伺服器。
* **MX 記錄 (電子郵件交換伺服器)** - 指定接收電子郵件的伺服器。
* **A 記錄 (地址)** - 指向要對應的 IP 位置。
* **CNAME (別名)** - 從一個域名指向另外一個域名,或是 `CNAME` (example.com 指向 www.example.com) 或指向一個 `A` 記錄。
[CloudFlare](https://www.cloudflare.com/dns/) 和 [Route 53](https://aws.amazon.com/route53/) 提供了 DNS 的服務。而這些 DNS 服務商透過以下幾種方式來決定流量如何被分派:
* [加權輪詢](http://g33kinfo.com/info/archives/2657)
* 防止流量進入正在維修中的伺服器
* 在不同大小的集群中進行負載平衡
* A/B 測試
* 基於延遲來路由請求
* 基於地理位置來路由請求
### DNS 的缺點
* 儘管可以透過快取來減輕 DNS 的延遲,但連接 DNS 伺服器還是帶來了些許的延遲。
* DNS 伺服器的管理是複雜的,儘管他通常由 [政府、ISP 業者或大公司](http://superuser.com/questions/472695/who-controls-the-dns-servers/472729) 來處理。
* DNS 伺服器會有 [DDoS 攻擊](http://dyn.com/blog/dyn-analysis-summary-of-friday-october-21-attack/) ,讓不知道 Twitter IP 的使用者無法訪問 Twitter 網站。
### 來源及延伸閱讀
* [DNS 架構](https://technet.microsoft.com/en-us/library/dd197427(v=ws.10).aspx)
* [維基百科](https://en.wikipedia.org/wiki/Domain_Name_System)
* [DNS 文章](https://support.dnsimple.com/categories/dns/)
## 內容傳遞網路(CDN)
<p align="center">
<img src="http://i.imgur.com/h9TAuGI.jpg"/>
<br/>
<i><a href=https://www.creative-artworks.eu/why-use-a-content-delivery-network-cdn/>來源:為什麼要使用 CDN</a></i>
</p>
內容傳遞網路(CDN)是一種全球性的分散式代理伺服器,它透過靠近使用者的伺服器來提供檔案。通常 HTML/CSS/JS、圖片或影片等檔案會靜態檔案會透過 CDN 來提供,儘管 Amazon 的 CloudFront 也支援了動態內容的 CDN 服務。而 CDN 的 DNS 服務會告知使用者要連接哪一台伺服器。
透過 CDN 來取得檔案可以大幅度地增加請求的效率,因為:
* 從靠近使用者的伺服器來拿檔案
* 透過 CDN 來回應使用者,你的原始伺服器不需要處理請求
### 推送式 CDNs
當你的伺服器有檔案變動時,推送 CDN 會接收到新的變動內容,並重寫 URL 位置指向新的內容。你可以設定檔案內容什麼時候過期以及何時更新,檔案內容只有在變更或新增的時候才會推送,最小化流量,但最大化儲存。
流量較小的網站,或是內容不是經常更新的網站使用推送式的 CDN 相當適合,因為內容會被經常放置在 CDN 內,而不是常常需要重新抓取新檔案。
### 拉取式 CDNs
拉取式的 CDN 指的是當地一個使用者來請求該資源時,才從伺服器上抓取對應檔案。將檔案留在伺服器上並且重寫指向 CDN 的 URL直到檔案被快取在 CDN 上為止,請求都會比較慢。
[存活時間 (TTL)](https://en.wikipedia.org/wiki/Time_to_live) 決定檔案要被緩存多久的時間。拉取式 CDN 可以節省儲存空間,但在過期的文件被更新之前,則會導致多餘的流量。
拉取式的 CDN 適合高流量的網站,因為檔案會被平均的分散在各個結點伺服器中。
### CDN 的缺點
* CDN 的成本取決於流量,在權衡評估後,你可能會因為成本而放棄使用。
* 如果在 TTL 過期之前就更新內容CDN 的緩存內容可能會過期。
* 需要改變靜態內容的網址來指向 CDN。
### 來源及延伸閱讀
* [全球性的 CDN](http://repository.cmu.edu/cgi/viewcontent.cgi?article=2112&context=compsci)
* [拉取式和推拉式 CDN 的差別](http://www.travelblogadvice.com/technical/the-differences-between-push-and-pull-cdns/)
* [維基百科](https://en.wikipedia.org/wiki/Content_delivery_network)
## 負載平衡器
<p align="center">
<img src="http://i.imgur.com/h81n9iK.png"/>
<br/>
<i><a href=http://horicky.blogspot.com/2010/10/scalable-system-design-patterns.html>來源:可擴展的系統設計模式</a></i>
</p>
負載平衡將使用者的請求分發到後端伺服器和資料庫,不管是哪種情況,負載平衡器會將回應返回給對應的使用者。而負載平衡器之所以有效在於以下幾點:
* 避免請求被轉到非正常運作的伺服器
* 避免資源過載
* 避免單點失敗
負載平衡器可以透過硬體(較昂貴)或 HAProxy 等軟體來實現。
其餘額外的好處有:
* **SSL 終結** - 將傳入的請求解密,並且加密伺服器的回應,如此一來後端伺服器就不需要進行這些高度消耗資源的運算
* 不需要在每一台機器上安裝 [X.509 憑證](https://en.wikipedia.org/wiki/X.509)。
* **Session 保存** - 發行 cookie並將特定使用者的請求路由到同樣的後端伺服器上。
為了避免故障,通常會採用 [主動到備用切換模式](#主動到備用切換模式(AP Mode)) 或 [雙主動切換模式](#雙主動切換模式(AA Mode)) 這樣多個負載平衡器的模式。
負載平衡器會基於多種方法來路由請求:
* 隨機
* 最少負載
* Session/cookies
* [輪詢調度或加權輪詢調度](http://g33kinfo.com/info/archives/2657)
* [第四層負載平衡](#第四層負載平衡)
* [第七層負載平衡](#第七層負載平衡)
### 第四層負載平衡
第四層的負載平衡器會監看 [傳輸層](#傳輸層) 的資訊來決定如何分發請求。一般來說,這包含了來源、目標 IP 位置,以及在 header 中的 port但不包含資料本身的內容。第四層的負載平衡器會透過 [網路地址轉換(NAT)](https://www.nginx.com/resources/glossary/layer-4-load-balancing/) 來向上游的伺服器轉發資料。
### 第七層負載平衡
第七層的負載平衡器會監看 [應用層](#應用層) 來決定如何分發請求。這包含了請求的 header、訊息和 cookies。這種負載平衡器會終結網路的流量、讀取訊息並做出如何轉發訊息的決定並把流量轉往對應的伺服器。舉例來說一個第七層的負載平衡器可以將影音的流量轉往負責影音流量的伺服器而將更敏感的使用者帳單的請求轉往安全性更強的伺服器。
第四層的負載平衡比起第七層的所要花費的時間和計算資源更低,雖然這對於現代商用硬體的性能影響已經微乎其微了。
### 水平擴展
負載平衡器一樣可以幫助水平擴展,提高性能與可用性。使用這種方式的擴展比起在單一機器的**垂直擴展**來說性價比更高,同時,聘請商用硬體的人才比起特定企業級系統人才來的更加容易。
#### 水平擴展的缺點
* 水平擴展會增加複雜性,同時也涉及了多台伺服器的議題
* 伺服器應該是無狀態的:不應該包括像是 session 或資料圖片等和使用者相關的內容
* Session 可以集中儲存在資料庫或 [快取](#快取)(Redis、Memcached) 等資料儲存中。
* 快取伺服器或資料庫需要隨著伺服器的增加而進行擴展,以便處理更多的請求。
### 負載平衡器的缺點
* 當負載平衡器資源不夠或沒有正確設定時,他可能會成為效能的瓶頸
* 使用負載平衡器來避免單點失敗會增加架構的複雜性
* 只有一台負載平衡器時,一樣有單點失敗的問題。而多台的負載平衡器一樣增加了架構的複雜性。
### 來源及延伸閱讀
* [NGINX 架構](https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/)
* [HAProxy 架構指南](http://www.haproxy.org/download/1.2/doc/architecture.txt)
* [可擴展性](http://www.lecloud.net/post/7295452622/scalability-for-dummies-part-1-clones)
* [維基百科](https://en.wikipedia.org/wiki/Load_balancing_(computing))
* [第四層負載平衡](https://www.nginx.com/resources/glossary/layer-4-load-balancing/)
* [第七層負載平衡](https://www.nginx.com/resources/glossary/layer-7-load-balancing/)
* [ELB 監聽器設定](http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-listener-config.html)
## 反向代理(網頁伺服器)
<p align="center">
<img src="http://i.imgur.com/n41Azff.png"/>
<br/>
<i><a href=https://upload.wikimedia.org/wikipedia/commons/6/67/Reverse_proxy_h2g2bob.svg>來源:維基百科</a></i>
<br/>
</p>
反向代理伺服器是一個集中內部服務,並提供統一個介面給公開使用者的伺服器。來自客戶端的請求會先被反向代理伺服器轉發到可以接收服務的伺服器,然後再由代理伺服器將結果返回給客戶端。
這樣做的好處有:
* **增加安全性** - 隱藏後端伺服器的資訊、可以設定 IP 的黑名單、限制每個客戶端的連線數量等。
* **增加可擴展性與靈活性** - 客戶端只會看到反向代理伺服器的 IP 或域名,這樣你就可以增加背後伺服器的數量或設定而不影響客戶端。
* **SSL 終止** - 解密傳入的請求、加密伺服器的回應,這樣後端伺服器就不需要進行這些高成本的操作
* 不需要在每一台伺服器安裝 [X.509 憑證](https://en.wikipedia.org/wiki/X.509)。
* **壓縮** - 壓縮伺服器的回應
* **快取** - 直接在代理伺服器回應命中快取的結果
* **靜態檔案** - 直接提供靜態內容
* HTML/CSS/JS
* 圖片
* 影片
* 等等
### 負載平衡器與反向代理伺服器
* 當有多台伺服器時,使用負載平衡非常有用,一般來說,負載平衡器會將流量路由給一組功能相同的伺服器上。
* 即使只有一台伺服器或應用伺服器,反向代理也是有用的。可以參考上述的好處。
* Nginx 或 HAProxy 等解決方案可以同時支援第七層的反向代理與負載平衡
### 反向代理伺服器的缺點
* 引入反向代理伺服器會增加系統複雜度。
* 只有一台反向代理伺服器會有單點失效的問題,而設定多台的反向代理伺服器(如 [故障轉移](https://en.wikipedia.org/wiki/Failover) )同樣會增加系統複雜度。
### 來源與延伸閱讀
* [反向代理伺服器與負載平衡](https://www.nginx.com/resources/glossary/reverse-proxy-vs-load-balancer/)
* [NGINX 架構](https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/)
* [HAProxy 架構指南](http://www.haproxy.org/download/1.2/doc/architecture.txt)
* [維基百科](https://en.wikipedia.org/wiki/Reverse_proxy)
## 應用層
<p align="center">
<img src="http://i.imgur.com/yB5SYwm.png"/>
<br/>
<i><a href=http://lethain.com/introduction-to-architecting-systems-for-scale/#platform_layer>資料來源:可縮放式系統架構介紹</a></i>
</p>
將 Web 服務層與應用層(也被稱為平台層)分離,如此一來這兩層就可以獨立縮放與設定,增加新的 API 服務只需要增加應用伺服器,而不需要增加額外的 Web 伺服器。
**單一職責原則**鼓勵小型、自治的服務與共同合作,小型團隊透過提供小型的服務可以更有效率地讓計畫成長。
在應用層中的工作程式可以實作 [非同步機制](#非同步機制)
### 微服務
相關的主題還有 [微服務](https://en.wikipedia.org/wiki/Microservices) ,指的是可以獨立運作、小型的模組化服務。每個服務會透過明確定義好的輕量級溝通機制,運作在一個獨立的流程中來共同實現一個目標。<sup><a href=https://smartbear.com/learn/api-design/what-are-microservices>1</a></sup>
舉例來說Pinterest 可能有以下這些微服務使用者資料、跟隨者、Feed、搜尋、照片上傳等等。
### 服務發現
[Consul](https://www.consul.io/docs/index.html)、[Etcd](https://coreos.com/etcd/docs/latest), 或是 [Zookeeper](http://www.slideshare.net/sauravhaloi/introduction-to-apache-zookeeper) 等系統可以透過註冊的名稱、位置、Port 等資訊來幫助各個服務發現彼此。[Health checks](https://www.consul.io/intro/getting-started/checks.html) 可以幫助確認服務的完整性以及是否經常使用一個 [HTTP](#hypertext-transfer-protocol-http) 的路徑。[鍵-值對的資料庫](#鍵-值對的資料庫) 則用來儲存設定的資訊與其他共享的資料。
### 應用層的缺點
* 設計多個鬆耦合微服務所組成的應用層,必須從架構、維運、流程等多個面向來考量,相對於單系統而言會非常不同。
* 微服務會增加部署與維運的複雜度。
### 來源與延伸閱讀
* [可擴展式系統架構介紹](http://lethain.com/introduction-to-architecting-systems-for-scale)
* [破解系統設計面試](http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview)
* [面向服務架構](https://en.wikipedia.org/wiki/Service-oriented_architecture)
* [Zookeeper 介紹](http://www.slideshare.net/sauravhaloi/introduction-to-apache-zookeeper)
* [建構微服務系統你所需要知道的一切](https://cloudncode.wordpress.com/2016/07/22/msa-getting-started/)
## 資料庫
<p align="center">
<img src="http://i.imgur.com/Xkm5CXz.png"/>
<br/>
<i><a href=https://www.youtube.com/watch?v=vg5onp8TU6Q>來源:擴展你的使用者數量到第一個一千萬量級</a></i>
</p>
### 關連式資料庫管理系統(RDBMS)
像 SQL 這種關連式資料庫是以一組表格的形式存在的資料集合。
**ACID** 是用來描述資料庫 [事務](https://en.wikipedia.org/wiki/Database_transaction) 的特性。
* **原子性** - 每一個資料庫事務操作要不就是全部完成,要不就是全部不完成。
* **一致性** - 任何一個資料庫事務操作都會讓資料庫從一個有效的狀態轉換到另外一個有效狀態。
* **隔離性** - 併發執行資料庫事務操作的結果會和循序執行的結果一致。
* **持久性** - 一旦一個事務被資料庫執行後,他的結果與影響是擁永久保存的。
要針對關聯式資料庫系統進行擴展有許多方法: **主從複寫**, **主動模式複寫**, **聯邦式資料庫**, **分片**, **反正規化**, 和 **SQL 優化**.
#### 主從複寫
主資料庫負責讀和寫,並且將寫入的資料複寫至一或多個從屬資料庫中,從屬資料庫只負責讀取。而從屬資料庫可以再將寫入複製到更多以樹狀結構的其他資料庫中。如果主資料庫離線了,系統可以以只讀模式運行,直到某個從屬資料庫被提升為主資料庫,或有新的主資料庫出現。
<p align="center">
<img src="http://i.imgur.com/C9ioGtn.png"/>
<br/>
<i><a href=http://www.slideshare.net/jboner/scalability-availability-stability-patterns/>來源: 可擴展性、可用性、穩定性及其模式</a></i>
</p>
##### 主從複寫的缺點
* 需要額外的處理邏輯來將從屬資料庫提升為主要資料庫。
* 參考 [複寫的缺點](#複寫的缺點) 章節,你可以看到主動模式複寫與主從模式**共同**的缺點。
#### 主動模式複寫
兩個主要的資料庫都負責讀取和寫入,並且兩者互相協調。如果其中一個主要資料庫離線,系統可以繼續運作。
<p align="center">
<img src="http://i.imgur.com/krAHLGg.png"/>
<br/>
<i><a href=http://www.slideshare.net/jboner/scalability-availability-stability-patterns/>來源: 可擴展性、可用性、穩定性及其模式</a></i>
</p>
##### 主動模式的缺點
* 你需要一個負載平衡器來或是在你的應用程式邏輯中做修改來決定要寫入哪個資料庫。
* 大多數的主動模式資料庫無法保證一致性(違反 ACID),或是會因為同步而產生了寫入延遲。
* 隨著更多寫入節點的增加和延遲的提高,如何解決衝突就顯得更加重要。
* 參考 [複寫的缺點](#複寫的缺點) 章節,你可以看到主動模式複寫與主從模式**共同**的缺點。
##### 複寫的缺點
* 如果在主要資料庫複製到其他結點前系統就失效,則會有資料丟失的可能。
* 當有過多寫入時,讀取的資料庫可能會因為過多寫入操作而被阻塞,導致讀取功能異常。
* 當讀取的資料庫越多時,需要複寫的資料越多,將會導致較為嚴重的延遲。
* 在某些資料庫系統中,寫入主資料庫的操作可以用多執行緒來並行寫入,但讀取的資料庫只支援單一執行緒來循序寫入。
* 複寫意味著更多的硬體以及更高的複雜度。
##### 來源及延伸閱讀
* [可擴展性、可用性、穩定性及其模式](http://www.slideshare.net/jboner/scalability-availability-stability-patterns/)
* [多主要資料庫複寫](https://en.wikipedia.org/wiki/Multi-master_replication)
#### 聯邦式資料庫
<p align="center">
<img src="http://i.imgur.com/U3qV33e.png"/>
<br/>
<i><a href=https://www.youtube.com/watch?v=vg5onp8TU6Q>來源:擴展你的使用者數量到第一個一千萬量級</a></i>
</p>
聯邦式資料庫(或是指功能式切分)是將資料庫按照對應的功能進行分割。例如:你可以三個資料庫,分別是:**論壇**、**使用者**和**產品**,而不僅僅是單一資料庫。這樣會減少每個資料庫寫入與讀取的流量,進而降低複製的延遲。較少的資料意味者更多適合放入記憶體中的資料,進而增加快取命中率。因為沒有循序寫入的中央式主資料庫,你可以並行寫入以增加吞吐量。
##### 聯邦式資料庫的缺點
* 如果你的資料表需要大量的功能和資料表,聯邦式資料庫的效率並不好。
* 需要更新應用程式的邏輯來決定如何讀取和寫入到哪個資料庫。
* 透過 [server link](http://stackoverflow.com/questions/5145637/querying-data-by-joining-two-tables-in-two-database-on-different-servers) 從兩個資料庫中關資料更加複雜。
* 聯邦式資料庫需要更多的硬體和額外的複雜度。
##### 來源及延伸閱讀
* [來源:擴展你的使用者數量到第一個一千萬量級](https://www.youtube.com/watch?v=vg5onp8TU6Q)
#### 分片
<p align="center">
<img src="http://i.imgur.com/wU8x5Id.png"/>
<br/>
<i><a href=http://www.slideshare.net/jboner/scalability-availability-stability-patterns/>來源: 可擴展性、可用性、穩定性及其模式</a></i>
</p>
分片是指將資料分配在不同的資料庫上,使每個資料庫只管理整個資料的部分子集。以使用者資料庫為例,隨著使用者數量的增加,越來越多的分片會被加入到群集當中。
類似於 [聯邦式資料庫](#聯邦式資料庫) 的優點,分片可以減少讀取和寫入的流量、減少複製並提高快取命中率。索引的容量也會減少,如此一來可以改善查詢的效能。當一個分片出現問題時,其餘的仍然可以正常運作,而為了避免資料遺失,你可能需要思考其他複寫的機制。如同聯邦式資料庫,分片的機制並沒有中央式的資料庫,你可以並行寫入以增加吞吐量。
以使用者資料庫為例,常見的做法是用使用者姓氏的部首或使用者地理問位置來區隔使用者資料表。
##### 分片的缺點
* 你需要修改應用程式的邏輯來實作分片,這可能會導致 SQL 變得複雜。
* 不合理的分片可能會導致資料負載不均,例如,頻繁被訪問的使用者資料如果被放置在同一個分片中,會導致該分片負載相對較高。
* 再平衡會需要額外的複雜度。基於 [一致性 hash](http://www.paperplanes.de/2011/12/9/the-magic-of-consistent-hashing.html) 的分片演算法可以減少這種情形。
* 從多個分片中操作資料會很複雜。
* 分片需要額外的硬體和複雜度。
##### 來源及延伸閱讀
* [分片時代來臨](http://highscalability.com/blog/2009/8/6/an-unorthodox-approach-to-database-design-the-coming-of-the.html)
* [分片資料庫架構](https://en.wikipedia.org/wiki/Shard_(database_architecture))
* [一致性 hashing](http://www.paperplanes.de/2011/12/9/the-magic-of-consistent-hashing.html)
#### 反正規化
反正規化嘗試以寫入的性能作為代價來改善讀取性能。透過在不同資料表中的重複資料來避免高成本的 Join 操作。
某些關連式資料庫,例如 [PostgreSQL](https://en.wikipedia.org/wiki/PostgreSQL) 和 Oracle 支援 [materialized views](https://en.wikipedia.org/wiki/Materialized_view) ,可以用來處理重複資料的儲存,以及保證這些資料的一致性。
一旦資料使用如 [聯合](#聯合) 或 [切片](#切片) 等技術被分割,處理跨資料中心 Join 操作的複雜度。反正規化可以避免這種複雜的操作。
在多數系統中,讀取的操作頻率會遠高於寫入的頻率,比例可能會到 100:1 甚至 1000:1。進行複雜讀取操作的成本很高會在硬碟上消耗大量的時間。
##### 反正規化的缺點
* 資料會重複存取
* Constraints 的機制可以讓重複的資料保持同步,但這樣會增加資料庫設計的複雜度。
* 反正規化的資料庫在大量寫入負載的情況下,性能表現可能會比正規化的資料庫差。
###### 來源及延伸閱讀
* [反正規化](https://en.wikipedia.org/wiki/Denormalization)
#### SQL 優化
SQL 優化是一個涵蓋範圍很廣的主題,有許多相關的 [參考書籍](https://www.amazon.com/s/ref=nb_sb_noss_2?url=search-alias%3Daps&field-keywords=sql+tuning) 可以做為參考。
透過 **效能測試** 和 **效能分析** 來模擬並發現系統的瓶頸是很重要的。
* **效能測試** - 透過 [ab](http://httpd.apache.org/docs/2.2/programs/ab.html) 等工具來測試高負載的情況。
* **效能分析** - 使用 [slow query log](http://dev.mysql.com/doc/refman/5.7/en/slow-query-log.html) 等工具來追蹤性能問題。
效能測試和效能分析可能會引導你到以下的優化方案。
##### 使用較為精準的 schema
* 為了加快存取速度MySQL 會在硬碟上使用連續的 block 來儲存資料。
* 使用 `CHAR` 來儲存固定長度的資料,不要使用 `VARCHAR`
* `CHAR` 在快速、隨機存取時效率很高。如果使用 `VARCHAR`,想要讀取下一個字元時,需要先讀取到目前字元的尾端。
* 使用 `TEXT` 來儲存大量的文字,例如部落格文章。`TEXT` 還可以使用布林搜尋。使用 `TEXT` 時,會在硬碟上保存一個指向硬碟區塊的指標。
* 使用 `INT` 來儲存數量級達到 2^32 或 40 億等較大的數字。
* 使用 `DECIMAL` 來儲存貨幣資料可以避免浮點數表達錯誤。
* 避免儲存龐大的 `BLOBS`,取而代之的,應該儲存存放該對象的位置。
* `VARCHAR(255)` 是使用 8 位數來儲存時的最大表示法,在某些關連式資料庫中,要最大限度地使用它。
* 在適用的情況下設定 `NOT NULL` 來 [提高搜尋性能](http://stackoverflow.com/questions/1017239/how-do-null-values-affect-performance-in-a-database-search)。
##### 使用正確的索引
* 當你使用 (`SELECT`, `GROUP BY`, `ORDER BY`, `JOIN`) 這些操作的對應欄位如果有使用索引就會查詢更快。
* 索引通常是使用平衡 [B 樹](https://en.wikipedia.org/wiki/B-tree) 表示,這樣可以保證資料是有序的,並允許在對數時間內進行搜尋、循序訪問以及插入、刪除等操作。
* 設定索引時,會將資料放置於記憶體中,會佔用更多記憶體空間。
* 寫入操作會變慢,因為索引會需要更新。
* 當讀取大量資料時,禁用索引再讀取,之後再重新建立索引,這樣也許會更快。
##### 避免高成本的 Join 操作
* 有性能需求時,可以進行 [反正規化](#反正規化)。
##### 分割資料表
* 將熱門的資料拆分到單獨的資料表中可以增加快取。
##### 調整查詢的快取
* 在某些情況下,[查詢快取](http://dev.mysql.com/doc/refman/5.7/en/query-cache) 可能會導致 [性能問題](https://www.percona.com/blog/2014/01/28/10-mysql-performance-tuning-settings-after-installation/)。
##### 來源及延伸閱讀
* [MySQL 查詢優化小提示](http://20bits.com/article/10-tips-for-optimizing-mysql-queries-that-dont-suck)
* [為什麼使用 VARCHAR(255) 很常見](http://stackoverflow.com/questions/1217466/is-there-a-good-reason-i-see-varchar255-used-so-often-as-opposed-to-another-l)
* [Null 值是如何影響資料庫性能](http://stackoverflow.com/questions/1017239/how-do-null-values-affect-performance-in-a-database-search)
* [慢 SQL log 查詢](http://dev.mysql.com/doc/refman/5.7/en/slow-query-log.html)
### NoSQL
NoSQL 指的是 **鍵-值對的資料庫**、**文件類型資料庫**、**列儲存型資料庫** 和 **圖形資料庫** 等的統稱。資料是非正規化的Join 大部分在應用端完成。大多數的 NoSQL 資料庫無法真正實現 ACID 的 transaction他們通常會支援 [最終一致性](#最終一致性)。
**BASE** 通常被用來描述 NoSQL 資料庫的特性。 跟 [CAP 理論](#cap 理論) 相比BASE 強調可用性而非一致性。
* **基本可用** - 系統保證可用性。
* **軟狀態** - 系統的狀態可能隨著時間改變,即使在沒有輸入的情況下也是如此。
* **最終一致性** - 經過一段時間之後,在沒有收到任何輸入的情況下,系統最終會達到一致。
除了在 [SQL 或 NoSQL](#sql-或-nosql) 之間做選擇,了解哪種類型的 NoSQL 資料庫最適合你的需求也是很有幫助的。我們會在下一節中快速瞭解一下 **鍵-值對的資料庫**、**文件類型資料庫**、**列儲存型資料庫** 和 **圖形資料庫** 等資料庫。
#### 鍵-值對的資料庫
> 抽象模型hash table
一個鍵值對的資料庫通常可以實現 O(1) 時間內的讀寫,同時,它的背後通常使用記憶體或 SSD 當作儲存媒介。資料在儲存時,可以按照 [字典順序](https://en.wikipedia.org/wiki/Lexicographical_order) 來維護鍵的數值,進而實踐鍵數值的高效率檢索。鍵值對的資料庫也可以用來儲存值的 metadata。
鍵值對資料庫的效能很好,通常用來儲存簡單的資料模型或是頻繁修改的資料,如放在記憶體中的快取層。鍵值對資料庫所提供的操作有限,如果要進行更多操作,會將其放在應用端進行。
鍵值對的資料庫在某些情況下,是更複雜系統的基礎,例如圖形資料庫或是文件類型的資料庫。
##### 來源及延伸閱讀
* [鍵值對資料庫](https://en.wikipedia.org/wiki/Key-value_database)
* [鍵值對資料庫的缺點](http://stackoverflow.com/questions/4056093/what-are-the-disadvantages-of-using-a-key-value-table-over-nullable-columns-or)
* [Redis 架構](http://qnimate.com/overview-of-redis-architecture/)
* [Memcached 架構](https://www.adayinthelifeof.nl/2011/02/06/memcache-internals/)
#### 文件類型資料庫
> 抽象模型:將文件當做值的鍵值對資料庫
文件類型的資料庫是以文件 (XML、JSON、二進制檔案等) 為核心,文件本身儲存了對應物件的所有資訊。這種類型的資料庫提供了 API 或相關的查詢方法來根據儲存物件本身的特性來實現查詢功能。請注意,許多鍵值對資料庫有儲存 metadata 的特性,這也模糊了這兩種資料庫之間的界線。
根據底層實作的不同文件資料庫可以根據集合、標籤、metadata 或目錄等來組織而成。儘管不同的文件可以被組織在一起或是分成一組,但彼此之間可能具有完全不同的內容。
某些文件型資料庫,例如 [MongoDB](https://www.mongodb.com/mongodb-architecture) 和 [CouchDB](https://blog.couchdb.org/2016/08/01/couchdb-2-0-architecture/) 同樣提供了類似於 SQL 查詢語句的功能來實現複雜的查詢。[DynamoDB](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/decandia07dynamo.pdf)則同時支援了鍵值對儲存和文件類型儲存的功能。
文件類型的資料庫具備高度靈活性,通常用於處理偶爾變化的資料。
##### 延伸閱讀
* [文件類型的資料庫](https://en.wikipedia.org/wiki/Document-oriented_database)
* [MongoDB 架構](https://www.mongodb.com/mongodb-architecture)
* [CouchDB 架構](https://blog.couchdb.org/2016/08/01/couchdb-2-0-architecture/)
* [Elasticsearch 架構](https://www.elastic.co/blog/found-elasticsearch-from-the-bottom-up)
#### 列儲存型資料庫
<p align="center">
<img src="http://i.imgur.com/n16iOGk.png"/>
<br/>
<i><a href=http://blog.grio.com/2015/11/sql-nosql-a-brief-history.html>來源SQL 和 NoSQL簡短的歷史介紹</a></i>
</p>
> 抽象模型: 巢狀的 Map `ColumnFamily<RowKey, Columns<ColKey, Value, Timestamp>>`
列儲存型資料庫的基本單元是一列 (名稱/值為一組)。每一列可以被分到一個列的族群中(類似於 SQL 中的資料表)。而每個列族群之上還可以有一個超級列群。你可以透過列的鍵值來存取每一列,每個值都有一個時間戳記來解決版本問題。
Google 發表了第一個列儲存型資料庫 [Bigtable](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/chang06bigtable.pdf),這影響了用於 Hadoop 系統中開源的 [HBase](https://www.mapr.com/blog/in-depth-look-hbase-architecture) often-used in the Hadoop ecosystem, 和 Facebook 的 [Cassandra](http://docs.datastax.com/en/archived/cassandra/2.0/cassandra/architecture/architectureIntro_c.html)。這些資料庫的儲存系統把鍵值利用字母順序來儲存,可以有效率的來讀取。
列儲存型態的資料的提供了高可用和高擴展性,通常被用在大量資料的儲存上。
##### 來源及延伸閱讀
* [SQL 和 NoSQL 歷史簡介](http://blog.grio.com/2015/11/sql-nosql-a-brief-history.html)
* [Bigtable 架構](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/chang06bigtable.pdf)
* [HBase 架構](https://www.mapr.com/blog/in-depth-look-hbase-architecture)
* [Cassandra 架構](http://docs.datastax.com/en/archived/cassandra/2.0/cassandra/architecture/architectureIntro_c.html)
#### 圖形資料庫
<p align="center">
<img src="http://i.imgur.com/fNcl65g.png"/>
<br/>
<i><a href=https://en.wikipedia.org/wiki/File:GraphDatabase_PropertyGraph.png>來源: 圖形化資料庫</a></i>
</p>
> 抽象模型:圖
在圖形資料庫中,每一個節點會對應一條紀錄,而每個邊描述兩個節點之間的關係。圖形資料庫針對表示外來鍵(Foreign Key)眾多的複雜關聯或多對多關聯進行優化。
圖形資料庫為了儲存複雜的資料結構,例如社群網路,提供了很高的性能。他們相對較新,尚未被廣泛使用,查詢工具或資源比較難取得,許多這種類型的資料庫只能透過 [REST API](#representational-state-transfer-rest) 來存取。
##### 來源及延伸閱讀
* [圖形資料庫](https://en.wikipedia.org/wiki/Graph_database)
* [Neo4j](https://neo4j.com/)
* [FlockDB](https://blog.twitter.com/2010/introducing-flockdb)
#### 來源及延伸閱讀NoSQL
* [資料庫術語解釋](http://stackoverflow.com/questions/3342497/explanation-of-base-terminology)
* [NoSQL 資料庫:調查與決策指南](https://medium.com/baqend-blog/nosql-databases-a-survey-and-decision-guidance-ea7823a822d#.wskogqenq)
* [可擴展性](http://www.lecloud.net/post/7994751381/scalability-for-dummies-part-2-database)
* [NoSQL 介紹](https://www.youtube.com/watch?v=qI_g07C_Q5I)
* [NoSQL 模式](http://horicky.blogspot.com/2009/11/nosql-patterns.html)
### SQL 或 NoSQL
<p align="center">
<img src="http://i.imgur.com/wXGqG5f.png"/>
<br/>
<i><a href=https://www.infoq.com/articles/Transition-RDBMS-NoSQL/>來源:從 RDBMS 轉換到 NoSQL</a></i>
</p>
選擇 **SQL** 的原因:
* 結構化資料
* 嚴格的 schema
* 關連式資料
* 需要複雜的 join
* 事務
* 清晰的擴展模式
* 既有資源更豐富:開發者、社群、原始碼、工具等
* 透過索引查詢很快
選擇 **NoSQL** 的原因:
* 半結構化資料
* 動態或具有彈性的 schema
* 非關連式資料
* 不需要複雜的 joins
* 儲存 TB (或 PB) 等級的資料
* 高資料密集量的工作負載
* IOPS 的高吞吐量
適合使用 NoSQL 的範例:
* 快速地得到點擊的日誌資料
* 排行榜或得分資料
* 暫時性的資料,像是購物車
* 經常頻繁存取的資料表
* Metadata 或 查找資料表
##### 來源及延伸閱讀: SQL 或 NoSQL
* [擴展你的使用者到第一個一千萬等級](https://www.youtube.com/watch?v=vg5onp8TU6Q)
* [SQL 和 NoSQL 的不同](https://www.sitepoint.com/sql-vs-nosql-differences/)
## 快取
<p align="center">
<img src="http://i.imgur.com/Q6z24La.png"/>
<br/>
<i><a href=http://horicky.blogspot.com/2010/10/scalable-system-design-patterns.html>來源:可擴展的系統設計模式</a></i>
</p>
快取可以提高頁面讀取速度,並且減少伺服器和資料庫的負載。在這種模型中,分派器會先檢查該次請求之前是否曾經被回應過,如果有的話,則直接拿之前的結果返回,避免真正執行處理的程序。
資料庫在資料均勻分布的情況下,讀取和寫入的效能是最好的。但是熱門的資料會讓讀取分佈不均,如此一來就會造成效能瓶頸。在資料庫前增加一個快取,就可以減少負載不均和突發流量所造成的影響。
### 客戶端快取
快取可以在客戶端(作業系統或瀏覽器)、[伺服器端](#反向代理伺服器) 或不同的緩存層等。
### CDN 快取
[內容傳遞網路(CDN)](#內容傳遞網路(CDN)) 也被視為一種快取。
### 網站伺服器快取
[反向代理](#反向代理網站伺服器) 以及像是 [Varnish](https://www.varnish-cache.org/) 等的快取服務可以提供靜態和動態內容。網站伺服器也可以快取使用者請求,返回結果,而不需要真正的處理這些請求。
### 資料庫快取
資料庫的預設設定中通常包含了快取的級別,針對一般的使用進行了優化。你可以針對不同的情況調整這些設定來進一步提高效能。
### 應用程式快取
基於記憶體的快取,像是 Memcached 和 Redis 是一種在應用層和資料庫之間的鍵值對快取。由於資料保存在記憶體中,比起存放在硬碟中的資料庫在存取上要快得多。記憶體的限制也比硬碟更多,所以像是 [least recently used (LRU)](https://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used) 的 [cache invalidation](https://en.wikipedia.org/wiki/Cache_algorithms) 方法可以讓 '熱門資料' 放在記憶體中,而比較 '冷門' 的資料在記憶體中失效。
Redis 還有以下額外的功能:
* 持久性的設定
* 內建一些資料結構,像是 Set 或 List
你可以快取的級別有好幾種,大致上分為兩類:**資料庫查詢** 和 **物件**
* 記錄級別
* 查詢級別
* 完整的可序列化物件
* 完整的 HTML
一般來說,你應該避免文件檔案的快取,因為這會讓複製和自動擴展變得困難。
### 資料庫查詢級別的快取
當你將查詢資料庫的查詢語句的 hash 值和查詢結果儲存到快取中時,這種方法會遇到以下問題:
* 當你的查詢很複雜時,很難刪除快取內容
* 如果某個資料表中的某個欄位值改變時,需要刪除所有可能包含該欄位值的快取結果。
### 物件級別的快取
將資料視為物件,就像對待你的程式碼一樣。讓應用程式將資料從資料庫中組合到類別實例或資料結構中:
* 如果物件內的基本資料已經改變,那應該要從快取中刪除這個物件
* 允許異步處理workers 透過使用最新的快取來組裝物件
建議快取的資料:
* 使用者 sessions
* 完整渲染的頁面
* 活動資訊
* 使用者資料圖表
### 什麼時候要更新快取
由於你只能在快取中儲存有限的資料,所以你需要選擇一個適用的快取策略。
#### 快取模式
<p align="center">
<img src="http://i.imgur.com/ONjORqk.png"/>
<br/>
<i><a href=http://www.slideshare.net/tmatyashovsky/from-cache-to-in-memory-data-grid-introduction-to-hazelcast>資料來源:從快取到記憶體資料網格</a></i>
</p>
應用程式負責從儲存裝置中進行讀取及寫入。快取不直接和儲存裝置進行互動,應用程式會執行以下操作:
* 從快取中尋找紀錄,如果所需要的紀錄在快取中找不到時
* 從資料庫中讀取紀錄
* 將該筆記錄儲存到快取
* 將資料返回
```python
def get_user(self, user_id):
user = cache.get("user.{0}", user_id)
if user is None:
user = db.query("SELECT * FROM users WHERE user_id = {0}", user_id)
if user is not None:
key = "user.{0}".format(user_id)
cache.set(key, json.dumps(user))
return user
```
[Memcached](https://memcached.org/) 通常被用在快取上。
加入快取中的資料讀取速度很快,快取的模式也被稱為延遲讀取。只有被請求過的資料會被加入到快取中,避免沒有被請求的資料佔滿了快取空間。
##### 快取的缺點
* 當請求的資料不在快取中時,就需要經過三個步驟來獲得資料,這會導致明顯的延遲。
* 如果資料庫中的資料被更新了,會導致快取中的資料過時,這需要透過設定 TTL 強制更新快取,或透過直接更新模式來解決這種問題。
* 當快取的某個節點發生故障時,會需要被一個新的節點取代,這會導致延遲。
#### 寫入模式
<p align="center">
<img src="http://i.imgur.com/0vBc0hN.png"/>
<br/>
<i><a href=http://www.slideshare.net/jboner/scalability-availability-stability-patterns/>資料來源:可獲展性、可用性、穩定性與模式</a></i>
</p>
應用程式使用快取當作主要的資料儲存服務,將資料寫入/讀取到快取中,由快取服務負責向資料庫讀寫資料。
* 應用程式向快取寫入/讀取資料
* 快取同步的將資料寫到資料庫進行儲存
* 返回所需要的內容
應用程式程式碼:
```
set_user(12345, {"foo":"bar"})
```
快取程式碼:
```python
def set_user(user_id, values):
user = db.query("UPDATE Users WHERE id = {0}", user_id, values)
cache.set(user_id, user)
```
直寫模式因為寫入操作的緣故,是一種較慢的操作,但讀取剛剛寫入的資料會很快,使用者通常比較能接受更新較慢,但讀取快速的情況。在快取中的資料不會過時。
##### 寫入模式的缺點
* 當發生故障或因為水平擴展而產生新的節點時,新的節點中將不會有快取資料,直到資料庫更新為止。將快取模式和寫入模式一起使用可以減緩這種現象。
* 被寫入多數的資料可能永遠都不會被讀取,你可以設定 TTL 來解決這種問題。
#### 事後寫入(回寫)
<p align="center">
<img src="http://i.imgur.com/rgSrvjG.png"/>
<br/>
<i><a href=http://www.slideshare.net/jboner/scalability-availability-stability-patterns/>資料來源:可獲展性、可用性、穩定性與模式</a></i>
</p>
在事後寫入的模式中,應用程式會執行以下步驟:
* 在快取中新增/更新資料
* 非同步的寫入資料到資料儲存單元,提高寫入的性能
##### 事後寫入的缺點
* 快取可能在資料成功寫入到儲存單元前就丟失
* 事後寫入比起快取模式或是直寫模式在實作上更為複雜
#### 更新式快取
<p align="center">
<img src="http://i.imgur.com/kxtjqgE.png"/>
<br/>
<i><a href=http://www.slideshare.net/tmatyashovsky/from-cache-to-in-memory-data-grid-introduction-to-hazelcast>來源:從快取到記憶體資料網格技術</a></i>
</p>
你可以將快取設定為在到期之前就自動更新為最新存取的內容。
如果快取可以準確的預測將來可能會存取哪些資料,那自動更新可以降低讀取的延遲。
##### 更新式快取的缺點
* 無法準確預測未來會使用的資料時,會導致性能降低,還不如使用其他模式。
### 快取的缺點
* 需要保持快取和資料庫之間資料的一致性,比如說要如何設定 [快取無效](https://en.wikipedia.org/wiki/Cache_algorithms)。
* 需要更改應用程式程式碼來支援像是 Redis 或 Memcached 等快取服務。
* 快取的無效性是個難題,而什麼時候要更新快取就是個對應的複雜問題。
### 來源及延伸閱讀
* [從快取到記憶體資料網格技術](http://www.slideshare.net/tmatyashovsky/from-cache-to-in-memory-data-grid-introduction-to-hazelcast)
* [可擴展的系統設計模式](http://horicky.blogspot.com/2010/10/scalable-system-design-patterns.html)
* [可擴展的系統架構介紹](http://lethain.com/introduction-to-architecting-systems-for-scale/)
* [可擴展性、可用性、穩定性與模式](http://www.slideshare.net/jboner/scalability-availability-stability-patterns/)
* [可擴展性](http://www.lecloud.net/post/9246290032/scalability-for-dummies-part-3-cache)
* [AWS ElastiCache 策略](http://docs.aws.amazon.com/AmazonElastiCache/latest/UserGuide/Strategies.html)
* [維基百科](https://en.wikipedia.org/wiki/Cache_(computing))
## 非同步機制
<p align="center">
<img src="http://i.imgur.com/54GYsSx.png"/>
<br/>
<i><a href=http://lethain.com/introduction-to-architecting-systems-for-scale/#platform_layer>資料來源:可縮放性系統架構介紹</a></i>
</p>
非同步的工作流程有助於減少原本按照同步順序進行請求的時間,透過提前進行一些耗時操作來將降低整體請求時間,比如說:定期的彙整資料。
### 訊息佇列
訊息佇列用來接收、保留以及傳遞訊息。如果一個操作按照順序執行太慢的時候,你可以透過訊息佇列和以下的流程搭配來完成此工作:
* 應用程式將訊息發送到佇列中,並通知使用者對應的狀態
* 一個 worker 從佇列中拿出該訊息並進行處理,然後完成後顯示對應資訊
這樣的工作流程,使用者不會被阻塞,同時工作會在背景完成。在這段期間,客戶端可以進行一些處理讓此任務看起來已經完成了。例如,當你要發送一則推文訊息時,此訊息可以馬上出現在你的時間軸上,但可能需要一段時間才會發送到你的追蹤者上面。
**Redis** 是一個簡單且令人滿意的訊息佇列服務,但訊息有可能會丟失。
**RabbitMQ** 很受歡迎,但你必須要使用 AMQP 通訊協定,並且要自己管理節點。
**Amazon SQS** 是一個被 AWS 託管的服務,但可能會有高延遲,並且訊息可能會被傳送兩次。
### 工作佇列
工作佇列會接收工作和對應的資料,執行它們,並且傳送其結果。他們可以支援排程,並且可以在背景運作計算密集型的工作。
**Celery** 支援排程,主要是使用 Python 開發。
### 背壓機制
當佇列開始明顯成長時,佇列的大小可能會超過記憶體,這會導致無法命中快取,降低整體效能。[背壓](http://mechanical-sympathy.blogspot.com/2012/05/apply-back-pressure-when-overloaded.html) 可以用來限制佇列的大小,讓佇列保持高吞吐率和良好的回應時間。一旦佇列滿了,客戶端將會得到 HTTP 503 的回應碼,以便讓他們在稍後重新嘗試。客戶端可以透過 [指數後退演算法](https://en.wikipedia.org/wiki/Exponential_backoff) 這種方式來進行重試。
### 非同步的缺點
* 簡單的運算和需要即時的工作可能更適合使用同步運算,導入佇列可能會增加延遲或系統複雜度。
### 來源及延伸閱讀
* [這是一個數字遊戲](https://www.youtube.com/watch?v=1KRYH75wgy4)
* [當過載時,使用背壓](http://mechanical-sympathy.blogspot.com/2012/05/apply-back-pressure-when-overloaded.html)
* [利特爾法則](https://en.wikipedia.org/wiki/Little%27s_law)
* [訊息佇列和工作佇列有什麼不同?](https://www.quora.com/What-is-the-difference-between-a-message-queue-and-a-task-queue-Why-would-a-task-queue-require-a-message-broker-like-RabbitMQ-Redis-Celery-or-IronMQ-to-function)
## 通訊
<p align="center">
<img src="http://i.imgur.com/5KeocQs.jpg"/>
<br/>
<i><a href=http://www.escotal.com/osilayer.html>來源OSI 七層模型</a></i>
</p>
### 超文件通訊協定 (HTTP)
HTTP 是一種在客戶端和伺服器端傳輸資料和定義編碼的方法。它是基於請求/回應的協議客戶端發出請求而伺服器端則針對請求內容完成對應的行為並進行回應。HTTP 是獨立的,它允許請求和回應經過許多負載平衡、快取、加密和壓縮的中間路由器和伺服器。
一個基本的 HTTP 請求是由一個動詞(方法)和一個資源(端點)所組成。以下是常見的 HTTP 動詞:
| 動詞 | 描述 | 冪等* | 安全性 | 可快取性 |
|--------|----------------------------------|-------|--------|-----------------------------------------|
| GET | 讀取資源 | Yes | Yes | Yes |
| POST | 建立資源,或是驅動處理資料的流程 | No | No | Yes如果回應包含更新的資訊 |
| PUT | 建立或更新資源 | Yes | No | No |
| PATCH | 更新部分資料 | No | No | Yes if response contains freshness info |
| DELETE | 刪除資料 | Yes | No | No |
*指的是當進行多次相同請求時,結果是相同的。
HTTP 是依賴於較底層的協議(例如:**TCP** 和 **UDP**) 的應用層協議。
#### 來源及延伸閱讀
* [什麼是 HTTP?](https://www.nginx.com/resources/glossary/http/)
* [HTTP 和 TCP 的差別](https://www.quora.com/What-is-the-difference-between-HTTP-protocol-and-TCP-protocol)
* [PUT 和 PATCH 的差別](https://laracasts.com/discuss/channels/general-discussion/whats-the-differences-between-put-and-patch?page=1)
### 傳輸控制通訊協定(TCP)
<p align="center">
<img src="http://i.imgur.com/JdAsdvG.jpg"/>
<br/>
<i><a href=http://www.wildbunny.co.uk/blog/2012/10/09/how-to-make-a-multi-player-game-part-1/>來源:如何開發多人遊戲</a></i>
</p>
TCP 是透過 [IP 網路](https://en.wikipedia.org/wiki/Internet_Protocol) 面向連線的通訊協定。連線是透過 [握手](https://en.wikipedia.org/wiki/Handshaking) 的方式來建立和斷開連接,所有發送的資料在接收時會保證順序,另外透過以下的機制來保證資料不會損毀:
* 每個資料的序列號碼和 [校驗碼](https://en.wikipedia.org/wiki/Transmission_Control_Protocol#Checksum_computation)
* [確認訊息](https://en.wikipedia.org/wiki/Acknowledgement_(data_networks)) 和自動重傳
如果發送端沒有收到正確的回應會重新發送資料如果有多次的逾期時連線就會斷開。TCP 實作了 [流量控制](https://en.wikipedia.org/wiki/Flow_control_(data)) 和 [阻塞控制](https://en.wikipedia.org/wiki/Network_congestion#Congestion_control),這些機制會導致延遲,而且通常傳輸的效率會比 UDP 來得低。
為了確保高吞吐量Web 伺服器會保持大量的 TCP 連線,進而導致記憶體用量變大。在 Web 伺服器之間使用大量的開放連線可能是昂貴的,更別說是在 memcached 快取中做這些事情。[連線池](https://en.wikipedia.org/wiki/Connection_pool) 可以幫助在適合的情況下切換到 UDP。
TCP 對於需要高可靠、低時間急迫性的應用來說很有用比如說Web 伺服器、資料庫、SMTP、FTP 和 SSH。
以下的情況請使用 TCP 而不是 UDP
* 你需要資料完整無缺
* 你想要自動地對網路的流量進行最佳評估
### 使用者資料流通訊協定 (UDP)
<p align="center">
<img src="http://i.imgur.com/yzDrJtA.jpg"/>
<br/>
<i><a href=http://www.wildbunny.co.uk/blog/2012/10/09/how-to-make-a-multi-player-game-part-1/>資料來源:如何製作多人遊戲</a></i>
</p>
UDP 是非連線型的通訊協定。資料流(類似於封包)只在資料流級別進行確保。資料可能會不按照順序地到達目的地也可能會遺失。UDP 並不支援阻塞處理,儘管 UDP 不像 TCP 一樣可靠,但通常效率更好。
UDP 可以透過廣播來傳送資料流到所有子網路中的所有裝置,這對於 [DHCP](https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol) 來說很有用,因為所有子網路中的設備還沒有分配到 IP 位置,而對 TCP 來說IP 是必須的。
UDP 的可靠性較低,但適合用在像是網路電話、視訊聊天、串流和多人線上即時遊戲中。
針對以下的案例,請使用 UDP 來代替 TCP
* 你需要低延遲
* 資料延遲的成本比資料遺失還高
* 你想要自己實作錯誤校正方法
#### 來源及延伸閱讀
* [遊戲程式撰寫的網路架構](http://gafferongames.com/networking-for-game-programmers/udp-vs-tcp/)
* [TCP 和 UDP 的關鍵區別](http://www.cyberciti.biz/faq/key-differences-between-tcp-and-udp-protocols/)
* [TCP 和 UDP 的差別](http://stackoverflow.com/questions/5970383/difference-between-tcp-and-udp)
* [傳輸控制協議(TCP)](https://en.wikipedia.org/wiki/Transmission_Control_Protocol)
* [使用者資料流協議(UDP)](https://en.wikipedia.org/wiki/User_Datagram_Protocol)
* [Memcache 在 Facebook 中的可擴展性設計](http://www.cs.bu.edu/~jappavoo/jappavoo.github.com/451/papers/memcache-fb.pdf)
### 遠端程式呼叫 (RPC)
<p align="center">
<img src="http://i.imgur.com/iF4Mkb5.png"/>
<br/>
<i><a href=http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview>資料來源:破解系統設計面試</a></i>
</p>
在一個 RPC 中,客戶端會去呼叫另外一個位置空間(通常是在遠端的伺服器)的方法。呼叫的方式就像是呼叫本地端的一個方法一樣,客戶端和伺服器溝通的具體過程被抽象化,而遠端呼叫相較於本地端呼叫來說一般較慢,而且可靠性較差,因此了解如何區別這兩種方法是必要的。熱門的 RPC 框架包含了 [Protobuf](https://developers.google.com/protocol-buffers/)、[Thrift](https://thrift.apache.org/) 和 [Avro](https://avro.apache.org/docs/current/)。
RPC 是一個請求-回應的通訊協定:
* **客戶端程序** - 呼叫客戶端的 stub 程序,就像呼叫本地端方法一樣,參數會被放入堆疊當中
* **客戶端 stub 程序** - 將請求過程的 id 和參數打包放入請求資訊中
* **客戶端通訊模組** - 作業系統將資訊從客戶端發送到伺服器端
* **伺服器端通訊模組** - 作業系統將收到的資訊傳送到伺服器端的 stub 程序
* **伺服器端 stub 程序** - 將結果解開後,依照過程中的 ID 來呼叫伺服器
* 伺服器回覆的順序會按照以上相反的順序來回覆
RPC 使用範例:
```
GET /someoperation?data=anId
POST /anotheroperation
{
"data":"anId";
"anotherdata": "another value"
}
```
RPC 專注於揭露行為,它通常用來處理內部通訊的效能問題,通常你可以手動處理本地端的呼叫來更加符合你的使用案例。
當遇到以下情況時,使用本地端函式庫(也就是 SDK
* 你知道你的目標平台
* 你想要控制如何訪問你的 "邏輯"
* 當你的函式庫發生錯誤時,你想要進行控制
* 效能和使用者體驗是你最關注的事情
遵守 **REST** 規範的 HTTP API 往往更適合用在公用的 API。
#### RPC 的缺點
* RPC 的客戶端會變得和伺服器的實作綁得更死
* 一個新的 API 必須在每個操作或使用案例中進行定義
* RPC 很難抓錯誤
* 你很難方便的修改現有的技術,舉例來說,如果你希望在 [Squid](http://www.squid-cache.org/) 這樣的快取伺服器上確保 [RPC 呼叫被正確的快取](http://etherealbits.com/2012/12/debunking-the-myths-of-rpc-rest/),你可以需要多費額外的努力了。
### 具象狀態轉移 (REST)
REST 是一個規範客戶端/伺服器端架構設計的模型。客戶端基於伺服器管理的系列操作,伺服器提供修改或取得資源的介面,所有的通訊必須是無狀態、可快取的。
Restful 的設計有四個原則:
* **標示資源 (HTTP 中的 URI)** - 無論任何操作都使用相同的 URI
* **表示層的改變 (HTTP 中的動作)** - 使用 HTTP 動詞、Headers 和 body
* **可自我描述的錯誤訊息 (HTTP 中的狀態碼)** - 使用狀態碼,不要重複造輪子
* **[HATEOAS](http://restcookbook.com/Basics/hateoas/) (HTTP 中的 HTML 介面)** - 你的 Web 伺服器應該要能夠透過瀏覽器訪問
REST 請求範例:
```
GET /someresources/anId
PUT /someresources/anId
{"anotherdata": "another value"}
```
REST 關注於揭露資料,減少客戶端/伺服器之間耦合的程度,並且經常用在公共的 HTTP API 設計上。REST 使用更通用和受規範的方法來透過 URI 來揭露資源,[透過 Headers 來描述](https://github.com/for-GET/know-your-http-well/blob/master/headers.md),並透過 GET、POST、PUT、DELETE 和 PATCH 等動作來進行操作因為無狀態的特性REST 易於橫向擴展和分片。
#### REST 的缺點
* 因為 REST 的重點是放在如何揭露資料所以當資料不是以自然的形式組成時或是結構相當複雜時REST 可能無法很好的處理他們。舉個範例,回傳過去一小時中與特定事件吻合的更新操作就很難透過路徑來表示,使用 REST可能會使用 URI、查詢參數和請求本身來實現。
* REST 一般依賴於幾個動詞操作(GET、POST、PUT、DELETE 和 PATCH),但有時候這些操作無法滿足你的需求,舉個範例,將過期的文件移動到歸檔文件資料庫中這樣的操作,可能就沒辦法簡單的使用以上幾個動詞操作來完成。
* 對於那些多層複雜的資源來說,需要在客戶端和伺服器端進行多次請求,例如:獲得部落格頁面及相關評論,而對於網路環境較不穩定的行動端應用來說,這些多次往返的請求是非常麻煩的。
* 隨著時間的增加API 的回應中可能會增加更多的欄位,比較舊的客戶端還是會收到所有新的回應內容,即時他們不需要這些回應,這會造成他們的負擔,並且造成更大的延遲。
### RPC 和 REST 呼叫的比較
| 操作 | RPC | REST |
|----------------------------|-----------------------------------------------------------------------|-----------------------------------------------|
| 註冊 | **POST** /signup | **POST** /persons |
| 取消 | **POST** /resign{"personid": "1234"} | **DELETE** /persons/1234 |
| 讀取使用者資訊 | **GET** /readPerson?personid=1234 | **GET** /persons/1234 |
| 讀取使用者物品清單 | **GET** /readUsersItemsList?personid=1234 | **GET** /persons/1234/items |
| 增加一個物品到使用者的清單 | **POST** /addItemToUsersItemsList{"personid": "1234";"itemid": "456"} | **POST** /persons/1234/items{"itemid": "456"} |
| 更新一個物品 | **POST** /modifyItem{"itemid": "456";"key": "value"} | **PUT** /items/456{"key": "value"} |
| 刪除一個物品 | **POST** /removeItem{"itemid": "456"} | **DELETE** /items/456 |
<p align="center">
<i><a href=https://apihandyman.io/do-you-really-know-why-you-prefer-rest-over-rpc/>資料來源:你真的知道為什麼你更喜歡 REST 而不是 RPC 嗎?</a></i>
</p>
#### 來源及延伸閱讀
* [你真的知道為什麼你更喜歡 REST 而不是 RPC 嗎?](https://apihandyman.io/do-you-really-know-why-you-prefer-rest-over-rpc/)
* [什麼時候 RPC 比 REST 更適合](http://programmers.stackexchange.com/a/181186)
* [REST 和 JSON-RPC](http://stackoverflow.com/questions/15056878/rest-vs-json-rpc)
* [揭開 RPC 和 REST 的神秘面紗](http://etherealbits.com/2012/12/debunking-the-myths-of-rpc-rest/)
* [使用 REST 的缺點](https://www.quora.com/What-are-the-drawbacks-of-using-RESTful-APIs)
* [破解系統設計面試](http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview)
* [Thrift](https://code.facebook.com/posts/1468950976659943/)
* [為什麼在內部要使用 REST 而不是 RPC](http://arstechnica.com/civis/viewtopic.php?t=1190508)
## 資訊安全
這一章節需要更多的貢獻,一起[加入](#如何貢獻)吧!
資訊安全是一個廣泛的議題,除非你有相當的經驗、資訊安全的背景或正在申請相關的職位要求對應的知識,否則了解以下的基礎內容即可:
*在傳輸和等待的過程中進行加密
* 對所有使用者輸入和從使用者得到的參數進行處理,以避免 [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) 和 [SQL injection](https://en.wikipedia.org/wiki/SQL_injection)
* 使用參數化輸入來避免 SQL injection
* 使用 [最小權限原則](https://en.wikipedia.org/wiki/Principle_of_least_privilege)
### 來源及延伸閱讀
* [為開發者準備的資訊安全指南](https://github.com/FallibleInc/security-guide-for-developers)
* [OWASP top ten](https://www.owasp.org/index.php/OWASP_Top_Ten_Cheat_Sheet)
## 附錄
某些時候你可能會被要求做一些保守估計,比如說,你可能需要預估從硬碟中生成 100 張圖片約略需要多少時間,或一個資料結構需要多少記憶體等。**2 的次方表** 和 **每個開發者都需要知道的一些時間資料** 都是一些很方便的參考料。
### 2 的次方表
```
次方 實際值 近似值 位元組
---------------------------------------------------------------
7 128
8 256
10 1024 1 thousand 1 KB
16 65,536 64 KB
20 1,048,576 1 million 1 MB
30 1,073,741,824 1 billion 1 GB
32 4,294,967,296 4 GB
40 1,099,511,627,776 1 trillion 1 TB
```
#### 來源及延伸閱讀
* [2 的次方](https://en.wikipedia.org/wiki/Power_of_two)
### 每個開發者都應該知道的延遲數量級
```
延遲比較數量級
--------------------------
L1 快取參考數量級 0.5 ns
Branch mispredict 5 ns
L2 快取參考數量級 7 ns 14x L1 cache
Mutex lock/unlock 25 ns
主記憶體參考數量級 100 ns 20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy 10,000 ns 10 us
Send 1 KB bytes over 1 Gbps network 10,000 ns 10 us
Read 4 KB randomly from SSD* 150,000 ns 150 us ~1GB/sec SSD
Read 1 MB sequentially from memory 250,000 ns 250 us
Round trip within same datacenter 500,000 ns 500 us
Read 1 MB sequentially from SSD* 1,000,000 ns 1,000 us 1 ms ~1GB/sec SSD, 4X memory
Disk seek 10,000,000 ns 10,000 us 10 ms 20x datacenter roundtrip
Read 1 MB sequentially from 1 Gbps 10,000,000 ns 10,000 us 10 ms 40x memory, 10X SSD
Read 1 MB sequentially from disk 30,000,000 ns 30,000 us 30 ms 120x memory, 30X SSD
Send packet CA->Netherlands->CA 150,000,000 ns 150,000 us 150 ms
Notes
-----
1 ns = 10^-9 seconds
1 us = 10^-6 seconds = 1,000 ns
1 ms = 10^-3 seconds = 1,000 us = 1,000,000 ns
```
一些基於上述數字的指標:
* 循序的從硬碟讀取資料大約 30 MB/s
* 循序的從 1 Gbps 坪寬的乙太網路讀取約 100 MB/s
* 循序的從 SSD 讀取大約 1 GB/s
* 循序的從主記憶體中讀取大約 4 GB/s
* 每秒大約可以繞地球 6-7 圈
* 資料中心內每秒約有 2000 次的往返
#### 視覺化延遲數
![](https://camo.githubusercontent.com/77f72259e1eb58596b564d1ad823af1853bc60a3/687474703a2f2f692e696d6775722e636f6d2f6b307431652e706e67)
#### 來源及延伸閱讀
* [每個程式設計師都應該知道的延遲數量級 - 1](https://gist.github.com/jboner/2841832)
* [每個程式設計師都應該知道的延遲數量級 - 2](https://gist.github.com/hellerbarde/2843375)
* [關於建置大型分散式系統所需要知道的設計方案、課程和建議](http://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf)
* [從軟體工程師的角度來看建置大型分散式系統](https://static.googleusercontent.com/media/research.google.com/en//people/jeff/stanford-295-talk.pdf)
### 其他的系統設計面試問題
> 常見的系統設計問題,同時提供如何解決該問題的連結
| 問題 | 來源 |
|----------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 設計一個類似於 Dropbox 的文件同步系統 | [youtube.com](https://www.youtube.com/watch?v=PE4gwstWhmc) |
| 設計一個類似於 Google 的搜尋引擎 | [queue.acm.org](http://queue.acm.org/detail.cfm?id=988407)<br/>[stackexchange.com](http://programmers.stackexchange.com/questions/38324/interview-question-how-would-you-implement-google-search)<br/>[ardendertat.com](http://www.ardendertat.com/2012/01/11/implementing-search-engines/)<br/>[stanford.edu](http://infolab.stanford.edu/~backrub/google.html) |
| 設計一個像 Google 一樣可擴展的網路爬蟲 | [quora.com](https://www.quora.com/How-can-I-build-a-web-crawler-from-scratch) |
| 設計一個 Google Docs | [code.google.com](https://code.google.com/p/google-mobwrite/)<br/>[neil.fraser.name](https://neil.fraser.name/writing/sync/) |
| 設計一個像 Redis 一樣的鍵值對系統 | [slideshare.net](http://www.slideshare.net/dvirsky/introduction-to-redis) |
| 設計一個像 Memcached 的快取系統 | [slideshare.net](http://www.slideshare.net/oemebamo/introduction-to-memcached) |
| 設計一個像 Amazon 一樣的推薦系統 | [hulu.com](http://tech.hulu.com/blog/2011/09/19/recommendation-system.html)[ijcai13.org](http://ijcai13.org/files/tutorial_slides/td3.pdf) |
| 設計一個像 Bitly 一樣的短網址服務 | [n00tc0d3r.blogspot.com](http://n00tc0d3r.blogspot.com/) |
| 設計一個像 WhatsApp 一樣的即時訊息系統 | [highscalability.com](http://highscalability.com/blog/2014/2/26/the-whatsapp-architecture-facebook-bought-for-19-billion.html) |
| 設計一個像 Instagram 一樣的相片服務 | [highscalability.com](http://highscalability.com/flickr-architecture)<br/>[highscalability.com](http://highscalability.com/blog/2011/12/6/instagram-architecture-14-million-users-terabytes-of-photos.html) |
| 設計一個像 Facebook 的新聞推薦方法 | [quora.com](http://www.quora.com/What-are-best-practices-for-building-something-like-a-News-Feed)<br/>[quora.com](http://www.quora.com/Activity-Streams/What-are-the-scaling-issues-to-keep-in-mind-while-developing-a-social-network-feed)<br/>[slideshare.net](http://www.slideshare.net/danmckinley/etsy-activity-feeds-architecture) |
| 設計一個 Facebook 時間軸功能 | [facebook.com](https://www.facebook.com/note.php?note_id=10150468255628920)<br/>[highscalability.com](http://highscalability.com/blog/2012/1/23/facebook-timeline-brought-to-you-by-the-power-of-denormaliza.html) |
| 設計 Facebook 的聊天功能 | [erlang-factory.com](http://www.erlang-factory.com/upload/presentations/31/EugeneLetuchy-ErlangatFacebook.pdf)<br/>[facebook.com](https://www.facebook.com/note.php?note_id=14218138919&id=9445547199&index=0) |
| 設計一個像 Facebook 的圖形化搜尋系統 | [facebook.com](https://www.facebook.com/notes/facebook-engineering/under-the-hood-building-out-the-infrastructure-for-graph-search/10151347573598920)<br/>[facebook.com](https://www.facebook.com/notes/facebook-engineering/under-the-hood-indexing-and-ranking-in-graph-search/10151361720763920)<br/>[facebook.com](https://www.facebook.com/notes/facebook-engineering/under-the-hood-the-natural-language-interface-of-graph-search/10151432733048920) |
| 設計一個像 CloudFlare 的內容傳輸網路 | [cmu.edu](http://repository.cmu.edu/cgi/viewcontent.cgi?article=2112&context=compsci) |
| 設計一個像 Twitter 的微網誌服務 | [michael-noll.com](http://www.michael-noll.com/blog/2013/01/18/implementing-real-time-trending-topics-in-storm/)<br/>[snikolov .wordpress.com](http://snikolov.wordpress.com/2012/11/14/early-detection-of-twitter-trends/) |
| 設計一個隨機 ID 生成系統 | [blog.twitter.com](https://blog.twitter.com/2010/announcing-snowflake)<br/>[github.com](https://github.com/twitter/snowflake/) |
| 給定一段時間,回傳次數排名前 K 的請求 | [ucsb.edu](https://icmi.cs.ucsb.edu/research/tech_reports/reports/2005-23.pdf)<br/>[wpi.edu](http://davis.wpi.edu/xmdv/docs/EDBT11-diyang.pdf) |
| 設計一個資料來源在多個資料中心的系統 | [highscalability.com](http://highscalability.com/blog/2009/8/24/how-google-serves-data-from-multiple-datacenters.html) |
| 設計一個線上多人卡牌遊戲 | [indieflashblog.com](http://www.indieflashblog.com/how-to-create-an-asynchronous-multiplayer-game.html)<br/>[buildnewgames.com](http://buildnewgames.com/real-time-multiplayer/) |
| 設計一個垃圾回收系統 | [stuffwithstuff.com](http://journal.stuffwithstuff.com/2013/12/08/babys-first-garbage-collector/)<br/>[washington.edu](http://courses.cs.washington.edu/courses/csep521/07wi/prj/rick.pdf) |
| 貢獻更多系統設計問題 | [Contribute](#如何貢獻) |
### 真實世界的架構
> 底下是關於真實世界的系統架構是如何設計的文章
<p align="center">
<img src="http://i.imgur.com/TcUo2fw.png"/>
<br/>
<i><a href=https://www.infoq.com/presentations/Twitter-Timeline-Scalability>資料來源:可擴展式的 Twitter 時間軸設計</a></i>
</p>
**不要關注以下文章的細節,而是注意以下幾點:**
* 找到這些文章中共通的原則、技術和模式
* 學習每個元件負責解決哪些問題、在什麼情況下使用、什麼情況下不適用
* 複習學習過的文章
| 種類 | 系統 | 參考來源 |
|----------|----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
| 資料處理 | **MapReduce** - Google 的分散式資料處理 | [research.google.com](http://static.googleusercontent.com/media/research.google.com/zh-CN/us/archive/mapreduce-osdi04.pdf) |
| 資料處理 | **Spark** - Databricks 的分散式資料處理 | [slideshare.net](http://www.slideshare.net/AGrishchenko/apache-spark-architecture) |
| 資料處理 | **Storm** - Twitter 的分散式資料處理 | [slideshare.net](http://www.slideshare.net/previa/storm-16094009) |
| | | |
| 資料儲存 | **Bigtable** - Google 的列式資料庫 | [harvard.edu](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/chang06bigtable.pdf) |
| 資料儲存 | **HBase** - Bigtable 的開放原始碼解決方案 | [slideshare.net](http://www.slideshare.net/alexbaranau/intro-to-hbase) |
| 資料儲存 | **Cassandra** - Facebook 的列式資料庫 | [slideshare.net](http://www.slideshare.net/planetcassandra/cassandra-introduction-features-30103666) |
| 資料儲存 | **DynamoDB** - Amazon 的文件式資料庫 | [harvard.edu](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/decandia07dynamo.pdf) |
| 資料儲存 | **MongoDB** - 文件式資料庫 | [slideshare.net](http://www.slideshare.net/mdirolf/introduction-to-mongodb) |
| 資料儲存 | **Spanner** - Google 的全球分散式資料庫 | [research.google.com](http://research.google.com/archive/spanner-osdi2012.pdf) |
| 資料儲存 | **Memcached** - 分散式的記憶體快取系統 | [slideshare.net](http://www.slideshare.net/oemebamo/introduction-to-memcached) |
| 資料儲存 | **Redis** - 具有持久化及值型別的分散式快取系統 | [slideshare.net](http://www.slideshare.net/dvirsky/introduction-to-redis) |
| | | |
| 檔案系統 | **Google File System (GFS)** - 分散式的檔案系統 | [research.google.com](http://static.googleusercontent.com/media/research.google.com/zh-CN/us/archive/gfs-sosp2003.pdf) |
| 檔案系統 | **Hadoop File System (HDFS)** - GFS 的開放原始碼解決方案 | [apache.org](https://hadoop.apache.org/docs/r1.2.1/hdfs_design.html) |
| | | |
| 其他 | **Chubby** - Google 的分散式系統低耦合鎖服務 | [research.google.com](http://static.googleusercontent.com/external_content/untrusted_dlcp/research.google.com/en/us/archive/chubby-osdi06.pdf) |
| 其他 | **Dapper** - 分散式系統監控基礎設施 | [research.google.com](http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36356.pdf) |
| 其他 | **Kafka** - LinkedIn 的 pub/sub 訊息佇列服務 | [slideshare.net](http://www.slideshare.net/mumrah/kafka-talk-tri-hug) |
| 其他 | **Zookeeper** - 集中式的基礎架構和協調服務 | [slideshare.net](http://www.slideshare.net/sauravhaloi/introduction-to-apache-zookeeper) |
| | 貢獻更多架構 | [Contribute](#如何貢獻) |
### 公司的系統架構
| 公司 | 參考 |
|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Amazon | [Amazon 的架構](http://highscalability.com/amazon-architecture) |
| Cinchcast | [每天產生 1,500 小時的音樂](http://highscalability.com/blog/2012/7/16/cinchcast-architecture-producing-1500-hours-of-audio-every-d.html) |
| DataSift | [每秒探勘 120,000 則 tweet](http://highscalability.com/blog/2011/11/29/datasift-architecture-realtime-datamining-at-120000-tweets-p.html) |
| DropBox | [我們如何擴展 Dropbox](https://www.youtube.com/watch?v=PE4gwstWhmc) |
| ESPN | [每秒操作 100,000 次 duh nuh nuhs](http://highscalability.com/blog/2013/11/4/espns-architecture-at-scale-operating-at-100000-duh-nuh-nuhs.html) |
| Google | [Google 的架構](http://highscalability.com/google-architecture) |
| Instagram | [一千四百萬個使用者TB 等級的照片儲存](http://highscalability.com/blog/2011/12/6/instagram-architecture-14-million-users-terabytes-of-photos.html)<br/>[什麼驅動著 Instagram](http://instagram-engineering.tumblr.com/post/13649370142/what-powers-instagram-hundreds-of-instances) |
| Justin.tv | [Justin.Tv 的即時影片廣播架構](http://highscalability.com/blog/2010/3/16/justintvs-live-video-broadcasting-architecture.html) |
| Facebook | [Facebook 可擴展的 memcached 架構](https://cs.uwaterloo.ca/~brecht/courses/854-Emerging-2014/readings/key-value/fb-memcached-nsdi-2013.pdf)<br/>[TAO: Facebook 為了社交網路架構的分散式資料儲存](https://cs.uwaterloo.ca/~brecht/courses/854-Emerging-2014/readings/data-store/tao-facebook-distributed-datastore-atc-2013.pdf)<br/>[Facebook 的圖片儲存架構](https://www.usenix.org/legacy/event/osdi10/tech/full_papers/Beaver.pdf) |
| Flickr | [Flickr 的架構](http://highscalability.com/flickr-architecture) |
| Mailbox | [在六週內從 0 到 100 萬個使用者](http://highscalability.com/blog/2013/6/18/scaling-mailbox-from-0-to-one-million-users-in-6-weeks-and-1.html) |
| Pinterest | [從零到每個月數十億次的瀏覽量](http://highscalability.com/blog/2013/4/15/scaling-pinterest-from-0-to-10s-of-billions-of-page-views-a.html)<br/>[1800 萬個訪問人次、10 倍成長、12 名員工](http://highscalability.com/blog/2012/5/21/pinterest-architecture-update-18-million-visitors-10x-growth.html) |
| Playfish | [月使用者量 5000 萬人次在成長](http://highscalability.com/blog/2010/9/21/playfishs-social-gaming-architecture-50-million-monthly-user.html) |
| PlentyOfFish | [PlentyOfFish 的架構](http://highscalability.com/plentyoffish-architecture) |
| Salesforce | [如何處理每天 13 億筆交易](http://highscalability.com/blog/2013/9/23/salesforce-architecture-how-they-handle-13-billion-transacti.html) |
| Stack Overflow | [Stack Overflow 的架構](http://highscalability.com/blog/2009/8/5/stack-overflow-architecture.html) |
| TripAdvisor | [4000 萬的訪問人次、2 億次頁面瀏覽量、30 TB 的資料](http://highscalability.com/blog/2011/6/27/tripadvisor-architecture-40m-visitors-200m-dynamic-page-view.html) |
| Tumblr | [每月 150 億的瀏覽量](http://highscalability.com/blog/2012/2/13/tumblr-architecture-15-billion-page-views-a-month-and-harder.html) |
| Twitter | [如何讓 Twitter 的速度成長 10000 倍](http://highscalability.com/scaling-twitter-making-twitter-10000-percent-faster)<br/>[使用 MySQL 儲存每天 2.5 億條 tweet](http://highscalability.com/blog/2011/12/19/how-twitter-stores-250-million-tweets-a-day-using-mysql.html)<br/>[1.5 億的活躍使用者、300K QPS、22 MB/S 的串流資料](http://highscalability.com/blog/2013/7/8/the-architecture-twitter-uses-to-deal-with-150m-active-users.html)<br/>[可擴展的 Timelines](https://www.infoq.com/presentations/Twitter-Timeline-Scalability)<br/>[Twitter 的大大小小的資料](https://www.youtube.com/watch?v=5cKTP36HVgI)<br/>[Twitter 的運營:擴展超過一億個使用者](https://www.youtube.com/watch?v=z8LU0Cj6BOU) |
| Uber | [Uber 是如何擴展他們的及時行銷平台](http://highscalability.com/blog/2015/9/14/how-uber-scales-their-real-time-market-platform.html) |
| WhatsApp | [讓 Facebook 用 $190 億購買下來的 WhatsApp 的架構](http://highscalability.com/blog/2014/2/26/the-whatsapp-architecture-facebook-bought-for-19-billion.html) |
| YouTube | [YouTube 的可擴展性](https://www.youtube.com/watch?v=w5WVu624fY8)[YouTube 的架構](http://highscalability.com/youtube-architecture) |
### 公司的工程部落格
> 你即將面試的公司的架構
>
> 你被問到的問題可能就來自於相同領域的問題
* [Airbnb Engineering](http://nerds.airbnb.com/)
* [Atlassian Developers](https://developer.atlassian.com/blog/)
* [Autodesk Engineering](http://cloudengineering.autodesk.com/blog/)
* [AWS Blog](https://aws.amazon.com/blogs/aws/)
* [Bitly Engineering Blog](http://word.bitly.com/)
* [Box Blogs](https://www.box.com/blog/engineering/)
* [Cloudera Developer Blog](http://blog.cloudera.com/blog/)
* [Dropbox Tech Blog](https://tech.dropbox.com/)
* [Engineering at Quora](http://engineering.quora.com/)
* [Ebay Tech Blog](http://www.ebaytechblog.com/)
* [Evernote Tech Blog](https://blog.evernote.com/tech/)
* [Etsy Code as Craft](http://codeascraft.com/)
* [Facebook Engineering](https://www.facebook.com/Engineering)
* [Flickr Code](http://code.flickr.net/)
* [Foursquare Engineering Blog](http://engineering.foursquare.com/)
* [GitHub Engineering Blog](http://githubengineering.com/)
* [Google Research Blog](http://googleresearch.blogspot.com/)
* [Groupon Engineering Blog](https://engineering.groupon.com/)
* [Heroku Engineering Blog](https://engineering.heroku.com/)
* [Hubspot Engineering Blog](http://product.hubspot.com/blog/topic/engineering)
* [High Scalability](http://highscalability.com/)
* [Instagram Engineering](http://instagram-engineering.tumblr.com/)
* [Intel Software Blog](https://software.intel.com/en-us/blogs/)
* [Jane Street Tech Blog](https://blogs.janestreet.com/category/ocaml/)
* [LinkedIn Engineering](http://engineering.linkedin.com/blog)
* [Microsoft Engineering](https://engineering.microsoft.com/)
* [Microsoft Python Engineering](https://blogs.msdn.microsoft.com/pythonengineering/)
* [Netflix Tech Blog](http://techblog.netflix.com/)
* [Paypal Developer Blog](https://devblog.paypal.com/category/engineering/)
* [Pinterest Engineering Blog](http://engineering.pinterest.com/)
* [Quora Engineering](https://engineering.quora.com/)
* [Reddit Blog](http://www.redditblog.com/)
* [Salesforce Engineering Blog](https://developer.salesforce.com/blogs/engineering/)
* [Slack Engineering Blog](https://slack.engineering/)
* [Spotify Labs](https://labs.spotify.com/)
* [Twilio Engineering Blog](http://www.twilio.com/engineering)
* [Twitter Engineering](https://engineering.twitter.com/)
* [Uber Engineering Blog](http://eng.uber.com/)
* [Yahoo Engineering Blog](http://yahooeng.tumblr.com/)
* [Yelp Engineering Blog](http://engineeringblog.yelp.com/)
* [Zynga Engineering Blog](https://www.zynga.com/blogs/engineering)
#### 來源及延伸閱讀
* [kilimchoi/engineering-blogs](https://github.com/kilimchoi/engineering-blogs)
## 仍在進行中
有興趣增加一些內容,或幫忙完善某些部分嗎? [來貢獻吧](#如何貢獻)!
* 使用 MapReduce 進行分散式運算
* 一致性的 hashing
* 直接記憶體存取
* [貢獻](#如何貢獻)
## 致謝
在這個 Repository 中提供的任何來源和開放原始碼庫
特別感謝:
* [Hired in tech](http://www.hiredintech.com/system-design/the-system-design-process/)
* [Cracking the coding interview](https://www.amazon.com/dp/0984782850/)
* [High scalability](http://highscalability.com/)
* [checkcheckzz/system-design-interview](https://github.com/checkcheckzz/system-design-interview)
* [shashank88/system_design](https://github.com/shashank88/system_design)
* [mmcgrana/services-engineering](https://github.com/mmcgrana/services-engineering)
* [System design cheat sheet](https://gist.github.com/vasanthk/485d1c25737e8e72759f)
* [A distributed systems reading list](http://dancres.github.io/Pages/)
* [Cracking the system design interview](http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview)
## 聯絡資訊
隨時歡迎與我討論相關的問題或意見。
我的聯絡資訊可以在我的 [GitHub 主頁](https://github.com/donnemartin) 中找到。
## 授權
*我已開放原始碼授權的方式提供你在此儲存庫中的程式碼和資源。因為這是我個人的儲存庫,所以你所以所收到的使用許可是來自於我,並非我的雇主(Facebook)*
Copyright 2017 Donne Martin
Creative Commons Attribution 4.0 International License (CC BY 4.0)
http://creativecommons.org/licenses/by/4.0/

218
README.md
View File

@ -1,9 +1,11 @@
*[English](README.md) ∙ [简体中文](README-zh-Hans.md) | [Brazilian Portuguese](https://github.com/donnemartin/system-design-primer/issues/40) ∙ [Italian](https://github.com/donnemartin/system-design-primer/issues/104) ∙ [Japanese](https://github.com/donnemartin/system-design-primer/issues/100) ∙ [Korean](https://github.com/donnemartin/system-design-primer/issues/102) ∙ [Persian](https://github.com/donnemartin/system-design-primer/issues/110) ∙ [Polish](https://github.com/donnemartin/system-design-primer/issues/68) ∙ [Russian](https://github.com/donnemartin/system-design-primer/issues/87) ∙ [Traditional Chinese](https://github.com/donnemartin/system-design-primer/issues/88) ∙ [Turkish](https://github.com/donnemartin/system-design-primer/issues/39) | [Add Translation](https://github.com/donnemartin/system-design-primer/issues/28)*
# The System Design Primer
<p align="center">
<img src="http://i.imgur.com/jj3A5N8.png">
<img src="http://i.imgur.com/jj3A5N8.png"/>
<br/>
</p>
@ -23,7 +25,7 @@ This repo is an **organized collection** of resources to help you learn how to b
### Learn from the open source community
This is an early draft of a continually updated, open source project.
This is a continually updated, open source project.
[Contributions](#contributing) are welcome!
@ -44,15 +46,15 @@ Additional topics for interview prep:
## Anki flashcards
<p align="center">
<img src="http://i.imgur.com/zdCAkB3.png">
<img src="http://i.imgur.com/zdCAkB3.png"/>
<br/>
</p>
The provided [Anki flashcard decks](https://apps.ankiweb.net/) use spaced repetition to help you retain key system design concepts.
* [System design deck](resources/flash_cards/System%20Design.apkg)
* [System design exercises deck](resources/flash_cards/System%20Design%20Exercises.apkg)
* [Object oriented design exercises deck](resources/flash_cards/OO%20Design.apkg)
* [System design deck](https://github.com/donnemartin/system-design-primer/tree/master/resources/flash_cards/System%20Design.apkg)
* [System design exercises deck](https://github.com/donnemartin/system-design-primer/tree/master/resources/flash_cards/System%20Design%20Exercises.apkg)
* [Object oriented design exercises deck](https://github.com/donnemartin/system-design-primer/tree/master/resources/flash_cards/OO%20Design.apkg)
Great for use while on-the-go.
@ -61,7 +63,7 @@ Great for use while on-the-go.
Looking for resources to help you prep for the [**Coding Interview**](https://github.com/donnemartin/interactive-coding-challenges)?
<p align="center">
<img src="http://i.imgur.com/b4YtAEN.png">
<img src="http://i.imgur.com/b4YtAEN.png"/>
<br/>
</p>
@ -91,7 +93,7 @@ Review the [Contributing Guidelines](CONTRIBUTING.md).
> Each section contains links to more in-depth resources.
<p align="center">
<img src="http://i.imgur.com/jrUBAF7.png">
<img src="http://i.imgur.com/jrUBAF7.png"/>
<br/>
</p>
@ -112,6 +114,7 @@ Review the [Contributing Guidelines](CONTRIBUTING.md).
* [Availability patterns](#availability-patterns)
* [Fail-over](#fail-over)
* [Replication](#replication)
* [Availability in numbers](#availability-in-numbers)
* [Domain name system](#domain-name-system)
* [Content delivery network](#content-delivery-network)
* [Push CDNs](#push-cdns)
@ -289,7 +292,7 @@ Check out the following links to get a better idea of what to expect:
| Question | |
|---|---|
| Design Pastebin.com (or Bit.ly) | [Solution](solutions/system_design/pastebin/README.md) |
| Design the Twitter timeline (or Facebook feed)<br/>Design Twitter search (or Facebook search) | [Solution](solutions/system_design/twitter/README.md) |
| Design the Twitter timeline and search (or Facebook feed and search) | [Solution](solutions/system_design/twitter/README.md) |
| Design a web crawler | [Solution](solutions/system_design/web_crawler/README.md) |
| Design Mint.com | [Solution](solutions/system_design/mint/README.md) |
| Design the data structures for a social network | [Solution](solutions/system_design/social_graph/README.md) |
@ -385,7 +388,7 @@ First, you'll need a basic understanding of common principles, learning about wh
### Step 2: Review the scalability article
[Scalability](http://www.lecloud.net/tagged/scalability)
[Scalability](http://www.lecloud.net/tagged/scalability/chrono)
* Topics covered:
* [Clones](http://www.lecloud.net/post/7295452622/scalability-for-dummies-part-1-clones)
@ -436,7 +439,7 @@ Generally, you should aim for **maximal throughput** with **acceptable latency**
### CAP theorem
<p align="center">
<img src="http://i.imgur.com/bgLMI2u.png">
<img src="http://i.imgur.com/bgLMI2u.png"/>
<br/>
<i><a href=http://robertgreiner.com/2014/08/cap-theorem-revisited>Source: CAP theorem revisited</a></i>
</p>
@ -455,14 +458,14 @@ Waiting for a response from the partitioned node might result in a timeout error
#### AP - availability and partition tolerance
Responses return the most recent version of the data available on the a node, which might not be the latest. Writes might take some time to propagate when the partition is resolved.
Responses return the most recent version of the data available on a node, which might not be the latest. Writes might take some time to propagate when the partition is resolved.
AP is a good choice if the business needs allow for [eventual consistency](#eventual-consistency) or when the system needs to continue working despite external errors.
### Source(s) and further reading
* [CAP theorem revisited](http://robertgreiner.com/2014/08/cap-theorem-revisited/)
* [A plain english introduction to CAP theorem](http://ksat.me/a-plain-english-introduction-to-cap-theorem/)
* [A plain english introduction to CAP theorem](http://ksat.me/a-plain-english-introduction-to-cap-theorem)
* [CAP FAQ](https://github.com/henryr/cap-faq)
## Consistency patterns
@ -527,10 +530,56 @@ This topic is further discussed in the [Database](#database) section:
* [Master-slave replication](#master-slave-replication)
* [Master-master replication](#master-master-replication)
### Availability in numbers
Availability is often quantified by uptime (or downtime) as a percentage of time the service is available. Availability is generally measured in number of 9s--a service with 99.99% availability is described as having four 9s.
#### 99.9% availability - three 9s
| Duration | Acceptable downtime|
|---------------------|--------------------|
| Downtime per year | 8h 45min 57s |
| Downtime per month | 43m 49.7s |
| Downtime per week | 10m 4.8s |
| Downtime per day | 1m 26.4s |
#### 99.99% availability - four 9s
| Duration | Acceptable downtime|
|---------------------|--------------------|
| Downtime per year | 52min 35.7s |
| Downtime per month | 4m 23s |
| Downtime per week | 1m 5s |
| Downtime per day | 8.6s |
#### Availability in parallel vs in sequence
If a service consists of multiple components prone to failure, the service's overall availability depends on whether the components are in sequence or in parallel.
###### In sequence
Overall availability decreases when two components with availability < 100% are in sequence:
```
Availability (Total) = Availability (Foo) * Availability (Bar)
```
If both `Foo` and `Bar` each had 99.9% availability, their total availability in sequence would be 99.8%.
###### In parallel
Overall availability increases when two components with availability < 100% are in parallel:
```
Availability (Total) = 1 - (1 - Availability (Foo)) * (1 - Availability (Bar))
```
If both `Foo` and `Bar` each had 99.9% availability, their total availability in parallel would be 99.9999%.
## Domain name system
<p align="center">
<img src="http://i.imgur.com/IOyLj4i.jpg">
<img src="http://i.imgur.com/IOyLj4i.jpg"/>
<br/>
<i><a href=http://www.slideshare.net/srikrupa5/dns-security-presentation-issa>Source: DNS security presentation</a></i>
</p>
@ -546,7 +595,7 @@ DNS is hierarchical, with a few authoritative servers at the top level. Your ro
Services such as [CloudFlare](https://www.cloudflare.com/dns/) and [Route 53](https://aws.amazon.com/route53/) provide managed DNS services. Some DNS services can route traffic through various methods:
* [Weighted round robin](http://g33kinfo.com/info/archives/2657)
* [Weighted round robin](https://www.g33kinfo.com/info/round-robin-vs-weighted-round-robin-lb)
* Prevent traffic from going to servers under maintenance
* Balance between varying cluster sizes
* A/B testing
@ -556,7 +605,7 @@ Services such as [CloudFlare](https://www.cloudflare.com/dns/) and [Route 53](ht
### Disadvantage(s): DNS
* Accessing a DNS server introduces a slight delay, although mitigated by caching described above.
* DNS server management could be complex, although they are generally managed by [governments, ISPs, and large companies](http://superuser.com/questions/472695/who-controls-the-dns-servers/472729).
* DNS server management could be complex and is generally managed by [governments, ISPs, and large companies](http://superuser.com/questions/472695/who-controls-the-dns-servers/472729).
* DNS services have recently come under [DDoS attack](http://dyn.com/blog/dyn-analysis-summary-of-friday-october-21-attack/), preventing users from accessing websites such as Twitter without knowing Twitter's IP address(es).
### Source(s) and further reading
@ -569,7 +618,7 @@ Services such as [CloudFlare](https://www.cloudflare.com/dns/) and [Route 53](ht
## Content delivery network
<p align="center">
<img src="http://i.imgur.com/h9TAuGI.jpg">
<img src="http://i.imgur.com/h9TAuGI.jpg"/>
<br/>
<i><a href=https://www.creative-artworks.eu/why-use-a-content-delivery-network-cdn/>Source: Why use a CDN</a></i>
</p>
@ -603,14 +652,14 @@ Sites with heavy traffic work well with pull CDNs, as traffic is spread out more
### Source(s) and further reading
* [Globally distributed content delivery](http://repository.cmu.edu/cgi/viewcontent.cgi?article=2112&context=compsci)
* [Globally distributed content delivery](https://figshare.com/articles/Globally_distributed_content_delivery/6605972)
* [The differences between push and pull CDNs](http://www.travelblogadvice.com/technical/the-differences-between-push-and-pull-cdns/)
* [Wikipedia](https://en.wikipedia.org/wiki/Content_delivery_network)
## Load balancer
<p align="center">
<img src="http://i.imgur.com/h81n9iK.png">
<img src="http://i.imgur.com/h81n9iK.png"/>
<br/>
<i><a href=http://horicky.blogspot.com/2010/10/scalable-system-design-patterns.html>Source: Scalable system design patterns</a></i>
</p>
@ -636,7 +685,7 @@ Load balancers can route traffic based on various metrics, including:
* Random
* Least loaded
* Session/cookies
* [Round robin or weighted round robin](http://g33kinfo.com/info/archives/2657)
* [Round robin or weighted round robin](https://www.g33kinfo.com/info/round-robin-vs-weighted-round-robin-lb)
* [Layer 4](#layer-4-load-balancing)
* [Layer 7](#layer-7-load-balancing)
@ -680,7 +729,7 @@ Load balancers can also help with horizontal scaling, improving performance and
## Reverse proxy (web server)
<p align="center">
<img src="http://i.imgur.com/n41Azff.png">
<img src="http://i.imgur.com/n41Azff.png"/>
<br/>
<i><a href=https://upload.wikimedia.org/wikipedia/commons/6/67/Reverse_proxy_h2g2bob.svg>Source: Wikipedia</a></i>
<br/>
@ -723,14 +772,12 @@ Additional benefits include:
## Application layer
<p align="center">
<img src="http://i.imgur.com/yB5SYwm.png">
<img src="http://i.imgur.com/yB5SYwm.png"/>
<br/>
<i><a href=http://lethain.com/introduction-to-architecting-systems-for-scale/#platform_layer>Source: Intro to architecting systems for scale</a></i>
</p>
Separating out the web layer from the application layer (also known as platform layer) allows you to scale and configure both layers independently. Adding a new API results in adding application servers without necessarily adding additional web servers.
The **single responsibility principle** advocates for small and autonomous services that work together. Small teams with small services can plan more aggressively for rapid growth.
Separating out the web layer from the application layer (also known as platform layer) allows you to scale and configure both layers independently. Adding a new API results in adding application servers without necessarily adding additional web servers. The **single responsibility principle** advocates for small and autonomous services that work together. Small teams with small services can plan more aggressively for rapid growth.
Workers in the application layer also help enable [asynchronism](#asynchronism).
@ -752,7 +799,7 @@ Systems such as [Consul](https://www.consul.io/docs/index.html), [Etcd](https://
### Source(s) and further reading
* [Intro to architecting systems for scale](http://lethain.com/introduction-to-architecting-systems-for-scale)
* [Crack the system design interview](http://www.puncsky.com/blog/2016/02/14/crack-the-system-design-interview/)
* [Crack the system design interview](http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview)
* [Service oriented architecture](https://en.wikipedia.org/wiki/Service-oriented_architecture)
* [Introduction to Zookeeper](http://www.slideshare.net/sauravhaloi/introduction-to-apache-zookeeper)
* [Here's what you need to know about building microservices](https://cloudncode.wordpress.com/2016/07/22/msa-getting-started/)
@ -760,9 +807,9 @@ Systems such as [Consul](https://www.consul.io/docs/index.html), [Etcd](https://
## Database
<p align="center">
<img src="http://i.imgur.com/Xkm5CXz.png">
<img src="http://i.imgur.com/Xkm5CXz.png"/>
<br/>
<i><a href=https://www.youtube.com/watch?v=vg5onp8TU6Q>Source: Scaling up to your first 10 million users</a></i>
<i><a href=https://www.youtube.com/watch?v=w95murBkYmU>Source: Scaling up to your first 10 million users</a></i>
</p>
### Relational database management system (RDBMS)
@ -783,7 +830,7 @@ There are many techniques to scale a relational database: **master-slave replica
The master serves reads and writes, replicating writes to one or more slaves, which serve only reads. Slaves can also replicate to additional slaves in a tree-like fashion. If the master goes offline, the system can continue to operate in read-only mode until a slave is promoted to a master or a new master is provisioned.
<p align="center">
<img src="http://i.imgur.com/C9ioGtn.png">
<img src="http://i.imgur.com/C9ioGtn.png"/>
<br/>
<i><a href=http://www.slideshare.net/jboner/scalability-availability-stability-patterns/>Source: Scalability, availability, stability, patterns</a></i>
</p>
@ -798,7 +845,7 @@ The master serves reads and writes, replicating writes to one or more slaves, wh
Both masters serve reads and writes and coordinate with each other on writes. If either master goes down, the system can continue to operate with both reads and writes.
<p align="center">
<img src="http://i.imgur.com/krAHLGg.png">
<img src="http://i.imgur.com/krAHLGg.png"/>
<br/>
<i><a href=http://www.slideshare.net/jboner/scalability-availability-stability-patterns/>Source: Scalability, availability, stability, patterns</a></i>
</p>
@ -826,9 +873,9 @@ Both masters serve reads and writes and coordinate with each other on writes. I
#### Federation
<p align="center">
<img src="http://i.imgur.com/U3qV33e.png">
<img src="http://i.imgur.com/U3qV33e.png"/>
<br/>
<i><a href=https://www.youtube.com/watch?v=vg5onp8TU6Q>Source: Scaling up to your first 10 million users</a></i>
<i><a href=https://www.youtube.com/watch?v=w95murBkYmU>Source: Scaling up to your first 10 million users</a></i>
</p>
Federation (or functional partitioning) splits up databases by function. For example, instead of a single, monolithic database, you could have three databases: **forums**, **users**, and **products**, resulting in less read and write traffic to each database and therefore less replication lag. Smaller databases result in more data that can fit in memory, which in turn results in more cache hits due to improved cache locality. With no single central master serializing writes you can write in parallel, increasing throughput.
@ -842,12 +889,12 @@ Federation (or functional partitioning) splits up databases by function. For ex
##### Source(s) and further reading: federation
* [Scaling up to your first 10 million users](https://www.youtube.com/watch?v=vg5onp8TU6Q)
* [Scaling up to your first 10 million users](https://www.youtube.com/watch?v=w95murBkYmU)
#### Sharding
<p align="center">
<img src="http://i.imgur.com/wU8x5Id.png">
<img src="http://i.imgur.com/wU8x5Id.png"/>
<br/>
<i><a href=http://www.slideshare.net/jboner/scalability-availability-stability-patterns/>Source: Scalability, availability, stability, patterns</a></i>
</p>
@ -931,18 +978,18 @@ Benchmarking and profiling might point you to the following optimizations.
##### Tune the query cache
* In some cases, the [query cache](http://dev.mysql.com/doc/refman/5.7/en/query-cache) could lead to [performance issues](https://www.percona.com/blog/2014/01/28/10-mysql-performance-tuning-settings-after-installation/).
* In some cases, the [query cache](https://dev.mysql.com/doc/refman/5.7/en/query-cache.html) could lead to [performance issues](https://www.percona.com/blog/2016/10/12/mysql-5-7-performance-tuning-immediately-after-installation/).
##### Source(s) and further reading: SQL tuning
* [Tips for optimizing MySQL queries](http://20bits.com/article/10-tips-for-optimizing-mysql-queries-that-dont-suck)
* [Tips for optimizing MySQL queries](http://aiddroid.com/10-tips-optimizing-mysql-queries-dont-suck/)
* [Is there a good reason i see VARCHAR(255) used so often?](http://stackoverflow.com/questions/1217466/is-there-a-good-reason-i-see-varchar255-used-so-often-as-opposed-to-another-l)
* [How do null values affect performance?](http://stackoverflow.com/questions/1017239/how-do-null-values-affect-performance-in-a-database-search)
* [Slow query log](http://dev.mysql.com/doc/refman/5.7/en/slow-query-log.html)
### NoSQL
NoSQL is a collection of data items represented in a **key-value store**, **document-store**, **wide column store**, or a **graph database**. Data is denormalized, and joins are generally done in the application code. Most NoSQL stores lack true ACID transactions and favor [eventual consistency](#eventual-consistency).
NoSQL is a collection of data items represented in a **key-value store**, **document store**, **wide column store**, or a **graph database**. Data is denormalized, and joins are generally done in the application code. Most NoSQL stores lack true ACID transactions and favor [eventual consistency](#eventual-consistency).
**BASE** is often used to describe the properties of NoSQL databases. In comparison with the [CAP Theorem](#cap-theorem), BASE chooses availability over consistency.
@ -950,7 +997,7 @@ NoSQL is a collection of data items represented in a **key-value store**, **docu
* **Soft state** - the state of the system may change over time, even without input.
* **Eventual consistency** - the system will become consistent over a period of time, given that the system doesn't receive input during that period.
In addition to choosing between [SQL or NoSQL](#sql-or-nosql), it is helpful to understand which type of NoSQL database best fits your use case(s). We'll review **key-value stores**, **document-stores**, **wide column stores**, and **graph databases** in the next section.
In addition to choosing between [SQL or NoSQL](#sql-or-nosql), it is helpful to understand which type of NoSQL database best fits your use case(s). We'll review **key-value stores**, **document stores**, **wide column stores**, and **graph databases** in the next section.
#### Key-value store
@ -975,7 +1022,7 @@ A key-value store is the basis for more complex systems such as a document store
A document store is centered around documents (XML, JSON, binary, etc), where a document stores all information for a given object. Document stores provide APIs or a query language to query based on the internal structure of the document itself. *Note, many key-value stores include features for working with a value's metadata, blurring the lines between these two storage types.*
Based on the underlying implementation, documents are organized in either collections, tags, metadata, or directories. Although documents can be organized or grouped together, documents may have fields that are completely different from each other.
Based on the underlying implementation, documents are organized by collections, tags, metadata, or directories. Although documents can be organized or grouped together, documents may have fields that are completely different from each other.
Some document stores like [MongoDB](https://www.mongodb.com/mongodb-architecture) and [CouchDB](https://blog.couchdb.org/2016/08/01/couchdb-2-0-architecture/) also provide a SQL-like language to perform complex queries. [DynamoDB](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/decandia07dynamo.pdf) supports both key-values and documents.
@ -991,7 +1038,7 @@ Document stores provide high flexibility and are often used for working with occ
#### Wide column store
<p align="center">
<img src="http://i.imgur.com/n16iOGk.png">
<img src="http://i.imgur.com/n16iOGk.png"/>
<br/>
<i><a href=http://blog.grio.com/2015/11/sql-nosql-a-brief-history.html>Source: SQL & NoSQL, a brief history</a></i>
</p>
@ -1000,7 +1047,7 @@ Document stores provide high flexibility and are often used for working with occ
A wide column store's basic unit of data is a column (name/value pair). A column can be grouped in column families (analogous to a SQL table). Super column families further group column families. You can access each column independently with a row key, and columns with the same row key form a row. Each value contains a timestamp for versioning and for conflict resolution.
Google introduced [Bigtable](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/chang06bigtable.pdf) as the first wide column store, which influenced the open-source [HBase](https://www.mapr.com/blog/in-depth-look-hbase-architecture) often-used in the Hadoop ecosystem, and [Cassandra](http://docs.datastax.com/en/archived/cassandra/2.0/cassandra/architecture/architectureIntro_c.html) from Facebook. Stores such as BigTable, HBase, and Cassandra maintain keys in lexicographic order, allowing efficient retrieval of selective key ranges.
Google introduced [Bigtable](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/chang06bigtable.pdf) as the first wide column store, which influenced the open-source [HBase](https://www.mapr.com/blog/in-depth-look-hbase-architecture) often-used in the Hadoop ecosystem, and [Cassandra](http://docs.datastax.com/en/cassandra/3.0/cassandra/architecture/archIntro.html) from Facebook. Stores such as BigTable, HBase, and Cassandra maintain keys in lexicographic order, allowing efficient retrieval of selective key ranges.
Wide column stores offer high availability and high scalability. They are often used for very large data sets.
@ -1009,12 +1056,12 @@ Wide column stores offer high availability and high scalability. They are often
* [SQL & NoSQL, a brief history](http://blog.grio.com/2015/11/sql-nosql-a-brief-history.html)
* [Bigtable architecture](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/chang06bigtable.pdf)
* [HBase architecture](https://www.mapr.com/blog/in-depth-look-hbase-architecture)
* [Cassandra architecture](http://docs.datastax.com/en/archived/cassandra/2.0/cassandra/architecture/architectureIntro_c.html)
* [Cassandra architecture](http://docs.datastax.com/en/cassandra/3.0/cassandra/architecture/archIntro.html)
#### Graph database
<p align="center">
<img src="http://i.imgur.com/fNcl65g.png">
<img src="http://i.imgur.com/fNcl65g.png"/>
<br/>
<i><a href=https://en.wikipedia.org/wiki/File:GraphDatabase_PropertyGraph.png>Source: Graph database</a></i>
</p>
@ -1042,7 +1089,7 @@ Graphs databases offer high performance for data models with complex relationshi
### SQL or NoSQL
<p align="center">
<img src="http://i.imgur.com/wXGqG5f.png">
<img src="http://i.imgur.com/wXGqG5f.png"/>
<br/>
<i><a href=https://www.infoq.com/articles/Transition-RDBMS-NoSQL/>Source: Transitioning from RDBMS to NoSQL</a></i>
</p>
@ -1078,13 +1125,13 @@ Sample data well-suited for NoSQL:
##### Source(s) and further reading: SQL or NoSQL
* [Scaling up to your first 10 million users](https://www.youtube.com/watch?v=vg5onp8TU6Q)
* [Scaling up to your first 10 million users](https://www.youtube.com/watch?v=w95murBkYmU)
* [SQL vs NoSQL differences](https://www.sitepoint.com/sql-vs-nosql-differences/)
## Cache
<p align="center">
<img src="http://i.imgur.com/Q6z24La.png">
<img src="http://i.imgur.com/Q6z24La.png"/>
<br/>
<i><a href=http://horicky.blogspot.com/2010/10/scalable-system-design-patterns.html>Source: Scalable system design patterns</a></i>
</p>
@ -1095,7 +1142,7 @@ Databases often benefit from a uniform distribution of reads and writes across i
### Client caching
Caches can be located on the client side (OS or browser), [server side](#reverse-proxy), or in a distinct cache layer.
Caches can be located on the client side (OS or browser), [server side](#reverse-proxy-web-server), or in a distinct cache layer.
### CDN caching
@ -1155,7 +1202,7 @@ Since you can only store a limited amount of data in cache, you'll need to deter
#### Cache-aside
<p align="center">
<img src="http://i.imgur.com/ONjORqk.png">
<img src="http://i.imgur.com/ONjORqk.png"/>
<br/>
<i><a href=http://www.slideshare.net/tmatyashovsky/from-cache-to-in-memory-data-grid-introduction-to-hazelcast>Source: From cache to in-memory data grid</a></i>
</p>
@ -1167,7 +1214,7 @@ The application is responsible for reading and writing from storage. The cache
* Add entry to cache
* Return entry
```
```python
def get_user(self, user_id):
user = cache.get("user.{0}", user_id)
if user is None:
@ -1191,7 +1238,7 @@ Subsequent reads of data added to cache are fast. Cache-aside is also referred
#### Write-through
<p align="center">
<img src="http://i.imgur.com/0vBc0hN.png">
<img src="http://i.imgur.com/0vBc0hN.png"/>
<br/>
<i><a href=http://www.slideshare.net/jboner/scalability-availability-stability-patterns/>Source: Scalability, availability, stability, patterns</a></i>
</p>
@ -1204,13 +1251,13 @@ The application uses the cache as the main data store, reading and writing data
Application code:
```
```python
set_user(12345, {"foo":"bar"})
```
Cache code:
```
```python
def set_user(user_id, values):
user = db.query("UPDATE Users WHERE id = {0}", user_id, values)
cache.set(user_id, user)
@ -1221,12 +1268,12 @@ Write-through is a slow overall operation due to the write operation, but subseq
##### Disadvantage(s): write through
* When a new node is created due to failure or scaling, the new node will not cache entries until the entry is updated in the database. Cache-aside in conjunction with write through can mitigate this issue.
* Most data written might never read, which can be minimized with a TTL.
* Most data written might never be read, which can be minimized with a TTL.
#### Write-behind (write-back)
<p align="center">
<img src="http://i.imgur.com/rgSrvjG.png">
<img src="http://i.imgur.com/rgSrvjG.png"/>
<br/>
<i><a href=http://www.slideshare.net/jboner/scalability-availability-stability-patterns/>Source: Scalability, availability, stability, patterns</a></i>
</p>
@ -1244,7 +1291,7 @@ In write-behind, the application does the following:
#### Refresh-ahead
<p align="center">
<img src="http://i.imgur.com/kxtjqgE.png">
<img src="http://i.imgur.com/kxtjqgE.png"/>
<br/>
<i><a href=http://www.slideshare.net/tmatyashovsky/from-cache-to-in-memory-data-grid-introduction-to-hazelcast>Source: From cache to in-memory data grid</a></i>
</p>
@ -1260,8 +1307,8 @@ Refresh-ahead can result in reduced latency vs read-through if the cache can acc
### Disadvantage(s): cache
* Need to maintain consistency between caches and the source of truth such as the database through [cache invalidation](https://en.wikipedia.org/wiki/Cache_algorithms).
* Need to make application changes such as adding Redis or memcached.
* Cache invalidation is a difficult problem, there is additional complexity associated with when to update the cache.
* Need to make application changes such as adding Redis or memcached.
### Source(s) and further reading
@ -1276,7 +1323,7 @@ Refresh-ahead can result in reduced latency vs read-through if the cache can acc
## Asynchronism
<p align="center">
<img src="http://i.imgur.com/54GYsSx.png">
<img src="http://i.imgur.com/54GYsSx.png"/>
<br/>
<i><a href=http://lethain.com/introduction-to-architecting-systems-for-scale/#platform_layer>Source: Intro to architecting systems for scale</a></i>
</p>
@ -1292,11 +1339,11 @@ Message queues receive, hold, and deliver messages. If an operation is too slow
The user is not blocked and the job is processed in the background. During this time, the client might optionally do a small amount of processing to make it seem like the task has completed. For example, if posting a tweet, the tweet could be instantly posted to your timeline, but it could take some time before your tweet is actually delivered to all of your followers.
**Redis** is useful as a simple message broker but messages can be lost.
**[Redis](https://redis.io/)** is useful as a simple message broker but messages can be lost.
**RabbitMQ** is popular but requires you to adapt to the 'AMQP' protocol and manage your own nodes.
**[RabbitMQ](https://www.rabbitmq.com/)** is popular but requires you to adapt to the 'AMQP' protocol and manage your own nodes.
**Amazon SQS**, is hosted but can have high latency and has the possibility of messages being delivered twice.
**[Amazon SQS](https://aws.amazon.com/sqs/)** is hosted but can have high latency and has the possibility of messages being delivered twice.
### Task queues
@ -1322,7 +1369,7 @@ If queues start to grow significantly, the queue size can become larger than mem
## Communication
<p align="center">
<img src="http://i.imgur.com/5KeocQs.jpg">
<img src="http://i.imgur.com/5KeocQs.jpg"/>
<br/>
<i><a href=http://www.escotal.com/osilayer.html>Source: OSI 7 layer model</a></i>
</p>
@ -1354,7 +1401,7 @@ HTTP is an application layer protocol relying on lower-level protocols such as *
### Transmission control protocol (TCP)
<p align="center">
<img src="http://i.imgur.com/JdAsdvG.jpg">
<img src="http://i.imgur.com/JdAsdvG.jpg"/>
<br/>
<i><a href=http://www.wildbunny.co.uk/blog/2012/10/09/how-to-make-a-multi-player-game-part-1/>Source: How to make a multiplayer game</a></i>
</p>
@ -1366,7 +1413,7 @@ TCP is a connection-oriented protocol over an [IP network](https://en.wikipedia.
If the sender does not receive a correct response, it will resend the packets. If there are multiple timeouts, the connection is dropped. TCP also implements [flow control](https://en.wikipedia.org/wiki/Flow_control_(data)) and [congestion control](https://en.wikipedia.org/wiki/Network_congestion#Congestion_control). These guarantees cause delays and generally result in less efficient transmission than UDP.
To ensure high throughput, web servers can keep a large number of TCP connections open, resulting in high memory usage. It can be expensive to have a large number of open connections between web server threads and say, a [memcached](#memcached) server. [Connection pooling](https://en.wikipedia.org/wiki/Connection_pool) can help in addition to switching to UDP where applicable.
To ensure high throughput, web servers can keep a large number of TCP connections open, resulting in high memory usage. It can be expensive to have a large number of open connections between web server threads and say, a [memcached](https://memcached.org/) server. [Connection pooling](https://en.wikipedia.org/wiki/Connection_pool) can help in addition to switching to UDP where applicable.
TCP is useful for applications that require high reliability but are less time critical. Some examples include web servers, database info, SMTP, FTP, and SSH.
@ -1378,7 +1425,7 @@ Use TCP over UDP when:
### User datagram protocol (UDP)
<p align="center">
<img src="http://i.imgur.com/yzDrJtA.jpg">
<img src="http://i.imgur.com/yzDrJtA.jpg"/>
<br/>
<i><a href=http://www.wildbunny.co.uk/blog/2012/10/09/how-to-make-a-multi-player-game-part-1/>Source: How to make a multiplayer game</a></i>
</p>
@ -1407,9 +1454,9 @@ Use UDP over TCP when:
### Remote procedure call (RPC)
<p align="center">
<img src="http://i.imgur.com/iF4Mkb5.png">
<img src="http://i.imgur.com/iF4Mkb5.png"/>
<br/>
<i><a href=http://www.puncsky.com/blog/2016/02/14/crack-the-system-design-interview/>Source: Crack the system design interview</a></i>
<i><a href=http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview>Source: Crack the system design interview</a></i>
</p>
In an RPC, a client causes a procedure to execute on a different address space, usually a remote server. The procedure is coded as if it were a local procedure call, abstracting away the details of how to communicate with the server from the client program. Remote calls are usually slower and less reliable than local calls so it is helpful to distinguish RPC calls from local calls. Popular RPC frameworks include [Protobuf](https://developers.google.com/protocol-buffers/), [Thrift](https://thrift.apache.org/), and [Avro](https://avro.apache.org/docs/current/).
@ -1505,7 +1552,7 @@ REST is focused on exposing data. It minimizes the coupling between client/serv
* [REST vs JSON-RPC](http://stackoverflow.com/questions/15056878/rest-vs-json-rpc)
* [Debunking the myths of RPC and REST](http://etherealbits.com/2012/12/debunking-the-myths-of-rpc-rest/)
* [What are the drawbacks of using REST](https://www.quora.com/What-are-the-drawbacks-of-using-RESTful-APIs)
* [Crack the system design interview](http://www.puncsky.com/blog/2016/02/14/crack-the-system-design-interview/)
* [Crack the system design interview](http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview)
* [Thrift](https://code.facebook.com/posts/1468950976659943/)
* [Why REST for internal use and not RPC](http://arstechnica.com/civis/viewtopic.php?t=1190508)
@ -1522,6 +1569,7 @@ Security is a broad topic. Unless you have considerable experience, a security
### Source(s) and further reading
* [API security checklist](https://github.com/shieldfy/API-Security-Checklist)
* [Security guide for developers](https://github.com/FallibleInc/security-guide-for-developers)
* [OWASP top ten](https://www.owasp.org/index.php/OWASP_Top_Ten_Cheat_Sheet)
@ -1556,7 +1604,7 @@ Latency Comparison Numbers
L1 cache reference 0.5 ns
Branch mispredict 5 ns
L2 cache reference 7 ns 14x L1 cache
Mutex lock/unlock 100 ns
Mutex lock/unlock 25 ns
Main memory reference 100 ns 20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy 10,000 ns 10 us
Send 1 KB bytes over 1 Gbps network 10,000 ns 10 us
@ -1603,12 +1651,12 @@ Handy metrics based on numbers above:
| Question | Reference(s) |
|---|---|
| Design a file sync service like Dropbox | [youtube.com](https://www.youtube.com/watch?v=PE4gwstWhmc) |
| Design a search engine like Google | [queue.acm.org](http://queue.acm.org/detail.cfm?id=988407)<br/>[stackexchange.com](http://programmers.stackexchange.com/questions/38324/interview-question-how-would-you-implement-google-search)<br/>[ardendertat.com](http://www.ardendertat.com/2012/01/11/implementing-search-engines/)<br>[stanford.edu](http://infolab.stanford.edu/~backrub/google.html) |
| Design a search engine like Google | [queue.acm.org](http://queue.acm.org/detail.cfm?id=988407)<br/>[stackexchange.com](http://programmers.stackexchange.com/questions/38324/interview-question-how-would-you-implement-google-search)<br/>[ardendertat.com](http://www.ardendertat.com/2012/01/11/implementing-search-engines/)<br/>[stanford.edu](http://infolab.stanford.edu/~backrub/google.html) |
| Design a scalable web crawler like Google | [quora.com](https://www.quora.com/How-can-I-build-a-web-crawler-from-scratch) |
| Design Google docs | [code.google.com](https://code.google.com/p/google-mobwrite/)<br/>[neil.fraser.name](https://neil.fraser.name/writing/sync/) |
| Design a key-value store like Redis | [slideshare.net](http://www.slideshare.net/dvirsky/introduction-to-redis) |
| Design a cache system like Memcached | [slideshare.net](http://www.slideshare.net/oemebamo/introduction-to-memcached) |
| Design a recommendation system like Amazon's | [hulu.com](http://tech.hulu.com/blog/2011/09/19/recommendation-system.html)<br/>[ijcai13.org](http://ijcai13.org/files/tutorial_slides/td3.pdf) |
| Design a recommendation system like Amazon's | [hulu.com](https://web.archive.org/web/20170406065247/http://tech.hulu.com/blog/2011/09/19/recommendation-system.html)<br/>[ijcai13.org](http://ijcai13.org/files/tutorial_slides/td3.pdf) |
| Design a tinyurl system like Bitly | [n00tc0d3r.blogspot.com](http://n00tc0d3r.blogspot.com/) |
| Design a chat app like WhatsApp | [highscalability.com](http://highscalability.com/blog/2014/2/26/the-whatsapp-architecture-facebook-bought-for-19-billion.html)
| Design a picture sharing system like Instagram | [highscalability.com](http://highscalability.com/flickr-architecture)<br/>[highscalability.com](http://highscalability.com/blog/2011/12/6/instagram-architecture-14-million-users-terabytes-of-photos.html) |
@ -1616,10 +1664,10 @@ Handy metrics based on numbers above:
| Design the Facebook timeline function | [facebook.com](https://www.facebook.com/note.php?note_id=10150468255628920)<br/>[highscalability.com](http://highscalability.com/blog/2012/1/23/facebook-timeline-brought-to-you-by-the-power-of-denormaliza.html) |
| Design the Facebook chat function | [erlang-factory.com](http://www.erlang-factory.com/upload/presentations/31/EugeneLetuchy-ErlangatFacebook.pdf)<br/>[facebook.com](https://www.facebook.com/note.php?note_id=14218138919&id=9445547199&index=0) |
| Design a graph search function like Facebook's | [facebook.com](https://www.facebook.com/notes/facebook-engineering/under-the-hood-building-out-the-infrastructure-for-graph-search/10151347573598920)<br/>[facebook.com](https://www.facebook.com/notes/facebook-engineering/under-the-hood-indexing-and-ranking-in-graph-search/10151361720763920)<br/>[facebook.com](https://www.facebook.com/notes/facebook-engineering/under-the-hood-the-natural-language-interface-of-graph-search/10151432733048920) |
| Design a content delivery network like CloudFlare | [cmu.edu](http://repository.cmu.edu/cgi/viewcontent.cgi?article=2112&context=compsci) |
| Design a content delivery network like CloudFlare | [figshare.com](https://figshare.com/articles/Globally_distributed_content_delivery/6605972) |
| Design a trending topic system like Twitter's | [michael-noll.com](http://www.michael-noll.com/blog/2013/01/18/implementing-real-time-trending-topics-in-storm/)<br/>[snikolov .wordpress.com](http://snikolov.wordpress.com/2012/11/14/early-detection-of-twitter-trends/) |
| Design a random ID generation system | [blog.twitter.com](https://blog.twitter.com/2010/announcing-snowflake)<br/>[github.com](https://github.com/twitter/snowflake/) |
| Return the top k requests during a time interval | [ucsb.edu](https://icmi.cs.ucsb.edu/research/tech_reports/reports/2005-23.pdf)<br/>[wpi.edu](http://davis.wpi.edu/xmdv/docs/EDBT11-diyang.pdf) |
| Return the top k requests during a time interval | [cs.ucsb.edu](https://www.cs.ucsb.edu/sites/cs.ucsb.edu/files/docs/reports/2005-23.pdf)<br/>[wpi.edu](http://davis.wpi.edu/xmdv/docs/EDBT11-diyang.pdf) |
| Design a system that serves data from multiple data centers | [highscalability.com](http://highscalability.com/blog/2009/8/24/how-google-serves-data-from-multiple-datacenters.html) |
| Design an online multiplayer card game | [indieflashblog.com](http://www.indieflashblog.com/how-to-create-an-asynchronous-multiplayer-game.html)<br/>[buildnewgames.com](http://buildnewgames.com/real-time-multiplayer/) |
| Design a garbage collection system | [stuffwithstuff.com](http://journal.stuffwithstuff.com/2013/12/08/babys-first-garbage-collector/)<br/>[washington.edu](http://courses.cs.washington.edu/courses/csep521/07wi/prj/rick.pdf) |
@ -1631,7 +1679,7 @@ Handy metrics based on numbers above:
> Articles on how real world systems are designed.
<p align="center">
<img src="http://i.imgur.com/TcUo2fw.png">
<img src="http://i.imgur.com/TcUo2fw.png"/>
<br/>
<i><a href=https://www.infoq.com/presentations/Twitter-Timeline-Scalability>Source: Twitter timelines at scale</a></i>
</p>
@ -1658,7 +1706,7 @@ Handy metrics based on numbers above:
| Data store | **Redis** - Distributed memory caching system with persistence and value types | [slideshare.net](http://www.slideshare.net/dvirsky/introduction-to-redis) |
| | | |
| File system | **Google File System (GFS)** - Distributed file system | [research.google.com](http://static.googleusercontent.com/media/research.google.com/zh-CN/us/archive/gfs-sosp2003.pdf) |
| File system | **Hadoop File System (HDFS)** - Open source implementation of GFS | [apache.org](https://hadoop.apache.org/docs/r1.2.1/hdfs_design.html) |
| File system | **Hadoop File System (HDFS)** - Open source implementation of GFS | [apache.org](http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html) |
| | | |
| Misc | **Chubby** - Lock service for loosely-coupled distributed systems from Google | [research.google.com](http://static.googleusercontent.com/external_content/untrusted_dlcp/research.google.com/en/us/archive/chubby-osdi06.pdf) |
| Misc | **Dapper** - Distributed systems tracing infrastructure | [research.google.com](http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36356.pdf)
@ -1678,9 +1726,10 @@ Handy metrics based on numbers above:
| Google | [Google architecture](http://highscalability.com/google-architecture) |
| Instagram | [14 million users, terabytes of photos](http://highscalability.com/blog/2011/12/6/instagram-architecture-14-million-users-terabytes-of-photos.html)<br/>[What powers Instagram](http://instagram-engineering.tumblr.com/post/13649370142/what-powers-instagram-hundreds-of-instances) |
| Justin.tv | [Justin.Tv's live video broadcasting architecture](http://highscalability.com/blog/2010/3/16/justintvs-live-video-broadcasting-architecture.html) |
| Facebook | [Scaling memcached at Facebook](https://cs.uwaterloo.ca/~brecht/courses/854-Emerging-2014/readings/key-value/fb-memcached-nsdi-2013.pdf)<br/>[TAO: Facebooks distributed data store for the social graph](https://cs.uwaterloo.ca/~brecht/courses/854-Emerging-2014/readings/data-store/tao-facebook-distributed-datastore-atc-2013.pdf)<br/>[Facebooks photo storage](https://www.usenix.org/legacy/event/osdi10/tech/full_papers/Beaver.pdf) |
| Facebook | [Scaling memcached at Facebook](https://cs.uwaterloo.ca/~brecht/courses/854-Emerging-2014/readings/key-value/fb-memcached-nsdi-2013.pdf)<br/>[TAO: Facebooks distributed data store for the social graph](https://cs.uwaterloo.ca/~brecht/courses/854-Emerging-2014/readings/data-store/tao-facebook-distributed-datastore-atc-2013.pdf)<br/>[Facebooks photo storage](https://www.usenix.org/legacy/event/osdi10/tech/full_papers/Beaver.pdf)<br/>[How Facebook Live Streams To 800,000 Simultaneous Viewers](http://highscalability.com/blog/2016/6/27/how-facebook-live-streams-to-800000-simultaneous-viewers.html) |
| Flickr | [Flickr architecture](http://highscalability.com/flickr-architecture) |
| Mailbox | [From 0 to one million users in 6 weeks](http://highscalability.com/blog/2013/6/18/scaling-mailbox-from-0-to-one-million-users-in-6-weeks-and-1.html) |
| Netflix | [A 360 Degree View Of The Entire Netflix Stack](http://highscalability.com/blog/2015/11/9/a-360-degree-view-of-the-entire-netflix-stack.html)<br/>[Netflix: What Happens When You Press Play?](http://highscalability.com/blog/2017/12/11/netflix-what-happens-when-you-press-play.html) |
| Pinterest | [From 0 To 10s of billions of page views a month](http://highscalability.com/blog/2013/4/15/scaling-pinterest-from-0-to-10s-of-billions-of-page-views-a.html)<br/>[18 million visitors, 10x growth, 12 employees](http://highscalability.com/blog/2012/5/21/pinterest-architecture-update-18-million-visitors-10x-growth.html) |
| Playfish | [50 million monthly users and growing](http://highscalability.com/blog/2010/9/21/playfishs-social-gaming-architecture-50-million-monthly-user.html) |
| PlentyOfFish | [PlentyOfFish architecture](http://highscalability.com/plentyoffish-architecture) |
@ -1688,8 +1737,8 @@ Handy metrics based on numbers above:
| Stack Overflow | [Stack Overflow architecture](http://highscalability.com/blog/2009/8/5/stack-overflow-architecture.html) |
| TripAdvisor | [40M visitors, 200M dynamic page views, 30TB data](http://highscalability.com/blog/2011/6/27/tripadvisor-architecture-40m-visitors-200m-dynamic-page-view.html) |
| Tumblr | [15 billion page views a month](http://highscalability.com/blog/2012/2/13/tumblr-architecture-15-billion-page-views-a-month-and-harder.html) |
| Twitter | [Making Twitter 10000 percent faster](http://highscalability.com/scaling-twitter-making-twitter-10000-percent-faster)<br/>[Storing 250 million tweets a day using MySQL](http://highscalability.com/blog/2011/12/19/how-twitter-stores-250-million-tweets-a-day-using-mysql.html)<br/>[150M active users, 300K QPS, a 22 MB/S firehose](http://highscalability.com/blog/2013/7/8/the-architecture-twitter-uses-to-deal-with-150m-active-users.html)<br/>[Timelines at scale](https://www.infoq.com/presentations/Twitter-Timeline-Scalability)<br/>[Big and small data at Twitter](https://www.youtube.com/watch?v=5cKTP36HVgI)<br/>[Operations at Twitter: scaling beyond 100 million users](https://www.youtube.com/watch?v=z8LU0Cj6BOU) |
| Uber | [How Uber scales their real-time market platform](http://highscalability.com/blog/2015/9/14/how-uber-scales-their-real-time-market-platform.html) |
| Twitter | [Making Twitter 10000 percent faster](http://highscalability.com/scaling-twitter-making-twitter-10000-percent-faster)<br/>[Storing 250 million tweets a day using MySQL](http://highscalability.com/blog/2011/12/19/how-twitter-stores-250-million-tweets-a-day-using-mysql.html)<br/>[150M active users, 300K QPS, a 22 MB/S firehose](http://highscalability.com/blog/2013/7/8/the-architecture-twitter-uses-to-deal-with-150m-active-users.html)<br/>[Timelines at scale](https://www.infoq.com/presentations/Twitter-Timeline-Scalability)<br/>[Big and small data at Twitter](https://www.youtube.com/watch?v=5cKTP36HVgI)<br/>[Operations at Twitter: scaling beyond 100 million users](https://www.youtube.com/watch?v=z8LU0Cj6BOU)<br/>[How Twitter Handles 3,000 Images Per Second](http://highscalability.com/blog/2016/4/20/how-twitter-handles-3000-images-per-second.html) |
| Uber | [How Uber scales their real-time market platform](http://highscalability.com/blog/2015/9/14/how-uber-scales-their-real-time-market-platform.html)<br/>[Lessons Learned From Scaling Uber To 2000 Engineers, 1000 Services, And 8000 Git Repositories](http://highscalability.com/blog/2016/10/12/lessons-learned-from-scaling-uber-to-2000-engineers-1000-ser.html) |
| WhatsApp | [The WhatsApp architecture Facebook bought for $19 billion](http://highscalability.com/blog/2014/2/26/the-whatsapp-architecture-facebook-bought-for-19-billion.html) |
| YouTube | [YouTube scalability](https://www.youtube.com/watch?v=w5WVu624fY8)<br/>[YouTube architecture](http://highscalability.com/youtube-architecture) |
@ -1701,11 +1750,10 @@ Handy metrics based on numbers above:
* [Airbnb Engineering](http://nerds.airbnb.com/)
* [Atlassian Developers](https://developer.atlassian.com/blog/)
* [Autodesk Engineering](http://cloudengineering.autodesk.com/blog/)
* [AWS Blog](https://aws.amazon.com/blogs/aws/)
* [Bitly Engineering Blog](http://word.bitly.com/)
* [Box Blogs](https://www.box.com/blog/engineering/)
* [Cloudera Developer Blog](http://blog.cloudera.com/blog/)
* [Box Blogs](https://blog.box.com/blog/category/engineering)
* [Cloudera Developer Blog](http://blog.cloudera.com/)
* [Dropbox Tech Blog](https://tech.dropbox.com/)
* [Engineering at Quora](http://engineering.quora.com/)
* [Ebay Tech Blog](http://www.ebaytechblog.com/)
@ -1728,14 +1776,14 @@ Handy metrics based on numbers above:
* [Microsoft Python Engineering](https://blogs.msdn.microsoft.com/pythonengineering/)
* [Netflix Tech Blog](http://techblog.netflix.com/)
* [Paypal Developer Blog](https://devblog.paypal.com/category/engineering/)
* [Pinterest Engineering Blog](http://engineering.pinterest.com/)
* [Pinterest Engineering Blog](https://medium.com/@Pinterest_Engineering)
* [Quora Engineering](https://engineering.quora.com/)
* [Reddit Blog](http://www.redditblog.com/)
* [Salesforce Engineering Blog](https://developer.salesforce.com/blogs/engineering/)
* [Slack Engineering Blog](https://slack.engineering/)
* [Spotify Labs](https://labs.spotify.com/)
* [Twilio Engineering Blog](http://www.twilio.com/engineering)
* [Twitter Engineering](https://engineering.twitter.com/)
* [Twitter Engineering](https://blog.twitter.com/engineering/)
* [Uber Engineering Blog](http://eng.uber.com/)
* [Yahoo Engineering Blog](http://yahooeng.tumblr.com/)
* [Yelp Engineering Blog](http://engineeringblog.yelp.com/)
@ -1743,9 +1791,9 @@ Handy metrics based on numbers above:
#### Source(s) and further reading
* [kilimchoi/engineering-blogs](https://github.com/kilimchoi/engineering-blogs)
Looking to add a blog? To avoid duplicating work, consider adding your company blog to the following repo:
The list of blogs here will be kept relatively small and [kilimchoi/engineering-blogs](https://github.com/kilimchoi/engineering-blogs) will contain the larger list to avoid duplicating work. Do consider adding your company blog to the engineering-blogs repo instead.
* [kilimchoi/engineering-blogs](https://github.com/kilimchoi/engineering-blogs)
## Under development
@ -1770,7 +1818,7 @@ Special thanks to:
* [mmcgrana/services-engineering](https://github.com/mmcgrana/services-engineering)
* [System design cheat sheet](https://gist.github.com/vasanthk/485d1c25737e8e72759f)
* [A distributed systems reading list](http://dancres.github.io/Pages/)
* [Cracking the system design interview](http://www.puncsky.com/blog/2016/02/14/crack-the-system-design-interview/)
* [Cracking the system design interview](http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview)
## Contact info

3
epub-metadata.yaml Normal file
View File

@ -0,0 +1,3 @@
title: System Design Primer
creator: Donne Martin
date: 2018

40
generate-epub.sh Executable file
View File

@ -0,0 +1,40 @@
#! /usr/bin/env sh
generate_from_stdin() {
outfile=$1
language=$2
echo "Generating '$language' ..."
pandoc --metadata-file=epub-metadata.yaml --metadata=lang:$2 --from=markdown -o $1 <&0
echo "Done! You can find the '$language' book at ./$outfile"
}
generate_with_solutions () {
tmpfile=$(mktemp /tmp/sytem-design-primer-epub-generator.XXX)
cat ./README.md >> $tmpfile
for dir in ./solutions/system_design/*; do
case $dir in *template*) continue;; esac
case $dir in *__init__.py*) continue;; esac
: [[ -d "$dir" ]] && ( cd "$dir" && cat ./README.md >> $tmpfile && echo "" >> $tmpfile )
done
cat $tmpfile | generate_from_stdin 'README.epub' 'en'
rm "$tmpfile"
}
generate () {
name=$1
language=$2
cat $name.md | generate_from_stdin $name.epub $language
}
generate_with_solutions
generate README-ja ja
generate README-zh-Hans zh-Hans
generate README-zh-TW zh-TW

View File

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer-primer)."
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer)."
]
},
{
@ -24,9 +24,9 @@
" * Operator, supervisor, director\n",
"* Can we assume operators always get the initial calls?\n",
" * Yes\n",
"* If there is no free operators or the operator can't handle the call, does the call go to the supervisors?\n",
"* If there is no available operators or the operator can't handle the call, does the call go to the supervisors?\n",
" * Yes\n",
"* If there is no free supervisors or the supervisor can't handle the call, does the call go to the directors?\n",
"* If there is no available supervisors or the supervisor can't handle the call, does the call go to the directors?\n",
" * Yes\n",
"* Can we assume the directors can handle all calls?\n",
" * Yes\n",

View File

@ -66,7 +66,7 @@ class Director(Employee):
super(Operator, self).__init__(employee_id, name, Rank.DIRECTOR)
def escalate_call(self):
raise NotImplemented('Directors must be able to handle any call')
raise NotImplementedError('Directors must be able to handle any call')
class CallState(Enum):
@ -112,6 +112,11 @@ class CallCenter(object):
return employee
return None
def notify_call_escalated(self, call): # ...
def notify_call_completed(self, call): # ...
def dispatch_queued_call_to_newly_freed_employee(self, call, employee): # ...
def notify_call_escalated(self, call):
pass
def notify_call_completed(self, call):
pass
def dispatch_queued_call_to_newly_freed_employee(self, call, employee):
pass

View File

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer-primer)."
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer)."
]
},
{

View File

@ -68,7 +68,7 @@ class Hand(object):
def score(self):
total_value = 0
for card in card:
for card in self.cards:
total_value += card.value
return total_value
@ -92,7 +92,7 @@ class BlackJackHand(Hand):
def possible_scores(self):
"""Return a list of possible scores, taking Aces into account."""
# ...
pass
class Deck(object):
@ -102,9 +102,9 @@ class Deck(object):
self.deal_index = 0
def remaining_cards(self):
return len(self.cards) - deal_index
return len(self.cards) - self.deal_index
def deal_card():
def deal_card(self):
try:
card = self.cards[self.deal_index]
card.is_available = False
@ -113,4 +113,5 @@ class Deck(object):
return None
return card
def shuffle(self): # ...
def shuffle(self):
pass

View File

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer-primer)."
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer)."
]
},
{

View File

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer-primer)."
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer)."
]
},
{
@ -21,7 +21,7 @@
"## Constraints and assumptions\n",
"\n",
"* What are we caching?\n",
" * We are cahing the results of web queries\n",
" * We are caching the results of web queries\n",
"* Can we assume inputs are valid or do we have to validate them?\n",
" * Assume they're valid\n",
"* Can we assume this fits memory?\n",
@ -84,7 +84,7 @@
" \n",
" Accessing a node updates its position to the front of the LRU list.\n",
" \"\"\"\n",
" node = self.lookup[query]\n",
" node = self.lookup.get(query)\n",
" if node is None:\n",
" return None\n",
" self.linked_list.move_to_front(node)\n",
@ -97,7 +97,7 @@
" If the entry is new and the cache is at capacity, removes the oldest entry\n",
" before the new entry is added.\n",
" \"\"\"\n",
" node = self.lookup[query]\n",
" node = self.lookup.get(query)\n",
" if node is not None:\n",
" # Key exists in cache, update the value\n",
" node.results = results\n",

View File

@ -11,9 +11,14 @@ class LinkedList(object):
self.head = None
self.tail = None
def move_to_front(self, node): # ...
def append_to_front(self, node): # ...
def remove_from_tail(self): # ...
def move_to_front(self, node):
pass
def append_to_front(self, node):
pass
def remove_from_tail(self):
pass
class Cache(object):
@ -24,12 +29,12 @@ class Cache(object):
self.lookup = {} # key: query, value: node
self.linked_list = LinkedList()
def get(self, query)
def get(self, query):
"""Get the stored query result from the cache.
Accessing a node updates its position to the front of the LRU list.
"""
node = self.lookup[query]
node = self.lookup.get(query)
if node is None:
return None
self.linked_list.move_to_front(node)
@ -42,7 +47,7 @@ class Cache(object):
If the entry is new and the cache is at capacity, removes the oldest entry
before the new entry is added.
"""
node = self.lookup[query]
node = self.lookup.get(query)
if node is not None:
# Key exists in cache, update the value
node.results = results

View File

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer-primer)."
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer)."
]
},
{

View File

@ -1,4 +1,5 @@
from abc import ABCMeta
from enum import Enum
class UserService(object):
@ -6,11 +7,20 @@ class UserService(object):
def __init__(self):
self.users_by_id = {} # key: user id, value: User
def add_user(self, user_id, name, pass_hash): # ...
def remove_user(self, user_id): # ...
def add_friend_request(self, from_user_id, to_user_id): # ...
def approve_friend_request(self, from_user_id, to_user_id): # ...
def reject_friend_request(self, from_user_id, to_user_id): # ...
def add_user(self, user_id, name, pass_hash):
pass
def remove_user(self, user_id):
pass
def add_friend_request(self, from_user_id, to_user_id):
pass
def approve_friend_request(self, from_user_id, to_user_id):
pass
def reject_friend_request(self, from_user_id, to_user_id):
pass
class User(object):
@ -25,12 +35,23 @@ class User(object):
self.received_friend_requests_by_friend_id = {} # key: friend id, value: AddRequest
self.sent_friend_requests_by_friend_id = {} # key: friend id, value: AddRequest
def message_user(self, friend_id, message): # ...
def message_group(self, group_id, message): # ...
def send_friend_request(self, friend_id): # ...
def receive_friend_request(self, friend_id): # ...
def approve_friend_request(self, friend_id): # ...
def reject_friend_request(self, friend_id): # ...
def message_user(self, friend_id, message):
pass
def message_group(self, group_id, message):
pass
def send_friend_request(self, friend_id):
pass
def receive_friend_request(self, friend_id):
pass
def approve_friend_request(self, friend_id):
pass
def reject_friend_request(self, friend_id):
pass
class Chat(metaclass=ABCMeta):
@ -51,8 +72,11 @@ class PrivateChat(Chat):
class GroupChat(Chat):
def add_user(self, user): # ...
def remove_user(self, user): # ...
def add_user(self, user):
pass
def remove_user(self, user):
pass
class Message(object):

View File

@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer-primer)."
"This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/system-design-primer)."
]
},
{

View File

@ -1,4 +1,5 @@
from abc import ABCMeta, abstractmethod
from enum import Enum
class VehicleSize(Enum):
@ -44,7 +45,7 @@ class Car(Vehicle):
super(Car, self).__init__(VehicleSize.COMPACT, license_plate, spot_size=1)
def can_fit_in_spot(self, spot):
return True if (spot.size == LARGE or spot.size == COMPACT) else False
return spot.size in (VehicleSize.LARGE, VehicleSize.COMPACT)
class Bus(Vehicle):
@ -53,7 +54,7 @@ class Bus(Vehicle):
super(Bus, self).__init__(VehicleSize.LARGE, license_plate, spot_size=5)
def can_fit_in_spot(self, spot):
return True if spot.size == LARGE else False
return spot.size == VehicleSize.LARGE
class ParkingLot(object):
@ -63,7 +64,7 @@ class ParkingLot(object):
self.levels = [] # List of Levels
def park_vehicle(self, vehicle):
for level in levels:
for level in self.levels:
if level.park_vehicle(vehicle):
return True
return False
@ -92,11 +93,11 @@ class Level(object):
def _find_available_spot(self, vehicle):
"""Find an available spot where vehicle can fit, or return None"""
# ...
pass
def _park_starting_at_spot(self, spot, vehicle):
"""Occupy starting at spot.spot_number to vehicle.spot_size."""
# ...
pass
class ParkingSpot(object):
@ -117,5 +118,8 @@ class ParkingSpot(object):
return False
return vehicle.can_fit_in_spot(self)
def park_vehicle(self, vehicle): # ...
def remove_vehicle(self): # ...
def park_vehicle(self, vehicle):
pass
def remove_vehicle(self):
pass

View File

@ -0,0 +1,440 @@
# 设计 Mint.com
**注意:这个文档中的链接会直接指向[系统设计主题索引](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#系统设计主题索引)中的有关部分,以避免重复的内容。您可以参考链接的相关内容,来了解其总的要点、方案的权衡取舍以及可选的替代方案。**
## 第一步:简述用例与约束条件
> 搜集需求与问题的范围。
> 提出问题来明确用例与约束条件。
> 讨论假设。
我们将在没有面试官明确说明问题的情况下,自己定义一些用例以及限制条件。
### 用例
#### 我们将把问题限定在仅处理以下用例的范围中
* **用户** 连接到一个财务账户
* **服务** 从账户中提取交易
* 每日更新
* 分类交易
* 允许用户手动分类
* 不自动重新分类
* 按类别分析每月支出
* **服务** 推荐预算
* 允许用户手动设置预算
* 当接近或者超出预算时,发送通知
* **服务** 具有高可用性
#### 非用例范围
* **服务** 执行附加的日志记录和分析
### 限制条件与假设
#### 提出假设
* 网络流量非均匀分布
* 自动账户日更新只适用于 30 天内活跃的用户
* 添加或者移除财务账户相对较少
* 预算通知不需要及时
* 1000 万用户
* 每个用户10个预算类别= 1亿个预算项
* 示例类别:
* Housing = $1,000
* Food = $200
* Gas = $100
* 卖方确定交易类别
* 50000 个卖方
* 3000 万财务账户
* 每月 50 亿交易
* 每月 5 亿读请求
* 10:1 读写比
* Write-heavy用户每天都进行交易但是每天很少访问该网站
#### 计算用量
**如果你需要进行粗略的用量计算,请向你的面试官说明。**
* 每次交易的用量:
* `user_id` - 8 字节
* `created_at` - 5 字节
* `seller` - 32 字节
* `amount` - 5 字节
* Total: ~50 字节
* 每月产生 250 GB 新的交易内容
* 每次交易 50 比特 * 50 亿交易每月
* 3年内新的交易内容 9 TB
* Assume most are new transactions instead of updates to existing ones
* 平均每秒产生 2000 次交易
* 平均每秒产生 200 读请求
便利换算指南:
* 每个月有 250 万秒
* 每秒一个请求 = 每个月 250 万次请求
* 每秒 40 个请求 = 每个月 1 亿次请求
* 每秒 400 个请求 = 每个月 10 亿次请求
## 第二步:概要设计
> 列出所有重要组件以规划概要设计。
![Imgur](http://i.imgur.com/E8klrBh.png)
## 第三步:设计核心组件
> 深入每个核心组件的细节。
### 用例:用户连接到一个财务账户
我们可以将 1000 万用户的信息存储在一个[关系数据库](https://github.com/donnemartin/system-design-primer#relational-database-management-system-rdbms)中。我们应该讨论一下[选择SQL或NoSQL之间的用例和权衡](https://github.com/donnemartin/system-design-primer#sql-or-nosql)了。
* **客户端** 作为一个[反向代理](https://github.com/donnemartin/system-design-primer#reverse-proxy-web-server),发送请求到 **Web 服务器**
* **Web 服务器** 转发请求到 **账户API** 服务器
* **账户API** 服务器将新输入的账户信息更新到 **SQL数据库** 的`accounts`表
**告知你的面试官你准备写多少代码**。
`accounts`表应该具有如下结构:
```
id int NOT NULL AUTO_INCREMENT
created_at datetime NOT NULL
last_update datetime NOT NULL
account_url varchar(255) NOT NULL
account_login varchar(32) NOT NULL
account_password_hash char(64) NOT NULL
user_id int NOT NULL
PRIMARY KEY(id)
FOREIGN KEY(user_id) REFERENCES users(id)
```
我们将在`id``user_id`和`created_at`等字段上创建一个[索引](https://github.com/donnemartin/system-design-primer#use-good-indices)以加速查找(对数时间而不是扫描整个表)并保持数据在内存中。从内存中顺序读取 1 MB数据花费大约250毫秒而从SSD读取是其4倍从磁盘读取是其80倍。<sup><a href=https://github.com/donnemartin/system-design-primer#latency-numbers-every-programmer-should-know>1</a></sup>
我们将使用公开的[**REST API**](https://github.com/donnemartin/system-design-primer#representational-state-transfer-rest):
```
$ curl -X POST --data '{ "user_id": "foo", "account_url": "bar", \
"account_login": "baz", "account_password": "qux" }' \
https://mint.com/api/v1/account
```
对于内部通信,我们可以使用[远程过程调用](https://github.com/donnemartin/system-design-primer#remote-procedure-call-rpc)。
接下来,服务从账户中提取交易。
### 用例:服务从账户中提取交易
如下几种情况下,我们会想要从账户中提取信息:
* 用户首次链接账户
* 用户手动更新账户
* 为过去 30 天内活跃的用户自动日更新
数据流:
* **客户端**向 **Web服务器** 发送请求
* **Web服务器** 将请求转发到 **帐户API** 服务器
* **帐户API** 服务器将job放在 **队列** 中,如 [Amazon SQS](https://aws.amazon.com/sqs/) 或者 [RabbitMQ](https://www.rabbitmq.com/)
* 提取交易可能需要一段时间,我们可能希望[与队列异步](https://github.com/donnemartin/system-design-primer#asynchronism)地来做,虽然这会引入额外的复杂度。
* **交易提取服务** 执行如下操作:
* 从 **Queue** 中拉取并从金融机构中提取给定用户的交易,将结果作为原始日志文件存储在 **对象存储区**。
* 使用 **分类服务** 来分类每个交易
* 使用 **预算服务** 来按类别计算每月总支出
* **预算服务** 使用 **通知服务** 让用户知道他们是否接近或者已经超出预算
* 更新具有分类交易的 **SQL数据库** 的`transactions`表
* 按类别更新 **SQL数据库** `monthly_spending`表的每月总支出
* 通过 **通知服务** 提醒用户交易完成
* 使用一个 **队列** (没有画出来) 来异步发送通知
`transactions`表应该具有如下结构:
```
id int NOT NULL AUTO_INCREMENT
created_at datetime NOT NULL
seller varchar(32) NOT NULL
amount decimal NOT NULL
user_id int NOT NULL
PRIMARY KEY(id)
FOREIGN KEY(user_id) REFERENCES users(id)
```
我们将在 `id``user_id`,和 `created_at`字段上创建[索引](https://github.com/donnemartin/system-design-primer#use-good-indices)。
`monthly_spending`表应该具有如下结构:
```
id int NOT NULL AUTO_INCREMENT
month_year date NOT NULL
category varchar(32)
amount decimal NOT NULL
user_id int NOT NULL
PRIMARY KEY(id)
FOREIGN KEY(user_id) REFERENCES users(id)
```
我们将在`id``user_id`字段上创建[索引](https://github.com/donnemartin/system-design-primer#use-good-indices)。
#### 分类服务
对于 **分类服务**,我们可以生成一个带有最受欢迎卖家的卖家-类别字典。如果我们估计 50000 个卖家,并估计每个条目占用不少于 255 个字节,该字典只需要大约 12 MB内存。
**告知你的面试官你准备写多少代码**。
```python
class DefaultCategories(Enum):
HOUSING = 0
FOOD = 1
GAS = 2
SHOPPING = 3
...
seller_category_map = {}
seller_category_map['Exxon'] = DefaultCategories.GAS
seller_category_map['Target'] = DefaultCategories.SHOPPING
...
```
对于一开始没有在映射中的卖家,我们可以通过评估用户提供的手动类别来进行众包。在 O(1) 时间内,我们可以用堆来快速查找每个卖家的顶端的手动覆盖。
```python
class Categorizer(object):
def __init__(self, seller_category_map, self.seller_category_crowd_overrides_map):
self.seller_category_map = seller_category_map
self.seller_category_crowd_overrides_map = \
seller_category_crowd_overrides_map
def categorize(self, transaction):
if transaction.seller in self.seller_category_map:
return self.seller_category_map[transaction.seller]
elif transaction.seller in self.seller_category_crowd_overrides_map:
self.seller_category_map[transaction.seller] = \
self.seller_category_crowd_overrides_map[transaction.seller].peek_min()
return self.seller_category_map[transaction.seller]
return None
```
交易实现:
```python
class Transaction(object):
def __init__(self, created_at, seller, amount):
self.timestamp = timestamp
self.seller = seller
self.amount = amount
```
### 用例:服务推荐预算
首先,我们可以使用根据收入等级分配每类别金额的通用预算模板。使用这种方法,我们不必存储在约束中标识的 1 亿个预算项目,只需存储用户覆盖的预算项目。如果用户覆盖预算类别,我们可以在
`TABLE budget_overrides`中存储此覆盖。
```python
class Budget(object):
def __init__(self, income):
self.income = income
self.categories_to_budget_map = self.create_budget_template()
def create_budget_template(self):
return {
'DefaultCategories.HOUSING': income * .4,
'DefaultCategories.FOOD': income * .2
'DefaultCategories.GAS': income * .1,
'DefaultCategories.SHOPPING': income * .2
...
}
def override_category_budget(self, category, amount):
self.categories_to_budget_map[category] = amount
```
对于 **预算服务** 而言,我们可以在`transactions`表上运行SQL查询以生成`monthly_spending`聚合表。由于用户通常每个月有很多交易,所以`monthly_spending`表的行数可能会少于总共50亿次交易的行数。
作为替代,我们可以在原始交易文件上运行 **MapReduce** 作业来:
* 分类每个交易
* 按类别生成每月总支出
对交易文件的运行分析可以显著减少数据库的负载。
如果用户更新类别,我们可以调用 **预算服务** 重新运行分析。
**告知你的面试官你准备写多少代码**.
日志文件格式样例以tab分割
```
user_id timestamp seller amount
```
**MapReduce** 实现:
```python
class SpendingByCategory(MRJob):
def __init__(self, categorizer):
self.categorizer = categorizer
self.current_year_month = calc_current_year_month()
...
def calc_current_year_month(self):
"""返回当前年月"""
...
def extract_year_month(self, timestamp):
"""返回时间戳的年,月部分"""
...
def handle_budget_notifications(self, key, total):
"""如果接近或超出预算调用通知API"""
...
def mapper(self, _, line):
"""解析每个日志行,提取和转换相关行。
参数行应为如下形式:
user_id timestamp seller amount
使用分类器来将卖家转换成类别生成如下形式的key-value对
(user_id, 2016-01, shopping), 25
(user_id, 2016-01, shopping), 100
(user_id, 2016-01, gas), 50
"""
user_id, timestamp, seller, amount = line.split('\t')
category = self.categorizer.categorize(seller)
period = self.extract_year_month(timestamp)
if period == self.current_year_month:
yield (user_id, period, category), amount
def reducer(self, key, value):
"""将每个key对应的值求和。
(user_id, 2016-01, shopping), 125
(user_id, 2016-01, gas), 50
"""
total = sum(values)
yield key, sum(values)
```
## 第四步:设计扩展
> 根据限制条件,找到并解决瓶颈。
![Imgur](http://i.imgur.com/V5q57vU.png)
**重要提示:不要从最初设计直接跳到最终设计中!**
现在你要 1) **基准测试、负载测试**。2) **分析、描述**性能瓶颈。3) 在解决瓶颈问题的同时评估替代方案、权衡利弊。4) 重复以上步骤。请阅读[「设计一个系统,并将其扩大到为数以百万计的 AWS 用户服务」](../scaling_aws/README.md) 来了解如何逐步扩大初始设计。
讨论初始设计可能遇到的瓶颈及相关解决方案是很重要的。例如加上一个配置多台 **Web 服务器**的**负载均衡器**是否能够解决问题?**CDN**呢?**主从复制**呢?它们各自的替代方案和需要**权衡**的利弊又有什么呢?
我们将会介绍一些组件来完成设计,并解决架构扩张问题。内置的负载均衡器将不做讨论以节省篇幅。
**为了避免重复讨论**,请参考[系统设计主题索引](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#系统设计主题的索引)相关部分来了解其要点、方案的权衡取舍以及可选的替代方案。
* [DNS](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#域名系统)
* [负载均衡器](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#负载均衡器)
* [水平拓展](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#水平扩展)
* [反向代理web 服务器)](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#反向代理web-服务器)
* [API 服务(应用层)](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#应用层)
* [缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#缓存)
* [关系型数据库管理系统 (RDBMS)](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#关系型数据库管理系统rdbms)
* [SQL 故障主从切换](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#故障切换)
* [主从复制](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#主从复制)
* [异步](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#异步)
* [一致性模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#一致性模式)
* [可用性模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#可用性模式)
我们将增加一个额外的用例:**用户** 访问摘要和交易数据。
用户会话,按类别统计的统计信息,以及最近的事务可以放在 **内存缓存**(如 Redis 或 Memcached )中。
* **客户端** 发送读请求给 **Web 服务器**
* **Web 服务器** 转发请求到 **读 API** 服务器
* 静态内容可通过 **对象存储** 比如缓存在 **CDN** 上的 S3 来服务
* **读 API** 服务器做如下动作:
* 检查 **内存缓存** 的内容
* 如果URL在 **内存缓存**中,返回缓存的内容
* 否则
* 如果URL在 **SQL 数据库**中,获取该内容
* 以其内容更新 **内存缓存**
参考 [何时更新缓存](https://github.com/donnemartin/system-design-primer#when-to-update-the-cache) 中权衡和替代的内容。以上方法描述了 [cache-aside缓存模式](https://github.com/donnemartin/system-design-primer#cache-aside).
我们可以使用诸如 Amazon Redshift 或者 Google BigQuery 等数据仓库解决方案,而不是将`monthly_spending`聚合表保留在 **SQL 数据库** 中。
我们可能只想在数据库中存储一个月的`交易`数据,而将其余数据存储在数据仓库或者 **对象存储区** 中。**对象存储区** 如Amazon S3) 能够舒服地解决每月 250 GB新内容的限制。
为了解决每秒 *平均* 2000 次读请求数(峰值时更高),受欢迎的内容的流量应由 **内存缓存** 而不是数据库来处理。 **内存缓存** 也可用于处理不均匀分布的流量和流量尖峰。 只要副本不陷入重复写入的困境,**SQL 读副本** 应该能够处理高速缓存未命中。
*平均* 200 次交易写入每秒(峰值时更高)对于单个 **SQL 写入主-从服务** 来说可能是棘手的。我们可能需要考虑其它的 SQL 性能拓展技术:
* [联合](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#联合)
* [分片](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#分片)
* [非规范化](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#非规范化)
* [SQL 调优](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#sql-调优)
我们也可以考虑将一些数据移至 **NoSQL 数据库**
## 其它要点
> 是否深入这些额外的主题,取决于你的问题范围和剩下的时间。
#### NoSQL
* [键-值存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#键-值存储)
* [文档类型存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#文档类型存储)
* [列型存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#列型存储)
* [图数据库](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#图数据库)
* [SQL vs NoSQL](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#sql-还是-nosql)
### 缓存
* 在哪缓存
* [客户端缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#客户端缓存)
* [CDN 缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#cdn-缓存)
* [Web 服务器缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#web-服务器缓存)
* [数据库缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#数据库缓存)
* [应用缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#应用缓存)
* 什么需要缓存
* [数据库查询级别的缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#数据库查询级别的缓存)
* [对象级别的缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#对象级别的缓存)
* 何时更新缓存
* [缓存模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#缓存模式)
* [直写模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#直写模式)
* [回写模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#回写模式)
* [刷新](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#刷新)
### 异步与微服务
* [消息队列](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#消息队列)
* [任务队列](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#任务队列)
* [背压](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#背压)
* [微服务](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#微服务)
### 通信
* 可权衡选择的方案:
* 与客户端的外部通信 - [使用 REST 作为 HTTP API](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#表述性状态转移rest)
* 服务器内部通信 - [RPC](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#远程过程调用协议rpc)
* [服务发现](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#服务发现)
### 安全性
请参阅[「安全」](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#安全)一章。
### 延迟数值
请参阅[「每个程序员都应该知道的延迟数」](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#每个程序员都应该知道的延迟数)。
### 持续探讨
* 持续进行基准测试并监控你的系统,以解决他们提出的瓶颈问题。
* 架构拓展是一个迭代的过程。

View File

@ -136,7 +136,7 @@ Data flow:
* The **Client** sends a request to the **Web Server**
* The **Web Server** forwards the request to the **Accounts API** server
* The **Accounts API** server places a job on a **Queue** such as Amazon SQS or [RabbitMQ](https://www.rabbitmq.com/)
* The **Accounts API** server places a job on a **Queue** such as [Amazon SQS](https://aws.amazon.com/sqs/) or [RabbitMQ](https://www.rabbitmq.com/)
* Extracting transactions could take awhile, we'd probably want to do this [asynchronously with a queue](https://github.com/donnemartin/system-design-primer#asynchronism), although this introduces additional complexity
* The **Transaction Extraction Service** does the following:
* Pulls from the **Queue** and extracts transactions for the given account from the financial institution, storing the results as raw log files in the **Object Store**
@ -182,7 +182,7 @@ For the **Category Service**, we can seed a seller-to-category dictionary with t
**Clarify with your interviewer how much code you are expected to write**.
```
```python
class DefaultCategories(Enum):
HOUSING = 0
@ -199,7 +199,7 @@ seller_category_map['Target'] = DefaultCategories.SHOPPING
For sellers not initially seeded in the map, we could use a crowdsourcing effort by evaluating the manual category overrides our users provide. We could use a heap to quickly lookup the top manual override per seller in O(1) time.
```
```python
class Categorizer(object):
def __init__(self, seller_category_map, self.seller_category_crowd_overrides_map):
@ -219,7 +219,7 @@ class Categorizer(object):
Transaction implementation:
```
```python
class Transaction(object):
def __init__(self, created_at, seller, amount):
@ -232,7 +232,7 @@ class Transaction(object):
To start, we could use a generic budget template that allocates category amounts based on income tiers. Using this approach, we would not have to store the 100 million budget items identified in the constraints, only those that the user overrides. If a user overrides a budget category, which we could store the override in the `TABLE budget_overrides`.
```
```python
class Budget(object):
def __init__(self, income):
@ -273,7 +273,7 @@ user_id timestamp seller amount
**MapReduce** implementation:
```
```python
class SpendingByCategory(MRJob):
def __init__(self, categorizer):

View File

@ -30,12 +30,12 @@ class SpendingByCategory(MRJob):
(2016-01, shopping), 100
(2016-01, gas), 50
"""
timestamp, seller, amount = line.split('\t')
timestamp, category, amount = line.split('\t')
period = self. extract_year_month(timestamp)
if period == self.current_year_month():
yield (period, category), amount
def reducer(self, key, value):
def reducer(self, key, values):
"""Sum values for each key.
(2016-01, shopping), 125

View File

@ -1,12 +1,16 @@
# -*- coding: utf-8 -*-
from enum import Enum
class DefaultCategories(Enum):
HOUSING = 0
FOOD = 1
GAS = 2
SHOPPING = 3
...
# ...
seller_category_map = {}
seller_category_map['Exxon'] = DefaultCategories.GAS
@ -44,4 +48,3 @@ class Budget(object):
def override_category_budget(self, category, amount):
self.categories_to_budget_map[category] = amount

View File

@ -0,0 +1,330 @@
# 设计 Pastebin.com (或者 Bit.ly)
**注意: 为了避免重复,当前文档会直接链接到[系统设计主题](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#系统设计主题的索引)的相关区域,请参考链接内容以获得综合的讨论点、权衡和替代方案。**
**设计 Bit.ly** - 是一个类似的问题,区别是 pastebin 需要存储的是 paste 的内容,而不是原始的未短化的 url。
## 第一步:概述用例和约束
> 收集这个问题的需求和范畴。
> 问相关问题来明确用例和约束。
> 讨论一些假设。
因为没有面试官来明确这些问题,所以我们自己将定义一些用例和约束。
### 用例
#### 我们将问题的范畴限定在如下用例
* **用户** 输入一段文本,然后得到一个随机生成的链接
* 过期设置
* 默认的设置是不会过期的
* 可以选择设置一个过期的时间
* **用户** 输入一个 paste 的 url 后,可以看到它存储的内容
* **用户** 是匿名的
* **Service** 跟踪页面分析
* 一个月的访问统计
* **Service** 删除过期的 pastes
* **Service** 需要高可用
#### 超出范畴的用例
* **用户** 可以注册一个账户
* **用户** 通过验证邮箱
* **用户** 可以用注册的账户登录
* **用户** 可以编辑文档
* **用户** 可以设置可见性
* **用户** 可以设置短链接
### 约束和假设
#### 状态假设
* 访问流量不是均匀分布的
* 打开一个短链接应该是很快的
* pastes 只能是文本
* 页面访问分析数据可以不用实时
* 一千万的用户量
* 每个月一千万的 paste 写入量
* 每个月一亿的 paste 读取量
* 读写比例在 10:1
#### 计算使用
**向面试官说明你是否应该粗略计算一下使用情况。**
* 每个 paste 的大小
* 每一个 paste 1 KB
* `shortlink` - 7 bytes
* `expiration_length_in_minutes` - 4 bytes
* `created_at` - 5 bytes
* `paste_path` - 255 bytes
* 总共 = ~1.27 KB
* 每个月新的 paste 内容在 12.7GB
* (1.27 * 10000000)KB / 月的 paste
* 三年内将近 450GB 的新 paste 内容
* 三年内 3.6 亿短链接
* 假设大部分都是新的 paste而不是需要更新已存在的 paste
* 平均 4paste/s 的写入速度
* 平均 40paste/s 的读取速度
简单的转换指南:
* 2.5 百万 req/s
* 1 req/s = 2.5 百万 req/m
* 40 req/s = 1 亿 req/m
* 400 req/s = 10 亿 req/m
## 第二步:创建一个高层次设计
> 概述一个包括所有重要的组件的高层次设计
![Imgur](http://i.imgur.com/BKsBnmG.png)
## 第三步:设计核心组件
> 深入每一个核心组件的细节
### 用例:用户输入一段文本,然后得到一个随机生成的链接
我们可以用一个 [关系型数据库](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#关系型数据库管理系统rdbms)作为一个大的哈希表,用来把生成的 url 映射到一个包含 paste 文件的文件服务器和路径上。
为了避免托管一个文件服务器,我们可以用一个托管的**对象存储**,比如 Amazon 的 S3 或者[NoSQL 文档类型存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#文档类型存储)。
作为一个大的哈希表的关系型数据库的替代方案,我们可以用[NoSQL 键值存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#键-值存储)。我们需要讨论[选择 SQL 或 NoSQL 之间的权衡](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#sql-还是-nosql)。下面的讨论是使用关系型数据库方法。
* **客户端** 发送一个创建 paste 的请求到作为一个[反向代理](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#反向代理web-服务器)启动的 **Web 服务器**
* **Web 服务器** 转发请求给 **写接口** 服务器
* **写接口** 服务器执行如下操作:
* 生成一个唯一的 url
* 检查这个 url 在 **SQL 数据库** 里面是否是唯一的
* 如果这个 url 不是唯一的,生成另外一个 url
* 如果我们支持自定义 url我们可以使用用户提供的 url也需要检查是否重复
* 把生成的 url 存储到 **SQL 数据库** 的 `pastes` 表里面
* 存储 paste 的内容数据到 **对象存储** 里面
* 返回生成的 url
**向面试官阐明你需要写多少代码**
`pastes` 表可以有如下结构:
```sql
shortlink char(7) NOT NULL
expiration_length_in_minutes int NOT NULL
created_at datetime NOT NULL
paste_path varchar(255) NOT NULL
PRIMARY KEY(shortlink)
```
我们将在 `shortlink` 字段和 `created_at` 字段上创建一个[数据库索引](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#使用正确的索引),用来提高查询的速度(避免因为扫描全表导致的长时间查询)并将数据保存在内存中,从内存里面顺序读取 1MB 的数据需要大概 250 微秒,而从 SSD 上读取则需要花费 4 倍的时间,从硬盘上则需要花费 80 倍的时间。<sup><a href=https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#每个程序员都应该知道的延迟数 > 1</a></sup>
为了生成唯一的 url我们可以
* 使用 [**MD5**](https://en.wikipedia.org/wiki/MD5) 来哈希用户的 IP 地址 + 时间戳
* MD5 是一个普遍用来生成一个 128-bit 长度的哈希值的一种哈希方法
* MD5 是一致分布的
* 或者我们也可以用 MD5 哈希一个随机生成的数据
* 用 [**Base 62**](https://www.kerstner.at/2012/07/shortening-strings-using-base-62-encoding/) 编码 MD5 哈希值
* 对于 urls使用 Base 62 编码 `[a-zA-Z0-9]` 是比较合适的
* 对于每一个原始输入只会有一个 hash 结果Base 62 是确定的(不涉及随机性)
* Base 64 是另外一个流行的编码方案,但是对于 urls会因为额外的 `+``-` 字符串而产生一些问题
* 以下 [Base 62 伪代码](http://stackoverflow.com/questions/742013/how-to-code-a-url-shortener) 执行的时间复杂度是 O(k)k 是数字的数量 = 7
```python
def base_encode(num, base=62):
digits = []
while num > 0
remainder = modulo(num, base)
digits.push(remainder)
num = divide(num, base)
digits = digits.reverse
```
* 取输出的前 7 个字符,结果会有 62^7 个可能的值,应该足以满足在 3 年内处理 3.6 亿个短链接的约束:
```python
url = base_encode(md5(ip_address+timestamp))[:URL_LENGTH]
```
我们将会用一个公开的 [**REST 风格接口**](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#表述性状态转移rest)
```shell
$ curl -X POST --data '{"expiration_length_in_minutes":"60", \"paste_contents":"Hello World!"}' https://pastebin.com/api/v1/paste
```
Response:
```json
{
"shortlink": "foobar"
}
```
用于内部通信,我们可以用 [RPC](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#远程过程调用协议rpc)。
### 用例:用户输入一个 paste 的 url 后可以看到它存储的内容
* **客户端** 发送一个获取 paste 请求到 **Web Server**
* **Web Server** 转发请求给 **读取接口** 服务器
* **读取接口** 服务器执行如下操作:
* 在 **SQL 数据库** 检查这个生成的 url
* 如果这个 url 在 **SQL 数据库** 里面,则从 **对象存储** 获取这个 paste 的内容
* 否则,返回一个错误页面给用户
REST API
```shell
curl https://pastebin.com/api/v1/paste?shortlink=foobar
```
Response:
```json
{
"paste_contents": "Hello World",
"created_at": "YYYY-MM-DD HH:MM:SS",
"expiration_length_in_minutes": "60"
}
```
### 用例: 服务跟踪分析页面
因为实时分析不是必须的,所以我们可以简单的 **MapReduce** **Web Server** 的日志,用来生成点击次数。
```python
class HitCounts(MRJob):
def extract_url(self, line):
"""Extract the generated url from the log line."""
...
def extract_year_month(self, line):
"""Return the year and month portions of the timestamp."""
...
def mapper(self, _, line):
"""Parse each log line, extract and transform relevant lines.
Emit key value pairs of the form:
(2016-01, url0), 1
(2016-01, url0), 1
(2016-01, url1), 1
"""
url = self.extract_url(line)
period = self.extract_year_month(line)
yield (period, url), 1
def reducer(self, key, values):
"""Sum values for each key.
(2016-01, url0), 2
(2016-01, url1), 1
"""
yield key, sum(values)
```
### 用例: 服务删除过期的 pastes
为了删除过期的 pastes我们可以直接搜索 **SQL 数据库** 中所有的过期时间比当前时间更早的记录,
所有过期的记录将从这张表里面删除(或者将其标记为过期)。
## 第四步:扩展这个设计
> 给定约束条件,识别和解决瓶颈。
![Imgur](http://i.imgur.com/4edXG0T.png)
**重要提示: 不要简单的从最初的设计直接跳到最终的设计**
说明您将迭代地执行这样的操作1)**Benchmark/Load 测试**2)**Profile** 出瓶颈3)在评估替代方案和权衡时解决瓶颈4)重复前面,可以参考[在 AWS 上设计一个可以支持百万用户的系统](../scaling_aws/README.md)这个用来解决如何迭代地扩展初始设计的例子。
重要的是讨论在初始设计中可能遇到的瓶颈,以及如何解决每个瓶颈。比如,在多个 **Web 服务器** 上添加 **负载平衡器** 可以解决哪些问题? **CDN** 解决哪些问题?**Master-Slave Replicas** 解决哪些问题? 替代方案是什么和怎么对每一个替代方案进行权衡比较?
我们将介绍一些组件来完成设计,并解决可伸缩性问题。内部的负载平衡器并不能减少杂乱。
**为了避免重复的讨论** 参考以下[系统设计主题](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#系统设计主题的索引)获取主要讨论要点、权衡和替代方案:
* [DNS](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#域名系统)
* [CDN](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#内容分发网络cdn)
* [负载均衡器](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#负载均衡器)
* [水平扩展](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#水平扩展)
* [反向代理web 服务器)](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#反向代理web-服务器)
* [应用层](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#应用层)
* [缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#缓存)
* [关系型数据库管理系统 (RDBMS)](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#关系型数据库管理系统rdbms)
* [SQL write master-slave failover](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#故障切换)
* [主从复制](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#主从复制)
* [一致性模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#一致性模式)
* [可用性模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#可用性模式)
**分析存储数据库** 可以用比如 Amazon Redshift 或者 Google BigQuery 这样的数据仓库解决方案。
一个像 Amazon S3 这样的 **对象存储**,可以轻松处理每月 12.7 GB 的新内容约束。
要处理 *平均* 每秒 40 读请求(峰值更高),其中热点内容的流量应该由 **内存缓存** 处理,而不是数据库。**内存缓存** 对于处理分布不均匀的流量和流量峰值也很有用。只要副本没有陷入复制写的泥潭,**SQL Read Replicas** 应该能够处理缓存丢失。
对于单个 **SQL Write Master-Slave***平均* 每秒 4paste 写入 (峰值更高) 应该是可以做到的。否则,我们需要使用额外的 SQL 扩展模式:
* [联合](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#联合)
* [分片](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#分片)
* [非规范化](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#非规范化)
* [SQL 调优](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#SQL调优)
我们还应该考虑将一些数据移动到 **NoSQL 数据库**
## 额外的话题
> 是否更深入探讨额外主题,取决于问题的范围和面试剩余的时间。
### NoSQL
* [键值存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#键-值存储)
* [文档存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#文档类型存储)
* [列型存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#列型存储)
* [图数据库](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#图数据库)
* [sql 还是 nosql](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#sql-还是-nosql)
### 缓存
* 在哪缓存
* [客户端缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#客户端缓存)
* [CDN 缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#cdn-缓存)
* [Web 服务器缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#web-服务器缓存)
* [数据库缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#数据库缓存)
* [应用缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#应用缓存)
* 缓存什么
* [数据库查询级别的缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#数据库查询级别的缓存)
* [对象级别的缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#对象级别的缓存)
* 何时更新缓存
* [缓存模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#缓存模式)
* [直写模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#直写模式)
* [回写模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#回写模式)
* [刷新](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#刷新)
### 异步和微服务
* [消息队列](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#消息队列)
* [任务队列](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#任务队列)
* [背压](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#背压)
* [微服务](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#微服务)
### 通信
* 讨论权衡:
* 跟客户端之间的外部通信 - [HTTP APIs following REST](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#表述性状态转移rest)
* 内部通信 - [RPC](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#远程过程调用协议rpc)
* [服务发现](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#服务发现)
### 安全
参考[安全](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#安全)。
### 延迟数字
见[每个程序员都应该知道的延迟数](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#每个程序员都应该知道的延迟数)。
### 持续进行
* 继续对系统进行基准测试和监控,以在瓶颈出现时解决它们
* 扩展是一个迭代的过程

View File

@ -126,11 +126,11 @@ To generate the unique url, we could:
* Alternatively, we could also take the MD5 hash of randomly-generated data
* [**Base 62**](https://www.kerstner.at/2012/07/shortening-strings-using-base-62-encoding/) encode the MD5 hash
* Base 62 encodes to `[a-zA-Z0-9]` which works well for urls, eliminating the need for escaping special characters
* There is only one hash result for the original input and and Base 62 is deterministic (no randomness involved)
* There is only one hash result for the original input and Base 62 is deterministic (no randomness involved)
* Base 64 is another popular encoding but provides issues for urls because of the additional `+` and `/` characters
* The following [Base 62 pseudocode](http://stackoverflow.com/questions/742013/how-to-code-a-url-shortener) runs in O(k) time where k is the number of digits = 7:
```
```python
def base_encode(num, base=62):
digits = []
while num > 0
@ -142,7 +142,7 @@ def base_encode(num, base=62):
* Take the first 7 characters of the output, which results in 62^7 possible values and should be sufficient to handle our constraint of 360 million shortlinks in 3 years:
```
```python
url = base_encode(md5(ip_address+timestamp))[:URL_LENGTH]
```
@ -194,7 +194,7 @@ Since realtime analytics are not a requirement, we could simply **MapReduce** th
**Clarify with your interviewer how much code you are expected to write**.
```
```python
class HitCounts(MRJob):
def extract_url(self, line):

View File

@ -26,7 +26,7 @@ class HitCounts(MRJob):
period = self.extract_year_month(line)
yield (period, url), 1
def reducer(self, key, value):
def reducer(self, key, values):
"""Sum values for each key.
(2016-01, url0), 2

View File

@ -0,0 +1,306 @@
# 设计一个键-值缓存来存储最近 web 服务查询的结果
**注意:这个文档中的链接会直接指向[系统设计主题索引](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#系统设计主题的索引)中的有关部分,以避免重复的内容。你可以参考链接的相关内容,来了解其总的要点、方案的权衡取舍以及可选的替代方案。**
## 第一步:简述用例与约束条件
> 搜集需求与问题的范围。
> 提出问题来明确用例与约束条件。
> 讨论假设。
我们将在没有面试官明确说明问题的情况下,自己定义一些用例以及限制条件。
### 用例
#### 我们将把问题限定在仅处理以下用例的范围中
* **用户**发送一个搜索请求,命中缓存
* **用户**发送一个搜索请求,未命中缓存
* **服务**有着高可用性
### 限制条件与假设
#### 提出假设
* 网络流量不是均匀分布的
* 经常被查询的内容应该一直存于缓存中
* 需要确定如何规定缓存过期、缓存刷新规则
* 缓存提供的服务查询速度要快
* 机器间延迟较低
* 缓存有内存限制
* 需要决定缓存什么、移除什么
* 需要缓存百万级的查询
* 1000 万用户
* 每个月 100 亿次查询
#### 计算用量
**如果你需要进行粗略的用量计算,请向你的面试官说明。**
* 缓存存储的是键值对有序表,键为 `query`(查询),值为 `results`(结果)。
* `query` - 50 字节
* `title` - 20 字节
* `snippet` - 200 字节
* 总计270 字节
* 假如 100 亿次查询都是不同的,且全部需要存储,那么每个月需要 2.7 TB 的缓存空间
* 单次查询 270 字节 * 每月查询 100 亿次
* 假设内存大小有限制,需要决定如何制定缓存过期规则
* 每秒 4,000 次请求
便利换算指南:
* 每个月有 250 万秒
* 每秒一个请求 = 每个月 250 万次请求
* 每秒 40 个请求 = 每个月 1 亿次请求
* 每秒 400 个请求 = 每个月 10 亿次请求
## 第二步:概要设计
> 列出所有重要组件以规划概要设计。
![Imgur](http://i.imgur.com/KqZ3dSx.png)
## 第三步:设计核心组件
> 深入每个核心组件的细节。
### 用例:用户发送了一次请求,命中了缓存
常用的查询可以由例如 Redis 或者 Memcached 之类的**内存缓存**提供支持,以减少数据读取延迟,并且避免**反向索引服务**以及**文档服务**的过载。从内存读取 1 MB 连续数据大约要花 250 微秒,而从 SSD 读取同样大小的数据要花费 4 倍的时间,从机械硬盘读取需要花费 80 倍以上的时间。<sup><a href=https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#每个程序员都应该知道的延迟数>1</a></sup>
由于缓存容量有限,我们将使用 LRU近期最少使用算法来控制缓存的过期。
* **客户端**向运行[反向代理](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#反向代理web-服务器)的 **Web 服务器**发送一个请求
* 这个 **Web 服务器**将请求转发给**查询 API** 服务
* **查询 API** 服务将会做这些事情:
* 分析查询
* 移除多余的内容
* 将文本分割成词组
* 修正拼写错误
* 规范化字母的大小写
* 将查询转换为布尔运算
* 检测**内存缓存**是否有匹配查询的内容
* 如果命中**内存缓存****内存缓存**将会做以下事情:
* 将缓存入口的位置指向 LRU 链表的头部
* 返回缓存内容
* 否则,**查询 API** 将会做以下事情:
* 使用**反向索引服务**来查找匹配查询的文档
* **反向索引服务**对匹配到的结果进行排名,然后返回最符合的结果
* 使用**文档服务**返回文章标题与片段
* 更新**内存缓存**,存入内容,将**内存缓存**入口位置指向 LRU 链表的头部
#### 缓存的实现
缓存可以使用双向链表实现:新元素将会在头结点加入,过期的元素将会在尾节点被删除。我们使用哈希表以便能够快速查找每个链表节点。
**向你的面试官告知你准备写多少代码**。
实现**查询 API 服务**
```python
class QueryApi(object):
def __init__(self, memory_cache, reverse_index_service):
self.memory_cache = memory_cache
self.reverse_index_service = reverse_index_service
def parse_query(self, query):
"""移除多余内容,将文本分割成词组,修复拼写错误,
规范化字母大小写,转换布尔运算。
"""
...
def process_query(self, query):
query = self.parse_query(query)
results = self.memory_cache.get(query)
if results is None:
results = self.reverse_index_service.process_search(query)
self.memory_cache.set(query, results)
return results
```
实现**节点**
```python
class Node(object):
def __init__(self, query, results):
self.query = query
self.results = results
```
实现**链表**
```python
class LinkedList(object):
def __init__(self):
self.head = None
self.tail = None
def move_to_front(self, node):
...
def append_to_front(self, node):
...
def remove_from_tail(self):
...
```
实现**缓存**
```python
class Cache(object):
def __init__(self, MAX_SIZE):
self.MAX_SIZE = MAX_SIZE
self.size = 0
self.lookup = {} # key: query, value: node
self.linked_list = LinkedList()
def get(self, query)
"""从缓存取得存储的内容
将入口节点位置更新为 LRU 链表的头部。
"""
node = self.lookup[query]
if node is None:
return None
self.linked_list.move_to_front(node)
return node.results
def set(self, results, query):
"""将所给查询键的结果存在缓存中。
当更新缓存记录的时候,将它的位置指向 LRU 链表的头部。
如果这个记录是新的记录,并且缓存空间已满,应该在加入新记录前
删除最老的记录。
"""
node = self.lookup[query]
if node is not None:
# 键存在于缓存中,更新它对应的值
node.results = results
self.linked_list.move_to_front(node)
else:
# 键不存在于缓存中
if self.size == self.MAX_SIZE:
# 在链表中查找并删除最老的记录
self.lookup.pop(self.linked_list.tail.query, None)
self.linked_list.remove_from_tail()
else:
self.size += 1
# 添加新的键值对
new_node = Node(query, results)
self.linked_list.append_to_front(new_node)
self.lookup[query] = new_node
```
#### 何时更新缓存
缓存将会在以下几种情况更新:
* 页面内容发生变化
* 页面被移除或者加入了新页面
* 页面的权值发生变动
解决这些问题的最直接的方法就是为缓存记录设置一个它在被更新前能留在缓存中的最长时间这个时间简称为存活时间TTL
参考 [「何时更新缓存」](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#何时更新缓存)来了解其权衡取舍及替代方案。以上方法在[缓存模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#缓存模式)一章中详细地进行了描述。
## 第四步:架构扩展
> 根据限制条件,找到并解决瓶颈。
![Imgur](http://i.imgur.com/4j99mhe.png)
**重要提示:不要从最初设计直接跳到最终设计中!**
现在你要 1) **基准测试、负载测试**。2) **分析、描述**性能瓶颈。3) 在解决瓶颈问题的同时评估替代方案、权衡利弊。4) 重复以上步骤。请阅读[「设计一个系统,并将其扩大到为数以百万计的 AWS 用户服务」](../scaling_aws/README.md) 来了解如何逐步扩大初始设计。
讨论初始设计可能遇到的瓶颈及相关解决方案是很重要的。例如加上一个配置多台 **Web 服务器**的**负载均衡器**是否能够解决问题?**CDN**呢?**主从复制**呢?它们各自的替代方案和需要**权衡**的利弊又有什么呢?
我们将会介绍一些组件来完成设计,并解决架构扩张问题。内置的负载均衡器将不做讨论以节省篇幅。
**为了避免重复讨论**,请参考[系统设计主题索引](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#系统设计主题的索引)相关部分来了解其要点、方案的权衡取舍以及可选的替代方案。
* [DNS](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#域名系统)
* [负载均衡器](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#负载均衡器)
* [水平拓展](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#水平扩展)
* [反向代理web 服务器)](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#反向代理web-服务器)
* [API 服务(应用层)](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#应用层)
* [缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#缓存)
* [一致性模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#一致性模式)
* [可用性模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#可用性模式)
### 将内存缓存扩大到多台机器
为了解决庞大的请求负载以及巨大的内存需求,我们将要对架构进行水平拓展。如何在我们的**内存缓存**集群中存储数据呢?我们有以下三个主要可选方案:
* **缓存集群中的每一台机器都有自己的缓存** - 简单,但是它会降低缓存命中率。
* **缓存集群中的每一台机器都有缓存的拷贝** - 简单,但是它的内存使用效率太低了。
* **对缓存进行[分片](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#分片),分别部署在缓存集群中的所有机器中** - 更加复杂,但是它是最佳的选择。我们可以使用哈希,用查询语句 `machine = hash(query)` 来确定哪台机器有需要缓存。当然我们也可以使用[一致性哈希](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#正在完善中)。
## 其它要点
> 是否深入这些额外的主题,取决于你的问题范围和剩下的时间。
### SQL 缩放模式
* [读取复制](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#主从复制)
* [联合](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#联合)
* [分片](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#分片)
* [非规范化](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#非规范化)
* [SQL 调优](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#sql-调优)
#### NoSQL
* [键-值存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#键-值存储)
* [文档类型存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#文档类型存储)
* [列型存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#列型存储)
* [图数据库](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#图数据库)
* [SQL vs NoSQL](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#sql-还是-nosql)
### 缓存
* 在哪缓存
* [客户端缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#客户端缓存)
* [CDN 缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#cdn-缓存)
* [Web 服务器缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#web-服务器缓存)
* [数据库缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#数据库缓存)
* [应用缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#应用缓存)
* 什么需要缓存
* [数据库查询级别的缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#数据库查询级别的缓存)
* [对象级别的缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#对象级别的缓存)
* 何时更新缓存
* [缓存模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#缓存模式)
* [直写模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#直写模式)
* [回写模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#回写模式)
* [刷新](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#刷新)
### 异步与微服务
* [消息队列](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#消息队列)
* [任务队列](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#任务队列)
* [背压](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#背压)
* [微服务](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#微服务)
### 通信
* 可权衡选择的方案:
* 与客户端的外部通信 - [使用 REST 作为 HTTP API](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#表述性状态转移rest)
* 服务器内部通信 - [RPC](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#远程过程调用协议rpc)
* [服务发现](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#服务发现)
### 安全性
请参阅[「安全」](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#安全)一章。
### 延迟数值
请参阅[「每个程序员都应该知道的延迟数」](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#每个程序员都应该知道的延迟数)。
### 持续探讨
* 持续进行基准测试并监控你的系统,以解决他们提出的瓶颈问题。
* 架构拓展是一个迭代的过程。

View File

@ -97,7 +97,7 @@ The cache can use a doubly-linked list: new items will be added to the head whil
**Query API Server** implementation:
```
```python
class QueryApi(object):
def __init__(self, memory_cache, reverse_index_service):
@ -121,7 +121,7 @@ class QueryApi(object):
**Node** implementation:
```
```python
class Node(object):
def __init__(self, query, results):
@ -131,7 +131,7 @@ class Node(object):
**LinkedList** implementation:
```
```python
class LinkedList(object):
def __init__(self):
@ -150,7 +150,7 @@ class LinkedList(object):
**Cache** implementation:
```
```python
class Cache(object):
def __init__(self, MAX_SIZE):
@ -247,7 +247,7 @@ To handle the heavy request load and the large amount of memory needed, we'll sc
### SQL scaling patterns
* [Read replicas](https://github.com/donnemartin/system-design-primer#master-slave)
* [Read replicas](https://github.com/donnemartin/system-design-primer#master-slave-replication)
* [Federation](https://github.com/donnemartin/system-design-primer#federation)
* [Sharding](https://github.com/donnemartin/system-design-primer#sharding)
* [Denormalization](https://github.com/donnemartin/system-design-primer#denormalization)

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
class QueryApi(object):
def __init__(self, memory_cache, reverse_index_cluster):
@ -52,7 +53,7 @@ class Cache(object):
self.lookup = {}
self.linked_list = LinkedList()
def get(self, query)
def get(self, query):
"""Get the stored query result from the cache.
Accessing a node updates its position to the front of the LRU list.

View File

@ -0,0 +1,338 @@
# 为 Amazon 设计分类售卖排行
**注意:这个文档中的链接会直接指向[系统设计主题索引](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#系统设计主题的索引)中的有关部分,以避免重复的内容。你可以参考链接的相关内容,来了解其总的要点、方案的权衡取舍以及可选的替代方案。**
## 第一步:简述用例与约束条件
> 搜集需求与问题的范围。
> 提出问题来明确用例与约束条件。
> 讨论假设。
我们将在没有面试官明确说明问题的情况下,自己定义一些用例以及限制条件。
### 用例
#### 我们将把问题限定在仅处理以下用例的范围中
* **服务**根据分类计算过去一周中最受欢迎的商品
* **用户**通过分类浏览过去一周中最受欢迎的商品
* **服务**有着高可用性
#### 不在用例范围内的有
* 一般的电商网站
* 只为售卖排行榜设计组件
### 限制条件与假设
#### 提出假设
* 网络流量不是均匀分布的
* 一个商品可能存在于多个分类中
* 商品不能够更改分类
* 不会存在如 `foo/bar/baz` 之类的子分类
* 每小时更新一次结果
* 受欢迎的商品越多,就需要更频繁地更新
* 1000 万个商品
* 1000 个分类
* 每个月 10 亿次交易
* 每个月 1000 亿次读取请求
* 100:1 的读写比例
#### 计算用量
**如果你需要进行粗略的用量计算,请向你的面试官说明。**
* 每笔交易的用量:
* `created_at` - 5 字节
* `product_id` - 8 字节
* `category_id` - 4 字节
* `seller_id` - 8 字节
* `buyer_id` - 8 字节
* `quantity` - 4 字节
* `total_price` - 5 字节
* 总计:大约 40 字节
* 每个月的交易内容会产生 40 GB 的记录
* 每次交易 40 字节 * 每个月 10 亿次交易
* 3年内产生了 1.44 TB 的新交易内容记录
* 假定大多数的交易都是新交易而不是更改以前进行完的交易
* 平均每秒 400 次交易次数
* 平均每秒 40,000 次读取请求
便利换算指南:
* 每个月有 250 万秒
* 每秒一个请求 = 每个月 250 万次请求
* 每秒 40 个请求 = 每个月 1 亿次请求
* 每秒 400 个请求 = 每个月 10 亿次请求
## 第二步:概要设计
> 列出所有重要组件以规划概要设计。
![Imgur](http://i.imgur.com/vwMa1Qu.png)
## 第三步:设计核心组件
> 深入每个核心组件的细节。
### 用例:服务需要根据分类计算上周最受欢迎的商品
我们可以在现成的**对象存储**系统(例如 Amazon S3 服务)中存储 **售卖 API** 服务产生的日志文本, 因此不需要我们自己搭建分布式文件系统了。
**向你的面试官告知你准备写多少代码**。
假设下面是一个用 tab 分割的简易的日志记录:
```
timestamp product_id category_id qty total_price seller_id buyer_id
t1 product1 category1 2 20.00 1 1
t2 product1 category2 2 20.00 2 2
t2 product1 category2 1 10.00 2 3
t3 product2 category1 3 7.00 3 4
t4 product3 category2 7 2.00 4 5
t5 product4 category1 1 5.00 5 6
...
```
**售卖排行服务** 需要用到 **MapReduce**,并使用 **售卖 API** 服务进行日志记录,同时将结果写入 **SQL 数据库**中的总表 `sales_rank` 中。我们也可以讨论一下[究竟是用 SQL 还是用 NoSQL](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#sql-还是-nosql)。
我们需要通过以下步骤使用 **MapReduce**
* **第 1 步** - 将数据转换为 `(category, product_id), sum(quantity)` 的形式
* **第 2 步** - 执行分布式排序
```python
class SalesRanker(MRJob):
def within_past_week(self, timestamp):
"""如果时间戳属于过去的一周则返回 True
否则返回 False。"""
...
def mapper(self, _ line):
"""解析日志的每一行,提取并转换相关行,
将键值对设定为如下形式:
(category1, product1), 2
(category2, product1), 2
(category2, product1), 1
(category1, product2), 3
(category2, product3), 7
(category1, product4), 1
"""
timestamp, product_id, category_id, quantity, total_price, seller_id, \
buyer_id = line.split('\t')
if self.within_past_week(timestamp):
yield (category_id, product_id), quantity
def reducer(self, key, value):
"""将每个 key 的值加起来。
(category1, product1), 2
(category2, product1), 3
(category1, product2), 3
(category2, product3), 7
(category1, product4), 1
"""
yield key, sum(values)
def mapper_sort(self, key, value):
"""构造 key 以确保正确的排序。
将键值对转换成如下形式:
(category1, 2), product1
(category2, 3), product1
(category1, 3), product2
(category2, 7), product3
(category1, 1), product4
MapReduce 的随机排序步骤会将键
值的排序打乱,变成下面这样:
(category1, 1), product4
(category1, 2), product1
(category1, 3), product2
(category2, 3), product1
(category2, 7), product3
"""
category_id, product_id = key
quantity = value
yield (category_id, quantity), product_id
def reducer_identity(self, key, value):
yield key, value
def steps(self):
""" 此处为 map reduce 步骤"""
return [
self.mr(mapper=self.mapper,
reducer=self.reducer),
self.mr(mapper=self.mapper_sort,
reducer=self.reducer_identity),
]
```
得到的结果将会是如下的排序列,我们将其插入 `sales_rank` 表中:
```
(category1, 1), product4
(category1, 2), product1
(category1, 3), product2
(category2, 3), product1
(category2, 7), product3
```
`sales_rank` 表的数据结构如下:
```
id int NOT NULL AUTO_INCREMENT
category_id int NOT NULL
total_sold int NOT NULL
product_id int NOT NULL
PRIMARY KEY(id)
FOREIGN KEY(category_id) REFERENCES Categories(id)
FOREIGN KEY(product_id) REFERENCES Products(id)
```
我们会以 `id`、`category_id` 与 `product_id` 创建一个 [索引](https://github.com/donnemartin/system-design-primer#use-good-indices)以加快查询速度(只需要使用读取日志的时间,不再需要每次都扫描整个数据表)并让数据常驻内存。从内存读取 1 MB 连续数据大约要花 250 微秒,而从 SSD 读取同样大小的数据要花费 4 倍的时间,从机械硬盘读取需要花费 80 倍以上的时间。<sup><a href=https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#每个程序员都应该知道的延迟数>1</a></sup>
### 用例:用户需要根据分类浏览上周中最受欢迎的商品
* **客户端**向运行[反向代理](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#反向代理web-服务器)的 **Web 服务器**发送一个请求
* 这个 **Web 服务器**将请求转发给**查询 API** 服务
* The **查询 API** 服务将从 **SQL 数据库**`sales_rank` 表中读取数据
我们可以调用一个公共的 [REST API](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#表述性状态转移rest)
```
$ curl https://amazon.com/api/v1/popular?category_id=1234
```
返回:
```
{
"id": "100",
"category_id": "1234",
"total_sold": "100000",
"product_id": "50",
},
{
"id": "53",
"category_id": "1234",
"total_sold": "90000",
"product_id": "200",
},
{
"id": "75",
"category_id": "1234",
"total_sold": "80000",
"product_id": "3",
},
```
而对于服务器内部的通信,我们可以使用 [RPC](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#远程过程调用协议rpc)。
## 第四步:架构扩展
> 根据限制条件,找到并解决瓶颈。
![Imgur](http://i.imgur.com/MzExP06.png)
**重要提示:不要从最初设计直接跳到最终设计中!**
现在你要 1) **基准测试、负载测试**。2) **分析、描述**性能瓶颈。3) 在解决瓶颈问题的同时评估替代方案、权衡利弊。4) 重复以上步骤。请阅读[「设计一个系统,并将其扩大到为数以百万计的 AWS 用户服务」](../scaling_aws/README.md) 来了解如何逐步扩大初始设计。
讨论初始设计可能遇到的瓶颈及相关解决方案是很重要的。例如加上一个配置多台 **Web 服务器**的**负载均衡器**是否能够解决问题?**CDN**呢?**主从复制**呢?它们各自的替代方案和需要**权衡**的利弊又有什么呢?
我们将会介绍一些组件来完成设计,并解决架构扩张问题。内置的负载均衡器将不做讨论以节省篇幅。
**为了避免重复讨论**,请参考[系统设计主题索引](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#系统设计主题的索引)相关部分来了解其要点、方案的权衡取舍以及可选的替代方案。
* [DNS](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#域名系统)
* [负载均衡器](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#负载均衡器)
* [水平拓展](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#水平扩展)
* [反向代理web 服务器)](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#反向代理web-服务器)
* [API 服务(应用层)](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#应用层)
* [缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#缓存)
* [关系型数据库管理系统 (RDBMS)](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#关系型数据库管理系统rdbms)
* [SQL 故障主从切换](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#故障切换)
* [主从复制](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#主从复制)
* [一致性模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#一致性模式)
* [可用性模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#可用性模式)
**分析数据库** 可以用现成的数据仓储系统,例如使用 Amazon Redshift 或者 Google BigQuery 的解决方案。
当使用数据仓储技术或者**对象存储**系统时我们只想在数据库中存储有限时间段的数据。Amazon S3 的**对象存储**系统可以很方便地设置每个月限制只允许新增 40 GB 的存储内容。
平均每秒 40,000 次的读取请求(峰值将会更高), 可以通过扩展 **内存缓存** 来处理热点内容的读取流量,这对于处理不均匀分布的流量和流量峰值也很有用。由于读取量非常大,**SQL Read 副本** 可能会遇到处理缓存未命中的问题,我们可能需要使用额外的 SQL 扩展模式。
平均每秒 400 次写操作(峰值将会更高)可能对于单个 **SQL 写主-从** 模式来说比较很困难,因此同时还需要更多的扩展技术
SQL 缩放模式包括:
* [联合](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#联合)
* [分片](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#分片)
* [非规范化](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#非规范化)
* [SQL 调优](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#sql-调优)
我们也可以考虑将一些数据移至 **NoSQL 数据库**
## 其它要点
> 是否深入这些额外的主题,取决于你的问题范围和剩下的时间。
#### NoSQL
* [键-值存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#键-值存储)
* [文档类型存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#文档类型存储)
* [列型存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#列型存储)
* [图数据库](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#图数据库)
* [SQL vs NoSQL](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#sql-还是-nosql)
### 缓存
* 在哪缓存
* [客户端缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#客户端缓存)
* [CDN 缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#cdn-缓存)
* [Web 服务器缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#web-服务器缓存)
* [数据库缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#数据库缓存)
* [应用缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#应用缓存)
* 什么需要缓存
* [数据库查询级别的缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#数据库查询级别的缓存)
* [对象级别的缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#对象级别的缓存)
* 何时更新缓存
* [缓存模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#缓存模式)
* [直写模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#直写模式)
* [回写模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#回写模式)
* [刷新](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#刷新)
### 异步与微服务
* [消息队列](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#消息队列)
* [任务队列](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#任务队列)
* [背压](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#背压)
* [微服务](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#微服务)
### 通信
* 可权衡选择的方案:
* 与客户端的外部通信 - [使用 REST 作为 HTTP API](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#表述性状态转移rest)
* 服务器内部通信 - [RPC](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#远程过程调用协议rpc)
* [服务发现](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#服务发现)
### 安全性
请参阅[「安全」](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#安全)一章。
### 延迟数值
请参阅[「每个程序员都应该知道的延迟数」](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#每个程序员都应该知道的延迟数)。
### 持续探讨
* 持续进行基准测试并监控你的系统,以解决他们提出的瓶颈问题。
* 架构拓展是一个迭代的过程。

View File

@ -102,7 +102,7 @@ We'll use a multi-step **MapReduce**:
* **Step 1** - Transform the data to `(category, product_id), sum(quantity)`
* **Step 2** - Perform a distributed sort
```
```python
class SalesRanker(MRJob):
def within_past_week(self, timestamp):

View File

@ -9,7 +9,7 @@ class SalesRanker(MRJob):
"""Return True if timestamp is within past week, False otherwise."""
...
def mapper(self, _ line):
def mapper(self, _, line):
"""Parse each log line, extract and transform relevant lines.
Emit key value pairs of the form:
@ -25,7 +25,7 @@ class SalesRanker(MRJob):
if self.within_past_week(timestamp):
yield (category, product_id), quantity
def reducer(self, key, value):
def reducer(self, key, values):
"""Sum values for each key.
(foo, p1), 2
@ -74,4 +74,4 @@ class SalesRanker(MRJob):
if __name__ == '__main__':
HitCounts.run()
SalesRanker.run()

View File

@ -0,0 +1,403 @@
# 在 AWS 上设计支持百万级到千万级用户的系统
**注释:为了避免重复,这篇文章的链接直接关联到 [系统设计主题](https://github.com/donnemartin/system-design-primer#index-of-system-design-topics) 的相关章节。为一讨论要点、折中方案和可选方案做参考。**
## 第 1 步:用例和约束概要
> 收集需求并调查问题。
> 通过提问清晰用例和约束。
> 讨论假设。
如果没有面试官提出明确的问题,我们将自己定义一些用例和约束条件。
### 用例
解决这个问题是一个循序渐进的过程1) **基准/负载 测试** 2) 瓶颈 **概述** 3) 当评估可选和折中方案时定位瓶颈4) 重复,这是向可扩展的设计发展基础设计的好模式。
除非你有 AWS 的背景或者正在申请需要 AWS 知识的相关职位,否则不要求了解 AWS 的相关细节。并且这个练习中讨论的许多原则可以更广泛地应用于AWS生态系统之外。
#### 我们就处理以下用例讨论这一问题
* **用户** 进行读或写请求
* **服务** 进行处理,存储用户数据,然后返回结果
* **服务** 需要从支持小规模用户开始到百万用户
* 在我们演化架构来处理大量的用户和请求时,讨论一般的扩展模式
* **服务** 高可用
### 约束和假设
#### 状态假设
* 流量不均匀分布
* 需要关系数据
* 从一个用户扩展到千万用户
* 表示用户量的增长
* 用户量+
* 用户量++
* 用户量+++
* ...
* 1000 万用户
* 每月 10 亿次写入
* 每月 1000 亿次读出
* 100:1 读写比率
* 每次写入 1 KB 内容
#### 计算使用
**向你的面试官厘清你是否应该做粗略的使用计算**
* 1 TB 新内容 / 月
* 1 KB 每次写入 * 10 亿 写入 / 月
* 36 TB 新内容 / 3 年
* 假设大多数写入都是新内容而不是更新已有内容
* 平均每秒 400 次写入
* 平均每秒 40,000 次读取
便捷的转换指南:
* 250 万秒 / 月
* 1 次请求 / 秒 = 250 万次请求 / 月
* 40 次请求 / 秒 = 1 亿次请求 / 月
* 400 次请求 / 秒 = 10 亿请求 / 月
## 第 2 步:创建高级设计方案
> 用所有重要组件概述高水平设计
![Imgur](http://i.imgur.com/B8LDKD7.png)
## 第 3 步:设计核心组件
> 深入每个核心组件的细节。
### 用例:用户进行读写请求
#### 目标
* 只有 1-2 个用户时,你只需要基础配置
* 为简单起见,只需要一台服务器
* 必要时进行纵向扩展
* 监控以确定瓶颈
#### 以单台服务器开始
* **Web 服务器** 在 EC2 上
* 存储用户数据
* [**MySQL 数据库**](https://github.com/donnemartin/system-design-primer#relational-database-management-system-rdbms)
运用 **纵向扩展**
* 选择一台更大容量的服务器
* 密切关注指标,确定如何扩大规模
* 使用基本监控来确定瓶颈:CPU、内存、IO、网络等
* CloudWatch, top, nagios, statsd, graphite等
* 纵向扩展的代价将变得更昂贵
* 无冗余/容错
**折中方案, 可选方案, 和其他细节:**
* **纵向扩展** 的可选方案是 [**横向扩展**](https://github.com/donnemartin/system-design-primer#horizontal-scaling)
#### 自 SQL 开始,但认真考虑 NoSQL
约束条件假设需要关系型数据。我们可以开始时在单台服务器上使用 **MySQL 数据库**
**折中方案, 可选方案, 和其他细节:**
* 查阅 [关系型数据库管理系统 (RDBMS)](https://github.com/donnemartin/system-design-primer#relational-database-management-system-rdbms) 章节
* 讨论使用 [SQL 或 NoSQL](https://github.com/donnemartin/system-design-primer#sql-or-nosql) 的原因
#### 分配公共静态 IP
* 弹性 IP 提供了一个公共端点,不会在重启时改变 IP。
* 故障转移时只需要把域名指向新 IP。
#### 使用 DNS 服务
添加 **DNS** 服务,比如 Route 53[Amazon Route 53](https://aws.amazon.com/cn/route53/) - 译者注),将域映射到实例的公共 IP 中。
**折中方案, 可选方案, 和其他细节:**
* 查阅 [域名系统](https://github.com/donnemartin/system-design-primer#domain-name-system) 章节
#### 安全的 Web 服务器
* 只开放必要的端口
* 允许 Web 服务器响应来自以下端口的请求
* HTTP 80
* HTTPS 443
* SSH IP 白名单 22
* 防止 Web 服务器启动外链
**折中方案, 可选方案, 和其他细节:**
* 查阅 [安全](https://github.com/donnemartin/system-design-primer#security) 章节
## 第 4 步:扩展设计
> 在给定约束条件下,定义和确认瓶颈。
### 用户+
![Imgur](http://i.imgur.com/rrfjMXB.png)
#### 假设
我们的用户数量开始上升,并且单台服务器的负载上升。**基准/负载测试** 和 **分析** 指出 **MySQL 数据库** 占用越来越多的内存和 CPU 资源,同时用户数据将填满硬盘空间。
目前,我们尚能在纵向扩展时解决这些问题。不幸的是,解决这些问题的代价变得相当昂贵,并且原来的系统并不能允许在 **MySQL 数据库****Web 服务器** 的基础上进行独立扩展。
#### 目标
* 减轻单台服务器负载并且允许独立扩展
* 在 **对象存储** 中单独存储静态内容
* 将 **MySQL 数据库** 迁移到单独的服务器上
* 缺点
* 这些变化会增加复杂性,并要求对 **Web服务器** 进行更改,以指向 **对象存储** 和 **MySQL 数据库**
* 必须采取额外的安全措施来确保新组件的安全
* AWS 的成本也会增加,但应该与自身管理类似系统的成本做比较
#### 独立保存静态内容
* 考虑使用像 S3 这样可管理的 **对象存储** 服务来存储静态内容
* 高扩展性和可靠性
* 服务器端加密
* 迁移静态内容到 S3
* 用户文件
* JS
* CSS
* 图片
* 视频
#### 迁移 MySQL 数据库到独立机器上
* 考虑使用类似 RDS 的服务来管理 **MySQL 数据库**
* 简单的管理,扩展
* 多个可用区域
* 空闲时加密
#### 系统安全
* 在传输和空闲时对数据进行加密
* 使用虚拟私有云
* 为单个 **Web 服务器** 创建一个公共子网,这样就可以发送和接收来自 internet 的流量
* 为其他内容创建一个私有子网,禁止外部访问
* 在每个组件上只为白名单 IP 打开端口
* 这些相同的模式应当在新的组件的实现中实践
**折中方案, 可选方案, 和其他细节:**
* 查阅 [安全](https://github.com/donnemartin/system-design-primer#security) 章节
### 用户+++
![Imgur](http://i.imgur.com/raoFTXM.png)
#### 假设
我们的 **基准/负载测试** 和 **性能测试** 显示,在高峰时段,我们的单一 **Web服务器** 存在瓶颈,导致响应缓慢,在某些情况下还会宕机。随着服务的成熟,我们也希望朝着更高的可用性和冗余发展。
#### 目标
* 下面的目标试图用 **Web服务器** 解决扩展问题
* 基于 **基准/负载测试** 和 **分析**,你可能只需要实现其中的一两个技术
* 使用 [**横向扩展**](https://github.com/donnemartin/system-design-primer#horizontal-scaling) 来处理增加的负载和单点故障
* 添加 [**负载均衡器**](https://github.com/donnemartin/system-design-primer#load-balancer) 例如 Amazon 的 ELB 或 HAProxy
* ELB 是高可用的
* 如果你正在配置自己的 **负载均衡器**, 在多个可用区域中设置多台服务器用于 [双活](https://github.com/donnemartin/system-design-primer#active-active) 或 [主被](https://github.com/donnemartin/system-design-primer#active-passive) 将提高可用性
* 终止在 **负载平衡器** 上的SSL以减少后端服务器上的计算负载并简化证书管理
* 在多个可用区域中使用多台 **Web服务器**
* 在多个可用区域的 [**主-从 故障转移**](https://github.com/donnemartin/system-design-primer#master-slave-replication) 模式中使用多个 **MySQL** 实例来改进冗余
* 分离 **Web 服务器** 和 [**应用服务器**](https://github.com/donnemartin/system-design-primer#application-layer)
* 独立扩展和配置每一层
* **Web 服务器** 可以作为 [**反向代理**](https://github.com/donnemartin/system-design-primer#reverse-proxy-web-server)
* 例如, 你可以添加 **应用服务器** 处理 **读 API** 而另外一些处理 **写 API**
* 将静态(和一些动态)内容转移到 [**内容分发网络 (CDN)**](https://github.com/donnemartin/system-design-primer#content-delivery-network) 例如 CloudFront 以减少负载和延迟
**折中方案, 可选方案, 和其他细节:**
* 查阅以上链接获得更多细节
### 用户+++
![Imgur](http://i.imgur.com/OZCxJr0.png)
**注意:** **内部负载均衡** 不显示以减少混乱
#### 假设
我们的 **性能/负载测试** 和 **性能测试** 显示我们读操作频繁100:1 的读写比率),并且数据库在高读请求时表现很糟糕。
#### 目标
* 下面的目标试图解决 **MySQL数据库** 的伸缩性问题
* * 基于 **基准/负载测试** 和 **分析**,你可能只需要实现其中的一两个技术
* 将下列数据移动到一个 [**内存缓存**](https://github.com/donnemartin/system-design-primer#cache),例如弹性缓存,以减少负载和延迟:
* **MySQL** 中频繁访问的内容
* 首先, 尝试配置 **MySQL 数据库** 缓存以查看是否足以在实现 **内存缓存** 之前缓解瓶颈
* 来自 **Web 服务器** 的会话数据
* **Web 服务器** 变成无状态的, 允许 **自动伸缩**
* 从内存中读取 1 MB 内存需要大约 250 微秒而从SSD中读取时间要长 4 倍,从磁盘读取的时间要长 80 倍。<sup><a href=https://github.com/donnemartin/system-design-primer#latency-numbers-every-programmer-should-know>1</a></sup>
* 添加 [**MySQL 读取副本**](https://github.com/donnemartin/system-design-primer#master-slave-replication) 来减少写主线程的负载
* 添加更多 **Web 服务器** and **应用服务器** 来提高响应
**折中方案, 可选方案, 和其他细节:**
* 查阅以上链接获得更多细节
#### 添加 MySQL 读取副本
* 除了添加和扩展 **内存缓存****MySQL 读副本服务器** 也能够帮助缓解在 **MySQL 写主服务器** 的负载。
* 添加逻辑到 **Web 服务器** 来区分读和写操作
* 在 **MySQL 读副本服务器** 之上添加 **负载均衡器** (不是为了减少混乱)
* 大多数服务都是读取负载大于写入负载
**折中方案, 可选方案, 和其他细节:**
* 查阅 [关系型数据库管理系统 (RDBMS)](https://github.com/donnemartin/system-design-primer#relational-database-management-system-rdbms) 章节
### 用户++++
![Imgur](http://i.imgur.com/3X8nmdL.png)
#### 假设
**基准/负载测试** 和 **分析** 显示,在美国,正常工作时间存在流量峰值,当用户离开办公室时,流量骤降。我们认为,可以通过真实负载自动转换服务器数量来降低成本。我们是一家小商店,所以我们希望 DevOps 尽量自动化地进行 **自动伸缩** 和通用操作。
#### 目标
* 根据需要添加 **自动扩展**
* 跟踪流量高峰
* 通过关闭未使用的实例来降低成本
* 自动化 DevOps
* Chef, Puppet, Ansible 工具等
* 继续监控指标以解决瓶颈
* **主机水平** - 检查一个 EC2 实例
* **总水平** - 检查负载均衡器统计数据
* **日志分析** - CloudWatch, CloudTrail, Loggly, Splunk, Sumo
* **外部站点的性能** - Pingdom or New Relic
* **处理通知和事件** - PagerDuty
* **错误报告** - Sentry
#### 添加自动扩展
* 考虑使用一个托管服务比如AWS **自动扩展**
* 为每个 **Web 服务器** 创建一个组,并为每个 **应用服务器** 类型创建一个组,将每个组放置在多个可用区域中
* 设置最小和最大实例数
* 通过 CloudWatch 来扩展或收缩
* 可预测负载的简单时间度量
* 一段时间内的指标:
* CPU 负载
* 延迟
* 网络流量
* 自定义指标
* 缺点
* 自动扩展会引入复杂性
* 可能需要一段时间才能适当扩大规模,以满足增加的需求,或者在需求下降时缩减规模
### 用户+++++
![Imgur](http://i.imgur.com/jj3A5N8.png)
**注释:** **自动伸缩** 组不显示以减少混乱
#### 假设
当服务继续向着限制条件概述的方向发展,我们反复地运行 **基准/负载测试** 和 **分析** 来进一步发现和定位新的瓶颈。
#### 目标
由于问题的约束,我们将继续提出扩展性的问题:
* 如果我们的 **MySQL 数据库** 开始变得过于庞大, 我们可能只考虑把数据在数据库中存储一段有限的时间, 同时在例如 Redshift 这样的数据仓库中存储其余的数据
* 像 Redshift 这样的数据仓库能够轻松处理每月 1TB 的新内容
* 平均每秒 40,000 次的读取请求, 可以通过扩展 **内存缓存** 来处理热点内容的读取流量,这对于处理不均匀分布的流量和流量峰值也很有用
* **SQL读取副本** 可能会遇到处理缓存未命中的问题, 我们可能需要使用额外的 SQL 扩展模式
* 对于单个 **SQL 写主-从** 模式来说,平均每秒 400 次写操作(明显更高)可能会很困难,同时还需要更多的扩展技术
SQL 扩展模型包括:
* [集合](https://github.com/donnemartin/system-design-primer#federation)
* [分片](https://github.com/donnemartin/system-design-primer#sharding)
* [反范式](https://github.com/donnemartin/system-design-primer#denormalization)
* [SQL 调优](https://github.com/donnemartin/system-design-primer#sql-tuning)
为了进一步处理高读和写请求,我们还应该考虑将适当的数据移动到一个 [**NoSQL数据库**](https://github.com/donnemartin/system-design-primer#nosql) ,例如 DynamoDB。
我们可以进一步分离我们的 [**应用服务器**](https://github.com/donnemartin/system-design-primer#application-layer) 以允许独立扩展。不需要实时完成的批处理任务和计算可以通过 Queues 和 Workers 异步完成:
* 以照片服务为例,照片上传和缩略图的创建可以分开进行
* **客户端** 上传图片
* **应用服务器** 推送一个任务到 **队列** 例如 SQS
* EC2 上的 **Worker 服务** 或者 Lambda 从 **队列** 拉取 work然后
* 创建缩略图
* 更新 **数据库**
* 在 **对象存储** 中存储缩略图
**折中方案, 可选方案, 和其他细节:**
* 查阅以上链接获得更多细节
## 额外的话题
> 根据问题的范围和剩余时间,还需要深入讨论其他问题。
### SQL 扩展模式
* [读取副本](https://github.com/donnemartin/system-design-primer#master-slave-replication)
* [集合](https://github.com/donnemartin/system-design-primer#federation)
* [分区](https://github.com/donnemartin/system-design-primer#sharding)
* [反规范化](https://github.com/donnemartin/system-design-primer#denormalization)
* [SQL 调优](https://github.com/donnemartin/system-design-primer#sql-tuning)
#### NoSQL
* [键值存储](https://github.com/donnemartin/system-design-primer#key-value-store)
* [文档存储](https://github.com/donnemartin/system-design-primer#document-store)
* [宽表存储](https://github.com/donnemartin/system-design-primer#wide-column-store)
* [图数据库](https://github.com/donnemartin/system-design-primer#graph-database)
* [SQL vs NoSQL](https://github.com/donnemartin/system-design-primer#sql-or-nosql)
### 缓存
* 缓存到哪里
* [客户端缓存](https://github.com/donnemartin/system-design-primer#client-caching)
* [CDN 缓存](https://github.com/donnemartin/system-design-primer#cdn-caching)
* [Web 服务缓存](https://github.com/donnemartin/system-design-primer#web-server-caching)
* [数据库缓存](https://github.com/donnemartin/system-design-primer#database-caching)
* [应用缓存](https://github.com/donnemartin/system-design-primer#application-caching)
* 缓存什么
* [数据库请求层缓存](https://github.com/donnemartin/system-design-primer#caching-at-the-database-query-level)
* [对象层缓存](https://github.com/donnemartin/system-design-primer#caching-at-the-object-level)
* 何时更新缓存
* [预留缓存](https://github.com/donnemartin/system-design-primer#cache-aside)
* [完全写入](https://github.com/donnemartin/system-design-primer#write-through)
* [延迟写 (写回)](https://github.com/donnemartin/system-design-primer#write-behind-write-back)
* [事先更新](https://github.com/donnemartin/system-design-primer#refresh-ahead)
### 异步性和微服务
* [消息队列](https://github.com/donnemartin/system-design-primer#message-queues)
* [任务队列](https://github.com/donnemartin/system-design-primer#task-queues)
* [回退压力](https://github.com/donnemartin/system-design-primer#back-pressure)
* [微服务](https://github.com/donnemartin/system-design-primer#microservices)
### 沟通
* 关于折中方案的讨论:
* 客户端的外部通讯 - [遵循 REST 的 HTTP APIs](https://github.com/donnemartin/system-design-primer#representational-state-transfer-rest)
* 内部通讯 - [RPC](https://github.com/donnemartin/system-design-primer#remote-procedure-call-rpc)
* [服务探索](https://github.com/donnemartin/system-design-primer#service-discovery)
### 安全性
参考 [安全章节](https://github.com/donnemartin/system-design-primer#security)
### 延迟数字指标
查阅 [每个程序员必懂的延迟数字](https://github.com/donnemartin/system-design-primer#latency-numbers-every-programmer-should-know)
### 正在进行
* 继续基准测试并监控你的系统以解决出现的瓶颈问题
* 扩展是一个迭代的过程

View File

@ -83,7 +83,7 @@ Handy conversion guide:
* **Web server** on EC2
* Storage for user data
* [**MySQL Database**](https://github.com/donnemartin/system-design-primer#sql)
* [**MySQL Database**](https://github.com/donnemartin/system-design-primer#relational-database-management-system-rdbms)
Use **Vertical Scaling**:
@ -344,7 +344,7 @@ We can further separate out our [**Application Servers**](https://github.com/don
### SQL scaling patterns
* [Read replicas](https://github.com/donnemartin/system-design-primer#master-slave)
* [Read replicas](https://github.com/donnemartin/system-design-primer#master-slave-replication)
* [Federation](https://github.com/donnemartin/system-design-primer#federation)
* [Sharding](https://github.com/donnemartin/system-design-primer#sharding)
* [Denormalization](https://github.com/donnemartin/system-design-primer#denormalization)

View File

@ -0,0 +1,348 @@
# 为社交网络设计数据结构
**注释:为了避免重复,这篇文章的链接直接关联到 [系统设计主题](https://github.com/donnemartin/system-design-primer#index-of-system-design-topics) 的相关章节。为一讨论要点、折中方案和可选方案做参考。**
## 第 1 步:用例和约束概要
> 收集需求并调查问题。
> 通过提问清晰用例和约束。
> 讨论假设。
如果没有面试官提出明确的问题,我们将自己定义一些用例和约束条件。
### 用例
#### 我们就处理以下用例审视这一问题
* **用户** 寻找某人并显示与被寻人之间的最短路径
* **服务** 高可用
### 约束和假设
#### 状态假设
* 流量分布不均
* 某些搜索比别的更热门,同时某些搜索仅执行一次
* 图数据不适用单一机器
* 图的边没有权重
* 1 千万用户
* 每个用户平均有 50 个朋友
* 每月 10 亿次朋友搜索
训练使用更传统的系统 - 别用图特有的解决方案例如 [GraphQL](http://graphql.org/) 或图数据库如 [Neo4j](https://neo4j.com/)。
#### 计算使用
**向你的面试官厘清你是否应该做粗略的使用计算**
* 50 亿朋友关系
* 1 亿用户 * 平均每人 50 个朋友
* 每秒 400 次搜索请求
便捷的转换指南:
* 每月 250 万秒
* 每秒 1 个请求 = 每月 250 万次请求
* 每秒 40 个请求 = 每月 1 亿次请求
* 每秒 400 个请求 = 每月 10 亿次请求
## 第 2 步:创建高级设计方案
> 用所有重要组件概述高水平设计
![Imgur](http://i.imgur.com/wxXyq2J.png)
## 第 3 步:设计核心组件
> 深入每个核心组件的细节。
### 用例: 用户搜索某人并查看到被搜人的最短路径
**和你的面试官说清你期望的代码量**
没有百万用户(点)的和十亿朋友关系(边)的限制,我们能够用一般 BFS 方法解决无权重最短路径任务:
```python
class Graph(Graph):
def shortest_path(self, source, dest):
if source is None or dest is None:
return None
if source is dest:
return [source.key]
prev_node_keys = self._shortest_path(source, dest)
if prev_node_keys is None:
return None
else:
path_ids = [dest.key]
prev_node_key = prev_node_keys[dest.key]
while prev_node_key is not None:
path_ids.append(prev_node_key)
prev_node_key = prev_node_keys[prev_node_key]
return path_ids[::-1]
def _shortest_path(self, source, dest):
queue = deque()
queue.append(source)
prev_node_keys = {source.key: None}
source.visit_state = State.visited
while queue:
node = queue.popleft()
if node is dest:
return prev_node_keys
prev_node = node
for adj_node in node.adj_nodes.values():
if adj_node.visit_state == State.unvisited:
queue.append(adj_node)
prev_node_keys[adj_node.key] = prev_node.key
adj_node.visit_state = State.visited
return None
```
我们不能在同一台机器上满足所有用户,我们需要通过 **人员服务器** [拆分](https://github.com/donnemartin/system-design-primer#sharding) 用户并且通过 **查询服务** 访问。
* **客户端** 向 **服务器** 发送请求,**服务器** 作为 [反向代理](https://github.com/donnemartin/system-design-primer#reverse-proxy-web-server)
* **搜索 API** 服务器向 **用户图服务** 转发请求
* **用户图服务** 有以下功能:
* 使用 **查询服务** 找到当前用户信息存储的 **人员服务器**
* 找到适当的 **人员服务器** 检索当前用户的 `friend_ids` 列表
* 把当前用户作为 `source` 运行 BFS 搜索算法同时 当前用户的 `friend_ids` 作为每个 `adjacent_node` 的 ids
* 给定 id 获取 `adjacent_node`:
* **用户图服务** 将 **再次** 和 **查询服务** 通讯,最后判断出和给定 id 相匹配的存储 `adjacent_node`**人员服务器**(有待优化)
**和你的面试官说清你应该写的代码量**
**注释**:简易版错误处理执行如下。询问你是否需要编写适当的错误处理方法。
**查询服务** 实现:
```python
class LookupService(object):
def __init__(self):
self.lookup = self._init_lookup() # key: person_id, value: person_server
def _init_lookup(self):
...
def lookup_person_server(self, person_id):
return self.lookup[person_id]
```
**人员服务器** 实现:
```python
class PersonServer(object):
def __init__(self):
self.people = {} # key: person_id, value: person
def add_person(self, person):
...
def people(self, ids):
results = []
for id in ids:
if id in self.people:
results.append(self.people[id])
return results
```
**用户** 实现:
```python
class Person(object):
def __init__(self, id, name, friend_ids):
self.id = id
self.name = name
self.friend_ids = friend_ids
```
**用户图服务** 实现:
```python
class UserGraphService(object):
def __init__(self, lookup_service):
self.lookup_service = lookup_service
def person(self, person_id):
person_server = self.lookup_service.lookup_person_server(person_id)
return person_server.people([person_id])
def shortest_path(self, source_key, dest_key):
if source_key is None or dest_key is None:
return None
if source_key is dest_key:
return [source_key]
prev_node_keys = self._shortest_path(source_key, dest_key)
if prev_node_keys is None:
return None
else:
# Iterate through the path_ids backwards, starting at dest_key
path_ids = [dest_key]
prev_node_key = prev_node_keys[dest_key]
while prev_node_key is not None:
path_ids.append(prev_node_key)
prev_node_key = prev_node_keys[prev_node_key]
# Reverse the list since we iterated backwards
return path_ids[::-1]
def _shortest_path(self, source_key, dest_key, path):
# Use the id to get the Person
source = self.person(source_key)
# Update our bfs queue
queue = deque()
queue.append(source)
# prev_node_keys keeps track of each hop from
# the source_key to the dest_key
prev_node_keys = {source_key: None}
# We'll use visited_ids to keep track of which nodes we've
# visited, which can be different from a typical bfs where
# this can be stored in the node itself
visited_ids = set()
visited_ids.add(source.id)
while queue:
node = queue.popleft()
if node.key is dest_key:
return prev_node_keys
prev_node = node
for friend_id in node.friend_ids:
if friend_id not in visited_ids:
friend_node = self.person(friend_id)
queue.append(friend_node)
prev_node_keys[friend_id] = prev_node.key
visited_ids.add(friend_id)
return None
```
我们用的是公共的 [**REST API**](https://github.com/donnemartin/system-design-primer#representational-state-transfer-rest)
```
$ curl https://social.com/api/v1/friend_search?person_id=1234
```
响应:
```
{
"person_id": "100",
"name": "foo",
"link": "https://social.com/foo",
},
{
"person_id": "53",
"name": "bar",
"link": "https://social.com/bar",
},
{
"person_id": "1234",
"name": "baz",
"link": "https://social.com/baz",
},
```
内部通信使用 [远端过程调用](https://github.com/donnemartin/system-design-primer#remote-procedure-call-rpc)。
## 第 4 步:扩展设计
> 在给定约束条件下,定义和确认瓶颈。
![Imgur](http://i.imgur.com/cdCv5g7.png)
**重要:别简化从最初设计到最终设计的过程!**
你将要做的是1) **基准/负载 测试** 2) 瓶颈 **概述** 3) 当评估可选和折中方案时定位瓶颈4) 重复。以 [在 AWS 上设计支持百万级到千万级用户的系统](../scaling_aws/README.md) 为参考迭代地扩展最初设计。
讨论最初设计可能遇到的瓶颈和处理方法十分重要。例如,什么问题可以通过添加多台 **Web 服务器** 作为 **负载均衡** 解决?**CDN****主从副本**?每个问题都有哪些替代和 **折中** 方案?
我们即将介绍一些组件来完成设计和解决扩展性问题。内部负载均衡不显示以减少混乱。
**避免重复讨论**,以下网址链接到 [系统设计主题](https://github.com/donnemartin/system-design-primer#index-of-system-design-topics) 相关的主流方案、折中方案和替代方案。
* [DNS](https://github.com/donnemartin/system-design-primer#domain-name-system)
* [负载均衡](https://github.com/donnemartin/system-design-primer#load-balancer)
* [横向扩展](https://github.com/donnemartin/system-design-primer#horizontal-scaling)
* [Web 服务器(反向代理)](https://github.com/donnemartin/system-design-primer#reverse-proxy-web-server)
* [API 服务器(应用层)](https://github.com/donnemartin/system-design-primer#application-layer)
* [缓存](https://github.com/donnemartin/system-design-primer#cache)
* [一致性模式](https://github.com/donnemartin/system-design-primer#consistency-patterns)
* [可用性模式](https://github.com/donnemartin/system-design-primer#availability-patterns)
解决 **平均** 每秒 400 次请求的限制(峰值),人员数据可以存在例如 Redis 或 Memcached 这样的 **内存** 中以减少响应次数和下游流量通信服务。这尤其在用户执行多次连续查询和查询哪些广泛连接的人时十分有用。从内存中读取 1MB 数据大约要 250 微秒,从 SSD 中读取同样大小的数据时间要长 4 倍,从硬盘要长 80 倍。<sup><a href=https://github.com/donnemartin/system-design-primer#latency-numbers-every-programmer-should-know>1</a></sup>
以下是进一步优化方案:
* 在 **内存** 中存储完整的或部分的BFS遍历加快后续查找
* 在 **NoSQL 数据库** 中批量离线计算并存储完整的或部分的BFS遍历加快后续查找
* 在同一台 **人员服务器** 上托管批处理同一批朋友查找减少机器跳转
* 通过地理位置 [拆分](https://github.com/donnemartin/system-design-primer#sharding) **人员服务器** 来进一步优化,因为朋友通常住得都比较近
* 同时进行两个 BFS 查找,一个从 source 开始,一个从 destination 开始,然后合并两个路径
* 从有庞大朋友圈的人开始找起,这样更有可能减小当前用户和搜索目标之间的 [离散度数](https://en.wikipedia.org/wiki/Six_degrees_of_separation)
* 在询问用户是否继续查询之前设置基于时间或跳跃数阈值,当在某些案例中搜索耗费时间过长时。
* 使用类似 [Neo4j](https://neo4j.com/) 的 **图数据库** 或图特定查询语法,例如 [GraphQL](http://graphql.org/)(如果没有禁止使用 **图数据库** 的限制的话)
## 额外的话题
> 根据问题的范围和剩余时间,还需要深入讨论其他问题。
### SQL 扩展模式
* [读取副本](https://github.com/donnemartin/system-design-primer#master-slave-replication)
* [集合](https://github.com/donnemartin/system-design-primer#federation)
* [分区](https://github.com/donnemartin/system-design-primer#sharding)
* [反规范化](https://github.com/donnemartin/system-design-primer#denormalization)
* [SQL 调优](https://github.com/donnemartin/system-design-primer#sql-tuning)
#### NoSQL
* [键值存储](https://github.com/donnemartin/system-design-primer#key-value-store)
* [文档存储](https://github.com/donnemartin/system-design-primer#document-store)
* [宽表存储](https://github.com/donnemartin/system-design-primer#wide-column-store)
* [图数据库](https://github.com/donnemartin/system-design-primer#graph-database)
* [SQL vs NoSQL](https://github.com/donnemartin/system-design-primer#sql-or-nosql)
### 缓存
* 缓存到哪里
* [客户端缓存](https://github.com/donnemartin/system-design-primer#client-caching)
* [CDN 缓存](https://github.com/donnemartin/system-design-primer#cdn-caching)
* [Web 服务缓存](https://github.com/donnemartin/system-design-primer#web-server-caching)
* [数据库缓存](https://github.com/donnemartin/system-design-primer#database-caching)
* [应用缓存](https://github.com/donnemartin/system-design-primer#application-caching)
* 缓存什么
* [数据库请求层缓存](https://github.com/donnemartin/system-design-primer#caching-at-the-database-query-level)
* [对象层缓存](https://github.com/donnemartin/system-design-primer#caching-at-the-object-level)
* 何时更新缓存
* [预留缓存](https://github.com/donnemartin/system-design-primer#cache-aside)
* [完全写入](https://github.com/donnemartin/system-design-primer#write-through)
* [延迟写 (写回)](https://github.com/donnemartin/system-design-primer#write-behind-write-back)
* [事先更新](https://github.com/donnemartin/system-design-primer#refresh-ahead)
### 异步性和微服务
* [消息队列](https://github.com/donnemartin/system-design-primer#message-queues)
* [任务队列](https://github.com/donnemartin/system-design-primer#task-queues)
* [回退压力](https://github.com/donnemartin/system-design-primer#back-pressure)
* [微服务](https://github.com/donnemartin/system-design-primer#microservices)
### 沟通
* 关于折中方案的讨论:
* 客户端的外部通讯 - [遵循 REST 的 HTTP APIs](https://github.com/donnemartin/system-design-primer#representational-state-transfer-rest)
* 内部通讯 - [RPC](https://github.com/donnemartin/system-design-primer#remote-procedure-call-rpc)
* [服务探索](https://github.com/donnemartin/system-design-primer#service-discovery)
### 安全性
参考 [安全章节](https://github.com/donnemartin/system-design-primer#security)
### 延迟数字指标
查阅 [每个程序员必懂的延迟数字](https://github.com/donnemartin/system-design-primer#latency-numbers-every-programmer-should-know)
### 正在进行
* 继续基准测试并监控你的系统以解决出现的瓶颈问题
* 扩展是一个迭代的过程

View File

@ -62,7 +62,7 @@ Handy conversion guide:
Without the constraint of millions of users (vertices) and billions of friend relationships (edges), we could solve this unweighted shortest path task with a general BFS approach:
```
```python
class Graph(Graph):
def shortest_path(self, source, dest):
@ -117,7 +117,7 @@ We won't be able to fit all users on the same machine, we'll need to [shard](htt
**Lookup Service** implementation:
```
```python
class LookupService(object):
def __init__(self):
@ -132,7 +132,7 @@ class LookupService(object):
**Person Server** implementation:
```
```python
class PersonServer(object):
def __init__(self):
@ -151,7 +151,7 @@ class PersonServer(object):
**Person** implementation:
```
```python
class Person(object):
def __init__(self, id, name, friend_ids):
@ -162,7 +162,7 @@ class Person(object):
**User Graph Service** implementation:
```
```python
class UserGraphService(object):
def __init__(self, lookup_service):
@ -290,7 +290,7 @@ Below are further optimizations:
### SQL scaling patterns
* [Read replicas](https://github.com/donnemartin/system-design-primer#master-slave)
* [Read replicas](https://github.com/donnemartin/system-design-primer#master-slave-replication)
* [Federation](https://github.com/donnemartin/system-design-primer#federation)
* [Sharding](https://github.com/donnemartin/system-design-primer#sharding)
* [Denormalization](https://github.com/donnemartin/system-design-primer#denormalization)

View File

@ -1,4 +1,12 @@
# -*- coding: utf-8 -*-
from collections import deque
from enum import Enum
class State(Enum):
unvisited = 0
visited = 1
class Graph(object):
@ -61,3 +69,4 @@ class UserGraphService(object):
def bfs(self, source, dest):
# Use self.visited_ids to track visited nodes
# Use self.lookup to translate a person_id to a Person
pass

View File

@ -0,0 +1,331 @@
# 设计推特时间轴与搜索功能
**注意:这个文档中的链接会直接指向[系统设计主题索引](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#系统设计主题的索引)中的有关部分,以避免重复的内容。你可以参考链接的相关内容,来了解其总的要点、方案的权衡取舍以及可选的替代方案。**
**设计 Facebook 的 feed** 与**设计 Facebook 搜索**与此为同一类型问题。
## 第一步:简述用例与约束条件
> 搜集需求与问题的范围。
> 提出问题来明确用例与约束条件。
> 讨论假设。
我们将在没有面试官明确说明问题的情况下,自己定义一些用例以及限制条件。
### 用例
#### 我们将把问题限定在仅处理以下用例的范围中
* **用户**发布了一篇推特
* **服务**将推特推送给关注者,给他们发送消息通知与邮件
* **用户**浏览用户时间轴(用户最近的活动)
* **用户**浏览主页时间轴(用户关注的人最近的活动)
* **用户**搜索关键词
* **服务**需要有高可用性
#### 不在用例范围内的有
* **服务**向 Firehose 与其它流数据接口推送推特
* **服务**根据用户的”是否可见“选项排除推特
* 隐藏未关注者的 @回复
* 关心”隐藏转发“设置
* 数据分析
### 限制条件与假设
#### 提出假设
普遍情况
* 网络流量不是均匀分布的
* 发布推特的速度需要足够快速
* 除非有上百万的关注者,否则将推特推送给粉丝的速度要足够快
* 1 亿个活跃用户
* 每天新发布 5 亿条推特,每月新发布 150 亿条推特
* 平均每条推特需要推送给 5 个人
* 每天需要进行 50 亿次推送
* 每月需要进行 1500 亿次推送
* 每月需要处理 2500 亿次读取请求
* 每月需要处理 100 亿次搜索
时间轴功能
* 浏览时间轴需要足够快
* 推特的读取负载要大于写入负载
* 需要为推特的快速读取进行优化
* 存入推特是高写入负载功能
搜索功能
* 搜索速度需要足够快
* 搜索是高负载读取功能
#### 计算用量
**如果你需要进行粗略的用量计算,请向你的面试官说明。**
* 每条推特的大小:
* `tweet_id` - 8 字节
* `user_id` - 32 字节
* `text` - 140 字节
* `media` - 平均 10 KB
* 总计: 大约 10 KB
* 每月产生新推特的内容为 150 TB
* 每条推特 10 KB * 每天 5 亿条推特 * 每月 30 天
* 3 年产生新推特的内容为 5.4 PB
* 每秒需要处理 10 万次读取请求
* 每个月需要处理 2500 亿次请求 * (每秒 400 次请求 / 每月 10 亿次请求)
* 每秒发布 6000 条推特
* 每月发布 150 亿条推特 * (每秒 400 次请求 / 每月 10 次请求)
* 每秒推送 6 万条推特
* 每月推送 1500 亿条推特 * (每秒 400 次请求 / 每月 10 亿次请求)
* 每秒 4000 次搜索请求
便利换算指南:
* 每个月有 250 万秒
* 每秒一个请求 = 每个月 250 万次请求
* 每秒 40 个请求 = 每个月 1 亿次请求
* 每秒 400 个请求 = 每个月 10 亿次请求
## 第二步:概要设计
> 列出所有重要组件以规划概要设计。
![Imgur](http://i.imgur.com/48tEA2j.png)
## 第三步:设计核心组件
> 深入每个核心组件的细节。
### 用例:用户发表了一篇推特
我们可以将用户自己发表的推特存储在[关系数据库](https://github.com/donnemartin/system-design-primer#relational-database-management-system-rdbms)中。我们也可以讨论一下[究竟是用 SQL 还是用 NoSQL](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#sql-还是-nosql)。
构建用户主页时间轴(查看关注用户的活动)以及推送推特是件麻烦事。将特推传播给所有关注者(每秒约递送 6 万条推特)这一操作有可能会使传统的[关系数据库](https://github.com/donnemartin/system-design-primer#relational-database-management-system-rdbms)超负载。因此,我们可以使用 **NoSQL 数据库**或**内存数据库**之类的更快的数据存储方式。从内存读取 1 MB 连续数据大约要花 250 微秒,而从 SSD 读取同样大小的数据要花费 4 倍的时间,从机械硬盘读取需要花费 80 倍以上的时间。<sup><a href=https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#每个程序员都应该知道的延迟数>1</a></sup>
我们可以将照片、视频之类的媒体存储于**对象存储**中。
* **客户端**向应用[反向代理](https://github.com/donnemartin/system-design-primer#reverse-proxy-web-server)的**Web 服务器**发送一条推特
* **Web 服务器**将请求转发给**写 API**服务器
* **写 API**服务器将推特使用 **SQL 数据库**存储于用户时间轴中
* **写 API**调用**消息输出服务**,进行以下操作:
* 查询**用户 图 服务**找到存储于**内存缓存**中的此用户的粉丝
* 将推特存储于**内存缓存**中的**此用户的粉丝的主页时间轴**中
* O(n) 复杂度操作: 1000 名粉丝 = 1000 次查找与插入
* 将特推存储在**搜索索引服务**中,以加快搜索
* 将媒体存储于**对象存储**中
* 使用**通知服务**向粉丝发送推送:
* 使用**队列**异步推送通知
**向你的面试官告知你准备写多少代码**。
如果我们用 Redis 作为**内存缓存**,那可以用 Redis 原生的 list 作为其数据结构。结构如下:
```
tweet n+2 tweet n+1 tweet n
| 8 bytes 8 bytes 1 byte | 8 bytes 8 bytes 1 byte | 8 bytes 8 bytes 1 byte |
| tweet_id user_id meta | tweet_id user_id meta | tweet_id user_id meta |
```
新发布的推特将被存储在对应用户(关注且活跃的用户)的主页时间轴的**内存缓存**中。
我们可以调用一个公共的 [REST API](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#表述性状态转移rest)
```
$ curl -X POST --data '{ "user_id": "123", "auth_token": "ABC123", \
"status": "hello world!", "media_ids": "ABC987" }' \
https://twitter.com/api/v1/tweet
```
返回:
```
{
"created_at": "Wed Sep 05 00:37:15 +0000 2012",
"status": "hello world!",
"tweet_id": "987",
"user_id": "123",
...
}
```
而对于服务器内部的通信,我们可以使用 [RPC](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#远程过程调用协议rpc)。
### 用例:用户浏览主页时间轴
* **客户端**向 **Web 服务器**发起一次读取主页时间轴的请求
* **Web 服务器**将请求转发给**读取 API**服务器
* **读取 API**服务器调用**时间轴服务**进行以下操作:
* 从**内存缓存**读取时间轴数据,其中包括推特 id 与用户 id - O(1)
* 通过 [multiget](http://redis.io/commands/mget) 向**推特信息服务**进行查询,以获取相关 id 推特的额外信息 - O(n)
* 通过 muiltiget 向**用户信息服务**进行查询,以获取相关 id 用户的额外信息 - O(n)
REST API
```
$ curl https://twitter.com/api/v1/home_timeline?user_id=123
```
返回:
```
{
"user_id": "456",
"tweet_id": "123",
"status": "foo"
},
{
"user_id": "789",
"tweet_id": "456",
"status": "bar"
},
{
"user_id": "789",
"tweet_id": "579",
"status": "baz"
},
```
### 用例:用户浏览用户时间轴
* **客户端**向**Web 服务器**发起获得用户时间线的请求
* **Web 服务器**将请求转发给**读取 API**服务器
* **读取 API**从 **SQL 数据库**中取出用户的时间轴
REST API 与前面的主页时间轴类似,区别只在于取出的推特是由用户自己发送而不是关注人发送。
### 用例:用户搜索关键词
* **客户端**将搜索请求发给**Web 服务器**
* **Web 服务器**将请求转发给**搜索 API**服务器
* **搜索 API**调用**搜索服务**进行以下操作:
* 对输入进行转换与分词,弄明白需要搜索什么东西
* 移除标点等额外内容
* 将文本打散为词组
* 修正拼写错误
* 规范字母大小写
* 将查询转换为布尔操作
* 查询**搜索集群**(例如[Lucene](https://lucene.apache.org/))检索结果:
* 对集群内的所有服务器进行查询,将有结果的查询进行[发散聚合Scatter gathers](https://github.com/donnemartin/system-design-primer#under-development)
* 合并取到的条目,进行评分与排序,最终返回结果
REST API
```
$ curl https://twitter.com/api/v1/search?query=hello+world
```
返回结果与前面的主页时间轴类似,只不过返回的是符合查询条件的推特。
## 第四步:架构扩展
> 根据限制条件,找到并解决瓶颈。
![Imgur](http://i.imgur.com/MzExP06.png)
**重要提示:不要从最初设计直接跳到最终设计中!**
现在你要 1) **基准测试、负载测试**。2) **分析、描述**性能瓶颈。3) 在解决瓶颈问题的同时评估替代方案、权衡利弊。4) 重复以上步骤。请阅读[「设计一个系统,并将其扩大到为数以百万计的 AWS 用户服务」](../scaling_aws/README.md) 来了解如何逐步扩大初始设计。
讨论初始设计可能遇到的瓶颈及相关解决方案是很重要的。例如加上一个配置多台 **Web 服务器**的**负载均衡器**是否能够解决问题?**CDN**呢?**主从复制**呢?它们各自的替代方案和需要**权衡**的利弊又有什么呢?
我们将会介绍一些组件来完成设计,并解决架构扩张问题。内置的负载均衡器将不做讨论以节省篇幅。
**为了避免重复讨论**,请参考[系统设计主题索引](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#系统设计主题的索引)相关部分来了解其要点、方案的权衡取舍以及可选的替代方案。
* [DNS](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#域名系统)
* [负载均衡器](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#负载均衡器)
* [水平拓展](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#水平扩展)
* [反向代理web 服务器)](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#反向代理web-服务器)
* [API 服务(应用层)](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#应用层)
* [缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#缓存)
* [关系型数据库管理系统 (RDBMS)](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#关系型数据库管理系统rdbms)
* [SQL 故障主从切换](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#故障切换)
* [主从复制](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#主从复制)
* [一致性模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#一致性模式)
* [可用性模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#可用性模式)
**消息输出服务**有可能成为性能瓶颈。那些有着百万数量关注着的用户可能发一条推特就需要好几分钟才能完成消息输出进程。这有可能使 @回复 这种推特时出现竞争条件,因此需要根据服务时间对此推特进行重排序来降低影响。
我们还可以避免从高关注量的用户输出推特。相反,我们可以通过搜索来找到高关注量用户的推特,并将搜索结果与用户的主页时间轴合并,再根据时间对其进行排序。
此外,还可以通过以下内容进行优化:
* 仅为每个主页时间轴在**内存缓存**中存储数百条推特
* 仅在**内存缓存**中存储活动用户的主页时间轴
* 如果某个用户在过去 30 天都没有产生活动,那我们可以使用 **SQL 数据库**重新构建他的时间轴
* 使用**用户 图 服务**来查询并确定用户关注的人
* 从 **SQL 数据库**中取出推特,并将它们存入**内存缓存**
* 仅在**推特信息服务**中存储一个月的推特
* 仅在**用户信息服务**中存储活动用户的信息
* **搜索集群**需要将推特保留在内存中,以降低延迟
我们还可以考虑优化 **SQL 数据库** 来解决一些瓶颈问题。
**内存缓存**能减小一些数据库的负载,靠 **SQL Read 副本**已经足够处理缓存未命中情况。我们还可以考虑使用一些额外的 SQL 性能拓展技术。
高容量的写入将淹没单个的 **SQL 写主从**模式,因此需要更多的拓展技术。
* [联合](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#联合)
* [分片](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#分片)
* [非规范化](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#非规范化)
* [SQL 调优](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#sql-调优)
我们也可以考虑将一些数据移至 **NoSQL 数据库**
## 其它要点
> 是否深入这些额外的主题,取决于你的问题范围和剩下的时间。
#### NoSQL
* [键-值存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#键-值存储)
* [文档类型存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#文档类型存储)
* [列型存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#列型存储)
* [图数据库](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#图数据库)
* [SQL vs NoSQL](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#sql-还是-nosql)
### 缓存
* 在哪缓存
* [客户端缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#客户端缓存)
* [CDN 缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#cdn-缓存)
* [Web 服务器缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#web-服务器缓存)
* [数据库缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#数据库缓存)
* [应用缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#应用缓存)
* 什么需要缓存
* [数据库查询级别的缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#数据库查询级别的缓存)
* [对象级别的缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#对象级别的缓存)
* 何时更新缓存
* [缓存模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#缓存模式)
* [直写模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#直写模式)
* [回写模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#回写模式)
* [刷新](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#刷新)
### 异步与微服务
* [消息队列](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#消息队列)
* [任务队列](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#任务队列)
* [背压](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#背压)
* [微服务](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#微服务)
### 通信
* 可权衡选择的方案:
* 与客户端的外部通信 - [使用 REST 作为 HTTP API](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#表述性状态转移rest)
* 服务器内部通信 - [RPC](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#远程过程调用协议rpc)
* [服务发现](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#服务发现)
### 安全性
请参阅[「安全」](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#安全)一章。
### 延迟数值
请参阅[「每个程序员都应该知道的延迟数」](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#每个程序员都应该知道的延迟数)。
### 持续探讨
* 持续进行基准测试并监控你的系统,以解决他们提出的瓶颈问题。
* 架构拓展是一个迭代的过程。

View File

@ -124,7 +124,7 @@ If our **Memory Cache** is Redis, we could use a native Redis list with the foll
```
tweet n+2 tweet n+1 tweet n
| 8 bytes 8 bytes 1 byte | 8 bytes 8 bytes 1 byte | 8 bytes 7 bytes 1 byte |
| 8 bytes 8 bytes 1 byte | 8 bytes 8 bytes 1 byte | 8 bytes 8 bytes 1 byte |
| tweet_id user_id meta | tweet_id user_id meta | tweet_id user_id meta |
```
@ -189,7 +189,7 @@ Response:
### Use case: User views the user timeline
* The **Client** posts a home timeline request to the **Web Server**
* The **Client** posts a user timeline request to the **Web Server**
* The **Web Server** forwards the request to the **Read API** server
* The **Read API** retrieves the user timeline from the **SQL Database**
@ -249,7 +249,7 @@ We'll introduce some components to complete the design and to address scalabilit
The **Fanout Service** is a potential bottleneck. Twitter users with millions of followers could take several minutes to have their tweets go through the fanout process. This could lead to race conditions with @replies to the tweet, which we could mitigate by re-ordering the tweets at serve time.
We could also avoid fanning out tweets from highly-followed users. Instead, we could search to find tweets for high-followed users, merge the search results with the user's home timeline results, then re-order the tweets at serve time.
We could also avoid fanning out tweets from highly-followed users. Instead, we could search to find tweets for highly-followed users, merge the search results with the user's home timeline results, then re-order the tweets at serve time.
Additional optimizations include:

View File

@ -0,0 +1,356 @@
# 设计一个网页爬虫
**注意:这个文档中的链接会直接指向[系统设计主题索引](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#系统设计主题的索引)中的有关部分,以避免重复的内容。你可以参考链接的相关内容,来了解其总的要点、方案的权衡取舍以及可选的替代方案。**
## 第一步:简述用例与约束条件
> 把所有需要的东西聚集在一起,审视问题。不停的提问,以至于我们可以明确使用场景和约束。讨论假设。
我们将在没有面试官明确说明问题的情况下,自己定义一些用例以及限制条件。
### 用例
#### 我们把问题限定在仅处理以下用例的范围中
* **服务** 抓取一系列链接:
* 生成包含搜索词的网页倒排索引
* 生成页面的标题和摘要信息
* 页面标题和摘要都是静态的,它们不会根据搜索词改变
* **用户** 输入搜索词后,可以看到相关的搜索结果列表,列表每一项都包含由网页爬虫生成的页面标题及摘要
* 只给该用例绘制出概要组件和交互说明,无需讨论细节
* **服务** 具有高可用性
#### 无需考虑
* 搜索分析
* 个性化搜索结果
* 页面排名
### 限制条件与假设
#### 提出假设
* 搜索流量分布不均
* 有些搜索词非常热门,有些则非常冷门
* 只支持匿名用户
* 用户很快就能看到搜索结果
* 网页爬虫不应该陷入死循环
* 当爬虫路径包含环的时候,将会陷入死循环
* 抓取 10 亿个链接
* 要定期重新抓取页面以确保新鲜度
* 平均每周重新抓取一次,网站越热门,那么重新抓取的频率越高
* 每月抓取 40 亿个链接
* 每个页面的平均存储大小500 KB
* 简单起见,重新抓取的页面算作新页面
* 每月搜索量 1000 亿次
用更传统的系统来练习 —— 不要使用 [solr](http://lucene.apache.org/solr/) 、[nutch](http://nutch.apache.org/) 之类的现成系统。
#### 计算用量
**如果你需要进行粗略的用量计算,请向你的面试官说明。**
* 每月存储 2 PB 页面
* 每月抓取 40 亿个页面,每个页面 500 KB
* 三年存储 72 PB 页面
* 每秒 1600 次写请求
* 每秒 40000 次搜索请求
简便换算指南:
* 一个月有 250 万秒
* 每秒 1 个请求,即每月 250 万个请求
* 每秒 40 个请求,即每月 1 亿个请求
* 每秒 400 个请求,即每月 10 亿个请求
## 第二步: 概要设计
> 列出所有重要组件以规划概要设计。
![Imgur](http://i.imgur.com/xjdAAUv.png)
## 第三步:设计核心组件
> 对每一个核心组件进行详细深入的分析。
### 用例:爬虫服务抓取一系列网页
假设我们有一个初始列表 `links_to_crawl`(待抓取链接),它最初基于网站整体的知名度来排序。当然如果这个假设不合理,我们可以使用 [Yahoo](https://www.yahoo.com/)、[DMOZ](http://www.dmoz.org/) 等知名门户网站作为种子链接来进行扩散 。
我们将用表 `crawled_links` (已抓取链接 )来记录已经处理过的链接以及相应的页面签名。
我们可以将 `links_to_crawl``crawled_links` 记录在键-值型 **NoSQL 数据库**中。对于 `crawled_links` 中已排序的链接,我们可以使用 [Redis](https://redis.io/) 的有序集合来维护网页链接的排名。我们应当在 [选择 SQL 还是 NoSQL 的问题上,讨论有关使用场景以及利弊 ](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#sql-还是-nosql)。
* **爬虫服务**按照以下流程循环处理每一个页面链接:
* 选取排名最靠前的待抓取链接
* 在 **NoSQL 数据库**的 `crawled_links` 中,检查待抓取页面的签名是否与某个已抓取页面的签名相似
* 若存在,则降低该页面链接的优先级
* 这样做可以避免陷入死循环
* 继续(进入下一次循环)
* 若不存在,则抓取该链接
* 在**倒排索引服务**任务队列中,新增一个生成[倒排索引](https://en.wikipedia.org/wiki/Search_engine_indexing)任务。
* 在**文档服务**任务队列中,新增一个生成静态标题和摘要的任务。
* 生成页面签名
* 在 **NoSQL 数据库**的 `links_to_crawl` 中删除该链接
* 在 **NoSQL 数据库**的 `crawled_links` 中插入该链接以及页面签名
**向面试官了解你需要写多少代码**。
`PagesDataStore` 是**爬虫服务**中的一个抽象类,它使用 **NoSQL 数据库**进行存储。
```python
class PagesDataStore(object):
def __init__(self, db);
self.db = db
...
def add_link_to_crawl(self, url):
"""将指定链接加入 `links_to_crawl`。"""
...
def remove_link_to_crawl(self, url):
"""从 `links_to_crawl` 中删除指定链接。"""
...
def reduce_priority_link_to_crawl(self, url)
"""在 `links_to_crawl` 中降低一个链接的优先级以避免死循环。"""
...
def extract_max_priority_page(self):
"""返回 `links_to_crawl` 中优先级最高的链接。"""
...
def insert_crawled_link(self, url, signature):
"""将指定链接加入 `crawled_links`。"""
...
def crawled_similar(self, signature):
"""判断待抓取页面的签名是否与某个已抓取页面的签名相似。"""
...
```
`Page` 是**爬虫服务**的一个抽象类,它封装了网页对象,由页面链接、页面内容、子链接和页面签名构成。
```python
class Page(object):
def __init__(self, url, contents, child_urls, signature):
self.url = url
self.contents = contents
self.child_urls = child_urls
self.signature = signature
```
`Crawler` 是**爬虫服务**的主类,由`Page` 和 `PagesDataStore` 组成。
```python
class Crawler(object):
def __init__(self, data_store, reverse_index_queue, doc_index_queue):
self.data_store = data_store
self.reverse_index_queue = reverse_index_queue
self.doc_index_queue = doc_index_queue
def create_signature(self, page):
"""基于页面链接与内容生成签名。"""
...
def crawl_page(self, page):
for url in page.child_urls:
self.data_store.add_link_to_crawl(url)
page.signature = self.create_signature(page)
self.data_store.remove_link_to_crawl(page.url)
self.data_store.insert_crawled_link(page.url, page.signature)
def crawl(self):
while True:
page = self.data_store.extract_max_priority_page()
if page is None:
break
if self.data_store.crawled_similar(page.signature):
self.data_store.reduce_priority_link_to_crawl(page.url)
else:
self.crawl_page(page)
```
### 处理重复内容
我们要谨防网页爬虫陷入死循环,这通常会发生在爬虫路径中存在环的情况。
**向面试官了解你需要写多少代码**.
删除重复链接:
* 假设数据量较小,我们可以用类似于 `sort | unique` 的方法。(译注: 先排序,后去重)
* 假设有 10 亿条数据,我们应该使用 **MapReduce** 来输出只出现 1 次的记录。
```python
class RemoveDuplicateUrls(MRJob):
def mapper(self, _, line):
yield line, 1
def reducer(self, key, values):
total = sum(values)
if total == 1:
yield key, total
```
比起处理重复内容,检测重复内容更为复杂。我们可以基于网页内容生成签名,然后对比两者签名的相似度。可能会用到的算法有 [Jaccard index](https://en.wikipedia.org/wiki/Jaccard_index) 以及 [cosine similarity](https://en.wikipedia.org/wiki/Cosine_similarity)。
### 抓取结果更新策略
要定期重新抓取页面以确保新鲜度。抓取结果应该有个 `timestamp` 字段记录上一次页面抓取时间。每隔一段时间,比如说 1 周,所有页面都需要更新一次。对于热门网站或是内容频繁更新的网站,爬虫抓取间隔可以缩短。
尽管我们不会深入网页数据分析的细节,我们仍然要做一些数据挖掘工作来确定一个页面的平均更新时间,并且根据相关的统计数据来决定爬虫的重新抓取频率。
当然我们也应该根据站长提供的 `Robots.txt` 来控制爬虫的抓取频率。
### 用例:用户输入搜索词后,可以看到相关的搜索结果列表,列表每一项都包含由网页爬虫生成的页面标题及摘要
* **客户端**向运行[反向代理](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#反向代理web-服务器)的 **Web 服务器**发送一个请求
* **Web 服务器** 发送请求到 **Query API** 服务器
* **查询 API** 服务将会做这些事情:
* 解析查询参数
* 删除 HTML 标记
* 将文本分割成词组 (译注: 分词处理)
* 修正错别字
* 规范化大小写
* 将搜索词转换为布尔运算
* 使用**倒排索引服务**来查找匹配查询的文档
* **倒排索引服务**对匹配到的结果进行排名,然后返回最符合的结果
* 使用**文档服务**返回文章标题与摘要
我们使用 [**REST API**](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#表述性状态转移rest) 与客户端通信:
```
$ curl https://search.com/api/v1/search?query=hello+world
```
响应内容:
```
{
"title": "foo's title",
"snippet": "foo's snippet",
"link": "https://foo.com",
},
{
"title": "bar's title",
"snippet": "bar's snippet",
"link": "https://bar.com",
},
{
"title": "baz's title",
"snippet": "baz's snippet",
"link": "https://baz.com",
},
```
对于服务器内部通信,我们可以使用 [远程过程调用协议RPC](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#远程过程调用协议rpc)
## 第四步:架构扩展
> 根据限制条件,找到并解决瓶颈。
![Imgur](http://i.imgur.com/bWxPtQA.png)
**重要提示:不要直接从最初设计跳到最终设计!**
现在你要 1) **基准测试、负载测试**。2) **分析、描述**性能瓶颈。3) 在解决瓶颈问题的同时评估替代方案、权衡利弊。4) 重复以上步骤。请阅读[设计一个系统,并将其扩大到为数以百万计的 AWS 用户服务](../scaling_aws/README.md) 来了解如何逐步扩大初始设计。
讨论初始设计可能遇到的瓶颈及相关解决方案是很重要的。例如加上一套配备多台 **Web 服务器**的**负载均衡器**是否能够解决问题?**CDN**呢?**主从复制**呢?它们各自的替代方案和需要**权衡**的利弊又有哪些呢?
我们将会介绍一些组件来完成设计,并解决架构规模扩张问题。内置的负载均衡器将不做讨论以节省篇幅。
**为了避免重复讨论**,请参考[系统设计主题索引](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#系统设计主题的索引)相关部分来了解其要点、方案的权衡取舍以及替代方案。
* [DNS](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#域名系统)
* [负载均衡器](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#负载均衡器)
* [水平扩展](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#水平扩展)
* [Web 服务器(反向代理)](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#反向代理web-服务器)
* [API 服务器(应用层)](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#应用层)
* [缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#缓存)
* [NoSQL](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#nosql)
* [一致性模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#一致性模式)
* [可用性模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#可用性模式)
有些搜索词非常热门,有些则非常冷门。热门的搜索词可以通过诸如 Redis 或者 Memcached 之类的**内存缓存**来缩短响应时间,避免**倒排索引服务**以及**文档服务**过载。**内存缓存**同样适用于流量分布不均匀以及流量短时高峰问题。从内存中读取 1 MB 连续数据大约需要 250 微秒,而从 SSD 读取同样大小的数据要花费 4 倍的时间,从机械硬盘读取需要花费 80 倍以上的时间。<sup><a href="https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#每个程序员都应该知道的延迟数">1</a></sup>
以下是优化**爬虫服务**的其他建议:
* 为了处理数据大小问题以及网络请求负载,**倒排索引服务**和**文档服务**可能需要大量应用数据分片和数据复制。
* DNS 查询可能会成为瓶颈,**爬虫服务**最好专门维护一套定期更新的 DNS 查询服务。
* 借助于[连接池](https://en.wikipedia.org/wiki/Connection_pool),即同时维持多个开放网络连接,可以提升**爬虫服务**的性能并减少内存使用量。
* 改用 [UDP](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#用户数据报协议udp) 协议同样可以提升性能
* 网络爬虫受带宽影响较大,请确保带宽足够维持高吞吐量。
## 其它要点
> 是否深入这些额外的主题,取决于你的问题范围和剩下的时间。
### SQL 扩展模式
* [读取复制](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#主从复制)
* [联合](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#联合)
* [分片](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#分片)
* [非规范化](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#非规范化)
* [SQL 调优](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#sql-调优)
#### NoSQL
* [键-值存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#键-值存储)
* [文档类型存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#文档类型存储)
* [列型存储](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#列型存储)
* [图数据库](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#图数据库)
* [SQL vs NoSQL](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#sql-还是-nosql)
### 缓存
* 在哪缓存
* [客户端缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#客户端缓存)
* [CDN 缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#cdn-缓存)
* [Web 服务器缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#web-服务器缓存)
* [数据库缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#数据库缓存)
* [应用缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#应用缓存)
* 什么需要缓存
* [数据库查询级别的缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#数据库查询级别的缓存)
* [对象级别的缓存](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#对象级别的缓存)
* 何时更新缓存
* [缓存模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#缓存模式)
* [直写模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#直写模式)
* [回写模式](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#回写模式)
* [刷新](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#刷新)
### 异步与微服务
* [消息队列](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#消息队列)
* [任务队列](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#任务队列)
* [背压](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#背压)
* [微服务](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#微服务)
### 通信
* 可权衡选择的方案:
* 与客户端的外部通信 - [使用 REST 作为 HTTP API](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#表述性状态转移rest)
* 内部通信 - [RPC](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#远程过程调用协议rpc)
* [服务发现](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#服务发现)
### 安全性
请参阅[安全](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#安全)。
### 延迟数值
请参阅[每个程序员都应该知道的延迟数](https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md#每个程序员都应该知道的延迟数)。
### 持续探讨
* 持续进行基准测试并监控你的系统,以解决他们提出的瓶颈问题。
* 架构扩展是一个迭代的过程。

View File

@ -100,7 +100,7 @@ We could store `links_to_crawl` and `crawled_links` in a key-value **NoSQL Datab
`PagesDataStore` is an abstraction within the **Crawler Service** that uses the **NoSQL Database**:
```
```python
class PagesDataStore(object):
def __init__(self, db);
@ -134,7 +134,7 @@ class PagesDataStore(object):
`Page` is an abstraction within the **Crawler Service** that encapsulates a page, its contents, child urls, and signature:
```
```python
class Page(object):
def __init__(self, url, contents, child_urls, signature):
@ -146,7 +146,7 @@ class Page(object):
`Crawler` is the main class within **Crawler Service**, composed of `Page` and `PagesDataStore`.
```
```python
class Crawler(object):
def __init__(self, data_store, reverse_index_queue, doc_index_queue):
@ -187,7 +187,7 @@ We'll want to remove duplicate urls:
* For smaller lists we could use something like `sort | unique`
* With 1 billion links to crawl, we could use **MapReduce** to output only entries that have a frequency of 1
```
```python
class RemoveDuplicateUrls(MRJob):
def mapper(self, _, line):
@ -294,7 +294,7 @@ Below are a few other optimizations to the **Crawling Service**:
### SQL scaling patterns
* [Read replicas](https://github.com/donnemartin/system-design-primer#master-slave)
* [Read replicas](https://github.com/donnemartin/system-design-primer#master-slave-replication)
* [Federation](https://github.com/donnemartin/system-design-primer#federation)
* [Sharding](https://github.com/donnemartin/system-design-primer#sharding)
* [Denormalization](https://github.com/donnemartin/system-design-primer#denormalization)

View File

@ -1,34 +1,35 @@
# -*- coding: utf-8 -*-
class PagesDataStore(object):
def __init__(self, db);
def __init__(self, db):
self.db = db
...
pass
def add_link_to_crawl(self, url):
"""Add the given link to `links_to_crawl`."""
...
pass
def remove_link_to_crawl(self, url):
"""Remove the given link from `links_to_crawl`."""
...
pass
def reduce_priority_link_to_crawl(self, url)
def reduce_priority_link_to_crawl(self, url):
"""Reduce the priority of a link in `links_to_crawl` to avoid cycles."""
...
pass
def extract_max_priority_page(self):
"""Return the highest priority link in `links_to_crawl`."""
...
pass
def insert_crawled_link(self, url, signature):
"""Add the given link to `crawled_links`."""
...
pass
def crawled_similar(self, signature):
"""Determine if we've already crawled a page matching the given signature"""
...
pass
class Page(object):
@ -41,7 +42,7 @@ class Page(object):
def create_signature(self):
# Create signature based on url and contents
...
pass
class Crawler(object):