MySQL 事務主要用於處理操作量大,復雜度高的數據。比如說,在人員管理系統中,你刪除一個人員,你既需要刪除人員的基本資料,也要刪除和該人員相關的信息,如信箱,文章等等,這樣,這些數據庫操作語句就構成一個事務!
- 在 MySQL 中只有使用了 Innodb 數據庫引擎的數據庫或表才支持事務。
- 事務處理可以用來維護數據庫的完整性,保證成批的 SQL 語句要么全部執行,要么全部不執行。
- 事務用來管理 insert,update,delete 語句
一般來說,事務是必須滿足4個條件(ACID)::原子性(Atomicity,或稱不可分割性)、一致性(Consistency)、隔離性(Isolation,又稱獨立性)、持久性(Durability)。
-
原子性:一個事務(transaction)中的所有操作,要么全部完成,要么全部不完成,不會結束在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
-
一致性:在事務開始之前和事務結束以后,數據庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預設規則,這包含資料的精確度、串聯性以及后續數據庫可以自發性地完成預定的工作。
-
隔離性:數據庫允許多個並發事務同時對其數據進行讀寫和修改的能力,隔離性可以防止多個事務並發執行時由於交叉執行而導致數據的不一致。事務隔離分為不同級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重復讀(repeatable read)和串行化(Serializable)。
-
持久性:事務處理結束后,對數據的修改就是永久的,即便系統故障也不會丟失。
在 MySQL 命令行的默認設置下,事務都是自動提交的,即執行 SQL 語句后就會馬上執行 COMMIT 操作。因此要顯式地開啟一個事務務須使用命令 BEGIN 或 START TRANSACTION,或者執行命令 SET AUTOCOMMIT=0,用來禁止使用當前會話的自動提交。
事務的隔離級別
-
Read uncommitted 讀未提交,顧名思義,就是一個事務可以讀取另一個未提交事務的數據。事例:老板要給程序員發工資,程序員的工資是3.6萬/月。但是發工資時老板不小心按錯了數字,按成3.9萬/月,該錢已經打到程序員的戶口,但是事務還沒有提交,就在這時,程序員去查看自己這個月的工資,發現比往常多了3千元,以為漲工資了非常高興。但是老板及時發現了不對,馬上回滾差點就提交了的事務,將數字改成3.6萬再提交。分析:實際程序員這個月的工資還是3.6萬,但是程序員看到的是3.9萬。他看到的是老板還沒提交事務時的數據。這就是臟讀。
-
Read committed 讀提交,顧名思義,就是一個事務要等另一個事務提交后才能讀取數據。事例:程序員拿着信用卡去享受生活(卡里當然是只有3.6萬),當他埋單時(程序員事務開啟),收費系統事先檢測到他的卡里有3.6萬,就在這個時候!!程序員的妻子要把錢全部轉出充當家用,並提交。當收費系統准備扣款時,再檢測卡里的金額,發現已經沒錢了(第二次檢測金額當然要等待妻子轉出金額事務提交完)。程序員就會很郁悶,明明卡里是有錢的…分析:這就是讀提交,若有事務對數據進行更新(UPDATE)操作時,讀操作事務要等待這個更新操作事務提交后才能讀取數據,可以解決臟讀問題。但在這個事例中,出現了一個事務范圍內兩個相同的查詢卻返回了不同數據,這就是不可重復讀。
-
Repeatable read 重復讀,就是在開始讀取數據(事務開啟)時,不再允許修改操作事例:程序員拿着信用卡去享受生活(卡里當然是只有3.6萬),當他埋單時(事務開啟,不允許其他事務的UPDATE修改操作),收費系統事先檢測到他的卡里有3.6萬。這個時候他的妻子不能轉出金額了。接下來收費系統就可以扣款了。分析:重復讀可以解決不可重復讀問題。寫到這里,應該明白的一點就是,不可重復讀對應的是修改,即UPDATE操作。但是可能還會有幻讀問題。因為幻讀問題對應的是插入INSERT操作,而不是UPDATE操作。
-
Serializable 序列化 Serializable 是最高的事務隔離級別,在該級別下,事務串行化順序執行,可以避免臟讀、不可重復讀與幻讀。但是這種事務隔離級別效率低下,比較耗數據庫性能,一般不使用。什么時候會出現幻讀?事例:程序員某一天去消費,花了2千元,然后他的妻子去查看他今天的消費記錄(全表掃描FTS,妻子事務開啟),看到確實是花了2千元,就在這個時候,程序員花了1萬買了一部電腦,即新增INSERT了一條消費記錄,並提交。當妻子打印程序員的消費記錄清單時(妻子事務提交),發現花了1.2萬元,似乎出現了幻覺,這就是幻讀。序列化解決幻讀。
事務控制語句:
-
COMMIT 也可以使用 COMMIT WORK,不過二者是等價的。COMMIT 會提交事務,並使已對數據庫進行的所有修改成為永久性的;
-
ROLLBACK 也可以使用 ROLLBACK WORK,不過二者是等價的。回滾會結束用戶的事務,並撤銷正在進行的所有未提交的修改;
-
SAVEPOINT identifier,SAVEPOINT 允許在事務中創建一個保存點,一個事務中可以有多個 SAVEPOINT;
-
RELEASE SAVEPOINT identifier 刪除一個事務的保存點,當沒有指定的保存點時,執行該語句會拋出一個異常;
-
ROLLBACK TO identifier 把事務回滾到標記點;
-
SET TRANSACTION 用來設置事務的隔離級別。InnoDB 存儲引擎提供事務的隔離級別有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE。
MYSQL 事務處理主要有兩種方法:
1、用 BEGIN, ROLLBACK, COMMIT來實現
- BEGIN /BEGIN WORK/START TRANSACTION開始一個事務
- ROLLBACK 事務回滾
- COMMIT 事務確認
2、直接用 SET 來改變 MySQL 的自動提交模式:
- SET @@AUTOCOMMIT=0 禁止自動提交
- SET @@AUTOCOMMIT=1 開啟自動提交
mysql> use RUNOOB; Database changed mysql> CREATE TABLE runoob_transaction_test( id int(5)) engine=innodb; # 創建數據表 Query OK, 0 rows affected (0.04 sec) mysql> select * from runoob_transaction_test; Empty set (0.01 sec) mysql> begin; # 開始事務 Query OK, 0 rows affected (0.00 sec) mysql> insert into runoob_transaction_test value(5); Query OK, 1 rows affected (0.01 sec) mysql> insert into runoob_transaction_test value(6); Query OK, 1 rows affected (0.00 sec) mysql> commit; # 提交事務 Query OK, 0 rows affected (0.01 sec) mysql> select * from runoob_transaction_test; +------+ | id | +------+ | 5 | | 6 | +------+ 2 rows in set (0.01 sec) mysql> begin; # 開始事務 Query OK, 0 rows affected (0.00 sec) mysql> insert into runoob_transaction_test values(7); Query OK, 1 rows affected (0.00 sec) mysql> rollback; # 回滾 Query OK, 0 rows affected (0.00 sec) mysql> select * from runoob_transaction_test; # 因為回滾所以數據沒有插入 +------+ | id | +------+ | 5 | | 6 | +------+ 2 rows in set (0.01 sec) mysql>
-
連接器 長鏈接和短鏈接長連接和短連接數據庫里面,長連接是指連接成功后,如果客戶端持續有請求,則一直使用同一個連接。短連接則是指每次執行完很少的幾次查詢就斷開連接,下次查詢再重新建立一個。建立連接的過程通常是比較復雜的,建議在使用中要盡量減少建立連接的動作,盡量使用長連接。但是全部使用長連接后,有時候 MySQL 占用內存漲得特別快,這是因為 MySQL 在執行過程中臨時使用的內存是管理在連接對象里面的。這些資源會在連接斷開的時候才釋放。所以如果長連接累積下來,可能導致內存占用太大,被系統強行殺掉(OOM),從現象看就是 MySQL 異常重啟了。
show variables like '%max_connections%' 查看最大連接數
show processlist 查看當前連接數
怎么解決這個問題呢?可以考慮以下兩種方案:
定期斷開長連接。使用一段時間,或者程序里面判斷執行過一個占用內存的大查詢后,斷開連接,之后要查詢再重連。MySQL 5.7 以上版本,可以在每次執行一個比較大的操作后,通過執行 mysql_reset_connection 來重新初始化連接資源。這個過程不需要重連和重新做權限驗證,但是會將連接恢復到剛剛創建完時的狀態。
- 查詢緩存 在建立連接后,就開始執行 select 語句了,執行前首先會查詢緩存。MySQL 拿到查詢請求后,會先查詢緩存,看是不是執行過這條語句。執行過的語句及其結果會以 key-value 對的形式保存在一定的內存區域中。key 是查詢的語句,value 是查詢的結果。如果你的查詢能夠直接在這個緩存中找到 key,那么這個value 就會被直接返回給客戶端。如果語句不在查詢緩存中,就會繼續后面的執行階段。執行完成后,執行結果會被存入查詢緩存中。如果查詢命中緩存,MySQL 不需要執行后面的復雜操作,就可以直接返回結果,會提升效率。但是查詢緩存的失效非常頻繁,5,這個表上所有的查詢緩存都會被清空。對於更新壓力大的數據庫來說,查詢緩存的命中率會非常低。如果業務中需要有一張靜態表,很長時間才會更新一次。比如,一個系統配置表,那這張表上的查詢才適合使用查詢緩存。MySQL 提供了這種按需使用的方式。可以將參數 query_cache_type 設置成 DEMAND,對於默認的 SQL 語句都將不使用查詢緩存。而對於你確定要使用查詢緩存的語句,可以SQL_CACHE 顯式指定
-
select SQL_CACHE * from user where id=1 mysql 8.0取消緩存查找
-
分析器 如果查詢緩存未命中,就要開始執行語句了。首先,MySQL 需要對 SQL 語句進行解析。分析器先會做詞法分析。SQL 語句是由多個字符串和空格組成的,MySQL 需要識別出里面的字符串分別是什么,代表什么。MySQL 從你輸入的 select 這個關鍵字識別出來,這是查詢語句。它也要把字符串 user_info 識別成表名,把字符串 id 識別成列名。之后就要做語法分析。根據詞法分析的結果,語法分析器會根據語法規則,判斷輸入的 SQL 語句是否滿足 MySQL 語法。
select name form user
如果你 SQL 語句不對,就會收到 You have an error in your SQL syntax 的錯誤提醒,比如下面這個語句 from 寫成了 form。
-
優化器 經過分析器的詞法分析和語法分析后,還要經過優化器的處理。優化器是在表里面有多個索引的時候,決定使用哪個索引;或者在一個語句有多表關聯(join)的時候,決定各個表的連接順序。比如你執行下面這樣的語句,這個語句是執行兩個表的 join:
SELECT * FROM order_master JOIN order_detail USING (order_id) WHERE order_master.pay_status = 0 AND order_detail.detail_id = 1558963262141624521;
既可以先從表 order_master 里面取出 pay_status = 0 的記錄的 order_id 值,再根據 order_id 值關聯到表 order_detail,再判斷 order_detail 里面 detail_id 的值是否等於 1558963262141624521。也 可以先從表 order_detail 里面取出 detail_id = 1558963262141624521 的記錄的 order_id 值,再根據 order_id 值關聯到 order_master,再判斷 order_master 里面 pay_status 的值是否等於 0。這兩種執行方法的邏輯結果是一樣的,但是執行的效率會有不同,而優化器的作用就是決定選擇使用哪一個方案。優化器階段完成后,這個語句的執行方案就確定下來了,然后進入執行器階段
-
執行器 MySQL 通過分析器知道了要做什么,通過優化器知道了該怎么做,於是就進入了執行器階段,開始執行語句。開始執行的時候,要先判斷一下你對這個表 user_info 有沒有執行查詢的權限,如果沒有,就會返回沒有權限的錯誤,如下所示 (如果命中查詢緩存,會在查詢緩存返回結果的時候,做權限驗證。查詢也會在優化器之前調用 precheck 驗證權限)。
-