1. 程式人生 > >mysql相關事務的介紹以及應用

mysql相關事務的介紹以及應用

 一、mysql相關知識準備

   1.1、mysql事務 

      事務是一組不可分割的mysql語句組,這些語句組要麼全部執行成功,要麼全部執行失敗。               

      事務的四大特性(ACID)

     1.原子性(atomicity):一個事務必須視為一個不可分割的最小工作單元,整個事務中的所有操作要麼全部提交成功,

   要麼全部失敗回滾,對於一個事務來說,不可能只執行其中的一部分操作,這就是事務的原子性。

     2.一致性(consistency):資料庫總數從一個一致性的狀態轉換到另一個一致性的狀態。

     3.隔離性(isolation):一個事務所做的修改在最終提交以前,對其他事務是不可見的。

     4.永續性(durability):一旦事務提交,則其所做的修改就會永久儲存到資料庫中。此時即使系統崩潰,修改的資料也不會丟失。

 1.2、mysql事務的相關操作

      1、查詢當前會話的事務級別

          select @@tx_isolation;

      

         repeatable read(MySQL預設隔離級別)

     2、查詢全域性會話的事務級別

        select @@global.tx_isolation

     

          repeatable read(MySQL預設隔離級別)

    3、 設定事務的mysql語法

   SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}

      4、設定當前會話的事務隔離級別

            SET SESSION  TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

    

       當前會話結束後事務的隔離級別又會恢復原樣

     5、設定全域性的事務隔離級別

            SET GLOBALTRANSACTION ISOLATION LEVEL READ UNCOMMITTED

     

          全域性設定後會保持不變,除非進行更改

    6、mysql事務的提交方式

              1、檢視預設的事務提交方式       

#查詢事務的提交方式(當前會話的 0表示關閉自動提交,1表示開啟自動提交(預設情況下))
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
|            1 |
+--------------+

          2、設定事務的提交方式 關閉自動提交

                        SET autocommit=0;   

    二、mysql中事務的隔離級別

         2.0、演示準備

            建立表來演示不同隔離級別下的髒讀,不可重複讀,幻讀等現象發生準備

             建表sql

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` varchar(20) NOT NULL,
  `name` varchar(20) DEFAULT NULL,
  `weath` float(20,0) DEFAULT NULL COMMENT '擁有的財富',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'xieqx', '1000');
INSERT INTO `user` VALUES ('2', 'xush', '1000');
INSERT INTO `user` VALUES ('3', 'lidn', '1000');
INSERT INTO `user` VALUES ('5', 'Bob', '1000');

                 建立多個事務(至少兩個進行處理)並設定對應的不同隔離級別來進行演示

                 關閉事務的自動提交

 

          2.1、read uncommitted
 

  1.  一個事務中可以看到另一個是事務中未提交的資料(髒讀)
#設定事務的隔離級別 為未提交讀(會發生髒讀,不可重複讀,和幻讀現象)
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+

 2、演示髒讀場景。公司發工資 開啟兩個事務 事務A表示公司 事務B表示個人金額賬戶 

  注意場景下 兩個事務的開啟時間需要一致(或者另一個事務開啟要在一個事務在處理 增刪改的操作之前開啟),因為事務的處理本身就是在並行操作的場景演示(兩者同時操作一條記錄(競爭資源)) 否則無法實現 切記切記 

 事務A操作:

#開啟事務
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

#查詢當前個人賬號金額 10
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name  | weath |
+----+-------+-------+
| 1  | xieqx |    10 |
+----+-------+-------+
1 row in set (0.00 sec)

//更新個人賬戶金額 新增1000
mysql> update user set weath = weath + 9000 where id = "1";
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

#查詢金額更新成功
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name  | weath |
+----+-------+-------+
| 1  | xieqx |  9010 |
+----+-------+-------+
##注意此時當前事務還沒有結束

  事務B操作:

#個人查詢賬號操作
#開啟事務
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

#查詢訂單(公司為更新操作之前)
mysql> select * from user where id ="1";
+----+-------+-------+
| id | name  | weath |
+----+-------+-------+
| 1  | xieqx |    10 |
+----+-------+-------+
1 row in set (0.00 sec)

#再次查詢 查詢到了另一個事務中未提交的資料 造成了髒讀現象
mysql> select * from user where id ="1";
+----+-------+-------+
| id | name  | weath |
+----+-------+-------+
| 1  | xieqx |  9010 |
+----+-------+-------+
1 row in set (0.00 sec)

          2.2、read committed
 
          1、一個事務A中讀取另一個事務B中已經提交的資料。但是另一個事務B中突然回滾,事務A再次讀取和原來不一樣的資料,所以可能造成多次讀取的資料結果不一致(不可重複讀,幻讀)。

#設定mysql的事務隔離級別 為已經提交讀
mysql> SET SESSION  TRANSACTION ISOLATION LEVEL READ COMMITTED;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+

  2、演示不可重複讀場景 公司發工資,但是客戶在同一個事務中獲取到兩個不同的金額

 事務A操作

#開始事務

mysql> select * from user where id ="1";
+----+-------+-------+
| id | name  | weath |
+----+-------+-------+
| 1  | xieqx |    10 |
+----+-------+-------+
1 row in set (0.00 sec)

#更新 金額
mysql> update user set weath = weath + 9000 where id = "1";
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

#執行提交
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

事務B操作:

#個人操作

#在個人事務中
#先進行查詢
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name  | weath |
+----+-------+-------+
| 1  | xieqx |    10 |
+----+-------+-------+
1 row in set (0.00 sec)

#公司事務中進行了更新,但是沒有提交 查詢的結果保持不變 說明在該隔離級別下 避免了髒讀現象的發生
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name  | weath |
+----+-------+-------+
| 1  | xieqx |    10 |
+----+-------+-------+
1 row in set (0.00 sec)

#公司的事務已經提交 ,但是還是在個人事務中,查詢出來兩種截然不同的金額結果 不可重複讀的現象發生了
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name  | weath |
+----+-------+-------+
| 1  | xieqx |  9010 |
+----+-------+-------+
1 row in set (0.00 sec)

           2.3、repeatable read(MySQL預設隔離級別)

     1、 可以重複讀取,但有幻讀。讀寫觀點:讀取的資料行不可寫,但是可以往表中新增資料。在MySQL中,其他事務新增的資料,看不到,不會產生幻讀。採用多版本併發控制(MVCC)機制解決幻讀問題。


#可重複讀(Repeated Read):可重複讀。在同一個事務內的查詢都是事務開始時刻一致的,
#InnoDB預設級別。在#SQL標準中,該隔離級別消除了不可重複讀,但是還存在幻象讀
mysql> SET SESSION  TRANSACTION ISOLATION LEVEL  REPEATABLE READ;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set, 1 warning (0.00 sec)

   2、幻讀場景的演示

   事務A操作:

#開啟事務
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
#查詢目前的金額
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name  | weath |
+----+-------+-------+
| 1  | xieqx | 20010 |
+----+-------+-------+

#更新金額
mysql> update user set weath=weath+10000 where id = "1";
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
#再次查詢
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name  | weath |
+----+-------+-------+
| 1  | xieqx | 30010 |
+----+-------+-------+
#提交
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

事務B操作:

#開啟事務
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

#A事務 更新金額但是沒有進行事務提交 (查詢出來原來的結果)
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name  | weath |
+----+-------+-------+
| 1  | xieqx | 20010 |
+----+-------+-------+
1 row in set (0.00 sec)

#A事務提交後,還是在一個B事務中查詢金額 和上面的查詢保持不變 避免了不可重複讀操作
mysql> select * from user where id = "1";
+----+-------+-------+
| id | name  | weath |
+----+-------+-------+
| 1  | xieqx | 20010 |
+----+-------+-------+

  幻讀發生

事務A

#開啟事務
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

#查詢使用者 還有6條記錄 最高id 為8
mysql> select * from user;
+----+-------+-------+
| id | name  | weath |
+----+-------+-------+
| 1  | xieqx | 30010 |
| 2  | xush  |    10 |
| 3  | lidn  |    10 |
| 5  | Bob   |    10 |
| 7  | guoqi |   200 |
| 8  | aaa   |   100 |
+----+-------+-------+
6 rows in set (0.00 sec)

#在該事務中新增一條記錄id為9
mysql> insert into user value("9","bbb",102);
Query OK, 1 row affected (0.00 sec)

#提交
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

       事務B

#事務B操作
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

#在事務B 新增一條記錄前查詢記錄數
mysql> select * from user;
+----+-------+-------+
| id | name  | weath |
+----+-------+-------+
| 1  | xieqx | 30010 |
| 2  | xush  |    10 |
| 3  | lidn  |    10 |
| 5  | Bob   |    10 |
| 7  | guoqi |   200 |
| 8  | aaa   |   100 |
+----+-------+-------+
6 rows in set (0.00 sec)

#事務A新增一條記錄並提交後 查詢還是沒有變化
mysql> select * from user;
+----+-------+-------+
| id | name  | weath |
+----+-------+-------+
| 1  | xieqx | 30010 |
| 2  | xush  |    10 |
| 3  | lidn  |    10 |
| 5  | Bob   |    10 |
| 7  | guoqi |   200 |
| 8  | aaa   |   100 |
+----+-------+-------+
6 rows in set (0.00 sec)

#重新再插入一條資料 發現這條資料id為9的資料已經插入了,命名沒有查到id為9的記錄,但是插入的時候
#出現記錄已經存在 出現了幻讀情況的發生
mysql> insert into user value("9","bbb","100");
ERROR 1062 (23000): Duplicate entry '8' for key 'PRIMARY'

#或者 更新資料id為9的資料 更新可以成功再次查詢會發現多了一條資料 (好像出現了幻覺)

mysql> update user set weath = 520 where id = 9;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

#更新後(自己查詢不到的id)結果發現查詢出來了這條記錄
mysql> select * from user;
+----+--------+-------+
| id | name   | weath |
+----+--------+-------+
| 1  | xieqx  | 30010 |
| 2  | xush   |    10 |
| 3  | lidn   |    10 |
| 5  | Bob    |    10 |
| 7  | guoqi  |   200 |
| 8  | aaa    |   100 |
| 9  | bbb    |   102 |
+----+--------+-------+
7 rows in set (0.00 sec)

 2.4、serializable 

      1、可讀,不可寫。像java中的鎖,寫資料必須等待另一個事務結束。   

#設定事務的隔離級別為 串形化
mysql> SET SESSION  TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE   |
+----------------+
1 row in set, 1 warning (0.00 sec)

      2、所謂串形化,它通過強制事務排序,使之不可能相互衝突,從而解決幻讀問題。通俗地講就是,假如兩個事務都操作到同一資料行,對於讀取操作相關的操作,所有的事務都可以獲取其中的共享鎖,但是針對其中的增刪改的操作只有一個事務會獲取其中的排他鎖,只有該事務提交後,才會釋放其中的鎖。避免了不可重複讀的操作。

     

 

              手打不易,如果幫到了你什麼,希望您點個贊

                       如果有欠缺,歡迎你在下方留言。

                         你的鼓勵是我堅持寫的動力。