24分鐘完成生產(chǎn)環(huán)境數(shù)據(jù)庫升級,零停機、零事故。這不是大廠SRE團隊的戰(zhàn)績,而是一個人的產(chǎn)品——Shipstry的實戰(zhàn)記錄。
作者Cloudflare Workers上跑著一個產(chǎn)品發(fā)布平臺,最近剛啃完一塊硬骨頭:把評論系統(tǒng)和支付模型徹底重構(gòu)。最狠的是全程對外服務(wù)沒斷過。怎么做到的?我翻完了他的完整操作日志,發(fā)現(xiàn)這套方法論的門檻比想象中低,但執(zhí)行紀律比想象中嚴。
![]()
一、為什么這次非升不可
觸發(fā)升級的是兩個"看起來還能用"的系統(tǒng),終于裝不下新業(yè)務(wù)了。
第一個是評論。產(chǎn)品評論和博客評論最初共用一套表結(jié)構(gòu),作者坦承這是"假裝它們一樣能換來簡單"。但隨著功能分化,這種假裝開始反噬——每次加新功能都要給兩種場景打補丁,"deferring cleanup"(推遲清理)的債越積越重。
第二個是支付。老的訂單模型最初只處理單一付費行為,后來平臺陸續(xù)支持了:
? 一次性購買
? 訂閱制
? 退款
? 部分退款
舊schema開始逼業(yè)務(wù)代碼背 legacy assumptions(遺留假設(shè))。作者想要的數(shù)據(jù)庫是"能直接回答商業(yè)問題",而不是讓新功能給歷史包袱打工。
核心重構(gòu)目標(biāo)就兩條:comments表按場景拆分,payments表把"錢怎么付"和"買了什么"徹底解耦。聽起來是標(biāo)準的數(shù)據(jù)庫范式作業(yè),但生產(chǎn)環(huán)境里沒有redos(重來機會)。
二、"讀寫分離"的粗暴解法
作者的第一條鐵律:reads stay up, writes freeze first(讀保持在線,寫先凍結(jié))。
不是"小心點操作",是真的造了一個開關(guān)。他在代碼里加了 centralized MAINTENANCE_WRITE_FREEZE 守衛(wèi),硬編碼進所有能改D1的地方:
? 所有POST/PUT/PATCH路由
? 后臺管理面板
? 定時任務(wù)和隊列處理器
「I was not optimizing for elegance. I was optimizing for control.」
這句話值得貼墻上。很多開發(fā)者追求"優(yōu)雅的零停機方案",結(jié)果在復(fù)雜度里淹死。作者反其道而行:承認停機不可避免,但把停機范圍壓縮到"寫操作"這一層。用戶能看頁面、能刷數(shù)據(jù),只是不能下單、不能評論——這對一個產(chǎn)品發(fā)布平臺來說,24分鐘完全可以接受。
開關(guān)的設(shè)計也透著狠勁:「If I have to do this again, I want one switch that really means one switch.」沒有多級配置、沒有環(huán)境變量迷宮,就是一個布爾值,全局生效。
三、備份必須"無聊"到讓人打哈欠
操作前的第一件事:export production D1。
作者的原話是:「That should feel boring. If backups feel exciting, you are already too late.」備份如果讓你腎上腺素飆升,說明你沒把它當(dāng)成肌肉記憶。
但備份只是門票。他還錄了基線數(shù)據(jù):
? 訂單總數(shù)
? 活躍訂閱數(shù)
? 待處理退款數(shù)
這些數(shù)字后來成了release gates(發(fā)布關(guān)卡)。不是"我筆記本上頁面能打開"就完事,是硬碰硬的before/after校驗。這種量化思維在獨立開發(fā)者身上不多見——很多人靠"感覺沒問題"上線,作者靠數(shù)字說話。
預(yù)檢階段還挖出一個小坑:生產(chǎn)環(huán)境居然還有l(wèi)egacy expedition values(遺留的加急配送值)散落在comments和orders兩張表里。這說明migration history(遷移歷史)不可信,「I had to trust the database I was actually about to operate on」——必須對著即將操作的數(shù)據(jù)庫做驗證,而不是相信理論上應(yīng)該干凈的狀態(tài)。
于是他reran the tie(重新跑了關(guān)聯(lián)修復(fù))。這個細節(jié)很關(guān)鍵:升級腳本本身沒問題,但數(shù)據(jù)預(yù)檢發(fā)現(xiàn)了歷史遺留的臟數(shù)據(jù),先清場再開工。
四、24分鐘的時間線拆解
作者沒給精確到秒的timeline,但從描述里能還原出關(guān)鍵節(jié)點:
T+0:凍結(jié)寫入
MAINTENANCE_WRITE_FREEZE置為true。所有變異操作返回503或排隊,用戶看到"維護中"提示。讀流量完全不受影響,public pages stayed up the whole time。
T+2min:執(zhí)行遷移腳本
這里出了一次意外:Cloudflare D1的remote execution(遠程執(zhí)行)拒絕了文件里的顯式transaction wrapper(事務(wù)包裝器)。腳本第一跑失敗。
但沒變成事故,因為safety rails(安全護欄)已經(jīng)就位。作者沒細說護欄具體是什么,但從上下文推斷:遷移腳本是冪等的、有前置條件檢查、失敗會回滾到已知狀態(tài)。D1的限制是已知的坑,他顯然提前讀過文檔。
T+5min:修復(fù)重跑
去掉顯式事務(wù)語法,改用D1隱式事務(wù)。腳本通過。這里有個值得注意的選擇:作者沒有試圖"優(yōu)雅地"繞過D1的限制,而是接受平臺約束,快速調(diào)整策略。
T+10min:數(shù)據(jù)校驗
對比基線數(shù)字。訂單總數(shù)、訂閱數(shù)、退款隊列——全部match。不是抽樣檢查,是全量核對。
T+15min:應(yīng)用層切換
新schema上線,代碼切到新模型。這里作者強調(diào)了一個容易忽略的點:the real release was the operational sequence(真正的發(fā)布是那個操作序列),不是單一的SQL文件。
序列包括:凍結(jié)→備份→遷移→校驗→切流量→解凍。每個步驟可獨立回滾,每個步驟有明確的成功標(biāo)準。
T+24min:解除凍結(jié)
MAINTENANCE_WRITE_FREEZE置為false。寫流量恢復(fù)。總窗口24分鐘。
五、把"發(fā)布"重新定義為"操作序列"
作者最核心的洞察藏在這句話里:「The release worked because I treated it like an operational change, not a schema change.」
大多數(shù)開發(fā)者把數(shù)據(jù)庫升級看成"改schema",注意力集中在DDL語句怎么寫。作者把它看成"操作變更"——人的動作、順序、檢查點、回滾路徑,和SQL語法同等重要。
這種視角轉(zhuǎn)換帶來幾個具體差異:
1. Schema change是單點,operational change是流程。前者關(guān)注"語句對不對",后者關(guān)注"如果第三步失敗怎么辦"。
2. Schema change追求自動化,operational追求可控。作者明明可以用更自動化的工具,但選擇了"一個開關(guān)"這種最樸素的手動控制。
3. Schema change的驗收是"結(jié)構(gòu)正確",operational change的驗收是"業(yè)務(wù)指標(biāo)無損"。基線數(shù)字的對比是硬性關(guān)卡,不是可選項。
這個區(qū)分對獨立開發(fā)者特別有價值。你沒有SRE團隊、沒有藍綠部署、沒有自動化回滾流水線,但你可以有嚴格的操作紀律。24分鐘的窗口不是技術(shù)奇跡,是紀律的副產(chǎn)品。
六、Cloudflare D1的隱性成本
這次升級也暴露了D1作為新數(shù)據(jù)庫的一些粗糙邊緣。顯式事務(wù)被拒絕是個信號:D1的SQL兼容性還在演進中,生產(chǎn)環(huán)境需要額外驗證。
但作者的選擇值得玩味:他沒有因為D1的限制而遷移到PostgreSQL或PlanetScale,而是接受了約束并設(shè)計 around it。這背后是成本計算——Workers生態(tài)的集成度、邊緣部署的延遲優(yōu)勢、運維負擔(dān)的降低,這些收益大于"完美SQL支持"的缺失。
對于在Workers上跑產(chǎn)品的開發(fā)者,這個案例提供了D1生產(chǎn)使用的真實邊界:
? 事務(wù)語義要測試,不能假設(shè)和SQLite完全一致
? 遷移腳本要在remote execution環(huán)境預(yù)跑,本地通過≠生產(chǎn)通過
? 備份/導(dǎo)出是原生支持的,但恢復(fù)流程要自己驗證
作者沒抱怨平臺,只是記錄事實并調(diào)整策略。這種務(wù)實態(tài)度比技術(shù)選型本身更值得學(xué)習(xí)。
七、獨立開發(fā)者的基礎(chǔ)設(shè)施啟示
讀完整篇日志,最沖擊我的是作者對"信任"的處理方式。
「do you actually trust your system?」——這是他開頭拋出的問題。大多數(shù)人的信任建立在"上次沒出事",作者的信任建立在"我能驗證每個環(huán)節(jié)"。
具體表現(xiàn)為:
信任需要具象化。MAINTENANCE_WRITE_FREEZE不是一個配置項,是硬編碼進每個變異路徑的守衛(wèi)。你能看到它、能測試它、能在staging環(huán)境模擬它失效的場景。
信任需要量化。基線數(shù)字不是"大概記得",是導(dǎo)出前鎖定的精確值。校驗不是"看起來對",是數(shù)值相等。
信任需要分層。備份是第一層,基線是第二層,冪等腳本是第三層,開關(guān)是第四層。任何一層失效,下一層接住。
這套方法論的門檻極低:一個布爾值、幾個SQL查詢、24分鐘的耐心。但執(zhí)行紀律極高:每個步驟不能跳過、每個檢查不能敷衍。
對于25-40歲的科技從業(yè)者,這個案例戳中了一個常見困境:我們知道該怎么做,但生產(chǎn)壓力讓我們跳過步驟。作者的回應(yīng)是——把流程設(shè)計得足夠簡單,簡單到?jīng)]有借口跳過。
八、如果復(fù)制這套方法,要注意什么
不是每個產(chǎn)品都能24分鐘搞定。作者的特定條件包括:
? 寫流量可短暫中斷(產(chǎn)品發(fā)布平臺,非實時交易)
? 數(shù)據(jù)量可控(D1的導(dǎo)出/導(dǎo)入在合理時間內(nèi)完成)
? 用戶預(yù)期可管理(維護窗口提前溝通)
如果你的場景是金融交易、實時游戲、IM系統(tǒng),"凍結(jié)寫入"可能不可接受。但核心原則仍然適用:把發(fā)布拆成可驗證的步驟,每個步驟有明確的回滾路徑。
具體可遷移的實踐:
1. 造一個真正的開關(guān)。不是環(huán)境變量,是運行時能toggle、能觀測、能測試的守衛(wèi)。代碼里搜一下你的"維護模式",如果分散在十個地方,就還沒做到位。
2. 定義你的基線數(shù)字。每次發(fā)布前鎖定三個關(guān)鍵指標(biāo),發(fā)布后強制對比。這個習(xí)慣養(yǎng)成本身就會暴露很多隱患。
3. 預(yù)檢生產(chǎn)數(shù)據(jù)。不要相信migration history,不要相信"應(yīng)該干凈了"。對著實際數(shù)據(jù)庫跑查詢,臟數(shù)據(jù)在升級前清掉。
4. 接受平臺約束。D1不支持顯式事務(wù)?去掉就是了,不要和平臺較勁。時間花在設(shè)計補償機制上,不要花在繞過限制上。
5. 記錄操作時間。作者沒給精確timeline,但每個階段的大致耗時是清晰的。你自己的升級要記時,下次才有優(yōu)化基準。
九、為什么這個案例值得被看見
技術(shù)媒體喜歡報道Netflix的混沌工程、Google的Borg升級,但那些距離大多數(shù)從業(yè)者太遠。一個獨立開發(fā)者在Workers+D1上的24分鐘實戰(zhàn),反而提供了更可復(fù)制的參考系。
關(guān)鍵差異在于資源約束下的決策質(zhì)量。作者沒有SRE團隊,但設(shè)計了SRE級別的發(fā)布流程;沒有企業(yè)級數(shù)據(jù)庫,但實現(xiàn)了企業(yè)級的數(shù)據(jù)校驗;沒有自動化平臺,但做到了人工操作的零失誤。
這指向一個被低估的能力:在有限資源下,把"足夠好"的定義從"能跑"提升到"能驗證、能回滾、能重復(fù)"。
Shipstry本身是個產(chǎn)品發(fā)布平臺,作者吃自己的狗糧——用這個平臺發(fā)布產(chǎn)品,同時升級支撐它的基礎(chǔ)設(shè)施。這種"自我宿主"(self-hosting)的壓力,逼出了格外扎實的工程習(xí)慣。
最后,作者的態(tài)度值得注意:沒有慶祝勝利,只是記錄過程。Feature work is fun, production upgrades are not——但后者決定了你能fun多久。
如果你的產(chǎn)品也在Workers+D1上跑,或者正在考慮這個棧,這篇日志是比官方文檔更真實的生產(chǎn)參考。如果你的技術(shù)棧完全不同,"操作序列"的思維方式仍然適用。
下一次你面對"能不能不停機升級"的問題時,不妨先問:我能不能接受短暫的寫凍結(jié)?我的開關(guān)真的只有一個嗎?我的基線數(shù)字是什么?
24分鐘不是目標(biāo),可驗證的24分鐘才是。
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺“網(wǎng)易號”用戶上傳并發(fā)布,本平臺僅提供信息存儲服務(wù)。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.