Database & Cache
系統設計一定會碰到 database 的問題,一個產品必定會碰到 database 的開發。因為當我們想要儲存資料時,是將資料結構化後儲存到資料庫,再由資料庫儲存到文件系統,資料庫幫我們經手與文件系統打交道這件事。
面試時,一個問題下來:「如何設計使用者系統?」
這時我們應該要用 4S 分析,分別是 Scenario, Service, Storage, Scale
- Scenario: 場景分析,與面試官聊天,了解需求,並順便導出 QPS 等等
- Service: 功能需要拆分成哪幾個 Service? 像是 AuthService, UserInfoService, FriendShipService 等等
- Storage: 根據 QPS 而選用哪種資料庫? 資料的 Schema 是? ER relation 等等
- Scale: 你要如何 scale?
QPS: query per second, 大致上需估算 average, peak 的 QPS,然後根據結果挑選 DB
- MySQL / PostgreSQL : 約 100 ~ 1000 QPS
- MongoDB / Cassandra : 約 10k QPS
- Memcached / Redis : 約 100k ~ 1M QPS
SQL 類的資料庫支援複雜的語句,所以速度較慢; NoSQL 則僅支援簡單的語句,所以速度較快,而且通常支援 scaling
Service 設計時,盡量簡單而不要 over design
如果 service 為讀多寫少 => 必用 cahce 優化; 反之則用 NoSQL 快速寫入
*** 練習 LRU Cache!!!!!!!
Cache 是一種概念,將「常用到的資料」儲存在某個地方(不是資料庫),下次要取用時可以快速取得
常見的有
- Memcached: 不支持持久化,關掉資料就消失
- Redis: 支持持久,也支持 conurrency, it is consistent ()
Cache 有分成 cache-through, cache-aside 類型,主要分別是 cache 是與 DB 直接溝通嗎? 如果是就是 cache-through 類型
Cache 並不是資料庫,而是一種「優化」
使用 Cache 時要記住,DB、cache 並不總是一致,不能假設所有操作都會成功,DB 操作的一個要點是要保持一致性
以下為較正確的寫法:
def setUser(key, value):
...
cache.delete(key)
database.set(key, value)
其餘的順序都會導致 db, cache 不一致,例如
database.set(key, value) -> 這行做完失敗的話,資料會不一致
cache.set(key, value)
用到期時間可以有效的降低不一致性
假設要設計 FriendShip Service,先詢問要有那些功能,EX: 好友查詢
好友查詢又可以想到說好友關係是雙向還是單向? 根據關係可以去想出資料庫的表應該怎麼做,資料庫怎麼挑等等
挑選 SQL 或 NoSQL
- 大部分情況下都可以
- 需要 Transaction 的不能用 NoSQL (NoSQL 不能確保一致)
- 想要複雜功能則用 SQL
- 是否需要 Sequential ID? 要則選 SQL
- 更高性能選用 NoSQL
萬年問題: How to scale?
除了 QPS,其他的 concern 為 single point failure, DB scaling
scale 可以從 load balance 和資料庫下手,
資料庫的話可以做 sharding, Replica 等等
sharding 又分為
- vertical sharding : 某些表存到某台機器,或是某些 column 存到某台機器,可以把較常 query 的擺一起,cache hit 會上升,但是缺點也很明顯,就是如果表的 row 很多還是沒用啊!
- horizontal sharding : 將 row 拆分到其他機器,暴力法為將 id % n,可是要遷移時會是很大的問題