這個PHP無解深坑,你能解出來嗎?(據說能解出來的都很秀)

歡迎你們前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~php

本文由 horstxu發表於 雲+社區專欄

1. 問題背景

PHP Laravel框架中的db migration是比較經常使用的一個功能了。在每一個版本迭代中,除了代碼會變更以外,通常數據庫的字段或者數據庫表也會有些變更。所以在新版本上線時,除了發佈新版代碼,不可避免地要把數據庫的變更也執行了。在沒有db migration功能以前,咱們的作法是把要變更庫表的SQL語句寫好(CREATE TABLEALTER TABLE等)存在一個sql文件中,而後在上線時鏈接數據庫,將sql語句執行一遍。html

這麼作比較大的一個缺點是沒有數據庫的版本管理,萬一上線失敗,要回滾版本,還要把sql文件裏的內容再寫個反向的SQL(DROP TABLEDROP COLUMN等)。這種方式也比較原始,在web開發中,咱們老是但願儘可能避免開發直接用原始的sql來操做數據庫,出錯風險很高,而且頗有可能出現不可逆的錯誤,每次操做都要提心吊膽。mysql

因而乎,PHP Laravel框架提供了db migration的功能,用代碼來管理數據庫。參考連接laravel

2. 問題描述

在一個新的版本中,我將本身的數據庫變動用以下方式記錄git

php artisan make:migration db_migration_for_new_version

這會在項目的database/migrations目錄下建立一個新的PHP文件,本身填入要變動的數據庫內容github

public function up {
    Schema::create('a_new_table', function(Blueprint $table) {
        $table->bigIncrements('id');
    });

    Schema::create('another_new_table', function(Blueprint $table) {
        $table->bigIncrements('id');
        $table->string('user', 64)->default(0)->comment('用戶名');

        // 這裏模擬出現錯誤的情形
        throw new \Exception("出現錯誤");
    });
}

在上面這個例子中,個人本意是想要建立兩個表格。然而在第一個表格建立完了之後,第二個表格出現錯誤致使建立失敗了。按照正常流程,我在上線時應該執行以下指令建立表格web

php artisan migrate

因爲第二個表格建立失敗,這時候上面的指令必然會報錯。然而報錯以後你應該怎麼作呢?首先固然是把代碼裏出現錯誤的地方修正,而後應該怎麼搞?此時數據庫裏面第一個表已經建好了,第二個表還沒建。這時候你若是再執行php artisan migrate會報錯:你第一張表格已經建立,不可重複建立表格。你可能會感受,我須要回滾一次,因而你可能會執行回滾操做php artisan migrate:rollback --step=1。這裏須要強調,此時千萬別回滾!!! sql

由於剛纔第一次執行migration出錯,致使數據庫並無生成一個新的版本號。這時候若是回滾,那你回滾的是上個版本發佈的時候作執行的數據庫操做,而不是你剛剛執行的這個版本的數據庫操做,這極可能是災難性的,會致使你數據丟失。目前數據庫最新版本是什麼,能夠參考數據庫中migrations表的batch字段(這個表是laravel migration功能自動生成和管理的,並不是業務表)。數據庫

總結一下這一無解深坑: db migration進行到一半時出錯,此時只能手動操做數據庫把已經執行的操做回滾掉,沒法再經過artisan指令進行回滾框架

3. 爲何無解?

其實GitHub和StackOverflow上有不少人已經碰到了這個問題,可是答案都很悲觀。

全部人的第一反應都是:能夠開啓事務操做麼?將一次migration的全部操做視爲一個總體,要麼都成功,要麼都失敗能夠麼?很遺憾,不支持事務操做。在mysql裏面,只有進行update、insert、delete這些常規操做時才能夠有事務,而咱們migration中執行的都是DDL(Data Definition Language)操做。這種建表(CREATE TABLE)、修改表結構(ALTER TABLE)的操做是沒法回滾的,即便開啓了事務也沒法回滾(參考連接)。把DDL操做放在一個事務(Transaction)中,會致使事務自動的提交(參考連接),這每每不是咱們代碼邏輯所指望的結果。

4. 那該怎麼辦?

若是你已經碰到了這種問題,那沒辦法只得手動去一條一條看數據庫發生了什麼變化,而後本身執行反向操做。

目前只能想到一些預防此問題出現的辦法。根據GitHub上的開發者建議,最好每個CREATE TABLEALTER TABLE操做都是一個單獨的migration。即每次migration只建一張表,或只改一個表結構,只作一個操做( 參考連接)……

還有一種辦法是,把本身的建表、改表操做都放在一個try catch結構中,一旦出現錯誤,直接調用migration文件中的down函數,把所作的操做回滾掉。不過這個須要注意up和down的兼容性。例如up中有ADD COLUMN操做,而down中有DROP COLUMN操做。在ADD COLLUMN操做執行以前就出錯,直接取執行down函數中的DROP COLUMN,也會有可能報COLUMN不存在的錯誤。

總之,這個問題並無十分完美的解決方案,堪稱無解深坑,尤爲要注意rollback操做不要亂作 ,不要爲了彌補一個坑,給本身挖了更大的一個坑。

問答
PHP功能濫用?
相關閱讀
一圖弄懂ASCII、GB23十二、GBK、GB18030編碼
其實你不必定懂csv文件格式
【每日課程推薦】機器學習實戰!快速入門在線廣告業務及CTR相應知識

此文已由做者受權騰訊雲+社區發佈,更多原文請點擊

搜索關注公衆號「雲加社區」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!

海量技術實踐經驗,盡在雲加社區

相關文章
相關標籤/搜索