1、InnoDB和MyISAM引擎鎖方面的區別
1. InnoDB
InnoDB默認支持行級鎖,也支持表級鎖。mysql
InnoDB在sql中用到索引時使用的時行級鎖;沒有用到索引的時候,或者索引不明確時(>,<,like)使用的是表級鎖。算法
InnoDB(增刪改)時會自動加鎖,select操做時不加鎖(默認是用的非鎖定讀),須要顯示加寫鎖(for update)和讀所(lock in share mode)sql
2. MyISAN
MyISAM默認支持表級鎖,不支持行級鎖。數據庫
MyISAM(增刪改查)都會自動上鎖。併發
MyISAM不支持事務mvc
2、數據庫事務四大特效
1.原子性
2.隔離性
3.一致性
4.持久性
3、Mysql事務隔離級別,以及各級別下的併發訪問問題
更新丟失--mysql全部事務隔離級別在數據庫層面上都可避免函數
髒讀--READ COMMITTED事務隔離級別以上可避免高併發
不可重複讀--REPEATABLE READ事務隔離級別以上可避免性能
幻讀--SERIALIZABLE事務隔離級別可避免spa
1. READ UNCOMMITTED (未提交讀)
事務中的修改,即便沒有提交,對其餘事務也都是可見的。
事務能夠讀取未提交的數據,出現髒讀(dirty read)(讀取別的事務還未提交的數據)
2. READ COMMITTED (提交讀)
一個事務從開始直到提交前,所作的任何修改對其餘事務都是不可見的;也叫不可重複讀(nonrepeatable read),由於兩次相同的查詢可能會獲得不同的結果。
大多數數據庫系統(Sql Server , Oracle)的默認隔離級別 READ COMMITTED(msyql不是)。
出現不可重複讀問題: 在當前事務中屢次讀取同一數據結果不一致(側重與同一數據的修改)
A: start transaction . | B: start transaction |
A: select 100; | |
B: update 100 + 100 | |
A: select 100 ; | |
B: commit; | |
A: select 200; | |
A: commit; |
3. REPEATABLE READ (可重複讀)
mysql默認事務隔離級別
InnoDB和XtraDB引擎經過多版本併發控制(MVCC, Multiversion Concurrency Control)解決了幻讀
解決了不可重複讀;
A: start transaction . | B: start transaction |
A: select 100; | |
B: update 100 + 100 | |
A: select 100 ; | |
B: commit; | |
A: select 100; | |
A: update + 100 | |
A: select 300 |
出現幻讀(側重於新增和刪除),當某個事務在讀取某個範圍內數據時,另一個事務又在該範圍內插入了新的記錄,當以前的事務再次讀取該範圍內記錄時,會產生幻行。
repeatable read隔離級別下
A: start transaction . | B: start transaction |
A: select ... lock in share mode(當前讀); | |
B: insert into .. value() A使用的是當前讀,阻塞沒法插入(避免了幻讀) |
|
A: commit |
A: start transaction . | B: start transaction |
A: select 10條 (當前讀) |
|
B: insert into ...value() | |
B: commit | |
A: update ...(修改了11條) | |
A: commit |
select 某記錄是否存在,不存在,準備插入此記錄,但執行 insert 時發現此記錄已存在,沒法插入,此時就發生了幻讀。
A: start transaction . | B: start transaction |
A: select where id = 1; empty |
|
B: insert into value(1) | |
B: commit | |
A: insert into value(1); error | |
A: select where id = 1; empty | |
A: commit |
read committed隔離級別下
A: start transaction . | B: start transaction |
A: select ... lock in share mode;(3行) | |
B: insert into .. value()(插入1行) (實際狀況:read committed 自動變爲repeatable read 級別,阻塞沒法插入) |
|
B: commit; | |
A: update (更新了4行) | |
A: commit |
4. SERIALIZABLE (可串行化)
SERIALIZABLE 最高的隔離級別;
經過強制事務串行執行,避免了幻讀問題;SERIALIZABLE會在讀取的每一行數據上都加鎖,因此可能致使大量的超時和鎖爭用問題。
實際應用中不多用到這種隔離級別,只有在很是須要確保數據一致性並且接受沒有併發的狀況下,才考慮採用該級別
4、死鎖
死鎖:指兩個或者多個事務在同一資源上相互佔有,並請求對方佔用的資源
InnoDB目前處理死鎖的方法是:將持有最少行級鎖的事務進行會滾
5、RC、RR級別下的InnoDB的非阻塞讀(快照讀)如何實現
數據行裏隱藏字段的DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID字段
DB_TRX_ID:該字段用來標識最近一次對本行記錄作修改(update ,delete)(最後一次修改本行記錄的事務id)
DB_ROLL_PTR: 回滾指針
DB_ROW_ID: 隱示建立的自增主鍵字段
undo日誌:對記錄變動操做時產生undo記錄,存儲的是老版數據,當一個久的事務須要讀取事務時,爲了可以讀取老版本的數據,須要順着undo鏈找到其知足可見性的記錄,分爲insert(事務回滾時須要,並在事務提交後當即丟棄)和update undo log
insert undo log: 事務回滾時須要,並在事務提交後當即丟棄
update undo log:事務對記錄進行delete 或update操做時產生的undo log, 在事務回滾時和快照讀時須要
1. 鎖住該行日誌,
2. 並將數據拷貝一份到undo log,
3. 修改當前行的值,
4. 填寫事務id(DB_TRX_ID),
5. 使用回滾指針(DB_ROLL_PTR)指向undo log中修改前的行
read view :作可見性判斷,當執行快照讀select時,會針對查詢的數據建立一個read view,來決定當前事務能看到的是哪一個版本的數據(多是最新數據,也可能只容許看undo log某個版本的數據),read view遵循一個可見性算法,將要修改的數據DB_TRX_ID取出來與系統其餘活躍事務id作對比,若是大於或等於這些事務id,就經過DB_ROLL_PTR指針去取出undo log上一層的DB_TRX_ID直到小於這些活躍事務id爲止,保證了獲取到的數據版本是當前可見的最穩定的版本。
mysql源碼,保存活動事務的地方,m_low_limit_id(活動事務最大id),m_up_imit_id(活動事務最小id)。start transtaction,越新開啓的事務id越多,遞增
6、InnoDB可重複讀隔離級別下如何避免幻讀
表象:快照讀(非阻塞讀)--僞MVCC,(並非多版本共存,undo log是串行化的結果),讀不加鎖,讀寫不衝突
內在:next-key鎖(行鎖+gap鎖)
(不管是當前讀仍是快照讀,在innodb的RR的事務隔離級別下均可以免幻讀。在快照讀的狀況下,innodb經過mvcc來避免幻讀;在當前讀的狀況下,innodb經過next-key鎖來避免幻讀)
當前讀:加了鎖的增刪改查, (不論是共享鎖仍是排他鎖均爲當前讀,insert, delete, update, select ... for update, select ... lock in share mode)
讀取的是記錄的最新版本,而且讀取以後還須要保證其餘併發事務不能修改當前記錄,對讀取的記錄加鎖。
快照讀:不加鎖的非阻塞讀,select;;
serializable隔離級別下是串行讀,快照讀退化成當前讀;
快照讀的實現基於多版本併發控制(MVCC),避免了加鎖操做,提升併發性能, 開銷更低;
快照讀有可能讀到的數據並非最新版本,多是以前的 歷史版本
read committed級別下,當前讀和快照讀的結果同樣的,數據版本同樣,(讀取的是另外一個事務提交後的結果)
A: start transaction . | B: start transaction |
A: select 100 (快照讀); 建立新的快照read view | |
B: update 100 + 100 | |
B: commit | |
A: select(快照讀) 200; 建立新的快照read view | |
A: select lock in share mode(當前讀) 200 | |
A: commit |
repeatable read級別下,快照讀(讀取的多是數據未修改前的版本,建立快照讀的時機決定了讀取數據的版本),當前讀(讀取的是數據的最新版本)
A: start transaction . | B: start transaction |
A: select 100(快照讀) ,會建立一個快照read view,將當前系統活躍的事務id記錄起來 | |
B: update 100 + 100 | |
B: commit | |
A: select (快照讀) 100,讀到的是歷史版本;事務開啓後未作讀取操做,則此時讀取的是最新版本 200 使用的是前一個快照的read view |
|
A: select lock in share mode(當前讀) 200,最新版本 | |
A: commit |
READ COMMITTD在每一次進行普通SELECT操做前都會
生成一個ReadView,而REPEATABLE READ只在第一次進行普通SELECT操做前生成一個ReadView,以後的查
詢操做都重複使用這個ReadView就行了
next-key鎖(行鎖 + gap鎖)
gap鎖,間隙鎖,鎖定一個範圍但不包括記錄自己,防止同一事務的兩次當前讀出現幻讀的狀況
gap鎖, read uncommitted, read committed 級別下沒有 ,沒法避免幻讀
gap lock會用在非惟一索引和不走索引的當前讀,以及僅命中檢索條件的部分結果集;主鍵索引和惟一索引的當前讀中;
repeatable read級別下當前讀(刪改查)對主鍵索引或者惟一索引會用gap鎖嗎?
1.對於主鍵索引和惟一索引的當前讀,若是where條件所有命中(精確查詢時全部記錄都有),則不會用gap鎖,只會加行鎖,若是範圍條件部分命中或者都不命中,則使用Gap鎖。
2.對於主鍵索引和惟一索引的當前讀,用相等條件檢索數據時,存在使用行鎖,不存在使用gap鎖///
3.對於非惟一索引的當前讀:使用Gap鎖
4.對於不走索引的當前讀:使用Gap鎖(至關於鎖表)
總結:插入操做對查詢結果有影響就gap鎖 + 行鎖,沒有隻加行鎖
select * from table where id in (1,3,5), id爲1,3,5均在該table中存在,所有命中, 事務B新增一條數據在事務A查詢的範圍以外,事務B提交後,事務A再作當前讀仍是原來的數據,不會出現幻讀。
A: start transaction . | B: start transaction |
A: delete from tb where id=9 (主鍵索引或惟一索引); |
|
B: insert into tb value(10), id=9數據存在,插入成功;不存在失敗阻塞,id=9數據周圍的間隙被鎖住了 |
|
A: rollback |
數據庫中數據:4,5,80, 100
當前讀要查詢數據90;(80,90] 和(90,100] 區間會被gap鎖住
7、關鍵語法
1. 統計相關函數
count, sum, max, min, avg
2. group by
3.HAVING
一般和group by子句一塊兒使用
沒有grup by子句,having和where的做用同樣
where過濾行,having過濾組
出如今同一sql中的順序 where > group by > having