關係型資料庫 (MySQL) 與文檔型資料庫 (MongoDB) 在處理「動態屬性」場景下的設計差異巨大。以手機產品庫為例,深度對比兩種建模思路、查詢模式與擴展性。
CREATE TABLE `mobiles` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(100) NOT NULL,
`brand` VARCHAR(100) NOT NULL
) ENGINE=InnoDB;
CREATE TABLE `mobile_params` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`mobile_id` INT UNSIGNED NOT NULL,
`name` VARCHAR(100) NOT NULL,
`value` VARCHAR(100) NOT NULL,
INDEX `idx_mobile_id` (`mobile_id`),
INDEX `idx_name_value` (`name`, `value`)
) ENGINE=InnoDB;
INSERT INTO `mobiles` VALUES (1, 'ME525', '摩托羅拉'), (2, 'E7', '諾基亞');
INSERT INTO `mobile_params` VALUES
(1, 1, '待機時間', '200'), (2, 1, '外觀設計', '直板'),
(3, 2, '待機時間', '500'), (4, 2, '外觀設計', '滑蓋');
-- 步驟 1:查待機 > 100 的 mobile_id
SELECT `mobile_id` FROM `mobile_params` WHERE `name`='待機時間' AND CAST(`value` AS UNSIGNED) > 100;
-- 步驟 2:查外觀 = 直板 的 mobile_id
SELECT `mobile_id` FROM `mobile_params` WHERE `name`='外觀設計' AND `value`='直板';
-- 步驟 3:應用層取交集 → 查 mobiles 主表
痛點: 字串儲存數值需 CAST、多條件需多查詢/自交集、索引效率低、Schema 變更仍需 DDL。
db.mobiles.createIndex({ "params.name": 1, "params.value": 1 });
db.mobiles.insert({
_id: 1, name: "ME525", brand: "摩托羅拉",
params: [
{ name: "待機時間", value: 200 },
{ name: "外觀設計", value: "直板" }
]
});
db.mobiles.insert({
_id: 2, name: "E7", brand: "諾基亞",
params: [
{ name: "待機時間", value: 500 },
{ name: "外觀設計", value: "滑蓋" }
]
});
db.mobiles.find({
params: {
$all: [
{ $elemMatch: { name: "待機時間", value: { $gt: 100 } } },
{ $elemMatch: { name: "外觀設計", value: "直板" } }
]
}
});
$all:陣列需同時滿足所有條件。$elemMatch:單一陣列元素同時滿足多欄位條件(避免跨元素誤匹配)。value 可為數值/字串/布林/日期,查詢運算子($gt、$lt)直接生效。| 維度 | MySQL (EAV) | MongoDB (文檔) |
|---|---|---|
| 建模複雜度 | 中(需設計參數表) | 低(自然映射物件) |
| 動態屬性新增 | 無需 DDL,僅寫入參數表 | 無需 DDL,直接寫入文檔 |
| 多條件查詢 | 多 SQL + 應用層交集 / 複雜自連接 | 單查詢 ($all + $elemMatch) |
| 型別安全 | 弱(全存字串,需 CAST) | 強(原生型別) |
| 索引效率 | 參數表索引碎片化、選擇度低 | 複合陣列索引精準命中 |
| 事務/一致性 | 強 (ACID) | 文檔級 ACID (4.0+ 多文檔事務) |
| 擴展性 | 垂直分表/分庫分表 | 原生 Sharding |
| 運維成熟度 | 極高 | 高(但需專業知識) |
CREATE TABLE `mobiles_v2` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(100), `brand` VARCHAR(100),
`params` JSON NOT NULL,
INDEX `idx_params_standby` ((CAST(JSON_EXTRACT(`params`, '$.待機時間') AS UNSIGNED))),
INDEX `idx_params_design` ((CAST(JSON_EXTRACT(`params`, '$.外觀設計') AS CHAR(20))))
) ENGINE=InnoDB;
INSERT INTO `mobiles_v2` VALUES
(1, 'ME525', '摩托羅拉', '{"待機時間": 200, "外觀設計": "直板"}'),
(2, 'E7', '諾基亞', '{"待機時間": 500, "外觀設計": "滑蓋"}');
-- 查詢
SELECT * FROM `mobiles_v2`
WHERE JSON_EXTRACT(`params`, '$.待機時間') > 100
AND JSON_EXTRACT(`params`, '$.外觀設計') = '直板';
業務核心是否為「動態屬性多條件查詢」? ├─ 是,且團隊具備 MongoDB 運維能力 → MongoDB 為首選 │ ├─ 否,或堅持關係型生態 → MySQL 5.7+ JSON + 虛擬欄位索引 │ ├─ 極簡場景、屬性固定化 → MySQL 傳統欄位/關聯表 │ └─ 混合負載 → 核心交易 MySQL,動態屬性/搜尋/日誌 MongoDB/ES
本例中 MongoDB 以單查詢解決 MySQL 需多步驟的問題,展現文檔模型在**半結構化、動態屬性、多條件組合篩選**場景的原生優勢。但選型從來不只是看單一場景,需綜合團隊技術棧、一致性要求、運維成熟度、成本預算決策。