輕鬆獲知數據庫事務

標題索引
sql


  • 事務做用數據庫

  • 事務流程服務器

  • 隔離級別ide

  • 實例驗證atom


事務做用
spa

    事務是確保數據庫系統數據的完整性的功能,如如今互聯網行業支付業務,無論服務器出於什麼緣由異常中斷,客戶要麼支付成功要麼支付不成功,支付成功數據庫金額即會發生變化,支付不成功客戶的金額就不發生變化,確保了交易業務的穩定性。支持事物的引擎必須知足ACID,知足ACID後才能知足事物,另外事物的回滾或恢復主要靠事物日誌來完成,ACID含義分別以下:操作系統

    A:atomicity(原子性):整個事物中全部的操做爲命令執行最小單元,所有執行、執行一半失敗回滾或失敗回滾;
線程

    C:consistency(一致性):數據庫從一個狀態轉化爲另一個狀態,狀態在轉化前和轉換後一致;rest

    I:isolation(隔離性):一個事物所作出的操做在提交以前,是不能被其餘所見,所以隔離就出現多種隔離級別,具體包括read-uncommitted讀爲提交、read-committed讀提交、repeatable-read可重複讀和serializable串行化;
日誌

    D:durability(持久性):一旦事物提交,所作的會永久性保存數據庫中。

事務流程

    事務的工做流程具體可見下圖

    事物1.png

圖1-1 事物工做流程

    由上圖可知,當數據庫經過start transaction啓動一個事物,啓動事物後對數據庫進行一系列的操做,最後提交事物,提交事物又有兩種,第一種爲commit提交,第二種rollback回滾,一旦提交事物數據庫即處於新的狀態保持持久性。另外在防止數據庫在事物提交後數據從內存寫入磁盤時,操做系統異常掉電致使沒法保存,而啓用日誌功能,只要啓用事物日誌功能,事物先在磁盤連續空間寫寫日誌,而後經過內存同步到磁盤,確保萬一內存同步磁盤時機器異常掉電,經過事物日誌進行恢復數據庫數據。

隔離級別

    隔離級別(INNODB默認隔離級別爲repeatable read):

    READ UNCOMMITTED(讀未提交):此種隔離級別帶來問題有髒讀和不可重複讀。

    READ COMMITTED(讀提交):此種隔離級別解決了髒讀,但仍然有不可重複度。

    REPEATABLE READ(可重讀):此種隔離級別解決了髒讀和不可重複讀,帶來問題幻讀。

    SERIALIZABLE(可串行化):此種隔離級別解決了髒讀、不可重複度和幻讀,但帶來的問題是加鎖讀。

    問題解釋

    髒讀:當用戶A修改數據但未提交,此時B用戶讀A修改後的數據,可是A用戶將數據進行rollback回滾,所以B用戶看到的是錯誤的數據;

    不可重複讀:如用戶A啓動一個事務設置某一值設爲ON,經查詢已經爲ON狀態,但B用戶修改數值爲OFF並提交,此時用戶A再次查詢時發現值又爲OFF,或者數據庫中又多了一條語句,表現爲在同一事務中每次查詢數據庫老是不一致;

    幻讀:當用戶A用戶在同一事務中看到某一值爲ON,用戶B已經將值修改成OFF,而且已經提交,用戶B看到的值爲OFF,但用戶A在此事務中一直看到的爲ON,底層數據確實被修改成OFF,所以就體現了幻讀,除非提交後再次生成一個事務查看值才爲OFF;

    加鎖讀:讀數據時加鎖,此時別人沒法再讀。

實例驗證

    根據理論概述,進行驗證事物的工做流程和隔離級別,確保透徹瞭解事物的原理,具體操做以下

MariaDB [(none)]> show processlist;                                      #查看數據庫的進程列表,顯示有兩終端鏈接
+----+------+-----------+------+---------+------+-------+------------------+----------+
| Id | User | Host      | db   | Command | Time | State | Info             | Progress |
+----+------+-----------+------+---------+------+-------+------------------+----------+
|  2 | root | localhost | NULL | Sleep   |   23 |       | NULL             |    0.000 |
| 10 | root | localhost | NULL | Query   |    0 | NULL  | show processlist |    0.000 |
+----+------+-----------+------+---------+------+-------+------------------+----------+
2 rows in set (0.00 sec)
MariaDB [(none)]> show global variables like 'tx_isolation';           #驗證事物的隔離級別
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.00 sec)

    驗證隔離級別 READ UNCOMMITTED(存在髒讀、不可重複讀)

    第一步:建立表並插入數據

MariaDB [test]> create table employee(id int,name varchar(20),age char(3));                             #建立表
Query OK, 0 rows affected (0.00 sec)
MariaDB [test]> insert into employee  values(1,'tangseng',38),(2,'sunwukong',505),(3,'zhubajie',485),(4,'shaheshang',408);      #給表中添加用戶
Query OK, 4 rows affected (0.01 sec)
Records: 4  Duplicates: 0  Warnings: 0

     第二步:在兩個數據庫鏈接線程的會話變量中設置隔離級別爲READ-UNCOMMITTED

會話1
MariaDB [(none)]> set tx_isolation='READ-UNCOMMITTED';
Query OK, 0 rows affected (0.00 sec)
MariaDB [test]> start transaction;                 #開啓事物
Query OK, 0 rows affected (0.00 sec)
會話2
MariaDB [(none)]> set tx_isolation='READ-UNCOMMITTED';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]>
MariaDB [test]> start transaction;                 #開啓事物
Query OK, 0 rows affected (0.00 sec)

    第三步:兩邊同時啓用事物,其中會話1添加bailongma,但不提交,在會話2上查看驗證

會話1                     #插入數據但未提交,會話2上查詢後驗證
MariaDB [test]> insert into employee values(5,'bailongma',300);
Query OK, 1 row affected (0.00 sec)
會話2                     #經查詢驗證會話1還沒有提交已經能夠讀取,若會話1回滾,會話2讀取數據爲髒數據
MariaDB [test]> select * from employee;
+------+------------+------+
| id   | name       | age  |
+------+------------+------+
|    1 | tangseng   | 38   |
|    2 | sunwukong  | 505  |
|    3 | zhubajie   | 485  |
|    4 | shaheshang | 408  |
|    5 | bailongma  | 300  |
+------+------------+------+
5 rows in set (0.00 sec)

    驗證隔離級別READ-COMMITTED(解決髒讀問題,存在不可重複讀)

    第一步:建立表並插入數據

MariaDB [test]> create table employee(id int,name varchar(20),age char(3));                             #建立表
Query OK, 0 rows affected (0.00 sec)
MariaDB [test]> insert into employee  values(1,'tangseng',38),(2,'sunwukong',505),(3,'zhubajie',485),(4,'shaheshang',408);      #給表中添加用戶
Query OK, 4 rows affected (0.01 sec)
Records: 4  Duplicates: 0  Warnings: 0

     第二步:在兩個數據庫鏈接線程的會話變量中設置隔離級別爲READ-COMMITTED

會話1
MariaDB [(none)]> set tx_isolation='READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)
MariaDB [test]> start transaction;              #開啓事務
Query OK, 0 rows affected (0.00 sec)
會話2
MariaDB [(none)]> set tx_isolation='READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)
MariaDB [test]> start transaction;              #開啓事務
Query OK, 0 rows affected (0.00 sec)

    第三步:在兩個鏈接數據庫的線程進程添加bailongma,但不提交進行驗證

會話1
MariaDB [test]> insert into employee values(5,'bailongma',305);
Query OK, 1 row affected (0.00 sec)
會話2              #在會話1未提交時,會話2是沒法讀取數據
MariaDB [test]> select * from employee;
+------+------------+------+
| id   | name       | age  |
+------+------------+------+
|    1 | tangseng   | 38   |
|    2 | sunwukong  | 505  |
|    3 | zhubajie   | 485  |
|    4 | shaheshang | 408  |
+------+------------+------+
4 rows in set (0.00 sec)

    第四步:在連會話1上進行提交,而後在會話2上進行驗證

會話1
MariaDB [test]> commit;
Query OK, 0 rows affected (0.00 sec)
會話2                 
MariaDB [test]> select * from employee;
+------+------------+------+
| id   | name       | age  |
+------+------------+------+
|    1 | tangseng   | 38   |
|    2 | sunwukong  | 505  |
|    3 | zhubajie   | 485  |
|    4 | shaheshang | 408  |
|    5 | bailongma  | 305  |
+------+------------+------+
5 rows in set (0.00 sec)    #說明讀提交能夠解決髒讀的問題

    驗隔離級別REPEATABLE READ(解決髒讀和重複讀的問題,帶來新的問題幻讀)

    第一步:建立表並插入數據

MariaDB [test]> create table employee(id int,name varchar(20),age char(3));                             #建立表
Query OK, 0 rows affected (0.00 sec)
MariaDB [test]> insert into employee  values(1,'tangseng',38),(2,'sunwukong',505),(3,'zhubajie',485),(4,'shaheshang',408);      #給表中添加用戶
Query OK, 4 rows affected (0.01 sec)
Records: 4  Duplicates: 0  Warnings: 0

    第二步:在兩個數據庫鏈接線程的會話變量中設置隔離級別爲REPEATABLE-READ

會話1
MariaDB [(none)]> set tx_isolation='REPEATABLE-READ';
Query OK, 0 rows affected (0.00 sec)
MariaDB [test]> start transaction;              #開啓事務
Query OK, 0 rows affected (0.00 sec)
會話2
MariaDB [(none)]> set tx_isolation='REPEATABLE-READ';
Query OK, 0 rows affected (0.00 sec)
MariaDB [test]> start transaction;              #開啓事務
Query OK, 0 rows affected (0.00 sec)

    第三步:先會話2中開啓一個事物查詢表中數據,而後在會話1中添加bailongma用戶,再次在會話2中的同一事務中查看錶中數據(發現會話1中數據已經發生變化,會話2的同一事物中任然是以前的數據,所以解決了事物的可重複讀)

會話2           #開啓一個事物並查詢表中數據
MariaDB [test]> select * from employee;
+------+------------+------+
| id   | name       | age  |
+------+------------+------+
|    1 | tangseng   | 38   |
|    2 | sunwukong  | 505  |
|    3 | zhubajie   | 485  |
|    4 | shaheshang | 408  |
+------+------------+------+
4 rows in set (0.00 sec)
會話1        #添加bailongma數據後,提交併查詢
MariaDB [test]> insert into employee values(5,'bailongma',305);
Query OK, 1 row affected (0.00 sec)

MariaDB [test]> commit;
Query OK, 0 rows affected (0.00 sec)
MariaDB [test]> select * from employee;
+------+------------+------+
| id   | name       | age  |
+------+------------+------+
|    1 | tangseng   | 38   |
|    2 | sunwukong  | 505  |
|    3 | zhubajie   | 485  |
|    4 | shaheshang | 408  |
|    5 | bailongma  | 305  |
+------+------------+------+
5 rows in set (0.00 sec)
會話2      #在會話2上再次查詢,結果任然是4條數據,緣由是會話2上的事物並未提交,而且解決了可重複讀,所以只能看到4條,除非提交事物後再次查詢;
MariaDB [test]> select * from employee;
+------+------------+------+
| id   | name       | age  |
+------+------------+------+
|    1 | tangseng   | 38   |
|    2 | sunwukong  | 505  |
|    3 | zhubajie   | 485  |
|    4 | shaheshang | 408  |
+------+------------+------+
4 rows in set (0.00 sec)
MariaDB [test]> select * from employee;
+------+------------+------+
| id   | name       | age  |
+------+------------+------+
|    1 | tangseng   | 38   |
|    2 | sunwukong  | 505  |
|    3 | zhubajie   | 485  |
|    4 | shaheshang | 408  |
|    5 | bailongma  | 305  |
+------+------------+------+
5 rows in set (0.00 sec)

    驗隔離級別SERIALIZABLE(解決重複讀的問題,需注意每次操做都須要重啓新的事物和提交,由於有加鎖,一個事物只能是一組語句)

     第一步:建立表並插入數據

MariaDB [test]> create table employee(id int,name varchar(20),age char(3));                             #建立表
Query OK, 0 rows affected (0.00 sec)
MariaDB [test]> insert into employee  values(1,'tangseng',38),(2,'sunwukong',505),(3,'zhubajie',485),(4,'shaheshang',408);      #給表中添加用戶
Query OK, 4 rows affected (0.01 sec)
Records: 4  Duplicates: 0  Warnings: 0

    第二步:在兩個數據庫鏈接線程的會話變量中設置隔離級別爲SERIALIZABLE

會話1
MariaDB [(none)]> set tx_isolation='SERIALIZABLE';
Query OK, 0 rows affected (0.00 sec)
MariaDB [test]> start transaction;              #開啓事物
Query OK, 0 rows affected (0.00 sec)
會話2
MariaDB [(none)]> set tx_isolation='SERIALIZABLE';
Query OK, 0 rows affected (0.00 sec)
MariaDB [test]> start transaction;              #開啓事物
Query OK, 0 rows affected (0.00 sec)

    第三步:當會話1上進行插入bailongma用戶前,在會話2上進查詢並提交,會話1添加bailongma並提交,而後再次在會話2上進行查詢

會話1       #添加用戶後,並未提交
MariaDB [test]> insert into employee values(5,'bailongma',305);
Query OK, 1 row affected (0.00 sec)
會話2       #在會話1上未提交時,會話1對錶進行加鎖,所以會話2上時沒法查詢,所以解決幻讀
MariaDB [test]> select * from employee;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
相關文章
相關標籤/搜索