四、mysql存儲引擎
支持多存儲引擎是mysql的特性,mysql中存儲引擎是針對表的。mysql5.5之後默認的存儲引擎由MyISAM變爲InnoDB。
InnoDB和MyISAM的區別
1.存儲文件:
InnoDB:.frm文件存儲表定義文件,.ibd文件存儲數據和索引文件
MyISAM:.frm文件存儲表定義文件,.myd文件存儲數據文件,.myi存儲索引文件
2.鎖的支持
InnoDB:支持行鎖和表鎖
MyISAM:只支持表鎖
(表鎖實在MySQL Server層實現的)
3.事務支持
InnoDB:支持事務
MyISAM:不支持事務
4.count
InnoDB:掃表計算得到
MyISAM:count值存儲在專門的地方
5.crud
InnoDB:讀寫都很好的支持
MyISAM:適用讀多場景,不適用高併發寫
另外,InnoDB和MyISAM索引結構都是B+tree。
mysql存儲引擎物理結構的文件類型分爲數據文件和日誌文件兩種。
日誌文件
只支持追加寫操作,順序io。
分爲錯誤日誌(error log),二進制日誌(bin log),通用查詢日誌(general query log),慢查詢日誌(slow query log),事務日誌(redo/undo log ,InnoDB),中繼日誌(relay log)
error log: 默認開啓,MySQL5.7之後無法關閉,記錄遇到的嚴重錯誤信息和每次啓動和關閉的詳細信息。默認存儲文件名:hostname.err
bin log: 默認關閉,配置log_bin=mysql_bin開啓,bin log記錄所有DDL和DML語句,不記錄DQL語句,並且只有事務提交了才進行記錄,可以用來做數據恢復。
general query log: 默認關閉,記錄查詢語句,不建議開啓
slow query log: 默認關閉,設置slow_query_log=on開啓,記錄執行時間超過long_query_time秒的查詢語句
redo/undo log:InnoDB特有,文件銘文ib_logfile0和ib_logfile1。默認在表空間所在目錄。還有一個名爲undo的日誌文件在ib_data目錄下
relay log:同步bin log,做主從使用
數據文件
查看數據文件:
SHOW VARIABLES LIKE ‘%datadir%’
InnoDB數據文件
.frm**文件:主要存放與表相關的數據信息,主要包括表結構的定義信息**
.ibd**:使用獨享表空間存儲表數據和索引**信息,一張表對應一個ibd文件。
ibdata**文件:使用共享表空間存儲表數據和索引**信息,所有表共同使用一個或者多個ibdata文件。
MyIsam數據文件
.frm**文件:主要存放與表相關的數據信息,主要包括表結構的定義信息**
.myd**文件:主要用來存儲表數據信息**。
.myi**文件:主要用來存儲表數據文件中任何索引的數據樹。**
五、MySQL併發控制
事務隔離級別(Transaction Isolation level)
讀未提交(read uncommit):
沒有提交的也能讀取到,一般不會用這個。會存在髒讀,即讀到其他會話未提交的數據,然後該會話回滾了,讀到的就是髒數據。
讀已提交(read commit)
也叫做不可重複讀。只能讀取已提交的數據,存在幻讀,即A會話兩次讀取可能不一致,因爲在A會話的兩次讀取之間可能會有事務提交。
可重複讀(repeatable read)
不會幻讀。在會話中讀取的是會話開始時的快照,每一次讀都一樣,所以不存在幻讀。
串行化(serializable)
退化爲基於鎖的併發控制,讀寫都加表鎖,併發性能最低。
MVCC
基於多版本的併發控制(multi-version concurrency control)
一般在mysql中,通過版本號和時間戳來進行併發控制,讀不加鎖,讀寫不衝突。讀分爲當前讀(current read)和快照讀(snapshot read)
當前讀:讀取最新版本,會加鎖。包括增刪改和一些特殊的select,如
select * from aa for update
select * from aa lock in share mode
快照讀:讀取可見版本(可能時歷史版本),不加鎖。簡單的select都是快照讀
鎖
按粒度分爲行級鎖,頁級鎖,表級鎖,全局鎖
按功能分爲共享讀鎖,排他寫鎖
按鎖的實現分爲樂觀鎖,悲觀鎖
全局鎖和表級鎖由mysql server層實現,各存儲引擎可以公用。
全局鎖:鎖整個數據庫。使用場景:數據備份時。
表級鎖:鎖整個表。開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低;
行級鎖:鎖的時某行,或某些行數據。由存儲引擎層實現,只有InnoDB和 xtradb實現行級鎖。開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高;
頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,併發度一般。
表級鎖有兩種,一種時表鎖,一種時元數據鎖(meta data lock,MDL)。查看錶級鎖的爭用狀態可用下面的語句:
show status like 'table%'
table_locks_immediate:產生表級鎖定的次數;
table_locks_waited:出現表級鎖定爭用而發生等待的次數;
手動增加表鎖
lock table 表名稱 read/write,表名稱2 read/write,其他
查看錶鎖情況
show open tables
釋放表鎖
unlock tables
在訪問表時會自動加上MDL鎖,增刪查改都會獲取MDL讀鎖,而DDL語句會獲取MDL寫鎖。讀鎖之間不互斥,因此可以有多個線程同時對一張表增刪改查。讀寫鎖之間、寫鎖之間是互斥的,用來保證變更表結構操作的安全性。因此,如果有兩個線程要同時給一個表加字段,其中一個要等另一個執行完才能開始執行。當sessionA獲取讀鎖,sessionB獲取寫鎖會阻塞,此時sessionC獲取讀鎖也會被阻塞。
行鎖,由存儲引擎層實現。這裏介紹的是InnoDB的行鎖。
InnoDB行鎖按照功能分爲排他鎖(X鎖)和共享鎖(S鎖)
按照鎖定範圍分爲
記錄鎖(Record Locks):鎖定索引中一條記錄。
間隙鎖(Gap Locks):要麼鎖住索引記錄中間的值,要麼鎖住第一個索引記錄前面的值或者最後一個索引記錄後面
的值。
Next-Key Locks:是索引記錄上的記錄鎖和在索引記錄之前的間隙鎖的組合。
對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖(X);
對於普通SELECT語句,InnoDB不會加任何鎖,事務可以通過以下語句顯示給記錄集加共享鎖或排他鎖。
SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE -- 手動加共享鎖
SELECT * FROM table_name WHERE ... FOR UPDATE --手動加排他鎖
在添加行鎖之前會先添加意向鎖,意向鎖分爲意向共享鎖(IX)意向排他鎖(IS),意向鎖是InnoDB實現的表鎖,且不用用戶自己加鎖,加上意向鎖之後,其他會話就不能獲取表鎖進行表的修改。如果沒有意向鎖,那麼對錶進行修改的時候首先要判斷是不是有表鎖,在判斷每一行是不是有行鎖,這樣效率太低。在加行鎖之前先加意向鎖直接判斷是不是有意向鎖就可以,提升判斷是否可以全表更新的性能。
下面分析當執行下面這條sql語句時的加鎖情況:
delete from tb1 where aaa = 1
情況一:aaa爲主鍵,事務隔離級別爲RC(讀已提交):
會在aaa=1這條記錄的主鍵索引上加X鎖(此時其他會話只能讀取歷史版本)。
情況二:aaa爲唯一索引,事務隔離級別爲RC:
在aaa=1這條唯一索引上加X鎖,然後在aaa=1的索引對應的主鍵索引上加X鎖。
情況三:aaa爲非唯一索引,事務隔離級別爲RC:
在aaa=1的對應的多條索引上加X鎖,然後在這幾條索引對應的主鍵索引上加X鎖。
情況四:aaa上沒有索引,事務隔離級別爲RC:
會進行全表掃描,對每條記錄添加X鎖,加鎖之後如果不符合條件會立刻釋放鎖,如果符合條件就會不會釋放這條主鍵索引上的X鎖。
情況五:aaa爲主鍵,事務隔離級別爲RR(可重複讀):同情況一
情況六:aaa爲唯一索引,事務隔離級別爲RR:同情況二
情況七:aaa爲非唯一索引,事務隔離級別爲RR:
假設有5條數據,aaa分別爲0,1, 1, 2,3,那麼會在aaa爲1的兩條非唯一上加X鎖並在0-1,1-1, 1-2 之間加Gap鎖(間隙鎖),從而保證不能再添加aaa爲1的數據,解決了幻讀。然後在aaa爲1的兩條索引對應的主鍵索引上加X鎖。
情況八:aaa無索引,事務隔離級別爲RR:
類似與情況四,並在主鍵索引之間加間隙鎖。
死鎖可能的情況:
情況1:
session1:delete from tb where id = 1;
session2:delete from tb where id = 2;
session1:delete from tb where id = 2;
session2:delete from tb where id = 1;
session1:commit;
session1:commit;
情況二:
session1:delete from tb where name = aaa;
session2: delete from tb where age = 10
age和name都是非唯一索引,name=aaa對應id=1和id=2兩條主鍵索引,age=10對應id=2和id=1兩條記錄,session1對id=1加上了X鎖申請對id=2的X鎖,session2對id=2加上了X鎖申請對id=1的X鎖,導致死鎖。