1. 能不加字段就不要加, 能不修改字段就不要修改, 能不刪除字段就不要刪除, 等等為什么要刪除字段呢? 如果沒事,不要蛋疼的找事。 實際上,我們那次更新失敗后, 我們並沒有增加那個字段, 然后我們一直運行到今天, 但是后來還是增加了其他字段
2. 增加字段的情況下, 如果可以通過增加一個新的表來增加這個字段, 那么就增加一個新的表, 通過cache 或 程序來實現join 的效果
3. 如果能停機, 並且停機的時間在你容忍范圍之內, 數據備份之后停機來做。 如果是主從備份,做這樣大的操作的時候,停掉主從備份, 萬一你掛了, 備份數據庫可以救你。 等到一切安全了, 重啟主從備份;
4. 如果上面都不行, 這個字段還是要改,要加, 需要用到下面的方法, 也是扇貝網正在使用的方法;
修改大數據表的方法:
1. 被修改的表 Table A 需要有一個記錄時間戳的字段, 這個時間戳就是每次數據更新,都會更新的字段, 這個字段需要有索引,在django里可以使用 auto_now=True
2. 創建一個新的臨時表 Table B, 不是tmp_table, 是一個新的表,但是是臨時使用的。 這個表和要修改的表擁有一模一樣的數據結構, 加上你要修改的部分, 比如增加的字段;
3. 記錄下Table A 的索引
4. 刪除 Table B 的全部索引
5. 把Table A 的數據全部復制到Table B, 是不是執行 INSERT INTO B(field1, field2) SELECT field1, field2 FROM A? 當然不是, 這么做不還是鎖死了Table A 么, 這里的遷移就是一個需要細分的地方,需要寫一個腳本, 讓程序每次讀取比如5000條數據出來, 插入到Table B里面, 因為Table B 是沒有索引的, 所以要當心不要使用多進程來做; 如果是多進程, 要確保插入到B的時候是不會有重復數據的; 如果是1000萬的數據,每次5000條, 假設這個操作需要500ms, 那么 2000*200ms = 16 分鍾。 這只是一個估值, 具體情況和服務器當時的情況有關, 不好細說。 另外, 我們要記錄這個遷移開始的時間點,記為t1;
6. 那么這個時候Table A 的數據是不是都進入了Table B 呢, 應當說差不多大部分都進入了, 但5中說, 這大概需要16分鍾, 這么長的時間里, 可能有新的數據進入了, 也有可能已有的數據發生了更新, 所以我們要把Table A 中在t1 之后發生變化的數據查找出來, 然后更新到Table B 中, 我們的做法是:
記錄這個操作對應的時間點 t2
BEGIN;
DELETE FROM B WHERE updated_time > t1;
INSERT INTO B(field1, field2) SELECT field1, field2 FROM A WHERE updated_time >t1;
COMMIT;
7. 現在A 和 B 差不多該同步了吧? 差不多了, 但是6 執行完之后, A仍然在寫, 子子孫孫無窮盡也 ... , 但這個時候 A 和 B 的差異已經非常非常小了, 所以在下一步,我們在一個transaction 里執行下面的操作:
BEGIN;
DELETE FROM B WHERE updated_time > t2;
INSERT INTO B(field1, field2) SELECT field1, field2 FROM A WHERE updated_time >t2;
ALTER TABLE A RENAME TO C;
ALTER TABLE B RENAME TO A;
COMMIT;
8. Done
PS: 如果A表沒有時間戳, 實際上沒有先見之明的人大概不會想到要預留一個時間戳的字段, 那么幾乎是沒有辦法快速比較兩個表的差異的, 這個時候我選擇的做法就是放棄修改的數據, 只管新的數據了, 所以只要把t1, t2 換成id 就可以了, 這樣delete 語句也省略了, 因為沒啥好delete的;
千萬不要想着根據ID 來JOIN 然后更新B表的字段來補齊新的數據, 如果能把兩個千萬級別的表JOIN起來, 內存有多大呢?
9. 上面的解決方案是我們第二次嘗試之后犯下了一個巨大的錯誤,這個錯誤導致網站癱瘓了大概20分鍾, 如果你和我一樣沒有發現問題,那么這就是悲劇的地方。 問題就在於我是根據上面的PS來操作的, 然后B就華麗地變成了A. B 表至今身上是沒有索引的, 立即悲劇。 所以應當在第5步之后按照A的索引為B建立索引, 待索引全部好了之后, 再繼續6。 如果不是走PS這條路, 而是有時間戳的字段的話, 在6的時候會發現這個問題, 因為那條Delete 慢的超出想像, 會明白這里是有問題的
10. 新手, 請在本地練習之后, 再實際操作; 可以多操作幾次, 寫一個腳本,服務器上直接執行腳本.
轉載:https://zhidao.baidu.com/question/917987491401145099.html