MySQL的事務理解


在學習事務這一概念前,我們需要需要構思一個場景

場景構思

假設該場景發生於一個銀行轉賬背景下,月中,又到了發工資的日子。學校打算給A老師發放一個月的工資。(此處,我們假設轉賬都是由人工操作的),整個過程本應該如下:

  1. 學校財務核對A老師工資單

  2. 確認學校賬上還有這么多錢

  3. 向銀行提出轉賬申請,銀行扣除學校財務卡上的指定金額

  4. 銀行向A老師工資卡打入指定金額

  5. 銀行匯報雙方交易完成

    但是,當這個過程執行完第3步的時候,突然大斷電!整個電力系統進入癱瘓。待電力系統回復之后,銀行並不會繼續執行4、5步甚至連1,2,3步的操作記錄都丟失了。此時出現了如下的問題:

  • 學校認為,工資已經發出

  • A老師認為,學校還沒有發工資

  • 銀行認為,從來就沒有發生過轉賬的事情

    其實整個過程可以用一個詞來描述:數據庫中的數據產生了“不一致性”

一致性

上述背景中設計到了一個概念,叫做不一致性,這是和一致性相對的概念。那么,什么是一致性呢?

一致性的意思是,在一系列數據庫行為的前后兩個時間點上,數據是正確對應的。放在上面的例子來看,就是操作前后,兩個賬戶的總金額是一樣的,這樣就保證不會憑空的丟失掉不該丟失掉的金錢。

原子操作

為了保證數據的一致性,我們可以將一系列會破壞一致性的操作聲明為原子操作。被聲明為原子操作的那些操作要么一起完成,要么一起失敗,這樣我們就避免了類似斷電這類情況導致的數據不一致性。

事務

那么如何才能實現MySQL中的原子操作呢?

MySQL以及大多數關系型數據庫都提供了一個叫做事務的技術。我們可以聲明一個事務的開始,在確認提交或者指明放棄前的所有操作,都先在一個叫做事務日志的臨時環境中進行操作。待操作完成,確保了數據一致性之后,那么我們可以手動確認提交,也可以選擇放棄以上操作。

注意: 一旦選擇了提交,那么便不能再利用放棄操作來撤銷更改了。

案例分析

​ 我們首先創建我們本次案例需要使用的表,並給一些測試數據

 
mysql> SHOW DATABASES;#查看有多少數據庫
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql             |
| performance_schema |
| python             |
| sys               |
+--------------------+
5 rows in set (0.00 sec)
mysql> USE `python`;#切換數據庫
Database changed
mysql> SELECT DATABASE();#查看當前使用的數據庫
+------------+
| DATABASE() |
+------------+
| python     |
+------------+
1 row in set (0.00 sec)
mysql> CREATE TABLE `account` (
    ->   `id` int PRIMARY KEY AUTO_INCREMENT,
    ->   `name` VARCHAR(20) NOT NULL,
    ->   `balance` DECIMAL(12,2)
    -> );
Query OK, 0 rows affected (0.04 sec)
mysql> INSERT INTO `account`(`name`, `balance`)
    ->   VALUES ('TanzhouEDU', 10000000),('Tuple', 10000)
    -> ;
Query OK, 2 rows affected (0.70 sec)
Records: 2 Duplicates: 0 Warnings: 0

斷電案例(非原子操作)

首先,查看現有的賬戶情況

 
mysql> SELECT * FROM `account`;
+----+------------+-------------+
| id | user       | balance     |
+----+------------+-------------+
|  1 | TanzhouEDU | 10000000.00 |
|  2 | Tuple      |    10000.00 |
+----+------------+-------------+
2 rows in set (0.00 sec)

現在,學校財務開始向A老師發工資

mysql> UPDATE `account`
    ->   SET `balance` = `balance` - 10000
    ->   WHERE `user` = 'TanzhouEDU'
    -> ;
Query OK, 1 row affected (0.05 sec)
Rows matched: 1  Changed: 1 Warnings: 0
mysql> SELECT * FROM `account`;
+----+------------+------------+
| id | user       | balance    |
+----+------------+------------+
|  1 | TanzhouEDU | 9990000.00 |
|  2 | Tuple      |   10000.00 |
+----+------------+------------+
2 rows in set (0.00 sec)

此時,發生了斷電,再登錄數據庫以后,錢已經消失了。

事務案例(原子操作)

MySQL中,事務操作包括4個:

  • START TRANSACTION:開始一個新的事務

  • COMMIT:提交當前事務,做出永久改變

  • ROLLBACK:回滾當前事務,放棄修改

  • SET autocommit = {0 | 1}:對當前會話禁用(0)或啟用(1)自動提交模式

    我們利用事務機制來重復上述斷電的情況。看看是否能夠有所幫助。

mysql> SELECT * FROM `account`;
+----+------------+------------+
| id | user       | balance    |
+----+------------+------------+
|  1 | TanzhouEDU | 9990000.00 |
|  2 | Tuple      |   10000.00 |
+----+------------+------------+
2 rows in set (0.00 sec)
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> UPDATE `account`
    ->   SET `balance` = `balance`-1000
    ->   WHERE `user` = 'TanzhouEDU'
    -> ;
Query OK, 1 row affected (0.01 sec)
mysql> SELECT * FROM `account`;
+----+------------+------------+
| id | user       | balance    |
+----+------------+------------+
|  1 | TanzhouEDU | 9989000.00 |
|  2 | Tuple      |   10000.00 |
+----+------------+------------+
2 rows in set (0.00 sec)
mysql> exit
Bye
tuple@MyVM:~$ mysql -utuple -p123456
mysql> USE `python`;
Database changed
mysql> SELECT * FROM `account`;
+----+------------+------------+
| id | user       | balance    |
+----+------------+------------+
|  1 | TanzhouEDU | 9990000.00 |
|  2 | Tuple      |   10000.00 |
+----+------------+------------+
2 rows in set (0.00 sec)

我們可以看到,數據在斷電后,自動恢復到了數據修改前的樣子,它相當於如下一個操作過程。

 
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> UPDATE `account`
    ->   SET `balance`=`balance`-1000
    ->   WHERE `user`='TanzhouEDU'
    -> ;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1 Warnings: 0
mysql> SELECT * FROM `account`;
+----+------------+------------+
| id | user       | balance    |
+----+------------+------------+
|  1 | TanzhouEDU | 9989000.00 |
|  2 | Tuple      |   10000.00 |
+----+------------+------------+
2 rows in set (0.00 sec)
mysql> ROLLBACK;
Query OK, 0 rows affected (0.02 sec)
mysql> SELECT * FROM `account`;
+----+------------+------------+
| id | user       | balance    |
+----+------------+------------+
|  1 | TanzhouEDU | 9990000.00 |
|  2 | Tuple      |   10000.00 |
+----+------------+------------+
2 rows in set (0.00 sec)

接着,我們來做一次正確的操作,真正的給A老師發一次工資。

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> UPDATE `account`
    ->   SET `balance` = `balance`-10000
    ->   WHERE `user` = 'TanzhouEDU'
    -> ;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1 Warnings: 0
mysql> UPDATE `account`
    ->   SET `balance` = `balance`+10000
    ->   WHERE `user`= 'Tuple'
    -> ;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1 Warnings: 0
mysql> SELECT * FROM `account`;
+----+------------+------------+
| id | user       | balance    |
+----+------------+------------+
|  1 | TanzhouEDU | 9980000.00 |
|  2 | Tuple      |   20000.00 |
+----+------------+------------+
2 rows in set (0.00 sec)
mysql> COMMIT;
Query OK, 0 rows affected (0.03 sec)
mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM `account`;
+----+------------+------------+
| id | user       | balance    |
+----+------------+------------+
|  1 | TanzhouEDU | 9980000.00 |
|  2 | Tuple      |   20000.00 |
+----+------------+------------+
2 rows in set (0.00 sec)

通過上面的例子可以看出,一旦commit了,那么rollback還是斷電都不能反悔了。

 

 

轉自:MySQL---事務知識,你搞明白沒有?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM