insert or update使用事務和不使用事務的核心差異
這樣一個語句,在go多協程情況下,採用事務和不採用事務出現的問題:
INSERT INTO web3_data (space_id, user_address, attr_name, attr_value)
VALUES (198, '0x56c9F75D92948a7BdeB8677b185111EeC3Fddc63', 'chain_interaction_count', 1)
ON DUPLICATE KEY UPDATE attr_value = attr_value + 1
使用事務(Transaction)情況下:
會報死鎖:Deadlock found when trying to get lock; try restarting transaction
不使用事務(Transaction)情況下:
不會報死鎖,採用mysql自帶的行鎖,最多鎖住幾百毫秒到1秒,但是不會死鎖。
=================================================================================
下面詳細說明下情況。
當在數據庫操作中考慮併發性和數據一致性時,使用事務和不使用事務之間存在一些核心差異。其中,一個重要的差異是在高併發環境下可能導致死鎖的發生。
使用事務和不使用事務的核心差異
使用事務(Transaction)
事務是將一系列數據庫操作視爲一個單一工作單元的機制。使用事務可以確保數據的一致性和隔離性。在併發環境中,多個事務同時操作相同的數據可能導致死鎖。
事務的特點:
- 原子性(Atomicity):事務中的所有操作要麼全部成功提交,要麼全部回滾。事務中的任何操作失敗都會導致整個事務回滾,保持數據的一致性。
- 一致性(Consistency):事務開始之前和結束之後,數據庫的狀態應該保持一致。如果事務在執行過程中發生錯誤,數據庫將回滾到事務開始之前的狀態。
- 隔離性(Isolation):事務的執行應該相互隔離,互不干擾。每個事務應該感知不到其他事務的存在,以避免數據的衝突和不一致。
- 持久性(Durability):一旦事務提交成功,其所做的更改應該永久保存在數據庫中,即使發生系統崩潰或重啓。
以下是使用事務執行 SQL 語句的示例代碼(使用 Go 語言和 MySQL 數據庫):
import (
"database/sql"
"log"
)
func incrementInteractionCountWithTransaction(db *sql.DB) error {
tx, err := db.Begin()
if err != nil {
return err
}
// 在事務中執行插入或更新操作
_, err = tx.Exec(`
INSERT INTO web3_data (space_id, user_address, attr_name, attr_value)
VALUES (198, '0x56c9F75D92948a7BdeB8677b185111EeC3Fddc63', 'chain_interaction_count', 1)
ON DUPLICATE KEY UPDATE attr_value = attr_value + 1
`)
if err != nil {
tx.Rollback()
return err
}
err = tx.Commit()
if err != nil {
return err
}
return nil
}
func main() {
// 初始化數據庫連接
db, err := sql.Open("mysql", "username:password@tcp(hostname:port)/database")
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = incrementInteractionCountWithTransaction(db)
if err != nil {
log.Fatal(err)
}
}
不使用事務(Non-transaction)
在不使用事務的情況下,每個數據庫操作都是獨立的,不受其他操作的影響。這意味着每個操作都會立即提交,不會等待其他操作的完成。
以下是不使用事務執行 SQL 語句的示例代碼(使用 Go 語言和 MySQL 數據庫):
import (
"database/sql"
"log"
)
func incrementInteractionCountWithoutTransaction(db *sql.DB) error {
// 直接執行插入或更新操作,不使用事務
_, err := db.Exec(`
INSERT INTO web3_data (space_id, user_address, attr_name, attr_value)
VALUES (198, '0x56c9F75D92948a7BdeB8677b185111EeC3Fddc63', 'chain_interaction_count', 1)
ON DUPLICATE KEY UPDATE attr_value = attr_value + 1
`)
if err != nil {
return err
}
return nil
}
func main() {
// 初始化數據庫連接
db, err := sql.Open("mysql", "username:password@tcp(hostname:port)/database")
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = incrementInteractionCountWithoutTransaction(db)
if err != nil {
log.Fatal(err)
}
}
在使用事務的代碼示例中,通過調用 db.Begin()
開始了一個事務。然後,在事務中執行插入或更新操作,並在出現錯誤時回滾事務(tx.Rollback()
)。最後,通過調用 tx.Commit()
提交事務。這樣可以確保在插入或更新操作期間出現錯誤時,事務會回滾到初始狀態,保持數據的一致性。
而在不使用事務的代碼示例中,直接執行了插入或更新操作,沒有顯式地使用事務。每個操作都是獨立的,立即提交,不會等待其他操作的完成。
死鎖問題
死鎖是在併發環境中可能發生的一種情況,其中多個事務相互等待對方釋放資源,導致它們無法繼續執行。在使用事務的情況下,多個事務同時操作相同的數據,由於事務之間的競爭,可能會導致死鎖的發生。
以下是一個可能導致死鎖的示例:
事務 A:
BEGIN;
SELECT * FROM table1 WHERE id = 1 FOR UPDATE;
UPDATE table2 SET value = value + 1 WHERE id = 2;
COMMIT;
事務 B:
BEGIN;
SELECT * FROM table2 WHERE id = 2 FOR UPDATE;
UPDATE table1 SET value = value + 1 WHERE id = 1;
COMMIT;
在上述示例中,事務 A 首先獲取了 table1
的行級鎖,然後嘗試獲取 table2
的行級鎖。同時,事務 B 首先獲取了 table2
的行級鎖,然後嘗試獲取 table1
的行級鎖。由於兩個事務都在等待對方釋放資源,它們陷入了死鎖狀態。
在不使用事務的情況下,併發操作是獨立的,每個操作都是立即提交的,因此不會發生死鎖。
結論
使用事務和不使用事務在數據庫操作中有着重要的差異。事務的使用可以確保數據的一致性和隔離性,但在高併發環境下,多個事務同時操作相同的數據可能導致死鎖的發生。不使用事務的情況下,每個操作是獨立的,不受其他操作的影響,但可能導致數據的不一致。
選擇使用事務還是不使用事務取決於應用程序對數據一致性和併發性的要求。如果對數據的完整性和一致性非常重要,建議使用事務來確保操作的原子性和隔離性。然而,要注意在高併發環境下使用事務可能導致死鎖的問題,需要進行適當的併發控制和資源管理。
希望這篇博客能夠幫助你理解使用事務和不使用事務的核心差異,並突出了使用事務可能導致死鎖的問題。如果你有任何其他