首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

FriendFeed 無 Schema 架構實踐:在 MySQL 上構建靈活、高效能的文檔存儲

     編輯:MySQL 資料庫相關知識

面對 2.5 億條記錄、頻繁 Schema 變更、傳統 ALTER TABLE 鎖表數小時的痛點,FriendFeed 團隊選擇在 MySQL 之上實作一套「無 Schema」存儲系統:實體表僅存 id + body(JSON blob),索引完全外置到獨立表,並開發「清潔工」異步維護索引一致性。本文深度解析其架構設計、一致性模型、效能優化與運維經驗。

架構核心:實體表 + 外置索引表

-- 實體表:僅存 id、updated、壓縮後的 body
CREATE TABLE entities (
    added_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,  -- 物理順序寫入,利於分區讀取
    id BINARY(16) NOT NULL,
    updated TIMESTAMP NOT NULL,
    body MEDIUMBLOB,  -- zlib 壓縮的 Python dict / JSON
    UNIQUE KEY (id),
    KEY (updated)
) ENGINE=InnoDB;
-- 索引表:每個需查詢的屬性一張表
CREATE TABLE index_user_id (
    user_id BINARY(16) NOT NULL,
    entity_id BINARY(16) NOT NULL UNIQUE,
    PRIMARY KEY (user_id, entity_id)
) ENGINE=InnoDB;

關鍵設計決策:

  • added_id 自增主鍵確保新實體物理順序寫入,熱數據集中,減少隨機 I/O。
  • body 經 zlib 壓縮,節省 70%+ 空間,CPU 成本極低。
  • 索引表主鍵 (屬性值, entity_id) 支援範圍查詢與去重。
  • 分庫分表按 shard_on 屬性哈希路由,擴展性線性增長。

寫入流程:最終一致性 + 應用層過濾

  1. 寫入 entities 表(InnoDB ACID 保證)。
  2. 非同步寫入所有相關索引表(可能失敗、延遲)。
  3. 讀取時:先從索引表取 entity_id,再回 entities 取完整實體,最後在應用層(Python)用真實屬性值過濾,修正索引不一致導致的誤判。

此設計犧牲強一致性,換取寫入零鎖、Schema 變更零停機。索引不一致窗口通常僅秒級(清潔工優先處理最新更新)。

清潔工:索引一致性的守護者

# 為新屬性 link 建立索引表後,啟動清潔工回填歷史資料
./rundatastorecleaner.py --index=index_link
  • 持續掃描 entities 表,比對索引表,補寫遺漏、刪除失效索引。
  • 優先處理最近更新實體,新寫入實體秒級可被索引。
  • 支援暫停、限流、指定分片,不影響線上服務。

效能成果

上線後頁面載入延遲顯著下降並趨於穩定,即使午間尖峰也無抖動。新增索引從「數小時鎖表」變為「一天內完成,零業務影響」。

適用性評估與啟示

優勢劣勢/限制
Schema 變更極致靈活(加/刪索引無鎖)無 JOIN、外鍵、複雜事務支援
寫入延遲極低、可線性擴展讀取需兩次查詢 + 應用層過濾,延遲略高
利用成熟 MySQL 生態(備份、複制、監控)存儲空間放大(索引表冗余屬性值)
避免遷移至陌生 NoSQL 系統的風險需自研資料存取層(Datastore SDK)

FriendFeed 方案證明:在關係型資料庫上構建文檔存儲,是「不想換引擎、又要 Schema 自由」團隊的務實選擇。核心思想——實體與索引解耦、寫入路徑極簡、異步修復一致性——值得所有面對大表 DDL 痛點的團隊參考。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved