1. 需求
產品第一版:用戶有用戶名、密碼、昵稱等三個屬性,對應表設計:
user(uid, name, passwd, nick)
第二版,產品經理增加了年齡,性別兩個屬性,表結構可能要變成:
user(uid, name, passwd, nick, age, sex)
假設數據量和並發量比較大,怎么變?
(1)alter table add column?不太可行,鎖表時間長
(2)新表+觸發器?如果數據量太大,新表不一定裝得下,何況觸發器對數據庫性能的影響比較高
(3)讓dba來搞?新表,遷移數據,一致性校驗,rename?dba真苦逼
2. 版本號 + 通用列
以上面的用戶表為例,假設只有uid和name上有查詢需求,表可以設計為
user(uid, name, version, ext)
(1)uid和name有查詢需求,必須設計為單獨的列並建立索引
(2)version是版本號字段,它對ext進行了版本解釋
(3)ext采用可擴展的字符串協議載體,承載被查詢的屬性
例如,最開始上線的時候,版本為0,此時只有passwd和nick兩個屬性,那么數據為:
| uid | name | version | ext |
| 1 | 張三 | 0 | {"passwd":"123","nick":NULL} |
| 2 | 李四 | 0 | {"passwd":"456","nick":"lisi"} |
當產品經理需要擴展屬性時,新數據將版本變為1,此時新增了age和sex兩個數據,數據變為:
| uid | name | version | ext |
| 1 | 張三 | 0 | {"passwd":"123","nick":NULL} |
| 2 | 李四 | 0 | {"passwd":"123","nick":"lisi"} |
| 3 | 王五 | 1 | {"passwd":"789","nick":NULL,"age":18,"sex":"F"} |
優點:
(1)可以隨時動態擴展屬性
(2)新舊兩種數據可以同時存在
(3)遷移數據方便,寫個小程序將舊版本ext的改為新版本的ext,並修改version
不足:
(1)ext里的字段無法建立索引
(2)ext里的key值有大量冗余,建議key短一些
改進:
(1)如果ext里的屬性有索引需求,可能Nosql的如MongoDB會更適合
3. 通過擴展行的方式來擴展屬性
以上面的用戶表為例,可以設計為
user(uid, key, value)
初期有name, passwd, nick三個屬性,那么數據為:
| uid | key | value |
| 1 | name | 張三 |
| 1 | passwd | 123 |
| 1 | nick | NULL |
| 2 | name | 李四 |
| 2 | passwd | 456 |
| 2 | nick | lisi |
未來擴展了age和sex兩個屬性,數據變為:
| uid | key | value |
| 1 | name | 張三 |
| 1 | passwd | 123 |
| 1 | nick | NULL |
| 2 | name | 李四 |
| 2 | passwd | 456 |
| 2 | nick | lisi |
| 3 | name | 王五 |
| 3 | passwd | 789 |
| 3 | nick | NULL |
| 3 | age | 18 |
| 3 | sex | F |
優點:
(1)可以隨時動態擴展屬性
(2)新舊兩種數據可以同時存在
(3)遷移數據方便,寫個小程序可以將新增的屬性加上
(4)各個屬性上都可以查詢
不足:
(1)key值有大量冗余,建議key短一些
(2)本來一條記錄很多屬性,會變成多條記錄,行數會增加很多
4. 在線表屬性擴展方案
4.1 哪些方案一定是不行的
(1)alter table add column
-- 大數據量和大並發情況下,鎖表時間太長
(2)通過增加表的方式擴展,通過外鍵join來查詢
-- 大數據高並發情況下,join性能較差
(3)通過增加表的方式擴展,同視圖來對外
-- 大數據高並發情況下,不推薦使用
4.2 在線表結構變更
新表 + 觸發器 + 遷移數據 + rename(pt-online-schema-change),這是業內非常成熟的擴展列的方案。
以user(uid, name, passwd)擴展到user(uid, name, passwd, age, sex)為例
基本原理是:
(1)先創建一個擴充字段后的新表user_new(uid, name, passwd, age, sex)
(2)在原表user上創建三個觸發器,對原表user進行的所有insert/delete/update操作,都會對新表user_new進行相同的操作
(3)分批將原表user中的數據insert到新表user_new,直至數據遷移完成
(4)刪掉觸發器,把原表移走(默認是drop掉)
(5)把新表user_new重命名(rename)成原表user
擴充字段完成。
優點:整個過程不需要鎖表,可以持續對外提供服務
操作過程中需要注意:
(1)變更過程中,最重要的是沖突的處理,一條原則,以觸發器的新數據為准,這就要求被遷移的表必須有主鍵(這個要求基本都滿足)
(2)變更過程中,寫操作需要建立觸發器,所以如果原表已經有很多觸發器,方案就不行(互聯網大數據高並發的在線業務,一般都禁止使用觸發器)
(3)觸發器的建立,會影響原表的性能,所以這個操作建議在流量低峰期進行
