mysql 存儲引擎 innodb(一) 簡單介紹

innodb 通過多版本併發控制(MVCC)來獲得高併發性,並且實現了SQL標準的4種隔離級別,默認爲REPEATABLE級別。同時,使用一種被稱爲next-key locking的策略來避免幻讀(phantom)現象的產生。除此之外,InnoDB存儲引擎還提供了插入緩衝(insert buffer)、二次讀寫(double write)、自適應哈希索引(adaptive hash index)、預讀(read ahead)等高性能和高可用的功能。

一、MVCC(Multi-Version Concurrency Control)多版本併發控制。

特點,讀不加鎖,讀寫不衝突。

1. mvcc讀操作

  • 快照讀 (snapshot read):
    讀取的是記錄的可見版本 (有可能是歷史版本),不用加鎖
  • 當前讀 (current read):讀取的是記錄的最新版本,並且,當前讀返回的記錄,都會加上鎖,保證其他事務不會再併發修改這條記錄。
  • 快照讀使用
# 簡單select操作,屬於快照讀(不加鎖)
select * from table where ? ;
  • 當前讀使用
#特殊的讀操作,插入/更新/刪除操作,屬於當前讀
#以下,除了第一句,加的是共享鎖(S鎖),其他加的都是排他鎖(X鎖)
select * from table where ? lock in share mode;
select * from table where ? for update; 
insert into table values (...);
update table set ? where ?;
delete from table where ?;
  • 解釋:爲什麼將 插入/更新/刪除 操作,都歸爲當前讀

update操作流程
1、mysql 收到update sql語句
2、mysql server根據where條件,讀取第一條滿足條件的記錄
3、InnoDB引擎將第一條記錄返回,並加鎖(current read)
4、mysql server收到這條加鎖的記錄之後,會再發起一個update請求,更新這條記錄。
5、重複第二條,再次讀取一條滿足條件的數據

2.mvcc實現
innodb的mvcc是通過在每行記錄後面保存兩個隱藏的列來實現的。一個列報錯了行的創建時間,一個保存行的過期時間(或刪除時間)(這邊說的時間不是具體的時間值,而是系統版本號,每開啓一個新事務,版本號遞增)。

select 只有滿足以下兩個條件,才能作爲結果返回

a. innodb只查找版本號早於當前事務版本號的數據行,這樣就能確保事務讀取的行,要麼是事務開始前已經存在,要麼是事務自身插入或者修改過。

b. 行的刪除版本號要麼未定義,要麼大於當前事務版本號。這樣能保證事務讀到的行,在事務開始前未被刪除

mvcc只在repeatable read 和 read uncommited 隔離級別下工作

二、InnoDB鎖類型。

InnoDB存儲引擎實現如下兩種標準的行級鎖

  • 共享鎖(S Lock), 允許事務讀一行數據
  • 排他鎖(X Lock),允許事務刪除或更新一行數據

共享鎖和排他鎖的兼容

type X S
X 不兼容 不兼容
S 不兼容 兼容

注意:
(1)S鎖和X鎖都是行鎖,兼容是指對同一記錄(row)鎖的兼容性.
(2)事務T1已經獲得行R的共享鎖,另一個事務T2可以立即獲得行R的共享鎖,這種情況稱爲鎖兼容。事務T3想獲得行R的排他鎖,則必須等待事務T1、T2釋放行R上的共享鎖,這種情況成爲鎖的不兼容.

意向鎖

作用:檢測表鎖和行鎖的衝突。

說明:對細粒度的對象上鎖,需要先對粗粒度的對象上鎖。

例1:對行R上X鎖,需要先對數據庫D、表A、頁P(innodb存儲的最小物理單位)上IX鎖。若任何一部分導致等待,該操作需要等粗粒度的鎖完成;
例2:在行R上X鎖之前,已經有事務對錶A加了S鎖,之後事務需要對錶A加IX鎖,由於不兼容,所以,該事務需要等待。

  • 共享意向鎖(IS LOCK)
  • 排他意向鎖(IX LOCK)

鎖兼容:

type IS IX S X
IS 兼容 兼容 兼容 不兼容
IX 兼容 兼容 不兼容 不兼容
S 兼容 不兼容 兼容 不兼容
X 不兼容 不兼容 不兼容 不兼容

.

三、事務隔離級別

在講事務隔離級別之前,我們先講講數據庫的併發操作,可能帶來的問題:

  • 更新丟失 : 事務1的更新覆蓋了事務2的更新操作。
  • 讀髒數據 :事務1讀到了事務2未提交的數據,事務2若回滾,事務1讀到的就是錯誤的數據。
  • 不可重複讀 : 事務1兩次讀取的數據不一致(原因,事務1兩次讀取的間隙,事務2修改/刪除了數據)。
  • 幻讀 : 事務1兩次讀取數據不一致(原因,事務1兩次讀取間隙,事務2新增了數據)。

如果不太明白,請繼續往下看。

SQL標準的四種隔離級別(Innodb全部支持)

  • READ UNCOMMITTED
    事務中的修改,即使沒有提交,對其他事務也是可見的。事務可以讀取未提交的數據,導致髒讀。
  • READ COMMITTED
    事務不能讀取未提交的數據,避免髒讀,但容易導致不可重複讀(兩次相同的查詢操作,可能讀到不同的數據)。
  • REPEATABLE READ
    該隔離級別下,保證同一事務中,兩次相同的查詢操作結果是一致的,避免不可重複讀的發生,但不能防止幻讀的發生 (innodb解決了該隔離級別下幻讀的發生,下面會介紹。)
  • SERIALIZABLE
    事務串行執行,避免幻讀。它會在讀的每一行數據都加鎖。

InnoDB存儲引擎默認支持的隔離級別是REPEATABLE READ,但是與標準的SQL不同的是,InnoDB存儲引擎在REPEATABLE READ事務隔離級別下使用Next-Key Lock算法,避免了幻讀的產生。達到了SQL標準的隔離級別SERIALIZABLE

理解Next-Key Lock
next-key lock是一個區間鎖,它鎖定的是一個範圍
下面通過實例來說明:

mysql> create table test2(id int)engine = innodb;
Query OK, 0 rows affected

mysql> insert into test2 value(1),(5),(9),(12),(18);
Query OK, 5 rows affected
Records: 5  Duplicates: 0  Warnings: 0

# session 1
mysql> start transaction;
Query OK, 0 rows affected

mysql> select * from test2 where id = 9 for update;
+----+
| id |
+----+
|  9 |
+----+
1 row in set

#session 2
mysql> insert into test2 select 6;

#session 2 這個操作會被一直阻塞,直到session1的事務提交

通過上面例子可以知道,test2表有1,5,9,12,18 五條數據,當我們爲9那行加排他鎖的時候,innodb會鎖住兩個區間5-9,9-12,包括5,不包括12。(可以自己試驗下).

修改數據庫事務隔離級別

set [session|global] 
transaction isolation level 
[READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE]

#例如
#設置當前會話的事務隔離級別爲READ UNCOMMITTED
set session transaction isolation level read uncommitted;
#設置全局的事務隔離級別爲REPEATABLE READ
set global transaction isolation level repeatable read;

四、Innodb後臺線程

InnoDB存儲引擎是多線程的模型,因此後臺有多個不同的後臺線程,負責處理不同的任務

  • Master Thread
    核心線程,負責將緩衝池中的數據異步刷新到磁盤,保證數據的一致性,包括髒頁的刷新、合併插入緩衝、UNDO頁的回收等。
  • IO Thread
    mysql存儲引擎中,大量使用AIO來處理寫IO請求,IO Thread用於處理這些IO請求的回調
  • Purge Thread
    事務提交後,其所使用的undolog可能不再需要,因此需要Purge Thread來回收已經使用並分配的undolog頁(1.1以前,purge操作在master thread中進行,1.1以後,獨立在purge thread中)
  • Page Cleaner Thread
    執行髒頁刷新操作(1.2以後引入的,之前在master thread中操作)

五、日誌

  • 錯誤日誌
# 查看錯誤日誌路徑
show variables LIKE 'log_error';

查詢結果:

Variable_name Value
log_error /var/log/mysqld.log

.

  • 慢查詢日誌

慢查詢日誌可以幫助我們定位可能存在問題的sql語句,從而進行sql語句層面的優化。
MySQL默認沒有開啓慢查詢日誌,需要手動開啓

# 查看 慢查詢日誌是否開啓  
show variables LIKE 'slow_query_log';

查詢結果:

Variable_name Value
slow_query_log OFF

設置slow_query_log 的值爲 ON

# 開啓慢查詢日誌
set global slow_query_log=ON;

其他慢查詢日誌參數配置 (使用同樣的方式,查看或修改參數值)

long_query_time     :  設定慢查詢的閥值,超出次設定值的SQL即被記錄到慢查詢日誌,缺省值爲10s
slow_query_log      :  指定是否開啓慢查詢日誌
log_slow_queries    :  指定是否開啓慢查詢日誌(該參數要被slow_query_log取代,做兼容性保留)
slow_query_log_file :  指定慢日誌文件存放位置,可以爲空,系統會給一個缺省的文件host_name-slow.log
min_examined_row_limit:查詢檢查返回少於該參數指定行的SQL不被記錄到慢查詢日誌
log_queries_not_using_indexes: 不使用索引的慢查詢日誌是否記錄到索引

[1]姜承堯 .MySQL技術內幕:InnoDB存儲引擎  機械工業出版社 
[2][http://hedengcheng.com/?p=771](http://hedengcheng.com/?p=771)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章