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

基於 MySQL 實現 Schemaless 存儲:FriendFeed 方案深度解析與實戰改良

     編輯:MySQL 資料庫相關知識

關係型資料庫的嚴格 Schema 在敏捷開發、快速迭代的業務場景下常成為瓶頸。FriendFeed 團隊曾分享一種「以 MySQL 實現 Schemaless」的創新架構:將物件序列化壓縮後存入單一 BLOB/VARBINARY 欄位,輔以獨立索引表支援查詢。本文深度解析此方案原理、實作細節與生產環境優化策略。

核心架構設計

主表:資料載體

CREATE TABLE `blogs` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `object` VARBINARY(20000) NOT NULL COMMENT '序列化+壓縮後的物件',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • object 欄位存放 pickle.dumps(obj) → zlib.compress() 後的二進位資料。
  • Schema 變更(增欄位、改型別)零 ALTER TABLE,應用層自行兼容。
  • 主鍵查詢 SELECT object FROM blogs WHERE id=? 極快,適合高頻單筆讀取。

索引表:查詢加速器

每個需查詢的欄位建立獨立索引表,不存放完整物件,僅存主鍵 ID 與索引欄位值:

-- 發文時間索引
CREATE TABLE `blog_idx_post_date` (
  `id` INT(10) UNSIGNED NOT NULL,
  `post_date` DATETIME NOT NULL,
  PRIMARY KEY (`id`),
  INDEX `idx_blogs_by_post_date` (`post_date`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 作者索引
CREATE TABLE `blog_idx_author` (
  `id` INT(10) UNSIGNED NOT NULL,
  `author` INT NOT NULL,
  PRIMARY KEY (`id`),
  INDEX `idx_blogs_by_author` (`author`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

資料流向與查詢模式

寫入流程

  1. 應用層建構物件 Blog(title=..., content=..., post_date=..., author=...)。
  2. 序列化壓縮 → 寫入 blogs 主表 → 取得 auto_increment ID。
  3. 同步寫入各索引表 (id, post_date)、(id, author)。
  4. (可選)非同步寫入外部搜尋引擎(Elasticsearch、Meilisearch)支援全文檢索。

讀取模式

查詢類型SQL 模式適用場景
主鍵精確查詢SELECT object FROM blogs WHERE id=?單筆詳情、快取回填
單一索引範圍查詢SELECT id FROM blog_idx_post_date WHERE post_date BETWEEN ? AND ? → 回主表時間軸列表、歸檔查詢
多條件組合查詢各索引表取 ID 集合 → INTERSECT(應用層或 IN (SELECT...)) → 回主表複雜篩選、後台管理
全文檢索外部搜尋引擎 → 取 ID → 回主表內容搜尋、模糊匹配

生產環境關鍵優化

1. 物件快取層(Redis/Memcached)

blogs 主表讀取後立即寫入 Redis(Key: blog:{id},TTL: 1h~24h)。測試顯示:快取命中率 > 95% 時,QPS 可提升 10-20 倍,且大幅降低 MySQL 連線壓力。

2. 索引表維護策略

  • 同步寫入: 交易內完成,強一致性,寫入延遲略增。
  • 非同步寫入(推薦): 寫入主表後發送訊息至 MQ(Kafka/RocketMQ),Consumer 更新索引表。極少數索引不一致可接受,大幅降低寫入尾部延遲。
  • 定期重建: 每日低峰期 TRUNCATE + INSERT SELECT 重建索引表,修正漂移資料。

3. 大物件處理

  • VARBINARY(20000) 限制單物件 ~20KB。超過者:拆分至 blob_chunks 子表 或改用 LONGBLOB(需評估 InnoDB 行溢出影響)。
  • 壓縮演算法:zlib 平衡速度/壓縮率;lz4 / zstd 更快,適合高寫入場景。

4. 遷移與版本演進

物件內嵌 __schema_version__ 欄位。讀取時由應用層 upgrader 負責舊版本資料轉換,實現「讀時遷移」,避免大鎖表操作。

與 MongoDB 的權衡

維度MySQL SchemalessMongoDB
事務支援✅ ACID 完整✅ 4.0+ 多文件事務
成熟度/穩定性✅ 極高⚠️ 早期版本有資料丟失案例
運維成本✅ 既有 MySQL 運維體系⚠️ 額外技能樹、監控體系
查詢靈活度⚠️ 需自建索引表✅ 原生豐富查詢
水平擴展⚠️ 需分庫分表/中間件✅ 原生 Sharding

結論: 若團隊深耕 MySQL 生態、業務以主鍵/單索引查詢為主、重視資料強一致與運維簡潔,MySQL Schemaless 是極具性價比的選擇;若需複雜多條件查詢、地理空間、原生水平擴展,MongoDB 更適合。

開源專案參考

本方案在專案中已孵化穩定運行半年,核心元件將於春節後開源。歡迎同好交流、貢獻代碼,共同完善 MySQL Schemaless 生態。

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