這篇文章是MySQL的進階學習,給大家詳細介紹一下MySQL中的事務、4大功能(ACID)以及事務的隔離級別,希望對大家有幫助!
【相關推薦:mysql影片教學】
本文操作與測試所使用的環境版本是5.7.21
mysql> select version(); +-----------+ | version() | +-----------+ | 5.7.21 | +-----------+ 1 row in set (0.00 sec)
記住:我們常見的MySQL儲存引擎中只有InnoDB是支援交易的。所以下面的操作也都是在InnoDB下方做的。
一. 什麼是交易
比較經典的案例就是銀行轉帳:小A向小B轉帳100元
#正常的狀況:小A的帳戶扣減100元,小B的帳戶增加100元。
非正常情況: 小A的帳戶扣減100元,小B帳戶金額不變。
非正常情況下,小A帳戶扣減100之後銀行系統出現問題,小B帳戶增加100元的操作並沒有執行。也就是兩邊金額對不上了,小A不願意,小B不願意,銀行也不願意啊。事務的出現就是為了避免非正常狀況的出現,讓大家都滿意。
二. 事務的4大特性(ACID)
事務的操作是不可分割的,要嘛都操作,要嘛都不操作,就像轉帳一樣,不存在中間狀態。而且這個原子性不是說只有一個動作,可能會有很多的操作,但是從結果來看是不可分割的,也就是說原子性是一個結果狀態。
執行事務的前後,資料保持一致,就像銀行帳戶系統一樣無論事務是否成功,兩者的帳戶總額應該是一樣的。
多個交易同時操作資料的時候,多個交易直接互相隔離,不會互相影響。
一個交易在提交後對資料的影響是永久的,寫入磁碟中不會遺失。
三.明確交易、隱含交易
#mysql的交易分為明確交易
與隱式事務
,預設的事務是隱式事務,由變數autocommit
在操作的時候會自動開啟,提交,回滾。
控制的關鍵指令如下
set autocommit=0; -- 关闭自动提交事务(显式) set autocommit=1; -- 开启自动提交事务(隐式) -- 当autocommit=0的时候手动控制事务 rollback; -- 回滚事务 commit; -- 提交事务 -- 当autocommit=1 自动提交事务,但是可以控制手动提交 start transaction; -- 开启事务(或者用begin开启事务) commit; -- 提交事务 rollback; -- 回滚事务 SAVEPOINT 保存点名称; -- 保存点(相当于存档,可以不用回滚全部操作) rollback to 保存点; -- 回滚到某个保存点 (这个后面就不测试,知道有这个操作就行)
先建一張表格ajisun
mysql> create table ajisun(id int(5), name varchar(20) character set utf8 COLLATE utf8_bin ) engine=innodb character set= utf8mb4 COLLATE = utf8mb4_bin; Query OK, 0 rows affected (0.03 sec)
1. 隱含交易
-- 看下当前autocommit的状态是,默认是on状态 mysql> show variables like 'autocommit'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.01 sec) -- 插入一条数据 mysql> insert into ajisun values(1,'阿纪'); Query OK, 1 row affected (0.00 sec) mysql> rollback; -- 执行rollback 也是没有效果的,还是能够查询到插入的数据(不需要我们手动控制commit) mysql> select * from ajisun; +------+--------+ | id | name | +------+--------+ | 1 | 阿纪 | +------+--------+ 1 row in set (0.00 sec)
#2. 明確事務方式1
明確事務由我們自己控制事務的開啟,提交,回滾等操作
-- 开启显式事务-回滚 mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select * from ajisun; +------+--------+ | id | name | +------+--------+ | 1 | 阿纪 | +------+--------+ 1 row in set (0.00 sec) mysql> insert into ajisun values(2,'纪先生'); Query OK, 1 row affected (0.00 sec) -- 插入后可以看见2条数据 mysql> select * from ajisun; +------+-----------+ | id | name | +------+-----------+ | 1 | 阿纪 | | 2 | 纪先生 | +------+-----------+ 2 rows in set (0.00 sec) -- 回滚之后上面插入的数据就没了 mysql> rollback; Query OK, 0 rows affected (0.00 sec) mysql> select * from ajisun; +------+--------+ | id | name | +------+--------+ | 1 | 阿纪 | +------+--------+ 1 row in set (0.00 sec)
-- 插入一条数据 mysql> insert into ajisun values(2,'ajisun'); Query OK, 1 row affected (0.01 sec) -- 提交 mysql> commit; Query OK, 0 rows affected (0.00 sec) -- 回滚 mysql> rollback; Query OK, 0 rows affected (0.00 sec) -- 先提交commit,在rollback 数据依然存在,说明commit生效,事务已提交,回滚就不生效了。 mysql> select * from ajisun; +------+--------+ | id | name | +------+--------+ | 1 | 阿纪 | | 2 | ajisun | +------+--------+ 2 rows in set (0.00 sec)
3. 明確事務方式2
使用start transaction
先改成預設的交易set autocommit=1;
-- 开启事务 mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> delete from ajisun where id=1; Query OK, 1 row affected (0.00 sec) -- 提交事务 mysql> commit; Query OK, 0 rows affected (0.01 sec) mysql> select * from ajisun; +------+--------+ | id | name | +------+--------+ | 2 | ajisun | +------+--------+ 1 row in set (0.00 sec)
-- 开启事务 mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> delete from ajisun where id =2; Query OK, 1 row affected (0.01 sec) -- 回滚事务 mysql> rollback; Query OK, 0 rows affected (0.01 sec) -- 删除操作失效了 mysql> select * from ajisun; +------+--------+ | id | name | +------+--------+ | 2 | ajisun | +------+--------+ 1 row in set (0.00 sec)
#四. 並發事務中的問題
如果對錶的操作同一時間只有一個事務就不會有問題,但是這是不可能的。現實中都是盡可能的利用,多個事務同時操作。多個事務就會帶來不少的問題,例如髒讀
,髒寫
,`不可重複讀取
,幻讀
1. 髒讀
一個交易讀取到另一個未提交交易修改後的資料這就是髒讀。
例如兩個事務a,b: 同時操作一筆記錄
a事務修改記錄後還沒有正式提交到資料庫,這時b事務去讀取,然後用讀取到的資料進行後續操作。
如果a交易回滾了,這個修改後的資料就不存在了,那麼b事務就是在使用一個不存在的資料。這種就是髒數據。
2. 髒寫(資料遺失)
一個交易修改了另一個未提交交易修改過的資料
#例如兩個事務a,b: 同時操作一筆記錄
a事務修改後沒有提交, 接著b事務也修改同一數據,然後b事務提交數據。
如果a事務回滾自己的修改,同時也把b事務的修改也回滾了,造成的問題就是:b事務修改了也提交了,但是資料庫並沒有改變,這種情況就是髒寫。
3. 不可重複讀取
一個事務只能讀到另一個已經提交的事務修改過的數據,並且其他事務每對該數據進行一次修改並提交後,該事務都能查詢得到最新值。
也就是在同一个事务中多次读取同一条记录,得到的内容都不一样(在每次读取之前都有其他事务完成修改并提交),这就是不可重复读
。
4. 幻读
在一个事务内 相同条件查询数据,先后查询到的记录数不一样
也就是一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来,那就意味着发生了幻读
不可重复读和幻读的区别:不可重复读重点在于同一条记录前后数据值不一样(内容的变化),而幻读重点在于相同查询条件前后所获取的记录数不一样(条数的变化)
五. 事务的隔离级别
上面说的事务的并发问题,在不同的场景下要求不一样,能接受的问题也不一样。他们之间的严重性排序如下:
脏写 > 脏读 > 不可重复读 > 幻读
MySQL中提供了4种隔离级别来处理这几个问题,如下
隔离级别 | 脏读 | 不可重复读 | 幻影读 |
---|---|---|---|
READ- UNCOMMITTED | √ | √ | √ |
READ-COMMITTED | × | √ | √ |
REPEATABLE-READ | × | × | √ |
SERIALIZABLE | × | × | × |
SQL 标准定义了四个隔离级别:
脏读
、不可重复读
和幻读
。但是并发度最高幻读
和不可重复读
仍有可能发生。脏读
和不可重复读
,但幻读
仍有可能发生。脏读
、不可重复读
以及幻读
。并发度也是最低的MySQL默认采用的 REPEATABLE_READ 隔离级别 Oracle默认采用的 READ_COMMITTED 隔离级别
1. 如何设置隔离级别
可以通过变量参数transaction_isolation
查看隔离级别
mysql> SELECT @@transaction_isolation; +-------------------------+ | @@transaction_isolation | +-------------------------+ | REPEATABLE-READ | +-------------------------+ 1 row in set (0.00 sec) mysql> show variables like '%transaction_isolation%'; +-----------------------+-----------------+ | Variable_name | Value | +-----------------------+-----------------+ | transaction_isolation | REPEATABLE-READ | +-----------------------+-----------------+ 1 row in set (0.02 sec)
修改的命令:SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL $[level];
level的值就是4中隔离级别READ-UNCOMMITTED
READ-COMMITTED
REPEATABLE-READ
SERIALIZABLE
设置全局隔离级别
只对执行完该语句之后产生的会话起作用。
当前已经存在的会话无效。
set global transaction_isolation='read-uncommitted'; set global transaction_isolation='read-committed'; set global transaction_isolation='repeatable-read'; set global transaction_isolation='serializable';
例如:
会话A
mysql> set global transaction_isolation='serializable'; Query OK, 0 rows affected (0.01 sec) mysql> select @@global.transaction_isolation; +--------------------------------+ | @@global.transaction_isolation | +--------------------------------+ | SERIALIZABLE | +--------------------------------+ 1 row in set (0.00 sec) -- 当前会话(设置之前就已经存在的会,级别是默认的) mysql> select @@transaction_isolation; +-------------------------+ | @@transaction_isolation | +-------------------------+ | REPEATABLE-READ | +-------------------------+ 1 row in set (0.00 sec)
会话B(set之后新建的会话)
mysql> select @@global.transaction_isolation; +--------------------------------+ | @@global.transaction_isolation | +--------------------------------+ | SERIALIZABLE | +--------------------------------+ 1 row in set (0.00 sec) mysql> select @@transaction_isolation; +-------------------------+ | @@transaction_isolation | +-------------------------+ | SERIALIZABLE | +-------------------------+ 1 row in set (0.00 sec)
设置会话的隔离级别
对当前会话的所有后续的事务有效
该语句可以在已经开启的事务中间执行,但不会影响当前正在执行的事务。
如果在事务之间执行,则对后续的事务有效。
set session transaction_isolation='read-uncommitted'; set session transaction_isolation='read-committed'; set session transaction_isolation='repeatable-read'; set session transaction_isolation='serializable';
比如:
会话A
mysql> set session transaction_isolation='read-uncommitted'; Query OK, 0 rows affected (0.00 sec) mysql> select @@transaction_isolation; +-------------------------+ | @@transaction_isolation | +-------------------------+ | READ-UNCOMMITTED | +-------------------------+ 1 row in set (0.00 sec)
新建会话B(依然是默认的级别:可重复读)
mysql> select @@transaction_isolation; +-------------------------+ | @@transaction_isolation | +-------------------------+ | REPEATABLE-READ | +-------------------------+ 1 row in set (0.00 sec)
2. 怎么选择隔离级别
一般情况下默认的可重复读
就好了,一般很少改这个,除非业务场景特殊
记住一点:隔离级别越高,并发问题就越少,但并发性也就越低,所以还是要根据业务选择来。
六. 总结
事务的四大特性:原子性,一致性,隔离性,持久性
事务的常见命令:
set autocommit=0/1; -- 关闭/开启自动提交事务 start transaction; -- 开启事务(或者用begin) rollback; -- 回滚事务 commit; -- 提交事务
并发事务的问题:脏写 > 脏读 > 不可重复读 > 幻读
需要熟悉事务的4种隔离级别以及MySQL默认级别
怎么设置隔离级别(global,session)
更多编程相关知识,请访问:编程入门!!
以上是深入了解MySQL中的事務、4大功能、隔離級別的詳細內容。更多資訊請關注PHP中文網其他相關文章!