一、mysql的併發控制
當有多個查詢需要同時修改同一個數據,就會產生併發控制的問題。mysql可以在兩個層面進行併發控制:服務器層和存儲引擎層。
mysql通過加鎖實現併發控制:
⑴鎖有兩類:
讀鎖:共享鎖,即一個讀鎖不會阻塞其它讀鎖(但會阻塞其它寫鎖),多個用戶可同時讀取同一個資源,而不互相干擾。
寫鎖:排他鎖,即一個寫鎖會阻塞其它讀寫鎖,在給定時間內,只有一個用戶能執行寫入。
⑵鎖粒度:
表級鎖:鎖定整張表
行級鎖:併發程度更高,但維護較麻煩,會增加系統開銷,易產生死鎖。行級鎖只能在存儲引擎級別實現,MyISAM存儲引擎不支持行級鎖
⑶鎖分類:
隱式鎖:由存儲引擎自動完成
顯式鎖:用戶可手動施加鎖(表級鎖)
⑷手動加解鎖:服務器級別
LOCK TABLES tb_name {READ|WRITE},...;
例:lock tables students write;
UNLOCK TABLES;
FLUSH TABLES WITH READ LOCK; #關閉所有已打開的表並全局施加讀鎖
InnoDB存儲引擎也支持另外一種顯式鎖(只鎖定挑選出的行):
SELECT ... LOCK IN SHARE MODE;
SELECT ... FOR UPDATE;
二、事務
事務(Transaction)是訪問並可能更新數據庫中各種數據項的一個程序執行單元(unit);
設想一個場景,A要向B轉賬500元,要經過兩個步驟:A從自己的賬戶中減去500元;向B的賬戶增加500元。顯而易見,只完成第一步或者只完成第二步都不合理,這兩個步驟必須放入一個事務中,當作一個不可分割的工作單位,要麼都執行,要麼都不執行。
⑴事務是恢復和併發控制的基本單位,要符合事務的概念,必須通過ACID測試:
原子性(atomicity):一個事務是一個不可分割的工作單位,事務中包括的諸操作要麼都做,要麼都不做。
一致性(consistency):事務必須是使數據庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。
隔離性(isolation):一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的數據對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。
持久性(durability):持久性也稱永久性(permanence),指一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的
⑵事務之間的隔離級別:
READ-UNCOMMITTED(讀未提交):事務中的修改,即使沒有提交,對其它事務也是可見的。事務可以讀取未提交的數據,這也稱爲“髒讀”;最低的隔離級別
READ-COMMITTED(讀提交):一個事務開始時,只能“看見”已提交的事務所做的修改,因此,再次執行同樣的查詢,可能得到不一樣的結果,稱爲“不可重複讀”。
REPEATABLE-READ(可重讀):保證了在同一個事務中多次讀取同樣記錄的結果是一致的,可能引起“幻讀”;mysql默認隔離級別
SERIALIZABILE(可串行化):加鎖讀,即事務請求不到鎖就無法讀,必須等到其它事務釋放鎖(提交或回滾)後才行。
⑶查看mysql的隔離級別:SELECT @@global.tx_isolation;
⑷事務的啓動、提交和加滾:
啓動:START TRANSACTION|BEGIN;
提交:COMMIT;
回滾:ROLLBACK;
保存點:SAVEPOINT identifier;
回滾至某個保存點:ROLLBACK [WORK] TO [SAVEPOINT] identifier;
刪除某保存點:RELEASE SAVEPOINT identifier;
⑸MySQL的自動提交功能:SELECT @@GLOBAL.autocommit;
默認是啓用的,修改autocommit僅對支持事務的存儲引擎產生影響;
自動提交能保證數據及時寫入磁盤,但會造成頻繁I/O,降低系統性能。
⑹MVCC:多版本併發控制;通過保存數據在某個時間點的快照實現。無論事務執行多長時間,其看到的數據都是一致的。根據事務開始的時間不同,每個事務對同一張表、同一時刻看到的數據可能是不一樣的。MVCC僅在第二、第三隔離級別下有效;
⑺事務日誌:將隨機I/O轉換爲順序I/O,以提升事務操作效率;事務日誌也稱爲Write-Ahead Logging。
數據從內存->事務日誌->數據文件
爲減輕磁盤壓力,宜將事務日誌和數據文件放於不同的磁盤上,事務日誌不宜太大;
⑻InnoDB支持事務,而MyISAM不支持
會話1:
這裏要注意:修改全局變量針對新建立的會話生效,而修改局部變量則立即生效 MariaDB [testdb]> set session tx_isolation = 'REPEATABLE-READ'; #將會話級的事務隔離級別改爲“讀提交” Query OK, 0 rows affected (0.00 sec) MariaDB [testdb]> start transaction; #啓動一個事務 Query OK, 0 rows affected (0.00 sec) MariaDB [testdb]> select * from students; +----+-------+------+--------+-------+ | ID | Name | Age | Gender | Class | +----+-------+------+--------+-------+ | 1 | jerry | NULL | f | NULL | | 2 | tom | NULL | m | NULL | +----+-------+------+--------+-------+ 2 rows in set (0.08 sec)
會話2:
MariaDB [testdb]> set session tx_isolation = 'REPEATABLE-READ'; Query OK, 0 rows affected (0.00 sec) MariaDB [testdb]> start transaction; #在會話2中也啓動一個事務 Query OK, 0 rows affected (0.00 sec) MariaDB [testdb]> insert students (Name,Gender) value ('rose','f'); #插入數據 Query OK, 1 row affected (0.08 sec)
會話1:
MariaDB [testdb]> select * from students; #會話1的事務看到的數據仍是原來的值 +----+-------+------+--------+-------+ | ID | Name | Age | Gender | Class | +----+-------+------+--------+-------+ | 1 | jerry | NULL | f | NULL | | 2 | tom | NULL | m | NULL | +----+-------+------+--------+-------+ 2 rows in set (0.00 sec)
會話2:
MariaDB [testdb]> commit; #事務2提交 Query OK, 0 rows affected (0.01 sec)
會話1:
MariaDB [testdb]> select * from students; #事務2可以看到事務1提交後的數據了 +----+-------+------+--------+-------+ | ID | Name | Age | Gender | Class | +----+-------+------+--------+-------+ | 1 | jerry | NULL | f | NULL | | 2 | tom | NULL | m | NULL | | 6 | rose | NULL | f | NULL | +----+-------+------+--------+-------+ 3 rows in set (0.00 sec)