什麼是事務?
用 MySQL 官方的一句話來描述事務是什麼? MySQL 事務主要用於處理操作量大,複雜度高的資料。那何為數據量大?何為複雜度高呢?我用我自己的理解來描述一下吧。事務其實就是 MySQL 中處理資料的一種方式,主要用在資料完整性高,資料之間依賴性大的情況下的一種資料處理方式。舉個例子,小張向小李的銀行卡打 200 塊錢,在小張點擊了確認轉帳的按鈕時,系統突然崩潰了。會出現這樣幾中不正確的情況:
1. 小張的錢打到小李的帳戶上,但是自己的帳戶上的錢沒被扣.
#2. 小張的錢打沒到小李的帳戶上了,但是自己帳戶上的錢被扣.
這樣的業務場景就需要MySQL 事務保持,即使機器出故障的情況下,數據仍然是正確的.
事務使用的條件
MySQL 要使用事務,需要MySQL 中的儲存引擎支援。現目前 MySQL 內建的儲存引擎支援事務的有 InnoDB、NDB cluster, 第三方的儲存引擎有 PBXT 和 XtrDB.
##事務有什麼特色?
MySQL 中的事務有以下幾個特點(ACID):#原子性(atomicity):
一個事務必須被作為一個不可分割的最小工作單元,每個事務中的所有操作必須要么成功,或者要么失敗,永遠不可能一些操作失敗,一些操作成功,這就是所謂的原子性的概念.#一致性(consistency):
一致性就像上面舉的一個例子一樣,當發生異常情況下,資料仍然是正確的。就是說當一個事務執行失敗了,資料之間是不會受異常的情況而影響,永遠保持著他的正確性.##隔離性(isolation):當一個交易還未提交,每個事務之間是相互隔離的,互補受到影響.
#持久性(durability):#當一個交易進行提交之後,發生的變更就會永遠保存在資料庫中.
#交易的隔離等級
在談及到MySQL 的隔離性的特點,就不得不說隔離性的幾種等級。至於為什麼會涉及到這一點,可以這樣簡單的理解:如果同一時刻,有兩個請求在執行事務的操作,並且這兩個事務是對同一條數據做操作,那麼到底最終的結果是以誰的為準呢?不同的隔離等級導致的結果不一樣,因此交易的隔離等級也是一個非常重要的點.
隔離等級分為如下幾點:
1. 未提交讀取(READ UNCOMMITTED)一個事務中對資料所做的修改,即使沒有提交,這個修改對其他的事務仍是可見的,這種情況下就容易出現髒讀,影響了資料的完整性.
舉例:小明在用支付寶支付時,查看了銀行卡的餘額還有300 塊,其實只有100 塊,只是因為他女朋友正在向銀行卡存款了200 塊,此時女朋友不想存了,點擊了回滾操作,小明進行支付卻失敗了.
##2. 讀取提交(READ COMMITTED)
一個事務開始時,只能看見其他已經提交過的事務。這種情況下容易出現不可重複讀(兩次讀的結果不一樣).
舉例:同樣用上面的例子舉例,當他女朋友在刷卡時卡里餘額有100 塊,但是在點擊最終支付時,提示餘額不足,此時看卡里的錢沒了。這是因為小明女朋友在支付時,小明操作的事務還未提交,所以小明女朋友兩次看到的結果不一樣.3. 可重複讀(REPEATABLE READ)
多次讀取記錄的結果都是一致的,可重複讀取可以解決上面的不可重複讀取的情況。但是有這樣一種情況,當一個事務在讀取某個範圍的記錄時,另外一個事務在這個範圍內插入了一條新的數據,當事務再次進行讀取數據時,發現比第一次讀取記錄多了一條,這就是所謂的幻讀,兩次讀取的結果不一致.
舉例:小明女朋友在查看銀行卡的記錄時,看見有5 條消費記錄,此時小明正在消費,這時候消費記錄裡面記錄了這條消費記錄,當女朋友再次讀取記錄時,發現有6 條記錄了.4. 可串行(SERIALIZABLE)
串行就像一個隊列一個樣,每個事務都是排隊等候執行,只有前一個事務提交之後,下一個事務才能進行操作。這種情況雖然可以解決上面的幻讀,但是他會在每一條數據上加一個鎖,容易導致大量的鎖超時和鎖競爭,特別不適用在一些高並發的業務場景下.
舉例:我們在銀行排隊存錢,只有前一個人全部操作完,下一個人才可以進行辦理。中間的人是不可以插隊的,只能一個一個的排對,事務的串行就是這樣的一個概念,其實所謂的串行模式都是這樣的一個概念.隔離性總結
透過上面的舉例,我們不難發現。髒讀和不可重複讀重在更新數據,然後幻讀重在插入數據.
多種存儲引擎時事務的處理方式
根據上面事務使用的條件,我們可以得知有的儲存引擎是不支援交易的,例如MyISAM 儲存引擎就不支援。那如果在一個事務中使用了事務性的存儲引擎和非事務性的存儲,提交是可以正常進行,但是回滾非事務性的存儲引擎則會顯示響應的錯誤信息,具體信息和存儲引擎有關.
如何使用事務
MySQL 中事務隱式開啟的,也就是說,一個sql 語句就是一個事務,當sql 語句執行完畢,事務就提交了。在示範的過程中,我們明確開啟.
MySQL 中的自動提交
上面提到了MySQL 中事務是隱式開啟的,則代表我們每一個sql 是自動提交的,需要關閉則需要設定autocommit 選項.
// 查看autocommit配置值(1或者ON则表示开启) mysql root@127.0.0.1:(none)> show variables like '%autocommit%'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set Time: 0.018s // 设置autocommit配置值 mysql root@127.0.0.1:(none)> set autocommit = 0; Query OK, 0 rows affected Time: 0.000s mysql root@127.0.0.1:(none)> show variables like '%autocommit%'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | OFF | +---------------+-------+ 1 row in set Time: 0.013s
1. 表結構如下
mysql root@127.0.0.1:test> desc user; +-------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | <null> | auto_increment | | name | varchar(255) | YES | | <null> | | | age | int(2) | YES | | <null> | | +-------+--------------+------+-----+---------+----------------+ 3 rows in set Time: 0.013s
SQL 語句
CREATE TABLE `test`.`Untitled` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `age` int(2) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
2. 使用交易
MySQL 實作交易
下面的程式碼,我們主要做了以下幾個操作
a. 開啟交易
b. 修改數據
c. 查詢資料是否改變
d. 資料回滾
e. 再次查詢數據,發現資料變回修改之前的狀態
f .修改數據
g. 事務提交
h. 查詢數據,發現數據變成最後一次修改的狀態
i. 嘗試事務回滾
j. 查詢驗證是否被回滾了,發現資料還是為最後一次修改的狀態,事務回滾失敗
// 我们先查看表中的数据,id为1的age字段是12 mysql root@127.0.0.1:test> select * from user; +----+------+-----+ | id | name | age | +----+------+-----+ | 1 | 张三 | 12 | | 2 | 李四 | 15 | +----+------+-----+ 2 rows in set Time: 0.013s // 开启事务 mysql root@127.0.0.1:test> begin; Query OK, 0 rows affected Time: 0.001s // 将id为1的age字段改为10 mysql root@127.0.0.1:test> update user set age=10 where id=1; Query OK, 1 row affected Time: 0.001s // 再次查询数据时,发现数据改为修改后的值 mysql root@127.0.0.1:test> select * from user; +----+------+-----+ | id | name | age | +----+------+-----+ | 1 | 张三 | 10 | | 2 | 李四 | 15 | +----+------+-----+ 2 rows in set Time: 0.012s // 此时我们进行回滚操作 mysql root@127.0.0.1:test> rollback; Query OK, 0 rows affected Time: 0.001s // 再次查询发现数据回到最初状态 mysql root@127.0.0.1:test> select * from user; +----+------+-----+ | id | name | age | +----+------+-----+ | 1 | 张三 | 12 | | 2 | 李四 | 15 | +----+------+-----+ 2 rows in set Time: 0.019s // 我们再次对数据进行修改 mysql root@127.0.0.1:test> update user set age=15 where id=1; Query OK, 1 row affected Time: 0.001s // 此时将事务进行提交 mysql root@127.0.0.1:test> commit; Query OK, 0 rows affected Time: 0.000s // 发现此时的数据变为我们最终提交的值 mysql root@127.0.0.1:test> select * from user; +----+------+-----+ | id | name | age | +----+------+-----+ | 1 | 张三 | 15 | | 2 | 李四 | 15 | +----+------+-----+ 2 rows in set Time: 0.012s // 我们尝试用刚才回滚的方式进行还原数据 mysql root@127.0.0.1:test> rollback; Query OK, 0 rows affected Time: 0.000s // 发现数据无法回退了,仍然是提交后的数据 mysql root@127.0.0.1:test> select * from user; +----+------+-----+ | id | name | age | +----+------+-----+ | 1 | 张三 | 15 | | 2 | 李四 | 15 | +----+------+-----+ 2 rows in set Time: 0.017s
PHP 實作事務實例代碼
<?php // 连接MySQL $mysqli = new mysqli('127.0.0.1', 'root', '123456', 'test', 3306); // 关闭事务自动提交 $mysqli->autocommit(false); // 1.开启事务 $mysqli->begin_transaction(); // 2.修改数据 $mysqli->query("update user set age=10 where id=1"); // 3.查看数据 $mysqli->query("select * from user"); // 4.事务回滚 $mysqli->rollback(); // 5.查看数据 $mysqli->query("select * from user"); // 7.修改数据 $mysqli->query("update user set age=15 where id=1"); // 8.事务提交 $mysqli->commit(); // 9.事务回滚 $mysqli->rollback(); // 10.查看数据 $mysqli->query("select * from user");
#如何設定交易的隔離等級
// 查看当前的事务隔离级别 mysql root@127.0.0.1:test> select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 1 row in set Time: 0.015s // 设置隔离级别 set session transaction isolation level 隔离级别(上面事务隔离级别中的英文单词);
以上是MySQL 事務最全詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!