併發控制:併發控制對於任何一個允許多個用戶連入進來並請求資源的服務來講都是必須要完成的功能
MySQL作爲服務器來講,其內部有很多數據有很多張表,表中有數據,無論是myisam還是InnoDB存儲引擎對於單張表來講如果沒做分區,那麼其數據都放在同一個文本文件,當兩個客戶端同時發起MySQL會話,連入MySQL時,MySQL服務會啓動兩個會話線程來給兩個用戶建立連接,假如第一個用戶發起select語句查詢表中的所有行,如表tb1,而第二個用戶希望在表中同時要插入數據,那麼這兩條語句能否同時執行?如果插入語句剛好插入一半查詢語句剛查到了剛插入的語句,那麼查詢的結果就不完整了,因此當併發有很多用戶同時訪問時,必須使用一種機制來實現其內部數據的併發訪問控制,以保證每個用戶所看到的數據儘可能是完整的。因此無論任何時候,只要有多個查詢需要在同一時刻修改數據,都會產生併發控制問題。
MySQL是插件式存儲引擎,及其併發訪問控制可以在兩個層次上完成,1.MySQL的核心程序【MySQL內部完成】2.MySQL存儲引擎層完成
在兩個層次上實現:
MySQL併發訪問控制大多數情況下都應該由存儲引擎自動完成,存儲引擎自身會實現對文件進行加鎖而且是隱式鎖,由存儲引擎自動完成。如:將來要備份數據,如果要備份很大的數據集,備份和修改的時間點不一致,數據拿來使用也是沒法用的,必要時要備份整個數據集就必須對整個數據進行加鎖,禁止任何人修改,把數據複製一份來做備份,必須要施加一個全局鎖,就可以在服務器層來完成。
服務器層
存儲引擎層
MySQL在大多數情況下可以實現併發控制,尤其在支持事務引擎的存儲引擎,其底層實現了併發控制的細節和複雜度非常高。
鎖:lock
對於數據庫的數據訪問來講,鎖一般分兩類
兩類:
讀鎖:共享鎖
寫鎖:獨佔鎖 拒絕其他人同時使用
鎖粒度:在表修改時是1到3行,而查找是在10行以後,沒有關聯性,能不能同時執行就取決於鎖粒度。如果是修改時就必須鎖表,那麼後面就不能查詢了
表級鎖 鎖一張表 myisam是表級鎖如果是數據倉庫,一個數據形成以後,很少改了,一次寫多次讀,就用表級鎖
行級鎖 鎖定時,只需要鎖相關的行 InnoDB是行級鎖,如果是讀寫訪問操作一樣多,選擇行級鎖,在線事務處理的場景一般都是用InnoDB存儲引擎, 鎖控制非常複雜,鎖狀態維護和檢查非常麻煩,容易產生死鎖
在行級鎖內部必須要隨時監控死鎖發生的位置,如果發生了,則需要讓一個後退,釋放一個資源,讓另外一個先使用
死鎖:
如表中有兩行1,2
A: 1, 其中A回話需要對1,2行加鎖來做操作,此時已請求了1行鎖,再去請求第2行時,發現B回話鎖定了第2行,B也需要使用第1行,此時B需要請求第1行,在互相請求對方鎖定的資源時,結果就出現死鎖
存儲引擎層施加的鎖由存儲引擎自己實現隱式維護的
鎖分類:
隱式鎖:由存儲引擎自動完成
顯式鎖:用戶可手動施加鎖,表級鎖
手動加鎖:服務器級別實現的
LOCK TABLES tb1 {READ|WRITE},... 對錶請求施加鎖,可以指定多個表用','分開,如果請求時,別人鎖定了就要延遲一段時間才能請求到,必須要等到別人釋放鎖,才能獲取到鎖
UNLOCK TABLES 釋放所有鎖
FLUSH TABLES WITH READ LOCK; 關閉表,請求施加所有表的讀鎖
UNLOCK TABLES;
MariaDB [hellodb]> lock tables students read;
Query OK, 0 rows affected (0.00 sec)
只要不釋放,鎖將一直存在
用別的回話請求可以讀students表
往表中插入數據
新開的會話
MariaDB [(none)]> insert into hellodb.students (Name,Age) values ('liufang',23);插入數據時會一直處於等待狀態,因爲添加了讀鎖,如果不釋放鎖,此處將無法執行,讀鎖可以共享給讀但是不能共享給寫,寫鎖是排他
釋放鎖後馬上就可以寫入成功了
MariaDB [hellodb]> unlock tables;
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> insert into hellodb.students (Name,Age) values ('liufang',23);
Query OK, 1 row affected (31.95 sec) 用來31.95s
施加寫鎖,自己會話讀寫都沒有問題,其他會話讀寫就會處於等待狀態
MariaDB [(none)]> lock tables hellodb.students write;
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> select * from hellodb.students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 22 | M | 2 | 3 |
| 2 | Shi Potian | 22 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
MariaDB [(none)]> insert into hellodb.students (Name,Age) values ('yuanyou',25);
Query OK, 1 row affected (0.01 sec)
另外的會話
MariaDB [hellodb]> select * from hellodb.students where StuID<3;處於等待當中
取消鎖
ariaDB [(none)]> unlock tables;
Query OK, 0 rows affected (0.00 sec)
此時執行成功
MariaDB [hellodb]> select * from hellodb.students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 22 | M | 2 | 3 |
| 2 | Shi Potian | 22 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (1 min 18.43 sec)
InnoDB存儲引擎也支持另外一種顯式鎖(只鎖定挑選出的行):
SELECT ... LOCK IN SHARE MODE; 施加讀鎖
SELECT ... FOR UPDATE;
如果說一個數據庫存儲引擎是支持事務的,則事務是另外一種併發訪問控制機制
事務:ACID【事務的目的也是實現併發訪問控制,而且事務本身是更高級別的訪問控制功能】
一個執行單元:多個SQL語句,要麼都執行,要麼都不執行;
以銀行轉賬列子
事務有兩類狀態:提交【執行成功,就全部成功】,回滾【事務失敗,全部失敗】
A:atomicity,原子性 執行單元那麼成功要麼失敗
C:consistency,一致性 一個一致性狀態轉到另一個一致性狀態 如賬戶總額,加和減要一致
I:isolation,隔離性 一個事務在修改在提交之前其他事務是不可見的,事務彼此之間是隔離的
隔離級別:關係型事務有4個級別
D:durability,持久性 一旦事務提交了,事務執行成功了,必須保證所有數據都能夠永久存儲到持久存儲中去,必須保證數據是提交後的狀態。即便數據庫服務器發生崩潰,其數據依然可以得到。 不一定能達到100%,因爲數據在內存中還沒寫到磁盤時就斷電了,那麼數據一定會丟失的。
機械硬盤寫的能力在80-100次之間每秒鐘,固態硬盤在300-500次之間每秒,PCI-E並行的固態硬盤可以達到數萬到數十萬個io
事務的隔離級別:事務如果完全執行隔離,由於數據集很少,而一個事務中可能包含多個語句,一個事務一旦發起,那麼其他事務就沒法運行,所以併發性很低,隔離性是比想象複雜多,在sql標準上實現了四種隔離級別,每一種都對應了事務所做的修改。
MySQL隔離級別是通過一個參數來設定的
MariaDB [(none)]> show global variables like '%iso%';
+---------------+-----------------+
| Variable_name | Value |
+---------------+-----------------+
| tx_isolation | REPEATABLE-READ | 是會話級別的動態變量,可在會話級別修改立即生效
+---------------+-----------------+
1 row in set (0.00 sec)
READ-UNCOMMITTED(讀未提交):最低隔離級別,會產生“髒讀”(還未提交就能讀到);你所看到的數據別人只要已改,你馬上看到,別人沒提交的數據也可以看到,別人滾過去了也可以看到,沒有隔離的意義,
READ-COMMTTED(讀提交):“不可重複讀” 產生的問題:比如在沒提交之前讀了一個數據是3,提交之後數字改爲5了,再一次讀時是5了。這同一個事務在兩次執行時不一樣,在一個事務啓動起來以後,事務是隔離的,事務在啓動和不啓動之間所看到的數據都是一樣的纔對。前後讀取數據不一致。
REPEATABLE-READ(可重讀):“幻讀” 在一個事務內部,只要自己不提交,看到的都是一模一樣的,只有自己提交後,才能看到別人提交後的數據,爲避免幻讀使用下一種方法
SERIALIZABILE(可串行化):使用加鎖讀 前後都是一致的,當別人提交一個數據以後,數據提交了,看到的就是最終的數據,如果別人一個事務啓動起來還沒提交,要讀這個數據需要等到提交後才能讀。 能夠提供最安全的數據保證,但是併發能力也是最低的。
級別越高併發性能越差,但數據安全程度越高
SELECT @@global.tx_isolation; 查看默認隔離級別
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
啓動事務:
START TRANSACTION
提交事務:COMMIT
回滾事務:ROLLBACK 執行後,前面那個執行的事務就回到初始值了(前提事務要未提交才能回滾到未更新狀態,提交後事務就完成了)
READ-UNCOMMITTED(讀未提交):最低隔離級別,會產生“髒讀”(還未提交就能讀到);你所看到的數據別人只要已改,你馬上看到,別人沒提交的數據也可以看到,別人滾過去了也可以看到
第1個會話
ariaDB [(none)]> set session tx_isolation='read-uncommitted';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
1 row in set (0.00 sec)
MariaDB [(none)]> select @@session.tx_isolation; 該會話下事務級別修改成功
+------------------------+
| @@session.tx_isolation |
+------------------------+
| READ-UNCOMMITTED |
+------------------------+
1 row in set (0.00 sec)
第2個會話
MariaDB [hellodb]> set session tx_isolation='read-uncommitted';
Query OK, 0 rows affected (0.00 sec)
手動的顯示啓動事務
start transaction
兩邊都啓動事務
MariaDB [(none)]> start transaction;
Query OK, 0 rows affected (0.00 sec)
MariaDB [hellodb]> start transaction;
Query OK, 0 rows affected (0.00 sec)
把表students,Shi Potian Age改爲200
MariaDB [hellodb]> select * from hellodb.students;
+-------+---------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+---------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 22 | M | 2 | 3 |
| 2 | Shi Potian | 22 | M | 1 | 7 |
| 3 | Xie Yanke | 53 | M | 2 | 16 |
MariaDB [hellodb]> update students set Age=200 where StuID=2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MariaDB [hellodb]> select Name,Age from students where StuID=2;
+------------+-----+
| Name | Age |
+------------+-----+
| Shi Potian | 200 |
+------------+-----+
1 row in set (0.00 sec)
MariaDB [(none)]> select * from hellodb.students;
+-------+---------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+---------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 22 | M | 2 | 3 |
| 2 | Shi Potian | 200 | M | 1 | 7 | Age值已改
| 3 | Xie Yanke | 53 | M | 2 | 16 |
回滾事務,以上事務就結束了
MariaDB [hellodb]> rollback;
Query OK, 0 rows affected, 1 warning (0.00 sec)
MariaDB [hellodb]> select Name,Age from students where StuID=2;
+------------+-----+
| Name | Age |
+------------+-----+
| Shi Potian | 200 | 發現沒有回滾回去
+------------+-----+
1 row in set (0.00 sec)
查看一下存儲引擎
MariaDB [hellodb]> show table status like 'students'\G
*************************** 1. row ***************************
Name: students
Engine: MyISAM 是myisam存儲引擎
Version: 10
Row_format: Dynamic
Rows: 27
Avg_row_length: 24
Data_length: 664
Max_data_length: 281474976710655
Index_length: 2048
Data_free: 0
Auto_increment: 28
Create_time: 2015-04-01 18:53:08
Update_time: 2015-04-02 13:42:01
Check_time: NULL
Collation: utf8_general_ci
Checksum: NULL
Create_options:
Comment:
1 row in set (0.00 sec)
myisam存儲引擎不支持事務,所以不能回滾
修改存儲引擎
MariaDB [hellodb]> alter table students engine=innodb; 此種修改方式很危險
Query OK, 27 rows affected (0.08 sec)
Records: 27 Duplicates: 0 Warnings: 0
MariaDB [hellodb]> show table status like 'students'\G
*************************** 1. row ***************************
Name: students
Engine: InnoDB
Version: 10
Row_format: Compact
Rows: 27
Avg_row_length: 606
Data_length: 16384
Max_data_length: 0
Index_length: 0
Data_free: 0
Auto_increment: 28
Create_time: 2015-04-02 14:31:10
Update_time: NULL
Check_time: NULL
Collation: utf8_general_ci
Checksum: NULL
Create_options:
Comment:
1 row in set (0.00 sec)
會話1 提交事務
MariaDB [hellodb]> commit;
Query OK, 0 rows affected (0.00 sec)
會話2
MariaDB [hellodb]> start transaction;
Query OK, 0 rows affected (0.00 sec)
會話1
MariaDB [hellodb]> start transaction;
Query OK, 0 rows affected (0.00 sec)
會話2
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 22 | M | 2 | 3 |
| 2 | Shi Potian | 200 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
MariaDB [hellodb]> update students set Age=99 where StuID=2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 22 | M | 2 | 3 |
| 2 | Shi Potian | 99 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
會話1
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 22 | M | 2 | 3 |
| 2 | Shi Potian | 99 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
會話2
MariaDB [hellodb]> rollback; 事務回滾
Query OK, 0 rows affected (0.00 sec)
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 22 | M | 2 | 3 |
| 2 | Shi Potian | 200 | M | 1 | 7 | 已經返回到未修改狀態了
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
會話1
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 22 | M | 2 | 3 |
| 2 | Shi Potian | 200 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
MariaDB [hellodb]> rollback;
Query OK, 0 rows affected (0.00 sec)
READ-COMMTTED(讀提交):“不可重複讀” 產生的問題:比如在沒提交之前讀了一個數據是3,提交之後數字改爲5了,再一次讀時是5了。這同一個事務在兩次執行時不一樣,在一個事務啓動起來以後,事務是隔離的,事務在啓動和不啓動之間所看到的數據都是一樣的纔對。前後讀取數據不一致。 提交後就可以查看到。提交前和提交後讀是不同的,所以不可重複讀
會話1
MariaDB [hellodb]> set session tx_isolation='read-committed';
Query OK, 0 rows affected (0.00 sec)
會話2
MariaDB [hellodb]> set session tx_isolation='read-committed';
Query OK, 0 rows affected (0.00 sec)
MariaDB [hellodb]> start transaction;
Query OK, 0 rows affected (0.00 sec)
會話1
MariaDB [hellodb]> start transaction;
Query OK, 0 rows affected (0.00 sec)
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 22 | M | 2 | 3 |
| 2 | Shi Potian | 200 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
MariaDB [hellodb]> update students set Age=60 where StuID=1;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 60 | M | 2 | 3 |
| 2 | Shi Potian | 200 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
會話2
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 22 | M | 2 | 3 |
| 2 | Shi Potian | 200 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec) 未提交
會話1
MariaDB [hellodb]> commit; 提交
Query OK, 0 rows affected (0.01 sec)
會話2
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 60 | M | 2 | 3 | 可以查看到了
| 2 | Shi Potian | 200 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
MariaDB [hellodb]> rollback;
Query OK, 0 rows affected (0.00 sec)
REPEATABLE-READ(可重讀):“幻讀” 在一個事務內部,只要自己不提交,看到的都是一模一樣的,只有自己提交後,才能看到別人提交後的數據,爲避免幻讀使用下一種方法
會話1
MariaDB [hellodb]> set session tx_isolation='repeatable-read';
Query OK, 0 rows affected (0.00 sec)
會話2
MariaDB [hellodb]> set session tx_isolation='repeatable-read';
Query OK, 0 rows affected (0.00 sec)
會話1
MariaDB [hellodb]> start transaction;
Query OK, 0 rows affected (0.00 sec)
會話2
MariaDB [hellodb]> start transaction;
Query OK, 0 rows affected (0.00 sec)
會話1
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 60 | M | 2 | 3 |
| 2 | Shi Potian | 200 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
MariaDB [hellodb]> update students set Age=68 where StuID=2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 60 | M | 2 | 3 |
| 2 | Shi Potian | 68 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
會話2
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 60 | M | 2 | 3 |
| 2 | Shi Potian | 200 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
會話1
MariaDB [hellodb]> commit;
Query OK, 0 rows affected (0.00 sec)
會話2
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 60 | M | 2 | 3 |
| 2 | Shi Potian | 200 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
MariaDB [hellodb]> update students set Age=Age+1 where StuID=2;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 60 | M | 2 | 3 |
| 2 | Shi Potian | 69 | M | 1 | 7 | 此時叫幻讀
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
MariaDB [hellodb]> commit;
Query OK, 0 rows affected (0.01 sec)
SERIALIZABILE(可串行化):使用加鎖讀 前後都是一致的,當別人提交一個數據以後,數據提交了,看到的就是最終的數據,如果別人一個事務啓動起來還沒提交,要讀這個數據需要等到提交後才能讀。 能夠提供最安全的數據保證,但是併發能力也是最低的。
會話1
MariaDB [hellodb]> set session tx_isolation='serializable';
Query OK, 0 rows affected (0.00 sec)
會話2
MariaDB [hellodb]> set session tx_isolation='serializable';
Query OK, 0 rows affected (0.00 sec)
會話1
MariaDB [hellodb]> start transaction;
Query OK, 0 rows affected (0.00 sec)
會話2
MariaDB [hellodb]> start transaction;
Query OK, 0 rows affected (0.00 sec)
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 60 | M | 2 | 3 |
| 2 | Shi Potian | 69 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
MariaDB [hellodb]> update students set Age=89 where StuID=2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 60 | M | 2 | 3 |
| 2 | Shi Potian | 89 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (0.00 sec)
會話1
MariaDB [hellodb]> select * from students where StuID<3;
處於等待狀態,因爲事務沒有提交,只有等待對方提交了才能看到,保證看到的是最新的數據
只要對方釋放鎖了就可以看到
會話2
MariaDB [hellodb]> rollback;
Query OK, 0 rows affected (0.00 sec)
會話1
MariaDB [hellodb]> select * from students where StuID<3;
+-------+-------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 60 | M | 2 | 3 |
| 2 | Shi Potian | 69 | M | 1 | 7 |
+-------+-------------+-----+--------+---------+-----------+
2 rows in set (22.99 sec)
請求不到鎖就不會讀數據
爲了保證服務器有更好的並行性,也不至於數據彼此間影響過大,所以MySQL使用了可重讀,即便使用了可重讀,中間還是會出現幻讀問題,由此建議用小事務替代大事務是比較理想的做法。默認情況下InnoDB存儲引擎,默認會啓動自動提交功能
MariaDB [hellodb]> select @@global.autocommit;
+---------------------+
| @@global.autocommit |
+---------------------+
| 1 |
+---------------------+
1 row in set (0.00 sec)
手動指定sql語句沒指定事務,它會自動執行完每一條語句都提交一下
自動提交能保證sql語句執行完成,就能夠得到數據持久保證,但是會極大的降低系統性能
建議使用InnoDB存儲引擎時,把自動提交關閉,自己手動啓動事務手動提交事務
無法autocommit是否爲1對myisam沒有影響,myisam不支持事務,myisam也是無法回滾的
SAVEPOINT 保存點,回滾時,只回滾到保存的時間點
SAVEPOINT identifier
ROLLBACK [WORK] TO [SAVEPOINT] identifier 回滾某個保存下來的位置
RELEASE SAVEPOINT identifier 釋放保存點
MariaDB [hellodb]> start transaction; 啓動事務
Query OK, 0 rows affected (0.00 sec)
MariaDB [hellodb]> select * from students where StuID > 3 and StuID<6;
+-------+-----------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+-----------+-----+--------+---------+-----------+
| 4 | Ding Dian | 32 | M | 4 | 4 |
| 5 | Yu Yutong | 26 | M | 3 | 1 |
+-------+-----------+-----+--------+---------+-----------+
2 rows in set (0.01 sec)
這裏修改數據
MariaDB [hellodb]> update students set Age=80 where StuID=5;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MariaDB [hellodb]> savepoint oop;
Query OK, 0 rows affected (0.00 sec)
MariaDB [hellodb]> delete from students where StuID=4 and StuID=5;
Query OK, 0 rows affected (0.00 sec)
MariaDB [hellodb]> savepoint ooo;
Query OK, 0 rows affected (0.00 sec)
MariaDB [hellodb]> delete from students where StuID=4 and StuID=5;
Query OK, 0 rows affected (0.00 sec)
MariaDB [hellodb]> savepoint ooo;
Query OK, 0 rows affected (0.00 sec)
MariaDB [hellodb]> delete from students where StuID=10;
Query OK, 1 row affected (0.00 sec)
MariaDB [hellodb]> select * from students;
+-------+---------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+---------------+-----+--------+---------+-----------+
| 1 | Shi Zhongyu | 60 | M | 2 | 3 |
| 2 | Shi Potian | 69 | M | 1 | 7 |
| 3 | Xie Yanke | 53 | M | 2 | 16 |
| 4 | Ding Dian | 32 | M | 4 | 4 |
| 5 | Yu Yutong | 80 | M | 3 | 1 |
| 6 | Shi Qing | 46 | M | 5 | NULL |
| 7 | Xi Ren | 19 | F | 3 | NULL |
| 8 | Lin Daiyu | 17 | F | 7 | NULL |
| 9 | Ren Yingying | 20 | F | 6 | NULL |
| 11 | Yuan Chengzhi | 23 | M | 6 | NULL |
| 12 | Wen Qingqing | 19 | F | 1 | NULL |
| 13 | Tian Boguang | 33 | M | 2 | NULL |
| 14 | Lu Wushuang | 17 | F | 3 | NULL |
| 15 | Duan Yu | 19 | M | 4 | NULL |
| 16 | Xu Zhu | 21 | M | 1 | NULL |
| 17 | Lin Chong | 25 | M | 4 | NULL |
| 18 | Hua Rong | 23 | M | 7 | NULL |
| 19 | Xue Baochai | 18 | F | 6 | NULL |
| 20 | Diao Chan | 19 | F | 7 | NULL |
| 21 | Huang Yueying | 22 | F | 6 | NULL |
| 22 | Xiao Qiao | 20 | F | 1 | NULL |
| 23 | Ma Chao | 23 | M | 4 | NULL |
| 24 | Xu Xian | 27 | M | NULL | NULL |
| 25 | Sun Dasheng | 100 | M | NULL | NULL |
| 26 | liufang | 23 | F | NULL | NULL |
| 27 | yuanyou | 25 | F | NULL | NULL |
+-------+---------------+-----+--------+---------+-----------+
26 rows in set (0.00 sec)
MariaDB [hellodb]> savepoint look;
Query OK, 0 rows affected (0.00 sec)
MariaDB [hellodb]> rollback to ooo;
Query OK, 0 rows affected (0.00 sec)
MariaDB [hellodb]> select * from students where StuID=10;
+-------+--------------+-----+--------+---------+-----------+
| StuID | Name | Age | Gender | ClassID | TeacherID |
+-------+--------------+-----+--------+---------+-----------+
| 10 | Yue Lingshan | 19 | F | 3 | NULL |
+-------+--------------+-----+--------+---------+-----------+
1 row in set (0.00 sec)
MariaDB [hellodb]> release savepoint oop;
Query OK, 0 rows affected (0.00 sec)
MySQL的自動提交功能:
SELECT @@GLOBAL.autocommit;
+---------------------+
| @@GLOBAL.autocommit |
+---------------------+
| 1 |
+---------------------+
MySQL下,事務通過事務日誌來保證其性能和ACID的測試的,依賴於事務日誌的功能
MVCC【Multiversion Concurrency Control多版本併發控制,對InnoDB來講啓動一個事務就相當於對MySQL中的所有數據做了一個快照,而後在快照的基礎上實現查找工作】: 通過保存數據在某個時間的快照實現。無論事務執行多長時間,其看到的數據都是一致的。
InnoDB這種存儲引擎其mvcc是在每行記錄後面添加了兩個隱藏字段,是看不到的,其中一個保存了行的創建時間,一個保存了行的過期時間或刪除時間【只是標記它看不到】。
MVCC僅在第二、第三隔離級別下有效;
事務日誌:目的將隨機I/O轉換爲順序I/O,以提升事務操作效率;事務日誌也稱爲Write-Ahead Logging 任何事務相關數據的修改都是先在內存中修改,修改後是不會組合,先寫到磁盤上一份而後寫到日誌文件中。事務日誌是一種追加式的log 由於要寫兩次磁盤,性能比較差,如何降低其對磁盤io的影響?將數據文件和事務日誌放在不同的磁盤上,這樣可以避免將事務回寫到數據文件中,但如果事務所在的磁盤壞了怎麼辦呢?因此事務日誌所在的硬盤應該具有容災性,比如說用raid1,或raid10, MySQL支持使用兩種事務日誌,一種寫 一種備份 事務日誌文件應該有多大?數據是否要在事務文件中一直存放着?放在事務文件中的存在的問題:有了事務日誌就能保存持久性,當服務器軟件程序崩潰,有些數據在事務文件沒在數據文件中,下次啓動時,必須想辦法把數據從事務日誌統統都寫到數據文件中去。並保證數據文件處於clean狀態,服務器才能夠繼續向外提供服務
,此過程就叫崩潰後恢復過程。如果事務文件很大,把事務文件寫到數據文件中都要很長時間,因此日誌文件一定不能太大,根據業務數據的容量來定大小。事務日誌文件是成組出現的,每一組當中不止一個 事務日誌兩個文件輪流使用,第一個文件填滿了,填第二個日誌文件,而此時把第一個日誌文件同步到數據文件中去。InnoDB存儲引擎自動管理的
數據從事務日誌寫到數據文件中,其頻繁度如何衡量?
越快,事務丟失的可能性越小,但磁盤io越大,是MySQL自我管理的
但是從內存同步到事務中必須要及時,只要一提交事務,就往事務日誌文件中寫