如何理解MySQL事務的隔離級別

關注我,更多精彩文章第一時間推送給你

  1. 讀未提交(READ UNCOMMITTED)
  2. 讀已提交(READ COMMITTED)
  3. 可重複讀(REPEATABLE READ)
  4. 可串行化(SERIALIZABLE)
  • MySQL的默認事務的隔離級別是**可重複讀**

<!--more-->mysql

-- 登陸mysql的root帳戶,-p待輸入密碼,-h mysql服務器地址 -P 端口號(注意大寫)
➜ mysql -u root -p -h localhost -P 3307
Enter password: ****
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 284
Server version: 5.7.28 MySQL Community Server (GPL)

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
-- 查看當前事務的隔離級別
mysql> show variables like 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+

-- 或者如此查看事務的隔離級別
mysql> SELECT @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+

-- 能夠看到MySQL的默認事務隔離級別是可重複讀

讀未提交

會產生全部的問題(髒讀、不可重複讀、幻讀)sql

讀已提交

在此事務隔離級別下,事務B只能在事務A修改而且已提交後才能讀取到事務A修改的數據。數據庫

避免了髒讀服務器

但仍是會產生(不可重複讀、幻讀)session

可重複讀

在此事務隔離級別下,事務B只能在事務A修改數據並提交後,本身也提交事務後,才能讀取到事務A修改的數據。併發

避免了髒讀和不可重複讀測試

但仍是會產生幻讀問題url

可串行化

此種事務隔離級別別最高,不會發生任何如下問題(髒讀、可重複讀、幻讀),經過加鎖實現.net

讀鎖和寫鎖,只在讀讀不阻塞,讀寫、寫讀、寫寫都會阻塞。code

解讀一下併發形成的問題(髒讀、不可重複讀、幻讀)

數據庫表t_user個人表字段

mysql> show columns from t_user;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(50) | NO   |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+

**注意:**MySQL 默認開啓事務自動提交模式,即除非顯式的開啓事務(BEGIN 或 START TRANSACTION),不然每條 SOL 語句都會被當作一個單獨的事務自動執行。

-- 能夠看到mysql默認開啓自動提交模式
mysql> SHOW VARIABLES LIKE 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+

-- 能夠設置是否自動提交
-- 值爲 0 和值爲 OFF:關閉事務自動提交。
-- 值爲 1 和值爲 ON:開啓事務自動提交。
-- 以下可設置的4種值
-- SET autocommit = 0|1|ON|OFF;
  • 髒讀

事務A 讀取到了 事務B 修改可是未提交的髒數據

開始以前必定調整事務的隔離級別爲讀未提交,否則不可能產生髒讀現象

mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-UNCOMMITTED        |
+-------------------------+

首先查看一下t_user表的數據

mysql> select * from t_user;
+----+--------+
| id | name   |
+----+--------+
|  1 | 達摩   |
+----+--------+

而後進行以下測試

-- session1 事務A 開始
mysql> begin;
-- session2 事務B 開始
mysql> begin;
-- session2 事務B 修改name = 呂布  (注意這裏並未提交)
mysql> update t_user set name = '呂布' where id = 1;
-- session1 事務A 讀取id = 1的數據 (注意,讀到了未提交的髒數據 name = 呂布)
mysql> select * from t_user where id = 1;
+----+--------+
| id | name   |
+----+--------+
|  1 | 呂布   |
+----+--------+
-- session1 事務A 對讀到的髒數據進行提交(事務A結束)
mysql> commit;
-- session2 事務B 對剛纔的修改進行回滾(事務B結束)
mysql> rollback;

-- 最終數據庫中的name並無改變,可是事務A讀到了髒數據,這就是髒讀
-- 此事務隔離級別(讀未提交)也會形成不可重複讀和幻讀,這裏不作演示,看下面的隔離級別中演示

  • 不可重複讀

事務A 讀取表的名字爲達摩(並未提交),以後事務B修改了名字爲曹操(修改完提交了),此時事務A在未提交的狀況下又讀取了name卻變成了曹操,這就是不可重複讀

設置事務的隔離級別爲讀已提交,也能夠爲讀未提交,由於讀未提交也會產生不可重複讀現象

-- 設置事務的隔離級別爲讀已提交
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
Query OK, 0 rows affected (0.00 sec)
-- 查看修改結果,已經爲讀已提交
mysql> SELECT @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-COMMITTED          |
+-------------------------+

首先查看一下t_user表的數據

mysql> select * from t_user;
+----+--------+
| id | name   |
+----+--------+
|  1 | 達摩   |
+----+--------+

而後進行以下測試

-- session1 事務A開始事務
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
-- session1 事務A 讀取表中數據(注意此時未提交事務)
mysql> select * from t_user where id = 1;
+----+--------+
| id | name   |
+----+--------+
|  1 | 達摩   |
+----+--------+
-- session2 事務B直接修改並提交事務
mysql> update t_user set name = '曹操' where id = 1;
Query OK, 1 row affected (0.03 sec)
-- session1 事務A 再次讀取表中數據,發現同一個事務中獲得了不同的結果,這就是不可重複讀
mysql> select * from t_user where id = 1;
+----+--------+
| id | name   |
+----+--------+
|  1 | 曹操   |
+----+--------+
-- 只要是事務A一直不提交,其餘事務修改提交以後,事務A再次查詢都能查到最新修改的結果
-- 此事務隔離級別也會形成幻讀,可是避免了髒讀
-- 測試是否避免髒讀,事務A此時未提交
-- session2 事務B開始
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
-- session2 事務B修改name = 伽羅 (注意此時未提交)
mysql> update t_user set name = '伽羅' where id = 1;
Query OK, 1 row affected (0.00 sec)
-- session1 事務A進行讀取,看到並無讀取到髒數據
mysql> select * from t_user where id = 1;
+----+--------+
| id | name   |
+----+--------+
|  1 | 曹操   |
+----+--------+
-- session2 事務B回滾
mysql> rollback;
Query OK, 0 rows affected (0.05 sec)
-- session1 事務A提交
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
  • 知識點:爲何加了寫鎖,別的事務還能夠進行讀操做?

  • 由於InnoDB有MVCC機制(多版本併發控制),能夠使用快照讀,而不會被阻塞。

  • 幻讀

事務A讀取表中數據爲一條(這時候未提交), 事務B向表中添加了一條記錄(提交),此時事務A再次查詢表中數據,結果是兩條,好像產生了幻覺,這就是幻讀

設置事務的隔離級別爲可重複讀,也能夠爲讀未提交,也能夠爲讀已提交,由於這三種事務隔離級別都會產生幻讀現象

-- 設置事務的隔離級別爲可重複讀
mysql> SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Query OK, 0 rows affected (0.00 sec)
-- 查看修改結果,已經爲可重複讀
mysql> SELECT @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+

首先查看一下t_user表的數據

mysql> select * from t_user;
+----+--------+
| id | name   |
+----+--------+
|  1 | 曹操   |
+----+--------+

以後進行的測試

-- session1 事務A開啓
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
-- session1 事務A查詢(注意未提交)
mysql> select * from t_user;
+----+--------+
| id | name   |
+----+--------+
|  1 | 曹操   |
+----+--------+
-- session2 事務B添加數據(提交)
mysql> insert into t_user(name) values('馬可波羅');
Query OK, 1 row affected (0.04 sec)
-- session1 事務A再次查詢(一個事務中兩次查詢多出了數據,幻讀)
mysql> select * from t_user;
+----+--------------+
| id | name         |
+----+--------------+
|  1 | 曹操         |
|  2 | 馬可波羅      |
+----+--------------+
-- 測試此事務隔離級別能避免不可重複讀現象(此時事務A仍是未提交)
-- session2 事務B修改id= 1的數據名字爲狄仁傑(提交)
mysql> update t_user set  name = '狄仁傑' where id = 1;
Query OK, 1 row affected (0.04 sec)
-- 此時session1 事務A再次讀取數據(能夠看到跟上一次同樣,證實避免了不可重複讀現象)
mysql> select * from t_user;
+----+--------------+
| id | name         |
+----+--------------+
|  1 | 曹操         |
|  2 | 馬可波羅      |
+----+--------------+

image-20200927155439699

以上講的都是併發操做可能形成的讀的問題

關於樂觀鎖和悲觀鎖解決併發寫操做可能形成的丟失更新的問題請訪問個人這篇文章

相關文章
相關標籤/搜索