準備知識:MySQL事務
1 基本操作
sql.Tx
tx會從串連池中取一個閒置串連,直至調用commit或者rollback才會釋放
tx, err := db.Begin() // 建立tx對象tx.Query(command1)tx.Exec(command2)tx.Commit()
2 並發
事務只有一個串連,事務內的操作是串列的
db, _ := sql.Open(...)rows, _ := db.Query("SELECT id FROM tt_users")for rows.Next(){ var ( user_id int openid string ) rows.Scan(&user_id) db.QueryRow("SELECT openid FROM tt_users_third WHERE user_id = ?", user_id).Scan(&openid)}
這段代碼在並發執行的時候Query和QueryRow是獨立的兩個串連。非tx對象的操作,每執行一個操作之前會從串連池取空閑串連。
rows, _ := tx.Query("SELECT id FROM tt_users")for rows.Next(){ var ( user_id int openid string ) rows.Scan(&user_id) tx.QueryRow("SELECT openid FROM tt_users_third WHERE user_id = ?", user_id).Scan(&openid)}
tx執行完Query方法之後,串連轉移到rows上,在Next方法中,tx.QueryRow將嘗試擷取該串連進行操作。因為還沒有調用rows.Close(),因此串連還處於busy狀態,tx無法進行QueryRow操作。這時候使用JOIN語句可以規避這個問題。
3 實踐
交易處理過程中出現了異常為保證資料完整一致性和及時釋放串連,需要rollback。
tx, err := db.Begin()if err != nil { log.Println(err)}var ( user_id uint openid string)err = tx.QueryRow("SELECT user_id FROM tt_users WHERE phone = ?", phone).Scan(&user_id)if err != nil { err = tx.Rollback() if err != nil { log.Println("tx.Rollback() Error:" + err.Error()) return }}err = tx.QueryRow("SELECT openid FROM tt_user_third WHERE user_id = ?", user_id).Scan(&openid)if err != nil { err = tx.Rollback() if err != nil { log.Println("tx.Rollback() Error:" + err.Error()) return }}err := tx.Commit()if err != nil { err = tx.Rollback() if err != nil { log.Println("tx.Rollback() Error:" + err.Error()) return }}
這篇文章相當於下面參考文章的一個簡單總結,感謝作者“人世間”的分享
參考文章:https://www.jianshu.com/p/bc8120bec94e