11. MySQL事務管理(上)

1. CURD不加控制,會有什么問題?

火車票售票系統tickets表
id
name
nums
10
西安<->蘭州
1
客戶端A 客戶端B
if (nums > 0) { if (nums > 0) { 賣票 賣票 // update nums=nums - 1 update nums=nums - 1 } }
當客戶端A檢查還有一張票時,將票賣掉,還沒有執行更新數據庫時,客戶端B檢查了票數,發現大于0,于是又賣了一次票。然后A將票數更新回數據庫。這是就出現了同一張票被賣了兩次。

2. CURD滿足什么屬性,能解決上述問題?

  1. 買票的過程得是原子的吧
  2. 買票互相應該不能影響吧
  3. 買完票應該要永久有效吧
  4. 買前,和買后都要是確定的狀態吧

3. 什么是事務?-> 多條 mysql 語句對象.

事務就是一組DML語句組成,這些語句在邏輯上存在相關性,這一組DML語句要么全部成功,要么全部失敗,是一個整體。MySQL提供一種機制,保證我們達到這樣的效果。事務還規定不同的客戶端看到的數據是不相同的。 要站在 mysql 的上層來看, 才有事務的這個概念, 這個有意義的行為往往對應多個 mysql 語句對應. 事務就是要做的或所做的事情,主要用于處理操作量大,復雜度高的數據。假設一種場景:你畢業了,學校的教務系統后臺MySQL中,不在需要你的數據,要刪除你的所有信息(一般不會:) ),那么要刪除你的基本信息(姓名,電話,籍貫等)的同時,也刪除和你有關的其他信息,比如:你的各科成績,你在校表現,甚至你在論壇發過的文章等。這樣, 就需要多條MySQL語句構成,那么所有這些操作合起來,就構成了一個事務。 正如我們上面所說,一個MySQL數據庫,可不止你一個事務在運行,同一時刻,甚至有大量的請求被包裝成事務,在向MySQL服務器發起事務處理請求。而每條事務至少一條SQL,最多很多SQL,這樣如果大家都訪問同樣的表數據,在不加保護的情況,就絕對會出現問題。甚至,因為事務由多條SQL構成,那么,也會存在執行到一半出錯或者不想再執行的情況,那么已經執行的怎么辦呢?所有,一個完整的事務,絕對不是簡單的sql集合,還需要滿足如下四個屬性:
  1. 原子性:一個事務(transaction)中的所有操作,要么全部完成,要么全部不完成,不會結束在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
  2. 一致性:在事務開始之前和事務結束以后,數據庫的完整性(可預期性, 確定的結果)沒有被破壞。這表示寫入的資料必須完全符合所有的預設規則,這包含資料的精確度、串聯性以及后續數據庫可以自發性地完成預定的工作。(在技術層面上沒有什么做額外工作, 這個 mysql 通過其他三個性質來保證一致性, 原子性 + 隔離性 + 持久性 -> 一致性)
  3. 隔離性:數據庫允許多個并發事務同時對其數據進行讀寫和修改的能力,隔離性可以防止多個事務并發執行時由于交叉執行而導致數據的不一致。事務隔離分為不同級別,包括讀未提交(Read uncommitted )、讀提交(read committed )、可重復讀(repeatable read )和串行化 (Serializable )
  4. 持久性:事務處理結束后,對數據的修改就是永久的,即便系統故障也不會丟失。
上面四個屬性,可以簡稱為 ACID 。原子性(Atomicity,或稱不可分割性)一致性(Consistency)隔離性(Isolation,又稱獨立性)持久性(Durability)。

4. 為什么會出現事務

事務被MySQL編寫者設計出來,本質是為了當應用程序訪問數據庫的時候, 事務能夠簡化我們的編程模型,不需要我們去考慮各種各樣的潛在錯誤和并發問題。可以想一下當我們使用事務時,要么提交,要么回滾,我們不會去考慮網絡異常了,服務器宕機了,同時更改一個數據怎么辦對吧? 因此事務本質上是為了應用層服務的,而不是伴隨著數據庫系統天生就有的。備注:我們后面把MySQL中的一行信息,稱為 一行記錄

5. 事務的版本支持

在MySQL中只有使用了Innodb數據庫引擎的數據庫或表才支持事務,MyISAM不支持。查看數據庫引擎
mysql> show engines; -- 表格顯示 mysql> show engines \G -- 行顯示 *************************** 1. row *************************** Engine: InnoDB -- 引擎名稱 Support: DEFAULT -- 默認引擎 Comment: Supports transactions, row-level locking, and foreign keys -- 描述 Transactions: YES -- 支持事務 XA: YES Savepoints: YES -- 支持事務保存點 *************************** 2. row *************************** Engine: MRG_MYISAM Support: YES Comment: Collection of identical MyISAM tables Transactions: NO XA: NO Savepoints: NO *************************** 3. row *************************** Engine: MEMORY --內存引擎 Support: YES Comment: Hash based, stored in memory, useful for temporary tables Transactions: NO XA: NO Savepoints: NO *************************** 4. row *************************** Engine: BLACKHOLE Support: YES Comment: /dev/null storage engine (anything you write to it disappears) Transactions: NO XA: NO Savepoints: NO *************************** 5 Engine: MyISAM Support: YES Comment: MyISAM storage engine Transactions: NO -- MyISAM不支持事務 XA: NO Savepoints: NO *************************** 6. row *************************** Engine: CSV Support: YES Comment: CSV storage engine Transactions: NO XA: NO Savepoints: NO *************************** 7. row *************************** Engine: ARCHIVE Support: YES Comment: Archive storage engine Transactions: NO XA: NO Savepoints: NO *************************** 8. row *************************** Engine: PERFORMANCE_SCHEMA Support: YES Comment: Performance Schema Transactions: NO XA: NO Savepoints: NO *************************** 9. row *************************** Engine: FEDERATED Support: NO Comment: Federated MySQL storage engine Transactions: NULL NULL Savepoints: NULL 9 rows in set (0.00 sec)

6. 事務提交方式

事務的提交方式常見的有兩種:
  • 自動提交
  • 手動提交
查看事務提交方式
mysql> show variables like 'autocommit'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.41 sec)
用SET來改變MySQL的自動提交模式:
mysql> SET AUTOCOMMIT=0 #SET AUTOCOMMIT=0 禁止自動提交 Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'autocommit'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | OFF | +---------------+-------+ 1 row in set (0.00 sec) mysql> SET AUTOCOMMIT=1 #SET AUTOCOMMIT=1 開啟自動提交 Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'autocommit'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.01 sec)

7. 事務常見操作方式(原子性 and 持久性)

簡單銀行用戶表 提前準備
## Centos 7云服務器,默認開啟3306 mysqld服務 [whb@VM-0-3-centos ~]$ sudo netstat -nltp [sudo] password for whb: Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State Foreign Address State PID/Program name tcp6 0 0 :::3306 :::* LISTEN 30415/mysqld ## 使用win cmd遠程訪問Centos 7云服務器,mysqld服務(需要win上也安裝了MySQL,這里看到結果即可) ## 注意,使用本地mysql客戶端,可能看不到鏈接效果,本地可能使用域間套接字,查不到鏈接 C:\Users\whb>mysql -uroot -p -h42.192.83.143 Enter password: *********** Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 3484 Server version: 5.7.33 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. ## 使用netstat查看鏈接情況,可知:mysql本質是一個客戶端進程 [whb@VM-0-3-centos ~]$ sudo netstat -ntp Active Internet connections (w/o servers) Proto Recv-Q Send-Q Local Address Foreign Address State Foreign Address State PID/Program name tcp6 0 0 172.17.0.3:3306 113.132.141.236:19354 ESTABLISHED 30415/mysqld
為了便于演示,我們將mysql的默認隔離級別設置成 讀未提交。具體操作我們后面專門會講,現在已使用為主。
mysql> set global transaction isolation level READ UNCOMMITTED; Query OK, 0 rows affected (0.00 sec) mysql> quit Bye ##需要重啟終端,進行查看 mysql> select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | READ-UNCOMMITTED | +-----------------+ 1 row in set, 1 warning (0.00 sec)
創建測試表
create table if not exists account( id int primary key, name varchar(50) not null default '', blance decimal(10,2) not null default 0.0 )ENGINE=InnoDB DEFAULT CHARSET=UTF8;
正常演示 - 證明事務的開始與回滾
mysql> show variables like 'autocommit'; -- 查看事務是否自動提交。我們故意設置成自動提交,看看該選項是否影響begin +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.00 sec) mysql> start transaction; -- 開始一個事務begin也可以,推薦begin Query OK, 0 rows affected (0.00 sec) mysql> savepoint save1; -- 創建一個保存點save1 Query OK, 0 rows affected (0.00 sec) mysql> insert into account values (1, '張三', 100); -- 插入一條記錄 Query OK, 1 row affected (0.05 sec) mysql> savepoint save2; -- 創建一個保存點save2 Query OK, 0 rows affected (0.01 sec) mysql> insert into account values (2, '李四', 10000); -- 在插入一條記錄 Query OK, 1 row affected (0.00 sec) mysql> select * from account; -- 兩條記錄都在了 +----+------+----------+ | id | name | blance | +----+------+----------+ | 1 | 張三 | 100.00 | | 2 | 李四 | 10000.00 | +----+------+----------+ 2 rows in set (0.00 sec) mysql> rollback to save2; -- 回滾到保存點save2 Query OK, 0 rows affected (0.03 sec) mysql> select * from account; -- 一條記錄沒有了 +----+------+----------+ | id | name | blance | +----+------+----------+ | 1 | 張三 | 100.00 | +----+------+----------+ 1 row in set (0.00 sec) mysql> rollback; -- 直接rollback,回滾在最開始 或者 commit 提交(提交了不能再回滾了) Query OK, 0 rows affected (0.00 sec) mysql> select * from account; -- 所有剛剛的記錄沒有了 Empty set (0.00 sec)
非正常演示1 - 證明 未commit,客戶端崩潰,MySQL自動會回滾(隔離級別設置為 讀未提交
-- 終端A mysql> select * from account; -- 當前表內無數據 Empty set (0.00 sec) mysql> show variables like 'autocommit'; -- 依舊自動提交 +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.00 sec) mysql> begin --開啟事務 Query OK, 0 rows affected (0.00 sec) mysql> insert into account values (1, '張三', 100); -- 插入記錄 Query OK, 1 row affected (0.00 sec) mysql> select * from account; --數據已經存在,但沒有commit,此時同時查看 終端B +----+------+----------+ | id | name | blance | +----+------+----------+ | 1 | 張三 | 100.00 | +----+------+----------+ 1 row in set (0.00 sec) mysql> Aborted 異常終止MySQL --終端B mysql> select * from account; --終端A崩潰前 +----+------+----------+ | id | name | blance | +----+------+----------+ | 1 | 張三 | 100.00 | +----+------+----------+ 1 row in set (0.00 sec) mysql> select * from account; --數據自動回滾 Empty set (0.00 sec)
非正常演示2 - 證明 commit了,客戶端崩潰,MySQL數據不會在受影響,已經持久化
--終端 A mysql> show variables like 'autocommit'; -- 依舊自動提交 +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.00 sec) mysql> select * from account; -- 當前表內無數據 Empty set (0.00 sec) mysql> begin -- 開啟事務 Query OK, 0 rows affected (0.00 sec) mysql> insert into account values (1, '張三', 100); -- 插入記錄 Query OK, 1 row affected (0.00 sec) mysql> commit; --提交事務 Query OK, 0 rows affected (0.04 sec) mysql> Aborted -- ctrl + \ 異常終止MySQL --終端 B --數據存在了,所以commit的作用是將數據持久 mysql> select * from account; 化到MySQL中 +----+------+----------+ | id | name | blance | +----+------+----------+ | 1 | 張三 | 100.00 | +----+------+----------+ 1 row in set (0.00 sec)
非正常演示3 - 對比試驗。證明begin操作會自動更改提交方式,不會受MySQL是否自動提交影響
-- 終端 A mysql> select *from account; --查看歷史數據 +----+------+----------+ | id | name | blance | +----+------+----------+ | 1 | 張三 | 100.00 | +----+------+----------+ 1 row in set (0.00 sec) mysql> show variables like 'autocommit'; --查看事務提交方式 +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.00 sec) mysql> set autocommit=0 --關閉自動提交 Query OK, 0 rows affected (0.00 sec) mysql> show variables like 'autocommit'; --查看關閉之后結果 +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | OFF | +---------------+-------+ 1 row in set (0.00 sec) mysql> begin --開啟事務 Query OK, 0 rows affected (0.00 sec) mysql> insert into account values (2, '李四', 10000); --插入記錄 Query OK, 1 row affected (0.00 sec) mysql> select *from account; --查看插入記錄,同時查看終端B +----+------+----------+ | id | name | blance | +----+------+----------+ | 1 | 張三 | 100.00 | | 2 | 李四 | 10000.00 | +----+------+----------+ 2 rows in set (0.00 sec) mysql> Aborted --再次異常終止 -- 終端B mysql> select * from account; --終端A崩潰前 +----+--------+----------+ | id | name | blance | +----+--------+----------+ | 1 | 張三 | 100.00 | | 2 | 李四 | 10000.00 | +----+--------+----------+ 2 rows in set (0.00 sec) mysql> select * from account; --終端A崩潰后,自動回滾 +----+--------+--------+ | id | name | blance | +----+--------+--------+ | 1 | 張三 | 100.00 | ----+--------+--------+ 1 row in set (0.00 sec)
非正常演示4 - 證明單條 SQL 與事務的關系: 單 sql 就是事務(默認會被 mysql 自動 commit)
--實驗一 -- 終端A mysql> select * from account; +----+--------+--------+ | id | name | blance | +----+--------+--------+ | 1 | 張三 | 100.00 | +----+--------+--------+ 1 row in set (0.00 sec) mysql> show variables like 'autocommit'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.00 sec) mysql> set autocommit=0; --關閉自動提交 Query OK, 0 rows affected (0.00 sec) mysql> insert into account values (2, '李四', 10000); --插入記錄 Query OK, 1 row affected (0.00 sec) mysql> select *from account; --查看結果,已經插入。此時可以在查 看終端B +----+--------+----------+ | id | name | blance | +----+--------+----------+ | 1 | 張三 | 100.00 | | 2 | 李四 | 10000.00 | +----+--------+----------+ 2 rows in set (0.00 sec) mysql> ^DBye --ctrl + \ or ctrl + d,終止終 端 --終端B mysql> select * from account; --終端A崩潰前 +----+--------+----------+ | id | name | blance | +----+--------+----------+ | 1 | 張三 | 100.00 | | 2 | 李四 | 10000.00 | +----+--------+----------+ 2 rows in set (0.00 sec) mysql> select * from account; --終端A崩潰后 +----+--------+--------+ | id | name | blance | +----+--------+--------+ | 1 | 張三 | 100.00 | +----+--------+--------+ 1 row in set (0.00 sec) -- 實驗二 --終端A mysql> show variables like 'autocommit'; --開啟默認提交 +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.00 sec) mysql> select * from account; +----+--------+--------+ | id | name | blance | +----+--------+--------+ | 1 | 張三 | 100.00 | +----+--------+--------+ 1 row in set (0.00 sec) mysql> insert into account values (2, '李四', 10000); Query OK, 1 row affected (0.01 sec) mysql> select *from account; --數據已經插入 +----+--------+----------+ | id | name | blance | +----+--------+----------+ | 1 | 張三 | 100.00 | | 2 | 李四 | 10000.00 | +----+--------+----------+ 2 rows in set (0.00 sec) mysql> Aborted --異常終止 --終端B mysql> select * from account; --終端A崩潰前 +----+--------+----------+ | id | name | blance | +----+--------+----------+ | 1 | 張三 | 100.00 | | 2 | 李四 | 10000.00 | +----+--------+----------+ 2 rows in set (0.00 sec) mysql> select * from account; --終端A崩潰后,并不影響,已經持久化。autocommit 起作用 +----+--------+----------+ | id | name | blance | +----+--------+----------+ | 1 | 張三 | 100.00 | | 2 | 李四 | 10000.00 | +----+--------+----------+ 2 rows in set (0.00 sec)
7.1. 結論:
只要輸入 begin或者 start transaction,事務便必須要通過commit提交,才會持久化,與是否設置 set autocommit無關。事務可以手動回滾,同時,當操作異常,MySQL會自動回滾
對于InnoDB每一條SQL語言都默認封裝成事務, 自動提交。(select有特殊情況,因為MySQL有MVCC )
從上面的例子,我們能看到事務本身的 原子性(回滾)持久性(commit)
那么隔離性?一致性?
7.2. 事務操作注意事項
  1. 如果沒有設置保存點,也可以回滾,只能回滾到事務的開始。直接使用rollback(前提是事務還沒有提交)
  2. 如果一個事務被提交了(commit),則不可以回退(rollback)
  3. 可以選擇回退到哪個保存點
  4. InnoDB支持事務,MyISAM不支持事務
  5. 開始事務可以使start transaction或者begin

8. 事務隔離級別

如何理解隔離性1 MySQL服務可能會同時被多個客戶端進程(線程)訪問,訪問的方式以事務方式進行
一個事務可能由多條SQL構成,也就意味著,任何一個事務,都有 執行前,執行中,執行后的階段。而 所謂的原子性,其實就是讓用戶層,要么看到執行前,要么看到執行后。執行中出現問題,可以隨時回滾。所以單個事務,對用戶表現出來的特性,就是原子性。
但,畢竟所有事務都要有個執行過程,那么在多個事務各自執行多個SQL的時候,就還是有可能會出現互相影響的情況。比如:多個事務同時訪問同一張表,甚至同一行數據。
就如同你媽媽給你說:你要么別學,要學就學到最好。至于你怎么學,中間有什么困難,你媽媽不關心。那么你的學習,對你媽媽來講,就是原子的。那么你學習過程中,很容易受別人干擾,此時,就需要將你的學習隔離開,保證你的學習環境是健康的。
數據庫中,為了保證事務執行 過程中盡量不受干擾,就有了一個重要特征: 隔離性
數據庫中,允許事務受 不同程度的干擾,就有了一種重要特征: 隔離級別
  1. 在事務的場景中, 隔離是必要的.
  1. 事務是整體的, 原子的
  2. 運行中的事務, 進行相互隔離
  1. 在事務運行中, 不會出現相互干擾, -> 隔離性
  2. 根據影響程度的不同, 隔離級別
8.1. 隔離級別
讀未提交【Read Uncommitted】:在該隔離級別,所有的事務都可以看到其他事務沒有提交的執行結果。(實際生產中不可能使用這種隔離級別的),但是相當于沒有任何隔離性,也會有很多并發問題,如臟讀,幻讀,不可重復讀等,我們上面為了做實驗方便,用的就是這個隔離性。
讀提交【Read Committed】 :該隔離級別是大多數數據庫的默認的隔離級別(不是MySQL默認的)。它滿足了隔離的簡單定義:一個事務只能看到其他的已經提交的事務所做的改變。這種隔離級別會引起不可重復讀,即一個事務執行時,如果多次select,可能得到不同的結果。
可重復讀【Repeatable Read】:這是MySQL默認的隔離級別,它確保同一個事務,在執行中,多次讀取操作數據時,會看到同樣的數據行。但是會有 幻讀問題。
串行化【Serializable】:這是事務的最高隔離級別,它通過強制事務排序,使之不可能相互沖突,從而解決了幻讀的問題。它在每個讀的數據行上面加上共享鎖。但是可能會導致超時和鎖競爭 (這種隔離級別太極端,實際生產基本不使用)
注: 在數據庫當中, 如果是多個事務同時對一個資源寫操作, 那么必須通過加鎖串行化執行!
隔離級別如何實現:隔離,基本都是通過鎖實現的,不同的隔離級別,鎖的使用是不同的。常見有,表鎖,行鎖,讀鎖,寫鎖,間隙鎖(GAP),Next-Key鎖(GAP + 行鎖)等。不過,我們目前現有這個認識就行,先關注上層使用。
8.2. 查看與設置隔離性
-- 查看
mysql> SELECT @@global.tx_isolation --查看全局隔級別 +-----------------+ | @@global.tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 1 row in set, 1 warning (0.00 sec) mysql> SELECT @@session.tx_isolation ; --查看會話(當前)全局隔級別(默認把全局隔離級別拷貝到當前會話的隔離級別) +-----------------+ | @@session.tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 1 row in set, 1 warning (0.00 sec) mysql> SELECT @@tx_isolation; --默認同上 +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 1 row in set, 1 warning (0.00 sec)
--設置-- 設置當前會話 or 全局隔離級別語法
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
--設置當前會話隔離性,另起一個會話,看不多,只影響當前會話
mysql> set session transaction isolation level serializable; -- 串行化 Query OK, 0 rows affected (0.00 sec) mysql> SELECT @@global.tx_isolation --全局隔離性還是RR +-----------------+ | @@global.tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 1 row in set, 1 warning (0.00 sec) mysql> SELECT @@session.tx_isolation --會話隔離性成為串行化 +-----------------+ | @@session.tx_isolation | +-----------------+ | SERIALIZABLE | +-----------------+ 1 row in set, 1 warning (0.00 sec) mysql> SELECT @@tx_isolation; --同上 +-----------------+ | @@tx_isolation | +-----------------+ | SERIALIZABLE | +-----------------+ 1 row in set, 1 warning (0.00 sec)
--設置全局隔離性,另起一個會話,會被影響
mysql> set global transaction isolation level READ UNCOMMITTED; Query OK, 0 rows affected (0.00 sec) mysql> SELECT @@global.tx_isolation; +-----------------+ | @@global.tx_isolation | +-----------------+ | READ-UNCOMMITTED | +-----------------+ 1 row in set, 1 warning (0.00 sec) mysql> SELECT @@session.tx_isolation; +-----------------+ | @@session.tx_isolation | +-----------------+ | READ-UNCOMMITTED | +-----------------+ 1 row in set, 1 warning (0.00 sec) mysql> SELECT @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | READ-UNCOMMITTED | +-----------------+ 1 row in set, 1 warning (0.00 sec)
-- 注意,如果沒有現象,關閉mysql客戶端,重新連接。
8.3. 讀未提交【Read Uncommitted】
--幾乎沒有加鎖,雖然 效率高,但是 問題太多,嚴重不建議采用--終端A-- 設置隔離級別為讀未提交
mysql> set global transaction isolation level read uncommitted; Query OK, 0 rows affected (0.00 sec) --重啟客戶端 mysql> select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | READ-UNCOMMITTED | +-----------------+ 1 row in set, 1 warning (0.00 sec) mysql> select * from account; | id | name | blance | |----|----|----| | 1 | 張三 | 100.00 | | 2 | 李四 | 10000.00 | 2 rows in set (0.00 sec) mysql> begin --開啟事務 Query OK, 0 rows affected (0.00 sec) mysql> update account set blance=123.0 where id=1; --更新指定行 Query OK, 1 row affected (0.05 sec) Rows matched: 1 Changed: 1 Warnings: 0 --沒有commit哦!!!
--終端B
mysql> begin; mysql> select * from account; | id | name | blance | |----|----|----| | 1 | 張三 | 123.00 -- 讀到終端A更新但是未commit的數據[insert,delete同樣] | | 2 | 李四 | 10000.00 | 2 rows in set (0.00 sec)
--一個事務在執行中,讀到另一個執行中事務的更新(或其他操作)但是未 commit的數據,這種現象叫做 臟讀(dirty read)
8.4. 讀提交【Read Committed】
-- 終端A
mysql> set global transaction isolation level read committed; Query OK, 0 rows affected (0.00 sec) --重啟客戶端 mysql> select * from account; --查看當前數據 | id | name | blance | |----|----|----| | 1 | 張三 | 123.00 | | 2 | 李四 | 10000.00 | 2 rows in set (0.00 sec) mysql> begin --手動開啟事務,同步的開始終端B事務 Query OK, 0 rows affected (0.00 sec) mysql> update account set blance=321.0 where id=1 ; --更新張三數據 Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 --切換終端到終端B,查看數據。 mysql> commit; --commit提交! Query OK, 0 rows affected (0.01 sec) --切換終端到終端B,再次查看數據。
--終端B
mysql> begin --手動開啟事務,和終端A一前一后 Query OK, 0 rows affected (0.00 sec) mysql> select * from account; --終端A commit之前,查看不到 | id | name | blance | |----|----|----| | 1 | 張三 | 123.00 --老的值 | | 2 | 李四 | 10000.00 | 2 rows in set (0.00 sec) --終端A commit之后,看到了! mysql> select *from account; | id | name | blance | |----|----|----| | 1 | 張三 | 321.00 --新的值 | | 2 | 李四 | 10000.00 | 2 rows in set (0.00 sec)
--but,此時還在當前事務中,并未commit,那么就造成了, 同一個事務內,同樣的讀取,在不同的時間段(依舊還在事務操作中!),讀取到了不同的值,這種現象叫做 不可重復讀(non reapeatable read)!!(這個是問題嗎?? 是問題)
8.5. 可重復讀【Repeatable Read】(MySQL 的默認隔離性)
--終端A
mysql> set global transaction isolation level repeatable read; --設置全局隔離級別RR Query OK, 0 rows affected (0.01 sec) --關閉終端重啟 mysql> select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | --隔離級別RR +-----------------+ 1 row in set, 1 warning (0.00 sec) mysql> select *from account; --查看當前數據 | id | name | blance | |----|----|----| | 1 | 張三 | 321.00 | | 2 | 李四 | 10000.00 | 2 rows in set (0.00 sec) mysql> begin --開啟事務,同步的,終端B也開始事務 Query OK, 0 rows affected (0.00 sec) mysql> update account set blance=4321.0 where id=1; --更新數據 Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 --切換到終端B,查看另一個事務是否能看到 mysql> commit; --提交事務 --切換終端到終端B,查看數據。
--終端B
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from account; --終端A中事務 commit之前,查看當前表中數據,數據未更新 | id | name | blance | |----|----|----| | 1 | 張三 | 321.00 | | 2 | 李四 | 10000.00 | 2 rows in set (0.00 sec) mysql> select * from account; --終端A中事務 commit 之后,查看當前表中數據,數據未更新 | id | name | blance | |----|----|----| | 1 | 張三 | 321.00 | | 2 | 李四 | 10000.00 | 2 rows in set (0.00 sec) --可以看到,在終端B中,事務無論什么時候進行查找,看到的結果都是一致的,這叫做可重復讀! mysql> commit; --結束事務 Query OK, 0 rows affected (0.00 sec) mysql> select * from account; --再次查看,看到最新的更新數據 | id | name | blance | |----|----|----| | 1 | 張三 | 4321.00 | | 2 | 李四 | 100
| 2 | 李四 | 10000.00 | 2 rows in set (0.00 sec)
--如果將上面的終端A中的update操作,改成insert操作,會有什么問題??--終端A
mysql> select *from account; | id | name | blance | |----|----|----| | 1 | 張三 | 321.00 | | 2 | 李四 | 10000.00 | 2 rows in set (0.00 sec) mysql> begin --開啟事務,終端B同步開啟 Query OK, 0 rows affected (0.00 sec) mysql> insert into account (id,name,blance) values(3, '王五', 5432.0); Query OK, 1 row affected (0.00 sec) --切換到終端B,查看另一個事務是否能看到 mysql> commit; --提交事務 Query OK, 0 rows affected (0.00 sec) --切換終端到終端B,查看數據。
--終端B
mysql> begin ; --開啟事務 Query OK, 0 rows affected (0.00 sec) mysql> select * from account; --終端A commit前 查看 | id | name | blance | |----|----|----| | 1 | 張三 | 4321.00 | | 2 | 李四 | 10000.00 | 2 rows in set (0.00 sec) mysql> select * from account; --終端A commit后 查看 | id | name | blance | |----|----|----| | 1 | 張三 | 4321.00 | | 2 | 李四 | 10000.00 | 2 rows in set (0.00 sec) mysql> select * from account; | id | name | blance | |----|----|----| | 1 | 張三 | 4321.00 | | 2 | 李四 | 10000.00 | 2 rows in set (0.00 sec) mysql> commit; --結束事務 Query OK, 0 rows affected (0.00 sec) mysql> select * from account; --看到更新 | id | name | blance | |----|----|----| | 1 | 張三 | 4321.00 | | 2 | 李四 | 10000.00 | | 3 | 王五 | 5432.00 | 3 rows in set (0.00 sec)
--多次查看,發現終端A在對應事務中insert的數據,在終端B的事務周期中,也沒有什么影響,也符合可重復的特點。但是,一般的數據庫在可重復讀情況的時候,無法屏蔽其他事務insert的數據( 為什么?因為隔離性實現是對數據加鎖完成的,而insert待插入的數據因為并不存在,那么一般加鎖無法屏蔽這類問題),會造成雖然大部分內容是可重復讀的,但是insert的數據在可重復讀情況被讀取出來,導致多次查找時,會多查找出來新的記錄,就如同產生了 幻覺。這種現象,叫做 幻讀(phantom read)。很明顯, MySQL在RR級別的時候,是解決了幻讀問題的(解決的方式是用Next-Key鎖(GAP+行鎖)解決的。這塊比較難,有興趣同學了解一下)。
8.6. 串行化【serializable】
--對所有操作全部加鎖( 事務),進行 串行化,不會有問題,但是只要串行化, 效率很低,幾乎完全不會被采用--終端A
mysql> set global 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) mysql> begin --開啟事務,終端B同步開啟 Query OK, 0 rows affected (0.00 sec) mysql> select * from account; --兩個讀取不會串行化,共享鎖 | id | name | blance | |----|----|----| | 1 | 張三 | 4321.00 | | 2 | 李四 | 10000.00 | | 3 | 王五 | 5432.00 | 3 rows in set (0.00 sec) mysql> update account set blance=1.00 where id=1; --終端A中有更新或者其他操作,會阻塞。直到終端B事務提交。 Query OK, 1 row affected (18.19 sec) Rows matched: 1 Changed: 1 Warnings: 0
--終端B
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from account; --兩個讀取不會串行化 | id | name | blance | |----|----|----| | 1 | 張三 | 4321.00 | | 2 | 李四 | 10000.00 | | 3 | 王五 | 5432.00 | 3 rows in set (0.00 sec) mysql> commit; --提交之后,終端A中的update才會提交。 Query OK, 0 rows affected (0.00 sec)
總結:其中隔離級別越嚴格,安全性越高,但數據庫的并發性能也就越低,往往需要在兩者之間找一個平衡點。不可重復讀的重點是修改和刪除:同樣的條件, 你讀取過的數據,再次讀取出來發現值不一樣了幻讀的重點在于新增:同樣的條件, 第1次和第2次讀出來的記錄數不一樣說明:mysql默認的隔離級別是可重復讀,一般情況下不要修改上面的例子可以看出,事務也有長短事務這樣的概念。事務間互相影響,指的是事務在并行執行的時候,即都沒有commit的時候,影響會比較大。
隔離級別
臟讀
不可重復讀
幻讀
加鎖讀
讀未提交 (read uncommited)
不加鎖
讀已提交(read commited)
×
不加鎖
可重復讀(repeatable read)
×
×
不加鎖
可串行化 (serializable)
×
×
×
加鎖
√:會發生該問題 X:不會發生該問題

9. 一致性(Consistency)

mysql 中的一致性由原子性保護: 事務執行的結果,必須使數據庫從一個一致性狀態,變到另一個一致性狀態。當數據庫只包含事務成功提交的結果時,數據庫處于一致性狀態。如果系統運行發生中斷,某個事務尚未完成而被迫中斷,而該未完成的事務對數據庫所做的修改已被寫入數據庫,此時數據庫就處于一種不正確(不一致)的狀態。因此一致性是通過原子性來保證的。
一致性由用戶維護: 其實一致性和用戶的業務邏輯強相關,一般MySQL提供技術支持,但是一致性還是要用戶業務邏輯做支撐,也就是,一致性,是由用戶決定的。
而技術上,通過AID保證C(一致性)

推薦閱讀

如何實現事務的隔離性 - 簡書 Innodb中的事務隔離級別和鎖的關系 - 美團技術團隊 Mysql 間隙鎖原理,以及Repeatable Read隔離級別下可以防止幻讀原理(百度) - aspirant - 博客園
備注:基本上,了解了上面的知識,在MySQL事務使用上,肯定沒有問題。不過,這塊設計很優秀,也是面試中可能被問到的,一般學生,如果能說出上面的內容,就已經不錯了。但是如果我們能更詳細,更深入的談論這個問題,那么對我們的面試與學習肯定是大大的有幫助。不過接下來的內容,會比較難一些,聽的不明白,也沒有太大問題。這塊內容,也要結合同學們聽課的情況,如果時間緊張,我們就不講了。如果有時間,可以現場給學生演示一下,在RR級別的時候,多個事務的update,多個事務的insert,多個事務的delete,是否會有加鎖現象。現象結果是,update,insert,delete之間是會有加鎖現象的,但是select和這些操作是不沖突的。這就是通過讀寫鎖(鎖有行鎖或者表鎖)+MVCC完成隔離性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/83638.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/83638.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/83638.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Beta分布Dirichlet分布

目錄 Beta分布Dirichlet分布Beta分布&Dirichlet分布從Dirichlet分布生成Beta樣本Beta分布&Dirichlet分布應用 Beta分布 Beta分布是定義在區間 [ 0 , 1 ] [0, 1] [0,1]上的連續概率分布&#xff0c;通常用于模擬概率或比例的隨機變量。Beta分布的概率密度函數&#xff…

嵌入式系統中常用的開源協議

目錄 1、GNU通用公共許可證&#xff08;GPL&#xff09; 2、GNU寬松通用公共許可證&#xff08;LGPL&#xff09; 3、MIT許可證 4、Apache許可證2.0 5、BSD許可證 6、如何選擇合適的協議 在嵌入式系統開發中&#xff0c;開源軟件的使用已成為主流趨勢。從物聯網設備到汽車…

告別延遲,擁抱速度:存儲加速仿真應用的解決方案【1】

需求分析 現代仿真&#xff08;如CFD流體動力學、FEA結構分析、電磁仿真、氣候模擬、自動駕駛場景仿真、芯片設計等&#xff09;會產生PB級甚至EB級的數據。海量數據的生成、處理和存儲&#xff0c;主要體現在以下幾個關鍵方面&#xff1a; 數據量爆炸式增長&#xff1a;高分…

vue封裝gsap自定義動畫指令

1、指令文件封裝 import { gsap } from gsap;// 動畫類型配置 const ANIMATION_TYPES {// 縮放scale: {from: { scale: 0.5, opacity: 0 },to: { scale: 1, opacity: 1 },hide: { scale: 0.5, opacity: 0 },},// 透明度fade: {from: { opacity: 0 },to: { opacity: 1, ease: …

HTTP 如何升級成 HTTPS

有一個自己的項目需要上線&#xff0c;域名解析完成后&#xff0c;發現只能使用 http 協議&#xff0c;這在瀏覽器上會限制&#xff0c;提示用戶不安全&#xff0c;所以需要把 HTTP 升級成 HTTPS 協議&#xff0c;但又不想花錢。 前提條件&#xff1a; 已經配置好 Nginx 服務器…

測試面試題總結一

目錄 列表、元組、字典的區別 nvicat連接出現問題如何排查 mysql性能調優 python連接mysql數據庫方法 參數化 pytest.mark.parametrize 裝飾器 list1 [1,7,4,5,5,6] for i in range(len(list1): assert list1[i] < list1[i1] 這段程序有問題嘛&#xff1f; pytest.i…

[藍橋杯]密文搜索

密文搜索 題目描述 福爾摩斯從 X 星收到一份資料&#xff0c;全部是小寫字母組成。 他的助手提供了另一份資料&#xff1a;許多長度為 8 的密碼列表。 福爾摩斯發現&#xff0c;這些密碼是被打亂后隱藏在先前那份資料中的。 請你編寫一個程序&#xff0c;從第一份資料中搜…

打卡第36天:模型可視化以及推理

知識點回顧&#xff1a; 1.三種不同的模型可視化方法&#xff1a;推薦torchinfo打印summary權重分布可視化 2.進度條功能&#xff1a;手動和自動寫法&#xff0c;讓打印結果更加美觀 3.推理的寫法&#xff1a;評估模式 作業&#xff1a;調整模型定義時的超參數&#xff0c;對…

8天Python從入門到精通【itheima】-68(元組)

目錄 65節——元組的定義和操作 1.學習目標 2.為什么要學習元組 3.元組的定義 4.定義元組的注意事項 5.元組的嵌套 6.元組的相關操作 【1】index方法 【2】count方法 【3】len方法 7.元組的遍歷 【1】while循環進行元組的遍歷 【2】for循環進行元組的變量 Python …

鏈表題解——環形鏈表【LeetCode】

141. 環形鏈表 方法一 核心思想&#xff1a; 使用一個集合 seen 來記錄已經訪問過的節點。遍歷鏈表&#xff0c;如果當前節點已經存在于集合中&#xff0c;說明鏈表存在環&#xff1b;否則&#xff0c;將當前節點添加到集合中&#xff0c;繼續遍歷。如果遍歷結束&#xff08;h…

【免費數據】1980-2022年中國2384個站點的水質數據

水&#xff0c;是生命之源&#xff0c;關乎著地球上每一個生物的生存與發展。健康的水生生態系統維持著整個水生態的平衡與活力&#xff1b;更是確保人類能持續獲得清潔水源的重要保障。水質數據在水質研究、海洋生物量測算以及生物多樣性評估等諸多關鍵領域都扮演著舉足輕重的…

分享推薦高精度磁阻式磁編碼器芯片

磁編碼器其通過感應旋轉磁場來實現角度、轉速的測量&#xff0c;因此&#xff0c;相較于傳統的光編碼器&#xff0c;磁編碼器對粉塵、污垢和油脂等污染物有很強的耐受性&#xff0c;即使在較為惡劣的環境中仍能夠保持高分辨率與檢測精度&#xff0c;安裝和維護簡捷方便&#xf…

Spring AI 項目實戰(四):Spring Boot + AI + DeepSeek 超參數優化——智能化機器學習平臺(附完整源碼)

系列文章 序號文章名稱1Spring AI 項目實戰&#xff08;一&#xff09;&#xff1a;Spring AI 核心模塊入門2Spring AI 項目實戰&#xff08;二&#xff09;&#xff1a;Spring Boot AI DeepSeek 深度實戰&#xff08;附完整源碼&#xff09;3Spring AI 項目實戰&#xff08…

高效VLM:VisionZip

論文&#xff1a;[2412.04467] VisionZip: Longer is Better but Not Necessary in Vision Language Models github&#xff1a;https://github.com/dvlab-research/VisionZip LLaVA論文&#xff1a;https://arxiv.org/abs/2310.03744 LLaVA倉庫&#xff1a;https://github.…

華為設備OSPF配置與實戰指南

一、基礎配置架構 sysname HUAWEI-ABR ospf 100 router-id 1.1.1.1area 0.0.0.0network 10.1.1.0 0.0.0.255 # 將接口加入區域0 interface GigabitEthernet0/0/1ospf enable 100 area 0.0.0.0 # 華為支持點分十進制區域號bandwidth-reference 10000 # 設置10Gbps參考帶寬…

區塊鏈架構深度解析:從 Genesis Block 到 Layer 2

# 區塊鏈架構深度解析&#xff1a;從 Genesis Block 到 Layer 2 目錄 一、Genesis Block&#xff1a;區塊鏈的起點 二、Layer 0&#xff1a;區塊鏈的底層網絡架構 三、Layer 1&#xff1a;核心協議層 &#x1f680; 四、Layer 2&#xff1a;擴展性解決方案 五、未來展望&a…

【位運算】丟失的數字(easy)

34. 丟失的數字&#xff08;easy&#xff09; 題?描述&#xff1a;方法一&#xff1a;排序解法&#xff08;位運算&#xff09;&#xff1a;C 算法代碼&#xff1a;Java 算法代碼&#xff1a; 題?鏈接&#xff1a; 268. 丟失的數字 題?描述&#xff1a; 給定?個包含 [0, n…

如何通過RL真正提升大模型的推理能力?NVIDIA提出長期強化學習訓練框架ProRL

原文&#xff1a;https://mp.weixin.qq.com/s/QLFKvb8Ol3CX9uWKBXSrow 論文&#xff1a;ProRL: Prolonged Reinforcement Learning Expands Reasoning Boundaries in Large Language Models Abs&#xff1a;https://arxiv.org/abs/2505.24864 權重下載&#xff1a;https://hugg…

ORM 框架的優缺點分析

ORM 框架的優缺點分析 一、ORM 框架概述 ORM(Object-Relational Mapping)是一種將關系型數據庫與面向對象編程進行映射的技術框架。它通過將數據庫表映射為編程語言中的類,將記錄映射為對象,將字段映射為屬性,實現了用面向對象的方式操作數據庫。 核心價值:ORM 在數據庫和…

1. 數據庫基礎

1.1 什么是數據庫 ? mysql 本質是一種網絡服務, 是基于 C(mysql) S(mysqld)的 網絡服務. 存儲數據用文件就可以了&#xff0c;為什么還要弄個數據庫&#xff1f;文件保存數據存在以下缺點&#xff1a; 文件的安全性問題。文件不利于數據查詢和管理。文件不利于存儲海量數據。…