1.2 第一次演進:Web 伺服器與資料庫分離部署#
從一個問題開始#
單機跑了幾個月,生意變好了,商品從幾百件變成幾萬件。某天凌晨,網站突然全掛了。起來排查,發現磁碟被 MySQL 的資料檔塞滿,Tomcat 連日誌都寫不進去——賣家傳一張商品圖都沒地方放。
這就是單機架構的第一個死因:應用和資料庫在同一台機器上互相搶資源。最自然的止血辦法是什麼?把資料庫搬出去,單獨給它一台機器。這就是架構演進的第一次拆分。
拆分:兩台機器,各做各的事#
演進後的拓撲很簡單:Web 機器只跑 Tomcat,DB 機器只跑 MySQL,兩者之間用內網 TCP 連線。應用第一次有了「遠端依賴」的概念。
flowchart LR
USER[使用者] --> WEB[Web 伺服器<br/>Tomcat]
WEB -->|JDBC 內網 :3306| DB[(DB 伺服器<br/>MySQL)]
WEB --> F[本地磁碟<br/>圖片暫存]這一步看似微小,卻是架構史上的關鍵一躍:計算(Compute)與儲存(Storage)第一次解耦。從此兩邊可以獨立調優、獨立重啟、獨立升級。
分離帶來什麼好處#
| 面向 | 分離前(單機) | 分離後(兩台) |
|---|---|---|
| 資源隔離 | Tomcat 與 MySQL 搶 CPU、內存、磁碟 | 各用各的機器,互不干擾 |
| 調優自由 | 改 MySQL 快取會擠壓 Tomcat 堆內存 | DB 可專門調大 InnoDB Buffer Pool |
| 故障隔離 | 一掛全掛,連日誌都查不到 | Web 掛了資料還在,DB 掛了可單獨搶修 |
| 安全性 | 資料庫與外網同機,風險高 | DB 藏進內網,只對 Web 開放 3306 |
| 代價 | 無 | 多一台機器錢 + 內網延遲(約 0.5ms) |
多出來的內網延遲幾乎可以忽略,換來的是運維上的巨大自由。這筆買賣在當年是穩賺不賠的。
連線字串的改變:第一次感受到「分散式」#
拆分在程式碼上只改一行,卻是心智上的分水嶺:
# 分離前:資料庫就在本機
jdbc.url=jdbc:mysql://localhost:3306/taobao?useUnicode=true
# 分離後:資料庫是遠端某個 IP,開始需要帳號、密碼、防火牆、連線池
jdbc.url=jdbc:mysql://192.168.1.11:3306/taobao?useUnicode=true
jdbc.username=app_user
jdbc.maxPoolSize=50
jdbc.timeout=3000
從此工程師必須思考新問題:內網 IP 變了怎麼辦?DB 機器連不上要重試幾次?連線池開多大?這些就是分散式系統的雛形——網路不再是可靠的本地呼叫,而是一個會超時、會斷線的第三方。
flowchart TD
APP[Tomcat 連線池<br/>最多 50 條連線] -->|TCP| FW[防火牆<br/>只放行 Web IP]
FW --> DB[(MySQL<br/>max_connections=500)]檔案系統的第一次尷尬#
資料庫搬走了,但商品圖片還在 Web 機器的本地磁碟上。這時埋下一個伏筆:如果以後加第二台 Web 機器(見第 3 章),兩台機器的圖片就不一致了。這個「檔案去哪了」的問題,最終會逼出獨立的檔案系統與 CDN,但那是後話。這裡先記住:每一次拆分都會暴露下一個耦合點。
本章小結#
- 第一次演進的動機是資源競爭:Web 與 DB 同機互相拖累
- 解法是計算與儲存分離:Tomcat 一台,MySQL 一台,內網互連
- 收益是隔離性、可調優性、安全性,代價是多一台機器與網路依賴
- 連線字串從
localhost變成內網 IP,工程師第一次面對超時與重試 - 圖片仍在本地磁碟,為下一次演進(無狀態化)埋下伏筆
想一想#
- 為什麼「把資料庫搬到另一台機器」幾乎沒有缺點?什麼情況下內網延遲會變成問題?
- 防火牆只放行 Web 機器的 IP 有什麼安全意義?如果把 DB 直接暴露到公網會怎樣?
- 連線池的
maxPoolSize設太大或太小分別會引發什麼故障?跟 DB 的max_connections是什麼關係?
中英術語對照#
| 中文 | 英文 |
|---|---|
| 計算與儲存分離 | Compute-Storage Separation |
| 資源隔離 | Resource Isolation |
| 連線池 | Connection Pool |
| 內網 / 防火牆 | Intranet / Firewall |
| 故障隔離 | Fault Isolation |