一、CURD不加控制,會有什么問題?
(1)因為,MySQL里面存的是數據,所以很有可能會被多個客戶訪問,所以mysqld可能一次會接受到多個關于CURD的請求。(2)且mysql內部是采用多線程來完成數據存儲等相關工作的,所以必然會存在對數據并發訪問的場景? ? ? ——>會導致一些多請求并發可能產生的異常結果
? ? ? ?比如同行轉賬,按道理是我減100,你加100,但是因為我是同行所以用的是一張數據庫的表,可能我減100的時候還沒做完網絡或者數據庫出問題等其他原因導致沒有給你加100,那么整個操作就會出現一個中間過程(我減了但是你沒有加),這就有問題,在這種情況下我們允許異常產生,一旦操作沒有完成我們應該把減掉的100再加回來,就好像什么都沒做,等待下次合適的時候再去轉賬。這就相當于轉賬之后不要有中間過程,而是在轉的時候一旦出現異常就直接進行回滾,因為不回滾的話就會有問題,必須得回滾保證和初始的狀態一樣,這就叫我們的回滾操作。在高并發的場景下數據或多或少都會出現這樣的問題,所以這也就要求mysql必須要有針對這類問題的解決方案。
二、CURD滿足什么屬性,能解決上述問題?
1. 買票的過程得是原子的吧(要么不搶,要么搶到,出現中間狀態會回滾)
2. 買票互相應該不能影響吧(我買的時候你正好過來,我的行為不能影響你,也就是彼此之間得是割裂的)
3. 買完票應該要永久有效吧 ( 購買成功這個情況必須得做持久化 )
4. 買前,和買后都要是確定的狀態吧(買前就是沒買,買后就是買了,不允許有不確定的狀態)
三、什么是事務?
? ? ? ?事務就是一組DML語句組成,這些語句在邏輯上存在相關性(單獨一條是沒有意義的,比如轉賬就應該至少有兩條sql語句,即我減100,你加100,整體在一起才有轉賬邏輯,所以事務一定要站在mysql的上層去看待sql語句,具體完成一個由多條sql語句構成的應用層功能,在業務上有具體含義的動作),這一組DML語句要么全部成功,要么全部失敗,是一個整體。MySQL提供一種機制,保證我們達到這樣的效果。事務還規定不同的客戶端看到的數據是不相同的。
? ??事務就是要做的或所做的事情,主要用于處理操作量大,復雜度高的數據。假設一種場景:你畢業了,學校的教務系統后臺 MySQL 中,不再需要你的數據,要刪除你的所有信息(一般不會:) ), 那么要刪除你的基本信息(姓名,電話,籍 貫等)的同時,也刪除和你有關的其他信息,比如:你的各科成績,你在校表現,甚至你在論壇發過的文章等。這樣,就需要多條 MySQL 語句構成,那么所有這些操作合起來,就構成了一個事務。
? ? ? 正如我們上面所說,一個 MySQL 數據庫,可不止你一個事務在運行,同一時刻,甚至有大量的請求被包裝成事務, 在向 MySQL 服務器發起事務處理請求。而每條事務至少一條 SQL ,最多很多 SQL ,這樣如果大家都訪問同樣的表數據,在不加保護的情況,就絕對會出現問題。甚至,因為事務由多條 SQL 構成,那么,也會存在執行到一半出錯或者 不想再執行的情況,那么已經執行的怎么辦呢???
? ? ? 所以,一個完整的事務,絕對不是簡單的sql集合,還需要滿足如下四個屬性:
1、原子性:一個事務(transaction)中的所有操作,要么全部完成,要么全部不完成,不會結束在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過 一樣。
2、一致性:在事務開始之前和事務結束以后,數據庫的完整性沒有被破壞。(一種狀態變為另一種狀態結果是可預期的)這表示寫入的資料必須完全符合所有的預設規則,這包含資料的精確度、串聯性以及后續數據庫可以自發性地完成預定的工作。
3、隔離性:數據庫允許多個并發事務同時對其數據進行讀寫和修改的能力,隔離性可以防止多個事務并發執行時由于交叉執行而導致數據的不一致。事務隔離分為不同級別,包括讀未提交( Read uncommitted )、讀提交 ( read committed )、可重復讀( repeatable read )和串行化( Serializable )
4、持久性:事務處理結束后,對數據的修改就是永久的,即便系統故障也不會丟失。
mysql從技術上只要保證了134,就可以做到2,所以134是因 2是果(還需要上層用戶配合)?
上面四個屬性,可以簡稱為 ACID 。
原子性(Atomicity,或稱不可分割性)
一致性(Consistency)
隔離性(Isolation,又稱獨立性)
持久性(Durability)
? ? ? ? mysql需要幫不同的客戶端處理不同的事務請求,所以運行期間在自身內部必然存在大量的事務,所以他必須得將事務按照先描述后組織的形式管理起來,所以mysql會把這些事務打包描述成對象,然后放入到事務執行列表里,并幫我們解決一系列執行事務時可能出現的問題
四、為什么會有事務
? ? ? ?事務被 MySQL 編寫者設計出來(不是天然就有的,而是使用過程中發現應用層需要才被設計出來的),本質是為了當應用程序訪問數據庫的時候,事務能夠簡化我們的編程模型,不需要我們去考慮各種各樣的潛在錯誤和并發問題.(你只需要說你的需求,其他的我幫你處理)可以想一下當我們使用事務時,要么提交,要么回滾,我們不會去考慮網絡異常了,服務器宕機了,同時更改一個數據怎么辦對吧?因此事務本質上是為了應用層服務的.而不是伴隨著數據庫系統天生就有的.
備注:我們后面把 MySQL 中的一行信息,稱為一行記錄
五、事務的版本支持
在 MySQL 中只有使用了 Innodb 數據庫引擎的數據庫或表才支持事務, MyISAM 不支持。
查看數據庫引擎?:
mysql> show engines \G ? ? ? ? -- 行顯示
*************************** 1. row ***************************Engine: InnoDB ? ?-- 引擎名稱Support: DEFAULT ? -- 默認引擎Comment: Supports transactions, row-level locking, and foreign keys--支持事務、行級鎖,外鍵
Transactions: YES ? ? ? -- 支持事務XA: YESSavepoints: YES ? ? ? -- 支持事務保存點
*************************** 2. row ***************************Engine: MRG_MYISAMSupport: YESComment: Collection of identical MyISAM tables
Transactions: NOXA: NOSavepoints: NO
*************************** 3. row ***************************Engine: MEMORY ? ?--內存引擎Support: YESComment: Hash based, stored in memory, useful for temporary tables
Transactions: NOXA: NO
Savepoints: NO
*************************** 4. row ***************************Engine: BLACKHOLESupport: YESComment: /dev/null storage engine (anything you write to it disappears)
Transactions: NOXA: NOSavepoints: NO
*************************** 5. row ***************************Engine: MyISAM ? ?Support: YESComment: MyISAM storage engine
Transactions: NO ? ? ? ? ? -- MyISAM不支持事務XA: NOSavepoints: NO
*************************** 6. row ***************************Engine: CSVSupport: YESComment: CSV storage engine
Transactions: NOXA: NOSavepoints: NO
*************************** 7. row ***************************Engine: ARCHIVESupport: YESComment: Archive storage engine
Transactions: NOXA: NOSavepoints: NO
*************************** 8. row ***************************Engine: PERFORMANCE_SCHEMASupport: YESComment: Performance Schema
Transactions: NOXA: NOSavepoints: NO
*************************** 9. row ***************************Engine: FEDERATEDSupport: NOComment: Federated MySQL storage engine
Transactions: NULLXA: NULLSavepoints: NULL
9 rows in set (0.00 sec)
六、事務的提交方式
事務的提交方式常見的有兩種: 自動提交、手動提交
查看事務提交方式 :
show variables like 'autocommit';
?
用 SET 來改變 MySQL 的自動提交模式:
SET AUTOCOMMIT=0; ? ? ? ? ? ?#SET AUTOCOMMIT=0 禁止自動提交
mysql> SET AUTOCOMMIT=1; ? ? ? ? ? #SET AUTOCOMMIT=1 開啟自動提交
七、事務常見操作方式
簡單銀行用戶表
## Centos 7 云服務器,默認開啟3306 mysqld服務
netstat -nltp
## 為了便于演示,我們將mysql的默認隔離級別設置成讀未提交
set global transaction isolation level READ UNCOMMITTED;
## 設置了卻沒有用?,因為需要重啟終端才可以
select @@tx_isolation;
創建測試表
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;
7.1 正常演示 - 證明事務的開始與(定向)回滾
mysql> show variables like 'autocommit'; ?-- 查看事務是否自動提交。我們故意設置成自動提交,看看該選項是否影響begin 從這一行往后所有的語句都屬于這個事務+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit ? | ON ? |
+---------------+-------+
1 row in set (0.00 sec)
mysql> start transaction; ? ? ? ? ? ? ? -- 開始一個事務begin也可以,推薦beginQuery 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; ? ? ? ? ? ? ? ? -- 創建一個保存點save2Query 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,回滾在最開始,哪怕你一個回滾點都沒設置也可以Query OK, 0 rows affected (0.00 sec)
mysql> select * from account; ? ? ? ? ? ? -- 所有剛剛的記錄沒有了Empty set (0.00 sec)commit;--就是把該事務給提交了 無法回滾
但是一般我們很少手動rollback,事務大多數都是為了非正常情況?
7.2?非正常演示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 ? ? ? ? ? ? ? ? ? ? ? ? ?-- ctrl + \ 異常終止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)
?7.3?非正常演示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
mysql> select * from account; --數據存在了,所以commit的作用是將數據持久化到MySQL中
+----+--------+--------+
| id | name | blance |
+----+--------+--------+
| 1 | 張三 | 100.00 |
+----+--------+--------+
1 row in set (0.00 sec)
7.4?非正常演示3 - 對比試驗。證明begin操作會自動更改提交方式,不會受MySQL是否自動提交影響
手動begin就必須手動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> 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)
7.5?非正常演示4 - 證明單條 SQL 與事務的關系
--實驗一
-- 終端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.6 結論
1、只要輸入begin或者start transaction,事務便必須要通過commit提交,才會持久化,與是否設置set autocommit無關。
2、事務可以手動回滾,同時,當操作異常,MySQL會自動回滾
3、對于 InnoDB 每一條 SQL 語言都默認封裝成事務,自動提交。(select有特殊情況,因為 MySQL 有MVCC )
從上面的例子,我們能看到事務本身的原子性(回滾),持久性(commit)
7.7?事務操作注意事項 ?
1、如果沒有設置保存點,也可以回滾,只能回滾到事務的開始。直接使用 rollback(前提是事務還沒有提交)
2、如果一個事務被提交了(commit),則不可以回退(rollback)
3、可以選擇回退到哪個保存點
savepoint save1; 設置保存點? ???rollback to save2;回退保存點
4、InnoDB 支持事務, MyISAM 不支持事務
5、開始事務可以使 start transaction 或者 begin
?0