《MySQL技術內幕:InnoDB存儲引擎(第2版)》書摘

MySQL技術內幕:InnoDB存儲引擎(第2版)node

姜承堯python

第1章 MySQL體系結構和存儲引擎mysql

 

>> 在上述例子中使用了mysqld_safe命令來啓動數據庫,固然啓動MySQL實例的方法還有不少,在各類平臺下的方式可能又會有所不一樣。算法

>> 當啓動實例時,MySQL數據庫會去讀取配置文件,根據配置文件的參數來啓動數據庫實例。這與Oracle的參數文件(spfile)類似,不一樣的是,Oracle中若是沒有參數文件,在啓動實例時會提示找不到該參數文件,數據庫啓動失敗。而在MySQL數據庫中,能夠沒有配置文件,在這種狀況下,MySQL會按照編譯時的默認參數設置啓動實例sql

>> 從概念上來講,數據庫是文件的集合,是依照某種數據模型組織起來並存放於二級存儲器中的數據集合;數據庫實例是程序,是位於用戶與操做系統之間的一層數據管理軟件,用戶對數據庫數據的任何操做,包括數據庫定義、數據查詢、數據維護、數據庫運行控制等都是在數據庫實例下進行的,應用程序只有經過數據庫實例才能和數據庫打交道。數據庫

>> 須要特別注意的是,存儲引擎是基於表的,而不是數據庫。編程

>> 關於NDB存儲引擎,有一個問題值得注意,那就是NDB存儲引擎的鏈接操做(JOIN)是在MySQL數據庫層完成的,而不是在存儲引擎層完成的數組

>> 相信在任何一本關於數據庫原理的書中,可能都會提到數據庫與傳統文件系統的最大區別在於數據庫是支持事務的緩存

>> MySQL提供了一個很是好的用來演示MySQL各項功能的示例數據庫,如SQL Server提供的AdventureWorks示例數據庫和Oracle提供的示例數據庫。據我所知,知道MySQL示例數據庫的人不多,多是由於這個示例數據庫沒有在安裝的時候提示用戶是否安裝(如Oracle和SQL Server)以及這個示例數據庫的下載居然和文檔放在一塊兒安全

>> 在Linux和UNIX環境下,還能夠使用UNIX域套接字。UNIX域套接字其實不是一個網絡協議,因此只能在MySQL客戶端和數據庫實例在一臺服務器上的狀況下使用。用戶能夠在配置文件中指定套接字文件的路徑,如--socket=/tmp/mysql.sock。當數據庫實例啓動後,用戶能夠經過下列命令來進行UNIX域套接字文件的查找:

 

第2章 InnoDB存儲引擎

 

>> 從MySQL數據庫的官方手冊可得知,著名的Internet新聞站點Slashdot.org運行在InnoDB上。Mytrix、Inc.在InnoDB上存儲超過1 TB的數據,還有一些其餘站點在InnoDB上處理插入/更新操做的速度平均爲800次/秒。這些都證實了InnoDB是一個高性能、高可用、高可擴展的存儲引擎。

>> 後臺線程的主要做用是負責刷新內存池中的數據,保證緩衝池中的內存緩存的是最近的數據。此外將已修改的數據文件刷新到磁盤文件,同時保證在數據庫發生異常的狀況下InnoDB能恢復到正常運行狀態。

>> 在InnoDB存儲引擎中大量使用了AIO(Async IO)來處理寫IO請求,這樣能夠極大提升數據庫的性能。而IO Thread的工做主要是負責這些IO請求的回調(call back)處理

>> 能夠經過命令SHOW ENGINE INNODB STATUS來觀察InnoDB中的IO Thread:

>> 具體來看,緩衝池中緩存的數據頁類型有:索引頁、數據頁、undo頁、插入緩衝(insert buffer)、自適應哈希索引(adaptive hash index)、InnoDB存儲的鎖信息(lock info)、數據字典信息(data dictionary)等

>> 從InnoDB 1.0.x版本開始,容許有多個緩衝池實例。每一個頁根據哈希值平均分配到不一樣緩衝池實例中。這樣作的好處是減小數據庫內部的資源競爭,增長數據庫的併發處理能力。能夠經過參數innodb_buffer_pool_instances來進行配置,該值默認爲1。

>> 從MySQL 5.6版本開始,還能夠經過information_schema架構下的表INNODB_BUFFER_POOL_STATS來觀察緩衝的狀態

>> 在InnoDB存儲引擎中,緩衝池中頁的大小默認爲16KB,一樣使用LRU算法對緩衝池進行管理

>> 。稍有不一樣的是InnoDB存儲引擎對傳統的LRU算法作了一些優化。在InnoDB的存儲引擎中,LRU列表中還加入了midpoint位置。新讀取到的頁,雖然是最新訪問的頁,但並非直接放入到LRU列表的首部,而是放入到LRU列表的midpoint位置。這個算法在InnoDB存儲引擎下稱爲midpoint insertion strategy。

>> 那爲何不採用樸素的LRU算法,直接將讀取的頁放入到LRU列表的首部呢?這是由於若直接將讀取到的頁放入到LRU的首部,那麼某些SQL操做可能會使緩衝池中的頁被刷新出,從而影響緩衝池的效率。

>> 常見的這類操做爲索引或數據的掃描操做。這類操做須要訪問表中的許多頁,甚至是所有的頁,而這些頁一般來講又僅在此次查詢操做中須要,並非活躍的熱點數據。若是頁被放入LRU列表的首部,那麼很是可能將所須要的熱點數據頁從LRU列表中移除,而在下一次須要讀取該頁時,InnoDB存儲引擎須要再次訪問磁盤。

>> Buffer pool hit rate,表示緩衝池的命中率,這個例子中爲100%,說明緩衝池運行狀態很是良好。一般該值不該該小於95%。若發生Buffer pool hit rate的值小於95%這種狀況,用戶須要觀察是不是因爲全表掃描引發的LRU列表被污染的問題。

>> 執行命令SHOW ENGINE INNODB STATUS顯示的不是當前的狀態,而是過去某個時間範圍內InnoDB存儲引擎的狀態。從上面的例子能夠發現,Per second averages calculated from the last 24 seconds表明的信息爲過去24秒內的數據庫狀態。

>> 在LRU列表中的頁被修改後,稱該頁爲髒頁(dirty page),即緩衝池中的頁和磁盤上的頁的數據產生了不一致。這時數據庫會經過CHECKPOINT機制將髒頁刷新回磁盤,而Flush列表中的頁即爲髒頁列表。須要注意的是,髒頁既存在於LRU列表中,也存在於Flush列表中

>> 重作日誌緩衝通常不須要設置得很大,由於通常狀況下每一秒鐘會將重作日誌緩衝刷新到日誌文件,所以用戶只須要保證每秒產生的事務量在這個緩衝大小以內便可

>> 當前3TB的MySQL數據庫已並很多見,可是3 TB的內存卻很是少見。目前Oracle Exadata旗艦數據庫一體機也就只有2 TB的內存。

>> 所以Checkpoint(檢查點)技術的目的是解決如下幾個問題:□ 縮短數據庫的恢復時間;□ 緩衝池不夠用時,將髒頁刷新到磁盤;□ 重作日誌不可用時,刷新髒頁。

>> 對於InnoDB存儲引擎而言,其是經過LSN(Log Sequence Number)來標記版本的。而LSN是8字節的數字,其單位是字節。每一個頁有LSN,重作日誌中也有LSN,Checkpoint也有LSN。能夠經過命令SHOW ENGINE INNODB STATUS來觀察:

>> InnoDB存儲引擎的關鍵特性包括:□ 插入緩衝(Insert Buffer)□ 兩次寫(Double Write)□ 自適應哈希索引(Adaptive Hash Index)□ 異步IO(Async IO)□ 刷新鄰接頁(Flush Neighbor Page)

>> InnoDB存儲引擎開創性地設計了Insert Buffer,對於非彙集索引的插入或更新操做,不是每一次直接插入到索引頁中,而是先判斷插入的非彙集索引頁是否在緩衝池中,若在,則直接插入;若不在,則先放入到一個Insert Buffer對象中,好似欺騙。數據庫這個非彙集的索引已經插到葉子節點,而實際並無,只是存放在另外一個位置。

>> 而後再以必定的頻率和狀況進行Insert Buffer和輔助索引頁子節點的merge(合併)操做,這時一般能將多個插入合併到一個操做中(由於在一個索引頁中),這就大大提升了對於非彙集索引插入的性能。

>> 輔助索引不能是惟一的,由於在插入緩衝時,數據庫並不去查找索引頁來判斷插入的記錄的惟一性。若是去查找確定又會有離散讀取的狀況發生,從而致使Insert Buffer失去了意

>> 正如前面所說的,目前Insert Buffer存在一個問題是:在寫密集的狀況下,插入緩衝會佔用過多的緩衝池內存(innodb_buffer_pool),默認最大能夠佔用到1/2的緩衝池內存。

>> InnoDB從1.0.x版本開始引入了Change Buffer,可將其視爲Insert Buffer的升級。從這個版本開始,InnoDB存儲引擎能夠對DML操做——INSERT、DELETE、UPDATE都進行緩衝,他們分別是:Insert Buffer、Delete Buffer、Purge buffer。

>> innodb_change_buffer_max_size值默認爲25,表示最多使用1/4的緩衝池內存空間。而須要注意的是,該參數的最大有效值爲50。

>> 可能令絕大部分用戶感到吃驚的是,Insert Buffer的數據結構是一棵B+樹。在MySQL 4.1以前的版本中每張表有一棵Insert Buffer B+樹。而在如今的版本中,全局只有一棵Insert Buffer B+樹,負責對全部的表的輔助索引進行Insert Buffer。

>> 而這棵B+樹存放在共享表空間中,默認也就是ibdata1中。所以,試圖經過獨立表空間ibd文件恢復表中數據時,每每會致使CHECK TABLE失敗

>> 。這是由於表的輔助索引中的數據可能還在Insert Buffer中,也就是共享表空間中,因此經過ibd文件進行恢復後,還須要進行REPAIR TABLE操做來重建表上全部的輔助索引。

>> 若是說Insert Buffer帶給InnoDB存儲引擎的是性能上的提高,那麼doublewrite(兩次寫)帶給InnoDB存儲引擎的是數據頁的可靠性。

>> 當發生數據庫宕機時,可能InnoDB存儲引擎正在寫入某個頁到表中,而這個頁只寫了一部分,好比16KB的頁,只寫了前4KB,以後就發生了宕機,這種狀況被稱爲部分寫失效(partial page write)。在InnoDB存儲引擎未使用doublewrite技術前,曾經出現過由於部分寫失效而致使數據丟失的狀況。有經驗的DBA也許會想,若是發生寫失效,能夠經過重作日誌進行恢復。這是一個辦法。可是必須清楚地認識到,重作日誌中記錄的是對頁的物理操做,如偏移量800,寫'aaaa'記錄。若是這個頁自己已經發生了損壞,再對其進行重作是沒有意義的。這就是說,在應用(apply)重作日誌前,用戶須要一個頁的副本,當寫入失效發生時,先經過頁的副原本還原該頁,再進行重作,這就是doublewrite

>> 在對緩衝池的髒頁進行刷新時,並不直接寫磁盤,而是會經過memcpy函數將髒頁先複製到內存中的doublewrite buffer,以後經過doublewrite buffer再分兩次,每次1MB順序地寫入共享表空間的物理磁盤上,而後立刻調用fsync函數,同步磁盤,避免緩衝寫帶來的問題在這個過程當中,由於doublewrite頁是連續的,所以這個過程是順序寫的,開銷並非很大。在完成doublewrite頁的寫入後,再將doublewrite buffer中的頁寫入各個表空間文件中,此時的寫入則是離散的。

>> InnoDB存儲引擎會監控對錶上各索引頁的查詢。若是觀察到創建哈希索引能夠帶來速度提高,則創建哈希索引,稱之爲自適應哈希索引(Adaptive Hash Index,AHI)。

>> 值得注意的是,哈希索引只能用來搜索等值的查詢,如SELECT*FROM table WHERE index_col='xxx'。而對於其餘查找類型,如範圍查找,是不能使用哈希索引的,所以這裏出現了non-hash searches/s的狀況

>> 用戶能夠在發出一個IO請求後當即再發出另外一個IO請求,當所有IO請求發送完畢後,等待全部IO操做的完成,這就是AIO。AIO的另外一個優點是能夠進行IO Merge操做,也就是將多個IO合併爲1個IO,這樣能夠提升IOPS的性能

>> 須要注意的是,Native AIO須要操做系統提供支持。Windows系統和Linux系統都提供Native AIO支持,而Mac OSX系統則未提供

>> 參數innodb_use_native_aio用來控制是否啓用Native AIO,在Linux操做系統下,默認值爲ON

>> 在關閉時,參數innodb_fast_shutdown影響着表的存儲引擎爲InnoDB的行爲。該參數可取值爲0、一、2,默認值爲1。

>> 參數innodb_force_recovery影響了整個InnoDB存儲引擎恢復的情況。該參數值默認爲0,表明當發生須要恢復時,進行全部的恢復操做,當不能進行有效恢復時,如數據頁發生了corruption,MySQL數據庫可能發生宕機(crash),並把錯誤寫入錯誤日誌中去。

>> 參數innodb_force_recovery還能夠設置爲6個非零值:1~6。大的數字表示包含了前面全部小數字表示的影響

 

第3章 文件

 

>> 默認狀況下,MySQL實例會按照必定的順序在指定的位置進行讀取,用戶只需經過命令mysql--help | grep my.cnf來尋找便可。

>> Oracle數據庫存在所謂的隱藏參數(undocumented parameter),以供Oracle「內部人士」使用,SQL Server也有相似的參數。有些DBA曾問我,MySQL中是否也有這類參數。個人回答是:沒有,也不須要。即便Oracle和SQL Server中都有些所謂的隱藏參數,在絕大多數的狀況下,這些數據庫廠商也不建議用戶在生產環境中對其進行很大的調整。

>> MySQL數據庫中的參數能夠分爲兩類:□ 動態(dynamic)參數□ 靜態(static)參數動態參數意味着能夠在MySQL實例運行中進行更改,靜態參數說明在整個實例生命週期內都不得進行更改,就好像是隻讀(read only)的

>> 當出現MySQL數據庫不能正常啓動時,第一個必須查找的文件應該就是錯誤日誌文件,該文件記錄了錯誤信息,能很好地指導用戶發現問題。

>> 設置long_query_time這個閾值後,MySQL數據庫會記錄運行時間超過該值的全部SQL語句,但運行時間正好等於long_query_time的狀況並不會被記錄下。也就是說,在源代碼中判斷的是大於long_query_time,而非大於等於

>> 另外一個和慢查詢日誌有關的參數是log_queries_not_using_indexes,若是運行的SQL語句沒有使用索引,則MySQL數據庫一樣會將這條SQL語句記錄到慢查詢日誌文件。

>> MySQL 5.6.5版本開始新增了一個參數log_throttle_queries_not_using_indexes,用來表示每分鐘容許記錄到slow log的且未使用索引的SQL語句次數。該值默認爲0,表示沒有限制。在生產環境下,若沒有使用索引,此類SQL語句會頻繁地被記錄到slow log,從而致使slow log文件的大小不斷增長,故DBA可經過此參數進行配置。

>> MySQL 5.1開始能夠將慢查詢的日誌記錄放入一張表中,這使得用戶的查詢更加方便和直觀。慢查詢表在mysql架構下,名爲slow_log

>> 查看slow_log表的定義會發現該表使用的是CSV引擎,對大數據量下的查詢效率可能不高。用戶能夠把slow_log表的引擎轉換到MyISAM,並在start_time列上添加索引以進一步提升查詢的效率。

>> 不能忽視的是,將slow_log表的存儲引擎更改成MyISAM後,仍是會對數據庫形成額外的開銷。

>> 用戶能夠經過額外的參數long_query_io將超過指定邏輯IO次數的SQL語句記錄到slow log中。該值默認爲100,即表示對於邏輯讀取次數大於100的SQL語句,記錄到slow log中。而爲了兼容原MySQL數據庫的運行方式,還添加了參數slow_query_type,用來表示啓用slow log的方式

>> 查詢日誌記錄了全部對MySQL數據庫請求的信息,不管這些請求是否獲得了正確的執行。默認文件名爲:主機名.log

>> 。一樣地,從MySQL 5.1開始,能夠將查詢日誌的記錄放入mysql架構下的general_log表中,該表的使用方法和前面小節提到的slow_log基本同樣

>> 二進制日誌(binary log)記錄了對MySQL數據庫執行更改的全部操做,可是不包括SELECT和SHOW這類操做,由於這類操做對數據自己並無修改。

>> 使用事務的表存儲引擎(如InnoDB存儲引擎)時,全部未提交(uncommitted)的二進制日誌會被記錄到一個緩存中去,等該事務提交(committed)時直接將緩衝中的二進制日誌寫入二進制日誌文件,而該緩衝的大小由binlog_cache_size決定,默認大小爲32K。

>> 此外,binlog_cache_size是基於會話(session)的,也就是說,當一個線程開始一個事務時,MySQL會自動分配一個大小爲binlog_cache_size的緩存,所以該值的設置須要至關當心,不能設置過大。當一個事務的記錄大於設定的binlog_cache_size時,MySQL會把緩衝中的日誌寫入一個臨時文件中,所以該值又不能設得過小

>> Binlog_cache_use記錄了使用緩衝寫二進制日誌的次數,binlog_cache_disk_use記錄了使用臨時文件寫二進制日誌的次數

>> 默認狀況下,二進制日誌並非在每次寫的時候同步到磁盤(用戶能夠理解爲緩衝寫)。所以,當數據庫所在操做系統發生宕機時,可能會有最後一部分數據沒有寫入二進制日誌文件中,這會給恢復和複製帶來問題

>> 即便將sync_binlog設爲1,仍是會有一種狀況致使問題的發生。當使用InnoDB存儲引擎時,在一個事務發出COMMIT動做以前,因爲sync_binlog爲1,所以會將二進制日誌當即寫入磁盤。若是這時已經寫入了二進制日誌,可是提交尚未發生,而且此時發生了宕機,那麼在MySQL數據庫下次啓動時,因爲COMMIT操做並無發生,這個事務會被回滾掉。可是二進制日誌已經記錄了該事務信息,不能被回滾。

>> 若是當前數據庫是複製中的slave角色,則它不會將從master取得並執行的二進制日誌寫入本身的二進制日誌文件中去。若是須要寫入,要設置log-slave-update。若是須要搭建master=>slave=>slave架構的複製,則必須設置該參數。

>> MySQL 5.1開始引入了binlog_format參數,該參數可設的值有STATEMENT、ROW和MIXED

>> 上面的這個例子告訴咱們,將參數binlog_format設置爲ROW,會對磁盤空間要求有必定的增長。而因爲複製是採用傳輸二進制日誌方式實現的,所以複製的網絡開銷也有所增長。

>> 要查看二進制日誌文件的內容,必須經過MySQL提供的工具mysqlbinlog。對於STATEMENT格式的二進制日誌文件,在使用mysqlbinlog後,看到的就是執行的邏輯SQL語句

>> 但不論表採用何種存儲引擎,MySQL都有一個以frm爲後綴名的文件,這個文件記錄了該表的表結構定義。

>> frm還用來存放視圖的定義,如用戶建立了一個v_a視圖,那麼對應地會產生一個v_a.frm文件,用來記錄視圖的定義,該文件是文本文件,能夠直接使用cat命令進行查看

>> 設置innodb_data_file_path參數後,全部基於InnoDB存儲引擎的表的數據都會記錄到該共享表空間中。若設置了參數innodb_file_per_table,則用戶能夠將每一個基於InnoDB存儲引擎的表產生一個獨立表空間

>> 。獨立表空間的命名規則爲:表名.ibd。經過這樣的方式,用戶不用將全部數據都存放於默認的表空間

>> 這些單獨的表空間文件僅存儲該表的數據、索引和插入緩衝BITMAP等信息,其他信息仍是存放在默認的表空間中

>> 在默認狀況下,在InnoDB存儲引擎的數據目錄下會有兩個名爲ib_logfile0和ib_logfile1的文件。在MySQL官方手冊中將其稱爲InnoDB存儲引擎的日誌文件,不過更準確的定義應該是重作日誌文件(redo log file)。爲何強調是重作日誌文件呢?由於重作日誌文件對於InnoDB存儲引擎相當重要,它們記錄了對於InnoDB存儲引擎的事務日誌。

>> 每一個InnoDB存儲引擎至少有1個重作日誌文件組(group),每一個文件組下至少有2個重作日誌文件,如默認的ib_logfile0和ib_logfile1。爲了獲得更高的可靠性,用戶能夠設置多個的鏡像日誌組(mirrored log groups),將不一樣的文件組放在不一樣的磁盤上,以此提升重作日誌的高可用性。在日誌組中每一個重作日誌文件的大小一致,並以循環寫入的方式運行。

>> InnoDB存儲引擎先寫重作日誌文件1,當達到文件的最後時,會切換至重作日誌文件2,再當重作日誌文件2也被寫滿時,會再切換到重作日誌文件1中。

>> 若磁盤自己已經作了高可用的方案,如磁盤陣列,那麼能夠不開啓重作日誌鏡像的功能

>> 二進制日誌會記錄全部與MySQL數據庫有關的日誌記錄,包括InnoDB、MyISAM、Heap等其餘存儲引擎的日誌。而InnoDB存儲引擎的重作日誌只記錄有關該存儲引擎自己的事務日誌。

>> 其次,記錄的內容不一樣,不管用戶將二進制日誌文件記錄的格式設爲STATEMENT仍是ROW,又或者是MIXED,其記錄的都是關於一個事務的具體操做內容,即該日誌是邏輯日誌。而InnoDB存儲引擎的重作日誌文件記錄的是關於每一個頁(Page)的更改的物理狀況。

>> 此外,寫入的時間也不一樣,二進制日誌文件僅在事務提交前進行提交,即只寫磁盤一次,不論這時該事務多大。而在事務進行的過程當中,卻不斷有重作日誌條目(redo entry)被寫入到重作日誌文件中。

>> 在InnoDB存儲引擎中,對於各類不一樣的操做有着不一樣的重作日誌格式。到InnoDB 1.2.x版本爲止,總共定義了51種重作日誌類型。

>> 在第2章中已經提到,寫入重作日誌文件的操做不是直接寫,而是先寫入一個重作日誌緩衝(redo log buffer)中,而後按照必定的條件順序地寫入日誌文件

>> 從重作日誌緩衝往磁盤寫入時,是按512個字節,也就是一個扇區的大小進行寫入。由於扇區是寫入的最小單位,所以能夠保證寫入一定是成功的。所以在重作日誌的寫入過程當中不須要有doublewrite。

>> 所以爲了保證事務的ACID中的持久性,必須將innodb_flush_log_at_trx_commit設置爲1,也就是每當有事務提交時,就必須確保事務都已經寫入重作日誌文件。那麼當數據庫由於意外發生宕機時,能夠經過重作日誌文件恢復,並保證能夠恢復已經提交的事務。

 

第4章 表

 

>> 在InnoDB存儲引擎中,表都是根據主鍵順序組織存放的,這種存儲方式的表稱爲索引組織表(index organized table)。

>> 在InnoDB存儲引擎表中,每張表都有個主鍵(Primary Key),若是在建立表時沒有顯式地定義主鍵,則InnoDB存儲引擎會按以下方式選擇或建立主鍵:□ 首先判斷表中是否有非空的惟一索引(Unique NOT NULL),若是有,則該列即爲主鍵。□ 如果不符合上述條件,InnoDB存儲引擎自動建立一個6字節大小的指針

>> 當表中有多個非空惟一索引時,InnoDB存儲引擎將選擇建表時第一個定義的非空惟一索引爲主鍵

>> 。這裏須要很是注意的是,主鍵的選擇根據的是定義索引的順序,而不是建表時列的順序。

>> 能夠經過下面的SQL語句判斷表的主鍵值:

>> _rowid能夠顯示錶的主鍵,所以經過上述查詢能夠找到表z的主鍵

>> 另外須要注意的是,_rowid只能用於查看單個列爲主鍵的狀況,對於多列組成的主鍵就顯得無能爲力了

>> 從InnoDB存儲引擎的邏輯存儲結構看,全部數據都被邏輯地存放在一個空間中,稱之爲表空間(tablespace)。表空間又由段(segment)、區(extent)、頁(page)組成。頁在一些文檔中有時也稱爲塊(block),

>> InnoDB存儲引擎的邏輯存儲結構大體如圖4-1所示。

>> 第3章中已經介紹了在默認狀況下InnoDB存儲引擎有一個共享表空間ibdata1,即全部數據都存放在這個表空間內。若是用戶啓用了參數innodb_file_per_table,則每張表內的數據能夠單獨放到一個表空間內。

>> 若是啓用了innodb_file_per_table的參數,須要注意的是每張表的表空間內存放的只是數據、索引和插入緩衝Bitmap頁其餘類的數據,如回滾(undo)信息,插入緩衝索引頁、系統事務信息,二次寫緩衝(Double write buffer)等仍是存放在原來的共享表空間內。這同時也說明了另外一個問題:即便在啓用了參數innodb_file_per_table以後,共享表空間仍是會不斷地增長其大小。

>> InnoDB存儲引擎不會在執行rollback時去收縮這個表空間。雖然InnoDB不會回收這些空間,可是會自動判斷這些undo信息是否還須要,若是不須要,則會將這些空間標記爲可用空間,供下次undo使用。

>> 我用python寫了一個py_innodb_page_info小工具,用來查看錶空間中各頁的類型和信息,用戶能夠在code.google.com上搜索david-mysql-tools進行查找

>> 由於前面已經介紹過了InnoDB存儲引擎表是索引組織的(index organized),所以數據即索引,索引即數據。那麼數據段即爲B+樹的葉子節點(圖4-1的Leaf node segment),索引段即爲B+樹的非索引節點(圖4-1的Non-leaf node segment)。

>> 區是由連續頁組成的空間,在任何狀況下每一個區的大小都爲1MB。爲了保證區中頁的連續性,InnoDB存儲引擎一次從磁盤申請4~5個區。在默認狀況下,InnoDB存儲引擎頁的大小爲16KB,即一個區中一共有64個連續的頁。

>> InnoDB 1.0.x版本開始引入壓縮頁,即每一個頁的大小能夠經過參數KEY_BLOCK_SIZE設置爲2K、4K、8K,所以每一個區對應頁的數量就應該爲5十二、25六、128。

>> InnoDB 1.2.x版本新增了參數innodb_page_size,經過該參數能夠將默認頁的大小設置爲4K、8K,可是頁中的數據庫不是壓縮。這時區中頁的數量一樣也爲25六、128。總之,不論頁的大小怎麼變化,區的大小老是爲1M。

>> 在用戶啓用了參數innodb_file_per_talbe後,建立的表默認大小是96KB。區中是64個連續的頁,建立的表的大小至少是1MB纔對啊?其實這是由於在每一個段開始時,先用32個頁大小的碎片頁(fragment page)來存放數據,在使用完這些頁以後纔是64個連續頁的申請。這樣作的目的是,對於一些小表,或者是undo這類的段,能夠在開始時申請較少的空間,節省磁盤容量的開銷

>> 由於已經用完了32個碎片頁,新的頁會採用區的方式進行空間的申請,若是此時用戶再經過py_innodb_page_info工具來看錶空間文件t1.ibd,應該能夠看到不少類型爲Freshly Allocated Page的頁:

>> 從InnoDB 1.2.x版本開始,能夠經過參數innodb_page_size將頁的大小設置爲4K、8K、16K。若設置完成,則全部表中頁的大小都爲innodb_page_size,不能夠對其再次進行修改。除非經過mysqldump導入和導出操做來產生新的庫

>> 每一個頁存放的行記錄也是有硬性定義的,最多容許存放16KB / 2-200行的記錄,即7992行記錄

>> InnoDB存儲引擎提供了Compact和Redundant兩種格式來存放行記錄數據,

>> 在MySQL 5.1版本中,默認設置爲Compact行格式。用戶能夠經過命令SHOW TABLE STATUS LIKE 'table_name'來查看當前表使用的行格式,其中row_format屬性表示當前所使用的行記錄結構類型。

>> 每行數據除了用戶定義的列外,還有兩個隱藏列,事務ID列和回滾指針列,分別爲6字節和7字節的大小。

>> 若InnoDB表沒有定義主鍵,每行還會增長一個6字節的rowid列。

>> InnoDB存儲引擎能夠將一條記錄中的某些數據存儲在真正的數據頁面以外。通常認爲BLOB、LOB這類的大對象列類型的存儲會把數據存放在數據頁面以外。可是,這個理解有點誤差,BLOB能夠不將數據放在溢出頁面,並且即使是VARCHAR列數據類型,依然有可能被存放爲行溢出數據

>> 從錯誤消息能夠看到InnoDB存儲引擎並不支持65535長度的VARCHAR。這是由於還有別的開銷,經過實際測試發現能存放VARCHAR類型的最大長度爲65532。

>> 所以從這個例子中用戶也應該理解VARCHAR(N)中的N指的是字符的長度。而文檔中說明VARCHAR類型最大支持65535,單位是字節。

>> 此外須要注意的是,MySQL官方手冊中定義的65535長度是指全部VARCHAR列的長度總和,若是列的長度總和超出這個長度,依然沒法建立

>> 即便能存放65532個字節,可是有沒有想過,InnoDB存儲引擎的頁爲16KB,即16384字節,怎麼能存放65532字節呢?所以,在通常狀況下,InnoDB存儲引擎的數據都是存放在頁類型爲B-tree node中。可是當發生行溢出時,數據存放在頁類型爲Uncompress BLOB頁中。

>> InnoDB存儲引擎表是索引組織的,即B+Tree的結構,這樣每一個頁中至少應該有兩條行記錄(不然失去了B+Tree的意義,變成鏈表了)。所以,若是頁中只能存放下一條記錄,那麼InnoDB存儲引擎會自動將行數據存放到溢出頁中

>> 通過屢次試驗測試,發現這個閾值的長度爲8098

>> 對於TEXT或BLOB的數據類型,用戶老是覺得它們是存放在Uncompressed BLOB Page中的,其實這也是不許確的。是放在數據頁中仍是BLOB頁中,和前面討論的VARCHAR同樣,至少保證一個頁能存放兩條記錄

>> InnoDB 1.0.x版本開始引入了新的文件格式(file format,用戶能夠理解爲新的頁格式),之前支持的Compact和Redundant格式稱爲Antelope文件格式,新的文件格式稱爲Barracuda文件格式。Barracuda文件格式下擁有兩種新的行記錄格式:Compressed和Dynamic。

>> 從MySQL 4.1版本開始,CHR(N)中的N指的是字符的長度,而不是以前版本的字節長度。也就說在不一樣的字符集下,CHAR類型列內部存儲的可能不是定長的數據

>> SELECT a,CHAR_LENGTH(a),LENGTH(a)

>> SELECT a,HEX(a)        -> FROM j\G;

>> SHOW VARIABLES LIKE 'innodb_file_format'\G;

>> 關係型數據庫系統和文件系統的一個不一樣點是,關係數據庫自己能保證存儲數據的完整性,不須要應用程序的控制,而文件系統通常須要在程序端進行控制

>> 經過設置參數sql_mode的值爲STRICT_TRANS_TABLES,此次MySQL數據庫對於輸入值的合法性進行了約束,並且針對不一樣的錯誤,提示的錯誤內容也都不一樣。參數sql_mode可設的值有不少,具體可參考MySQL官方手冊。

>> 最多能夠爲一個表創建6個觸發器,即分別爲INSERT、UPDATE、DELETE的BEFORE和AFTER各定義一個。

>> 假設有張用戶消費表,每次用戶購買同樣物品後其金額都是減的,若這時有「不懷好意」的用戶作了相似減去一個負值的操做,這樣用戶的錢沒減小反而會不斷增長

>> 通常來講,稱被引用的表爲父表,引用的表稱爲子表。外鍵定義時的ON DELETE和ON UPDATE表示在對父表進行DELETE和UPDATE操做時,對子表所作的操做

>> CASCADE表示當父表發生DELETE或UPDATE操做時,對相應的子表中的數據也進行DELETE或UPDATE操做。SET NULL表示當父表發生DELETE或UPDATE操做時,相應的子表中的數據被更新爲NULL值,可是子表中相對應的列必須容許爲NULL值。NO ACTION表示當父表發生DELETE或UPDATE操做時,拋出錯誤,不容許這類操做發生。RESTRICT表示當父表發生DELETE或UPDATE操做時,拋出錯誤,不容許這類操做發生。

>> 在其餘數據庫中,如Oracle數據庫,有一種稱爲延時檢查(deferred check)的外鍵約束,即檢查在SQL語句運行完成後再進行。而目前MySQL數據庫的外鍵約束都是即時檢查(immediate check)

>> 雖然視圖是基於基表的一個虛擬表,可是用戶能夠對某些視圖進行更新操做,其本質就是經過視圖的定義來更新基本表。通常稱能夠進行更新操做的視圖爲可更新視圖(updatable view)。視圖定義中的WITH CHECK OPTION就是針對於可更新的視圖的,即更新的值是否須要檢查

>> MySQL數據庫DBA的一個經常使用的命令是SHOW TABLES,該命令會顯示出當前數據庫下全部的表。但由於視圖是虛表,一樣被做爲表顯示出來

>> 用戶只想查看當前架構下的基表,能夠經過information_schema架構下的TABLE表來查詢,並搜索表類型爲BASE TABLE的表

>> Oracle數據庫支持物化視圖——該視圖不是基於基表的虛表,而是根據基表實際存在的實表,即物化視圖的數據存儲在非易失的存儲設備上。物化視圖能夠用於預先計算並保存多表的連接(JOIN)或彙集(GROUP BY)等耗時較多的SQL操做結果。這樣,在執行復雜查詢時,就能夠避免進行這些耗時的操做,從而快速獲得結果。物化視圖的好處是對於一些複雜的統計類查詢能直接查出結果。在Microsoft SQL Server數據庫中,稱這種視圖爲索引視圖。

>> 分區功能並非在存儲引擎層完成的,所以不是隻有InnoDB存儲引擎支持分區,常見的存儲引擎MyISAM、NDB等都支持。但也並非全部的存儲引擎都支持,如CSV、FEDORATED、MERGE等就不支持。在使用分區功能前,應該對選擇的存儲引擎對分區的支持有所瞭解。

>> MySQL數據庫支持的分區類型爲水平分[插圖],並不支持垂直分[插圖]。此外,MySQL數據庫的分區是局部分區索引,一個分區中既存放了數據又存放了索引。而全局分區是指,數據存放在各個分區中,可是全部數據的索引放在一個對象中。目前,MySQL數據庫還不支持全局分區。

>> 不論建立何種類型的分區,若是表中存在主鍵或惟一索引時,分區列必須是惟一索引的一個組成部分

>> 惟一索引能夠是容許NULL值的,而且分區列只要是惟一索引的一個組成部分,不須要整個惟一索引列都是分區列

>> 若是建表時沒有指定主鍵,惟一索引,能夠指定任何一個列爲分區列

>> 查看錶在磁盤上的物理文件,啓用分區以後,表再也不由一個ibd文件組成了,而是由創建分區時的各個分區ibd文件組成,以下面的t#P#p0.ibd,t#P#p1.ibd

>> 經過EXPLAIN PARTITION命令咱們能夠發現,在上述語句中,SQL優化器只須要去搜索p2008這個分區,而不會去搜索全部的分區——稱爲Partition Pruning(分區修剪),故查詢的速度獲得了大幅度的提

>> 在前面介紹的RANGE、LIST、HASH和KEY這四種分區中,分區的條件是:數據必須是整型(interger),若是不是整型,那應該須要經過函數將其轉化爲整型,如YEAR(),TO_DAYS(),MONTH()等函數。MySQL5.5版本開始支持COLUMNS分區,可視爲RANGE分區和LIST分區的一種進化。COLUMNS分區能夠直接使用非整型的數據進行分區,分區根據類型直接比較而得,不須要轉化爲整型。此外,RANGE COLUMNS分區能夠對多個列的值進行分區。

>> 子分區(subpartitioning)是在分區的基礎上再進行分區,有時也稱這種分區爲複合分區(composite partitioning)。MySQL數據庫容許在RANGE和LIST的分區上再進行HASH或KEY的子分區

>> 子分區的創建須要注意如下幾個問題:□ 每一個子分區的數量必須相同。□ 要在一個分區表的任何分區上使用SUBPARTITION來明肯定義任何子分區,就必須定義全部的子分區。

>> 子分區能夠用於特別大的表,在多個磁盤間分別分配數據和索引

>> MySQL數據庫容許對NULL值作分區,可是處理的方法與其餘數據庫可能徹底不一樣。MYSQL數據庫的分區老是視NULL值視小於任何的一個非NULL值,這和MySQL數據庫中處理NULL值的ORDER BY操做是同樣的

>> 。所以對於不一樣的分區類型,MySQL數據庫對於NULL值的處理也是各不相同。

>> HASH和KEY分區對於NULL的處理方式和RANGE分區、LIST分區不同。任何分區函數都會將含有NULL值的記錄返回爲0

>> 對於OLAP的應用,分區的確是能夠很好地提升查詢的性能,由於OLAP應用大多數查詢須要頻繁地掃描一張很大的表。假設有一張1億行的表,其中有一個時間戳屬性列。用戶的查詢須要從這張表中獲取一年的數據。若是按時間戳進行分區,則只須要掃描相應的分區便可。這就是前面介紹的Partition Pruning技術。

>> 然而對於OLTP的應用,分區應該很是當心。在這種應用下,一般不可能會獲取一張大表中10%的數據,大部分都是經過索引返回幾條記錄便可。而根據B+樹索引的原理可知,對於一張大表,通常的B+樹須要2~3次的磁盤IO。所以B+樹能夠很好地完成操做,不須要分區的幫助,而且設計很差的分區會帶來嚴重的性能問題。

>> 我發現不少開發團隊會認爲含有1000W行的表是一張很是巨大的表,因此他們每每會選擇採用分區,如對主鍵作10個HASH的分區,這樣每一個分區就只有100W的數據了,所以查詢應該變得更快了,如SELECT * FROM TABLE WHERE PK=@pk。可是有沒有考慮過這樣一種狀況:100W和1000W行的數據自己構成的B+樹的層次都是同樣的,可能都是2層。

>> MySQL 5.6開始支持ALTER TABLE … EXCHANGE PARTITION語法。該語句容許分區或子分區中的數據與另外一個非分區的表中的數據進行交換。若是非分區表中的數據爲空,那麼至關於將分區中的數據移動到非分區表中。若分區表中的數據爲空,則至關於將外部表中的數據導入到分區中。

 

第5章 索引與算法

 

>>  B+樹中的B不是表明二叉(binary),而是表明平衡(balance),由於B+樹是從最先的平衡二叉樹演化而來,可是B+樹不是一個二叉樹。

>> 另外一個經常被DBA忽視的問題是:B+樹索引並不能找到一個給定鍵值的具體行。B+樹索引能找到的只是被查找數據行所在的頁。而後數據庫經過把頁讀入到內存,再在內存中進行查找,最後獲得要查找的數據。

>> 平衡二叉樹的查找性能是比較高的,但不是最高的,只是接近最高性能。最好的性能須要創建一棵最優二叉樹,可是最優二叉樹的創建和維護須要大量的操做,所以,用戶通常只需創建一棵平衡二叉樹便可。

>> 平衡二叉樹的查詢速度的確很快,可是維護一棵平衡二叉樹的代價是很是大的。一般來講,須要1次或屢次左旋和右旋來獲得插入或更新後樹的平衡性。

>> B+樹由B樹和索引順序訪問方法(ISAM,是否是很熟悉?對,這也是MyISAM引擎最初參考的數據結構)演化而來,可是在現實使用過程當中幾乎已經沒有使用B樹的狀況了。

>> B+樹是爲磁盤或其餘直接存取輔助設備設計的一種平衡查找樹。在B+樹中,全部記錄節點都是按鍵值的大小順序存放在同一層的葉子節點上,由各葉子節點指針進行鏈接。

>> 能夠看到,無論怎麼變化,B+樹老是會保持平衡。可是爲了保持平衡對於新插入的鍵值可能須要作大量的拆分頁(split)操做。由於B+樹結構主要用於磁盤,頁的拆分意味着磁盤的操做,因此應該在可能的狀況下儘可能減小頁的拆分操做。

>> 所以,B+樹一樣提供了相似於平衡二叉樹的旋轉(Rotation)功能。

>> B+樹索引的本質就是B+樹在數據庫中的實現。

>> 可是B+索引在數據庫中有一個特色是高扇出性,所以在數據庫中,B+樹的高度通常都在2~4層,這也就是說查找某一鍵值的行記錄時最多隻須要2到4次IO,這倒不錯。由於當前通常的機械磁盤每秒至少能夠作100次IO,2~4次的IO意味着查詢時間只需0.02~0.04秒。

>> 數據庫中的B+樹索引能夠分爲彙集索引(clustered inex)和輔助索引(secondary index[插圖],可是無論是彙集仍是輔助的索引,其內部都是B+樹的,即高度平衡的,葉子節點存放着全部的數據。彙集索引與輔助索引不一樣的是,葉子節點存放的是不是一整行的信息。

>> 彙集索引(clustered index)就是按照每張表的主鍵構造一棵B+樹,同時葉子節點中存放的即爲整張表的行記錄數據,也將彙集索引的葉子節點稱爲數據頁。

>> 因爲實際的數據頁只能按照一棵B+樹進行排序,所以每張表只能擁有一個彙集索引

>> 許多數據庫的文檔會這樣告訴讀者:彙集索引按照順序物理地存儲數據。若是看圖5-14,可能也會有這樣的感受。可是試想一下,若是彙集索引必須按照特定順序存放物理記錄,則維護成本顯得很是之高。因此,彙集索引的存儲並非物理上連續的,而是邏輯上連續的。

>> 對於輔助索引(Secondary Index,也稱非彙集索引),葉子節點並不包含行記錄的所有數據。葉子節點除了包含鍵值之外,每一個葉子節點中的索引行中還包含了一個書籤(bookmark)。該書籤用來告訴InnoDB存儲引擎哪裏能夠找到與索引相對應的行數據

>> 。因爲InnoDB存儲引擎表是索引組織表,所以InnoDB存儲引擎的輔助索引的書籤就是相應行數據的彙集索引鍵。

>> 若是在一棵高度爲3的輔助索引樹中查找數據,那須要對這棵輔助索引樹遍歷3次找到指定主鍵,若是彙集索引樹的高度一樣爲3,那麼還須要對彙集索引樹進行3次查找,最終找到一個完整的行數據所在的頁,所以一共須要6次邏輯IO訪問以獲得最終的一個數據頁。

>> 用戶能夠設置對整個列的數據進行索引,也能夠只索引一個列的開頭部分數據,如前面建立的表t,列b爲varchar(8000),可是用戶能夠只索引前100個字段

>>  Cardinality:很是關鍵的值,表示索引中惟一值的數目的估計值。Cardinality表的行數應儘量接近1,若是很是小,那麼用戶須要考慮是否能夠刪除此索引。

>> Cardinality值很是關鍵,優化器會根據這個值來判斷是否使用這個索引。可是這個值並非實時更新的,即並不是每次索引的更新都會更新該值,由於這樣代價太大了

>> Cardinality爲NULL,在某些狀況下可能會發生索引創建了卻沒有用到的狀況。或者對兩條基本同樣的語句執行EXPLAIN,可是最終出來的結果不同:一個使用索引,另一個使用全表掃描。這時最好的解決辦法就是作一次ANALYZE TABLE的操做。

>> MySQL 5.5版本以前(不包括5.5)存在的一個廣泛被人詬病的問題是MySQL數據庫對於索引的添加或者刪除的這類DDL操做,MySQL數據庫的操做過程爲:

>> InnoDB存儲引擎從InnoDB 1.0.x版本開始支持一種稱爲Fast Index Creation(快速索引建立)的索引建立方式——簡稱FIC。

>> 對於輔助索引的建立,InnoDB存儲引擎會對建立索引的表加上一個S鎖。在建立的過程當中,不須要重建表,所以速度較以前提升不少,而且數據庫的可用性也獲得了提升。刪除輔助索引操做就更簡單了,InnoDB存儲引擎只需更新內部視圖,並將輔助索引的空間標記爲可用,同時刪除MySQL數據庫內部視圖上對該表的索引定義便可。這裏須要特別注意的是,臨時表的建立路徑是經過參數tmpdir進行設置的。用戶必須保證tmpdir有足夠的空間能夠存放臨時表,不然會致使建立索引失敗。

>> 因爲FIC在索引的建立的過程當中對錶加上了S鎖,所以在建立的過程當中只能對該表進行讀操做,如有大量的事務須要對目標表進行寫操做,那麼數據庫的服務一樣不可用

>> Facebook採用PHP腳原本現實OSC,而並非經過修改InnoDB存儲引擎源碼的方式。OSC最初由Facebook的員工Vamsi Ponnekanti開發。此外,OSC借鑑了開源社區以前的工具The openarkkit toolkit oak-online-alter-table。實現OSC步驟以下:

>> MySQL 5.6版本開始支持Online DDL(在線數據定義)操做,其容許輔助索引建立的同時,還容許其餘諸如INSERT、UPDATE、DELETE這類DML操做,這極大地提升了MySQL數據庫在生產環境中的可用性。

>> LOCK部分爲索引建立或刪除時對錶添加鎖的狀況,可有的選擇爲:

>> InnoDB存儲引擎實現Online DDL的原理是在執行建立或者刪除操做的同時,將INSERT、UPDATE、DELETE這類DML操做日誌寫入到一個緩存中。待完成索引建立後再將重作應用到表上,以此達到數據的一致性。這個緩存的大小由參數innodb_online_alter_log_max_size控制,默認的大小爲128MB。若用戶更新的表比較大,而且在建立過程當中伴有大量的寫事務,如遇到innodb_online_alter_log_max_size的空間不能存放日誌時,會拋出相似以下的錯誤:

>> 對於這個錯誤,用戶能夠調大參數innodb_online_alter_log_max_size,以此得到更大的日誌緩存空間。此外,還能夠設置ALTER TABLE的模式爲SHARE,這樣在執行過程當中不會有寫事務發生,所以不須要進行DML日誌的記錄。

>> 若是某個字段的取值範圍很廣,幾乎沒有重複,即屬於高選擇性,則此時使用B+樹索引是最適合的。例如,對於姓名字段,基本上在一個應用中不容許重名的出現。

>> 在InnoDB存儲引擎中,Cardinality統計信息的更新發生在兩個操做中:INSERT和UPDATE。

>> 聯合索引的第二個好處是已經對第二個鍵值進行了排序處理。例如,在不少狀況下應用程序都須要查詢某個用戶的購物狀況,並按照時間進行排序,最後取出最近三次的購買記錄,這時使用聯合索引能夠避免多一次的排序操做,由於索引自己在葉子節點已經排序了。

>> 正如前面所介紹的那樣,聯合索引(a,b)實際上是根據列a、b進行排序,所以下列語句能夠直接使用聯合索引獲得結果:    SELECT ... FROM TABLE WHERE a=xxx ORDER BY b

>> InnoDB存儲引擎支持覆蓋索引(covering index,或稱索引覆蓋),即從輔助索引中就能夠獲得查詢的記錄,而不須要查詢彙集索引中的記錄。使用覆蓋索引的一個好處是輔助索引不包含整行記錄的全部信息,故其大小要遠小於彙集索引,所以能夠減小大量的IO操做。

>> 覆蓋索引的另外一個好處是對某些統計問題而言的

>> 表buy_log有(userid,buy_date)的聯合索引,這裏只根據列b進行條件查詢,通常狀況下是不能進行該聯合索引的,可是這句SQL查詢是統計操做,而且能夠利用到覆蓋索引的信息,所以優化器會選擇該聯合索引

>> 這是爲何呢?緣由在於用戶要選取的數據是整行信息,而OrderID索引不能覆蓋到咱們要查詢的信息,所以在對OrderID索引查詢到指定數據後,還須要一次書籤訪問來查找整行數據的信息。雖然OrderID索引中數據是順序存放的,可是再一次進行書籤查找的數據則是無序的,所以變爲了磁盤上的離散讀操做。若是要求訪問的數據量很小,則優化器仍是會選擇輔助索引,可是當訪問的數據佔整個表中數據的蠻大一部分時(通常是20%左右),優化器會選擇經過彙集索引來查找數據。由於以前已經提到過,順序讀要遠遠快於離散讀。

>> 所以對於不能進行索引覆蓋的狀況,優化器選擇輔助索引的狀況是,經過輔助索引查找的數據是少許的。這是由當前傳統機械硬盤的特性所決定的,即利用順序讀來替換隨機讀的查找。若用戶使用的磁盤是固態硬盤,隨機讀操做很是快,同時有足夠的自信來確認使用輔助索引能夠帶來更好的性能,那麼能夠使用關鍵字FORCE INDEX來強制使用某個索引

>> 所以,USE INDEX只是告訴優化器能夠選擇該索引,實際上優化器仍是會再根據本身的判斷進行選擇。而若是使用FORCE INDEX的索引提示,如:

>> MySQL5.6版本開始支持Multi-Range Read(MRR)優化。Multi-Range Read優化的目的就是爲了減小磁盤的隨機訪問,並且將隨機訪問轉化爲較爲順序的數據訪問,這對於IO-bound類型的SQL查詢語句可帶來性能極大的提高。

>> MRR的工做方式以下:□ 將查詢獲得的輔助索引鍵值存放於一個緩存中,這時緩存中的數據是根據輔助索引鍵值排序的。□ 將緩存中的鍵值根據RowID進行排序。□ 根據RowID的排序順序來訪問實際的數據文件

>> 以前的MySQL數據庫版本不支持Index Condition Pushdown,當進行索引查詢時,首先根據索引來查找記錄,而後再根據WHERE條件來過濾記錄。在支持Index Condition Pushdown後,MySQL數據庫會在取出索引的同時,判斷是否能夠進行WHERE條件的過濾,也就是將WHERE的部分過濾操做放在了存儲引擎層

>> 當優化器選擇Index Condition Pushdown優化時,可在執行計劃的列Extra看到Using index condition提示

>> 自適應哈希索引採用以前討論的哈希表的方式實現。不一樣的是,這僅是數據庫自身建立並使用的,DBA自己並不能對其進行干預。自適應哈希索引經哈希函數映射到一個哈希表中,所以對於字典類型的查找很是快速

>> 全文檢索一般使用倒排索引(inverted index)來實現。倒排索引同B+樹索引同樣,也是一種索引結構。它在輔助表(auxiliary table)中存儲了單詞與單詞自身在一個或多個文檔中所在位置之間的映射。這一般利用關聯數組實現,其擁有兩種表現形式:

>> full inverted index還存儲了單詞所在的位置信息,如code這個單詞出如今(1∶6),即文檔1的第6個單詞爲code。相比之下,full inverted index佔用更多的空間,可是能更好地定位數據,並擴充一些其餘的搜索特性。

>> InnoDB存儲引擎從1.2.x版本開始支持全文檢索的技術,其採用full inverted index的方式

>> 。在InnoDB存儲引擎中,將(DocumentId,Position)視爲一個「ilist」。所以在全文檢索的表中,有兩個列,一個是word字段,另外一個是ilist字段,而且在word字段上有設有索引

>> 正如以前所說的那樣,倒排索引須要將word存放到一張表中,這個表稱爲Auxiliary Table(輔助表)。在InnoDB存儲引擎中,爲了提升全文檢索的並行性能,共有6張Auxiliary Table,目前每張表根據word的Latin編碼進行分區。

>> Auxiliary Table是持久的表,存放於磁盤上。然而在InnoDB存儲引擎的全文索引中,還有另一個重要的概念FTS Index Cache(全文檢索索引緩存),其用來提升全文檢索的性能FTS Index Cache是一個紅黑樹結構,其根據(word,ilist)進行排序。這意味着插入的數據已經更新了對應的表,可是對全文索引的更新可能在分詞操做後還在FTS Index Cache中,Auxiliary Table可能尚未更新。InnoDB存儲引擎會批量對Auxiliary Table進行更新,而不是每次插入後更新一次Auxiliary Table

>> FTS Document ID是另一個重要的概念。在InnoDB存儲引擎中,爲了支持全文檢索,必須有一個列與word進行映射,在InnoDB中這個列被命名爲FTS_DOC_ID,其類型必須是BIGINT UNSIGNED NOT NULL,而且InnoDB存儲引擎自動會在該列上加入一個名爲FTS_DOC_ID_INDEX的Unique Index

>> 文檔中分詞的插入操做是在事務提交時完成,然而對於刪除操做,其在事務提交時,不刪除磁盤Auxiliary Table中的記錄,而只是刪除FTS Cache Index中的記錄

>> 因爲文檔的DML操做實際並不刪除索引中的數據,相反還會在對應的DELETED表中插入記錄,所以隨着應用程序的容許,索引會變得很是大,即便索引中的有些數據已經被刪除,查詢也不會選擇這類記錄。

>> 經過設置參數innodb_ft_aux_table來查看分詞對應的信息:

>> SELECT * FROM information_schema.INNODB_FT_INDEX_TABLE;

>> 當前InnoDB存儲引擎的全文檢索還存在如下的限制:□ 每張表只能有一個全文檢索的索引。□ 由多列組合而成的全文檢索的索引列必須使用相同的字符集與排序規則。□ 不支持沒有單詞界定符(delimiter)的語言,如中文、日語、韓語等。

 

第6章 鎖

 

>> 鎖是數據庫系統區別於文件系統的一個關鍵特性。鎖機制用於管理對共享資源的併發訪[插圖]

>> 2005版本,Microsoft SQL Server開始支持樂觀併發和悲觀併發,在樂觀併發下開始支持行級鎖,可是其實現方式與InnoDB存儲引擎的實現方式徹底不一樣。用戶會發如今Microsoft SQL Server下,鎖是一種稀有的資源,鎖越多開銷就越大,所以它會有鎖升級。在這種狀況下,行鎖會升級到表鎖,這時併發的性能又回到了之前。

>> latch通常稱爲閂鎖(輕量級的鎖),由於其要求鎖定的時間必須很是短。若持續的時間長,則應用的性能會很是差。在InnoDB存儲引擎中,latch又能夠分爲mutex(互斥量)和rwlock(讀寫鎖)。其目的是用來保證併發線程操做臨界資源的正確性,而且一般沒有死鎖檢測的機制。

>> 對於InnoDB存儲引擎中的latch,能夠經過命令SHOW ENGINE INNODB MUTEX來進行查看

>> 相對於latch的查看,lock信息就顯得直觀多了。用戶能夠經過命令SHOW ENGINE INNODB STATUS及information_schema架構下的表INNODB_TRX、INNODB_LOCKS、INNODB_LOCK_WAITS來觀察鎖的信息

>> 若是一個事務T1已經得到了行r的共享鎖,那麼另外的事務T2能夠當即得到行r的共享鎖,由於讀取並無改變行r的數據,稱這種狀況爲鎖兼容(Lock Compatible)。但如有其餘的事務T3想得到行r的排他鎖,則其必須等待事務T一、T2釋放行r上的共享鎖——這種狀況稱爲鎖不兼容。

>> S和X鎖都是行鎖,兼容是指對同一記錄(row)鎖的兼容性狀況。

>> nnoDB存儲引擎支持意向鎖設計比較簡練,其意向鎖即爲表級別的鎖。設計目的主要是爲了在一個事務中揭示下一行將被請求的鎖類型。

>> 在InnoDB 1.0版本以前,用戶只能經過命令SHOW FULL PROCESSLIST,SHOW ENGINE INNODB STATUS等來查看當前數據庫中鎖的請求,而後再判斷事務鎖的狀況。從InnoDB1.0開始,在INFORMATION_SCHEMA架構下添加了表INNODB_TRX、INNODB_LOCKS、INNODB_LOCK_WAITS。經過這三張表,用戶能夠更簡單地監控當前事務並分析可能存在的鎖問題

>> 在經過表INNODB_LOCKS查看了每張表上鎖的狀況後,用戶就能夠來判斷由此引起的等待狀況了。當事務較小時,用戶就能夠人爲地、直觀地進行判斷了。可是當事務量很是大,其中鎖和等待也時常發生,這個時候就不這麼容易判斷。可是經過表INNODB_LOCK_WAITS,能夠很直觀地反映當前事務的等待

>> 一致性的非鎖定讀(consistent nonlocking read)是指InnoDB存儲引擎經過行多版本控制(multi versioning)的方式來讀取當前執行時間數據庫中行的數據。若是讀取的行正在執行DELETE或UPDATE操做,這時讀取操做不會所以去等待行上鎖的釋放。相反地,InnoDB存儲引擎會去讀取行的一個快照數據。

>> 之因此稱其爲非鎖定讀,由於不須要等待訪問的行上X鎖的釋放。快照數據是指該行的以前版本的數據,該實現是經過undo段來完成。而undo用來在事務中回滾數據,所以快照數據自己是沒有額外的開銷

>> 非鎖定讀機制極大地提升了數據庫的併發性。在InnoDB存儲引擎的默認設置下,這是默認的讀取方式,即讀取不會佔用和等待表上的鎖。可是在不一樣事務隔離級別下,讀取的方式不一樣,並非在每一個事務隔離級別下都是採用非鎖定的一致性讀。此外,即便都是使用非鎖定的一致性讀,可是對於快照數據的定義也各不相同。

>> 一個行記錄可能有不止一個快照數據,通常稱這種技術爲行多版本技術。由此帶來的併發控制,稱之爲多版本併發控制(Multi Version Concurrency Control,MVCC)。

>> 在事務隔離級別READ COMMITTED和REPEATABLE READ(InnoDB存儲引擎的默認事務隔離級別)下,InnoDB存儲引擎使用非鎖定的一致性讀。然而,對於快照數據的定義卻不相同。在READ COMMITTED事務隔離級別下,對於快照數據,非一致性讀老是讀取被鎖定行的最新一份快照數據。而在REPEATABLE READ事務隔離級別下,對於快照數據,非一致性讀老是讀取事務開始時的行數據版本

>> 須要特別注意的是,對於READ COMMITTED的事務隔離級別而言,從數據庫理論的角度來看,其違反了事務ACID中的I的特性,即隔離性

>> 可是在某些狀況下,用戶須要顯式地對數據庫讀取操做進行加鎖以保證數據邏輯的一致性。而這要求數據庫支持加鎖語句,即便是對於SELECT的只讀操做。InnoDB存儲引擎對於SELECT語句支持兩種一致性的鎖定讀(locking read)操做:□ SELECT…FOR UPDATE□ SELECT…LOCK IN SHARE MODE

>> SELECT…FOR UPDATE對讀取的行記錄加一個X鎖,其餘事務不能對已鎖定的行加上任何鎖。SELECT…LOCK IN SHARE MODE對讀取的行記錄加一個S鎖,其餘事務能夠向被鎖定的行加S鎖,可是若是加X鎖,則會被阻塞。

>> 對於一致性非鎖定讀,即便讀取的行已被執行了SELECT…FOR UPDATE,也是能夠進行讀取的,這和以前討論的狀況同樣。此外,SELECT…FOR UPDATE,SELECT…LOCK IN SHARE MODE必須在一個事務中,當事務提交了,鎖也就釋放了。

>> 插入操做會依據這個自增加的計數器值加1賦予自增加列。這個實現方式稱作AUTO-INC Locking。這種鎖實際上是採用一種特殊的表鎖機制,爲了提升插入的性能,鎖不是在一個事務完成後才釋放,而是在完成對自增加值插入的SQL語句後當即釋放

>> 雖然AUTO-INC Locking從必定程度上提升了併發插入的效率,但仍是存在一些性能上的問題。首先,對於有自增加值的列的併發插入性能較差,事務必須等待前一個插入的完成(雖然不用等待事務的完成)。其次,對於INSERT…SELECT的大數據量的插入會影響插入的性能,由於另外一個事務中的插入會被阻塞。

>> 從MySQL 5.1.22版本開始,InnoDB存儲引擎中提供了一種輕量級互斥量的自增加實現機制,這種機制大大提升了自增加值插入的性能。

>> 還須要特別注意的是InnoDB存儲引擎中自增加的實現和MyISAM不一樣,MyISAM存儲引擎是表鎖設計,自增加不用考慮併發插入的問題。所以在master上用InnoDB存儲引擎,在slave上用MyISAM存儲引擎的replication架構下,用戶必須考慮這種狀況。

>> InnoDB存儲引擎有3種行鎖的算法,其分別是:□ Record Lock:單個行記錄上的鎖□ Gap Lock:間隙鎖,鎖定一個範圍,但不包含記錄自己□ Next-Key Lock∶Gap Lock+Record Lock,鎖定一個範圍,而且鎖定記錄自己

>> 當查詢的索引含有惟一屬性時,InnoDB存儲引擎會對Next-Key Lock進行優化,將其降級爲Record Lock,即僅鎖住索引自己,而不是範圍。

>> InnoDB存儲引擎默認的事務隔離級別是REPEATABLE READ,在該隔離級別下,其採用Next-Key Locking的方式來加鎖。而在事務隔離級別READ COMMITTED下,其僅採用Record Lock,所以在上述的示例中,會話A須要將事務的隔離級別設置爲READ COMMITTED。

>> 不可重複讀是指在一個事務內屢次讀取同一數據集合。在這個事務尚未結束時,另一個事務也訪問該同一數據集合,並作了一些DML操做。所以,在第一個事務中的兩次讀數據之間,因爲第二個事務的修改,那麼第一個事務兩次讀到的數據多是不同的。這樣就發生了在一個事務內兩次讀到的數據是不同的狀況,這種狀況稱爲不可重複讀。

>> 不可重複讀和髒讀的區別是:髒讀是讀到未提交的數據,而不可重複讀讀到的倒是已經提交的數據,可是其違反了數據庫事務一致性的要求。

>> 在MySQL官方文檔中將不可重複讀的問題定義爲Phantom Problem,即幻像問題。

>> 丟失更新是另外一個鎖致使的問題,簡單來講其就是一個事務的更新操做會被另外一個事務的更新操做所覆蓋,從而致使數據的不一致。

>> 由於不一樣鎖之間的兼容性關係,在有些時刻一個事務中的鎖須要等待另外一個事務中的鎖釋放它所佔用的資源,這就是阻塞。阻塞並非一件壞事,其是爲了確保事務能夠併發且正常地運行。

>> 在InnoDB存儲引擎中,參數innodb_lock_wait_timeout用來控制等待的時間(默認是50秒),innodb_rollback_on_timeout用來設定是否在等待超時時對進行中的事務進行回滾操做(默認是OFF,表明不回滾)。

Spring應該是會幫咱們處理回滾的。本身用JDBC寫咱們捕獲異常後,通常也會手動回滾。

>須要牢記的是,在默認狀況下InnoDB存儲引擎不會回滾超時引起的錯誤異常。其實InnoDB存儲引擎在大部分狀況下都不會對異常進行回滾。

>> 解決死鎖問題最簡單的方式是不要有等待,將任何的等待都轉化爲回滾,而且事務從新開始。

>> 毫無疑問,這的確能夠避免死鎖問題的產生。然而在線上環境中,這可能致使併發性能的降低,甚至任何一個事務都不能進行。而這所帶來的問題遠比死鎖問題更爲嚴重,由於這很難被發現而且浪費資源。

>> 解決死鎖問題最簡單的一種方法是超時,即當兩個事務互相等待時,當一個等待時間超過設置的某一閾值時,其中一個事務進行回滾,另外一個等待的事務就能繼續進行。在InnoDB存儲引擎中,參數innodb_lock_wait_timeout用來設置超時的時間。

>> 所以,除了超時機制,當前數據庫還都廣泛採用wait-for graph(等待圖)的方式來進行死鎖檢測。較之超時的解決方案,這是一種更爲主動的死鎖檢測方式。InnoDB存儲引擎也採用的這種方式

>> 經過上述的介紹,能夠發現wait-for graph是一種較爲主動的死鎖檢測機制,在每一個事務請求鎖併發生等待時都會判斷是否存在迴路,若存在則有死鎖,一般來講InnoDB存儲引擎選擇回滾undo量最小的事務。

有併發就有可能發生,跟開發的實現也有關係,可是機率也比較小,出現具體問題的時候再具體分析!

>若是程序是串行的,那麼不可能發生死鎖。死鎖只存在於併發的狀況,而數據庫自己就是一個併發運行的程序,所以可能會發生死鎖

>> 若是程序是串行的,那麼不可能發生死鎖。死鎖只存在於併發的狀況,而數據庫自己就是一個併發運行的程序,所以可能會發生死鎖。

理論上Spring應該也是作了這個判斷的…本身用JDBC實現的時候就要注意…

>還記得6.6節中所說的內容嗎?InnoDB存儲引擎並不會回滾大部分的錯誤異常,可是死鎖除外。發現死鎖後,InnoDB存儲引擎會立刻回滾一個事務,這點是須要注意的。所以若是在應用程序中捕獲了1213這個錯誤,其實並不須要對其進行回滾。

>> Oracle數據庫中產生死鎖的常見緣由是沒有對外鍵添加索引,而InnoDB存儲引擎會自動對其進行添加,於是可以很好地避免了這種狀況的發生

>> 此外還存在另外一種死鎖,即當前事務持有了待插入記錄的下一個記錄的X鎖,可是在等待隊列中存在一個S鎖的請求,則可能會發生死鎖

>> 鎖升級(Lock Escalation)是指將當前鎖的粒度下降。舉例來講,數據庫能夠把一個表的1000個行鎖升級爲一個頁鎖,或者將頁鎖升級爲表鎖。若是在數據庫的設計中認爲鎖是一種稀有資源,並且想避免鎖的開銷,那數據庫中會頻繁出現鎖升級現象。Microsoft SQL Server數據庫的設計認爲鎖是一種稀有的資源,在適合的時候會自動地將行、鍵或分頁鎖升級爲更粗粒度的表級鎖。這種升級保護了系統資源,防止系統使用太多的內存來維護鎖,在必定程度上提升了效率。

>> InnoDB存儲引擎不存在鎖升級的問題。由於其不是根據每一個記錄來產生行鎖的,相反,其根據每一個事務訪問的每一個頁對鎖進行管理的,採用的是位圖的方式。所以無論一個事務鎖住頁中一個記錄仍是多個記錄,其開銷一般都是一致的。

>> 假設一張表有3000 000個數據頁,每一個頁大約有100條記錄,那麼總共有300000 000條記錄。如有一個事務執行全表更新的SQL語句,則須要對全部記錄加X鎖。若根據每行記錄產生鎖對象進行加鎖,而且每一個鎖佔用10字節,則僅對鎖管理就須要差很少須要3GB的內存。而InnoDB存儲引擎根據頁進行加鎖,並採用位圖方式,假設每一個頁存儲的鎖信息佔用30個字節,則鎖對象僅需90MB的內存。

 

第7章 事務

 

>> 事務(Transaction)是數據庫區別於文件系統的重要特性之一。在文件系統中,若是正在寫文件,可是操做系統忽然崩潰了,這個文件就頗有可能被破壞。固然,有一些機制能夠把文件恢復到某個時間點。不過,若是須要保證兩個文件同步,這些文件系統可能就顯得無能爲力了。例如,在須要更新兩個文件時,更新完一個文件後,在更新完第二個文件以前系統重啓了,就會有兩個不一樣步的文件。

>> 這正是數據庫系統引入事務的主要目的:事務會把數據庫從一種一致狀態轉換爲另外一種一致狀態。

>> 理論上說,事務有着極其嚴格的定義,它必須同時知足四個特性,即一般所說的事務的ACID特性。值得注意的是,雖然理論上定義了嚴格的事務要求,可是數據庫廠商出於各類目的,並無嚴格去知足事務的ACID標準。例如,對於MySQL的NDB Cluster引擎來講,雖然其支持事務,可是不知足D的要求,即持久性的要求。對於Oracle數據庫來講,其默認的事務隔離級別爲READ COMMITTED,不知足I的要求,即隔離性的要求。雖然在大多數的狀況下,這並不會致使嚴重的結果,甚至可能還會帶來性能的提高,可是用戶首先須要知道嚴謹的事務標準,並在實際的生產應用中避免可能存在的潛在問題。對於InnoDB存儲引擎而言,其默認的事務隔離級別爲READ REPEATABLE,徹底遵循和知足事務的ACID特性。

原子性就是說,事務中的兩個SQL確定都應該是一塊兒執行或者一塊兒不執行,就是一個組合體,最小單元。

>A(Atomicity),原子性

>> 若是事務中的操做都是隻讀的,要保持原子性是很簡單的。一旦發生任何錯誤,要麼重試,要麼返回錯誤代碼。由於只讀操做不會改變系統中的任何相關部分。

事務提交之後,要麼成功,要麼失敗,失敗了就必定要回滾。

>C(consistency),一致性。一致性指事務將數據庫從一種狀態轉變爲下一種一致的狀態

隔離性主要是處理和避免併發狀況下出現的一些異常的問題。

>I(isolation),隔離性。隔離性還有其餘的稱呼,如併發控制(concurrency control)、可串行化(serializability)、鎖(locking)等。

>> D(durability),持久性。事務一旦提交,其結果就是永久性的。即便發生宕機等故障,數據庫也能將數據恢復。須要注意的是,只能從事務自己的角度來保證結果的永久性。例如,在事務提交後,全部的變化都是永久的。即便當數據庫由於崩潰而須要恢復時,也能保證恢復後提交的數據都不會丟失。但若不是數據庫自己發生故障,而是一些外部的緣由,如RAID卡損壞、天然災害等緣由致使數據庫發生問題,那麼全部提交的數據可能都會丟失。所以持久性保證事務系統的高可靠性(High Reliability),而不是高可用性(High Availability)。

>> 扁平事務的主要限制是不能提交或者回滾事務的某一部分,或分幾個步驟提交。

>> 帶有保存點的扁平事務(Flat Transactions with Savepoint),除了支持扁平事務支持的操做外,容許在事務執行過程當中回滾到同一事務中較早的一個狀態。這是由於某些事務可能在執行過程當中出現的錯誤並不會致使全部的操做都無效,放棄整個事務不合乎要求,開銷也太大

>> 鏈事務(Chained Transaction)可視爲保存點模式的一種變種。帶有保存點的扁平事務,當發生系統崩潰時,全部的保存點都將消失,由於其保存點是易失的(volatile),而非持久的(persistent)。這意味着當進行恢復時,事務須要從開始處從新執行,而不能從最近的一個保存點繼續執行。

>> 鏈事務的思想是:在提交一個事務時,釋放不須要的數據對象,將必要的處理上下文隱式地傳給下一個要開始的事務

>> 。注意,提交事務操做和開始下一個事務操做將合併爲一個原子操做

>> 子事務既能夠提交也能夠回滾。可是它的提交操做並不立刻生效,除非其父事務已經提交。所以能夠推論出,任何子事物都在頂層事務提交後才真正的提交。

>> 樹中的任意一個事務的回滾會引發它的全部子事務一同回滾,故子事務僅保留A、C、I特性,不具備D的特性。

>> redo log稱爲重作日誌,用來保證事務的原子性和持久性。undo log用來保證事務的一致性。

>> 有的DBA或許會認爲undo是redo的逆過程,其實否則。redo和undo的做用均可以視爲是一種恢復操做,redo恢復提交事務修改的頁操做,而undo回滾行記錄到某個特定版本。所以二者記錄的內容不一樣,redo一般是物理日誌,記錄的是頁的物理修改操做。undo是邏輯日誌,根據每行記錄進行記錄。

>> redo log基本上都是順序寫的,在數據庫運行時不須要對redo log的文件進行讀取操做。而undo log是須要進行隨機讀寫的。

>> 二進制日誌只在事務提交完成後進行一次寫入。而InnoDB存儲引擎的重作日誌在事務進行中不斷地被寫入,這表現爲日誌並非隨事務提交的順序進行寫入的。

>> InnoDB存儲引擎在啓動時無論上次數據庫運行時是否正常關閉,都會嘗試進行恢復操做。由於重作日誌記錄的是物理日誌,所以恢復的速度比邏輯日誌,如二進制日誌,要快不少。與此同時,InnoDB存儲引擎自身也對恢復進行了必定程度的優化,如順序讀取及並行應用重作日誌,這樣能夠進一步地提升數據庫恢復的速度。

>> 用戶一般對undo有這樣的誤解:undo用於將數據庫物理地恢復到執行語句或事務以前的樣子——但事實並不是如此。undo是邏輯日誌,所以只是將數據庫邏輯地恢復到原來的樣子。全部修改都被邏輯地取消了,可是數據結構和頁自己在回滾以後可能大不相同。這是由於在多用戶併發系統中,可能會有數10、數百甚至數千個併發事務。數據庫的主要任務就是協調對數據記錄的併發訪問。好比,一個事務在修改當前一個頁中某幾條記錄,同時還有別的事務在對同一個頁中另幾條記錄進行修改。所以,不能將一個頁回滾到事務開始的樣子,由於這樣會影響其餘事務正在進行的工做。

>> 當InnoDB存儲引擎回滾時,它實際上作的是與先前相反的工做。對於每一個INSERT,InnoDB存儲引擎會完成一個DELETE;對於每一個DELETE,InnoDB存儲引擎會執行一個INSERT;對於每一個UPDATE,InnoDB存儲引擎會執行一個相反的UPDATE,將修改前的行放回去。

>> 除了回滾操做,undo的另外一個做用是MVCC,即在InnoDB存儲引擎中MVCC的實現是經過undo來完成。當用戶讀取一行記錄時,若該記錄已經被其餘事務佔用,當前事務能夠經過undo讀取以前的行版本信息,以此實現非鎖定讀取。

>> 事務提交後並不能立刻刪除undo log及undo log所在的頁。這是由於可能還有其餘事務須要經過undo log來獲得行記錄以前的版本。故事務提交時將undo log放入一個鏈表中,是否能夠最終刪除undo log及undo log所在頁由purge線程來判斷。

>> 若爲每個事務分配一個單獨的undo頁會很是浪費存儲空間,特別是對於OLTP的應用類型

>> 所以,在InnoDB存儲引擎的設計中對undo頁能夠進行重用。具體來講,當事務提交時,首先將undo log放入鏈表中,而後判斷undo頁的使用空間是否小於3/4,如果則表示該undo頁能夠被重用,以後新的undo log記錄在當前undo log的後面

>> History list length就表明了undo log的數量,這裏爲12。purge操做會減小該值。然而因爲undo log所在的頁能夠被重用,所以即便操做發生,History list length的值也能夠不爲0。

>> update undo log記錄的是對delete和update操做產生的undo log。該undo log可能須要提供MVCC機制,所以不能在事務提交時就進行刪除

>> InnoSQL對information_schema進行了擴展,添加了兩張數據字典表,這樣用戶能夠很是方便和快捷地查看undo的信息。首先增長的數據字典表爲INNODB_TRX_ROLLBACK_SEGMENT。

>> 另外一張數據字典表爲INNODB_TRX_UNDO,用來記錄事務對應的undo log,方便DBA和開發人員詳細瞭解每一個事務產生的undo量

>> 經過上面的例子能夠看到,delete操做並不直接刪除記錄,而只是將記錄標記爲已刪除,也就是將記錄的delete flag設置爲1。而記錄最終的刪除是在purge操做中完成的。

>> 全局動態參數innodb_purge_batch_size用來設置每次purge操做須要清理的undo page數量。在InnoDB1.2以前,該參數的默認值爲20。而從1.2版本開始,該參數的默認值爲300。一般來講,該參數設置得越大,每次回收的undo page也就越多,這樣可供重用的undo page就越多,減小了磁盤存儲空間與分配的開銷。不過,若該參數設置得太大,則每次須要purge處理更多的undo page,從而致使CPU和磁盤IO過於集中於對undo log的處理,使性能降低。所以對該參數的調整須要由有經驗的DBA來操做,而且須要長期觀察數據庫的運行的狀態。正如官方的MySQL數據庫手冊所說的,普通用戶不須要調整該參數。

>> 爲了提升磁盤fsync的效率,當前數據庫都提供了group commit的功能,即一次fsync能夠刷新確保多個事務日誌被寫入文件。對於InnoDB存儲引擎來講,事務提交時會進行兩個階段的操做:1)修改內存中事務對應的信息,而且將日誌寫入重作日誌緩衝。2)調用fsync將確保日誌都從重作日誌緩衝寫入磁盤。

>> COMMIT和COMMIT WORK語句基本是一致的,都是用來提交事務。不一樣之處在於COMMIT WORK用來控制事務結束後的行爲是CHAIN仍是RELEASE的。若是是CHAIN方式,那麼事務就變成了鏈事務。

>> 用戶能夠經過參數completion_type來進行控制,該參數默認爲0,表示沒有任何操做。在這種設置下COMMIT和COMMIT WORK是徹底等價的。當參數completion_type的值爲1時,COMMIT WORK等同於COMMIT AND CHAIN,表示立刻自動開啓一個相同隔離級別的事務,

>> 參數completion_type爲2時,COMMIT WORK等同於COMMIT AND RELEASE。在事務提交後會自動斷開與服務器的鏈接

>> TRUNCATE TABLE語句是DDL,所以雖然和對整張表執行DELETE的結果是同樣的,但它是不能被回滾的(這又是和Microsoft SQL Server數據不一樣的地方)。

>> 計算TPS的方法是(com_commit+com_rollback)/time。可是利用這種方法進行計算的前提是:全部的事務必須都是顯式提交的,若是存在隱式地提交和回滾(默認autocommit=1),不會計算到com_commit和com_rollback變量中。

>> 隔離級別越低,事務請求的鎖越少或保持鎖的時間就越短。這也是爲何大多數數據庫系統默認的事務隔離級別是READ COMMITTED。

>> 據瞭解,大部分的用戶質疑SERIALIZABLE隔離級別帶來的性能問題,可是根據Jim Gray在《Transaction Processing》一書中指出,二者的開銷幾乎是同樣的,甚至SERIALIZABLE可能更優!!!所以在InnoDB存儲引擎中選擇REPEATABLE READ的事務隔離級別並不會有任何性能的損失

>> 由於InnoDB存儲引擎在REPEATABLE READ隔離級別下就能夠達到3°的隔離,所以通常不在本地事務中使用SERIALIABLE的隔離級別。SERIALIABLE的事務隔離級別主要用於InnoDB存儲引擎的分佈式事務。

>> XA事務容許不一樣數據庫之間的分佈式事務,如一臺服務器是MySQL數據庫的,另外一臺是Oracle數據庫的,又可能還有一臺服務器是SQL Server數據庫的,只要參與在全局事務中的每一個節點都支持XA事務

>> 在單個節點上運行分佈式事務沒有太大的實際意義,可是要在MySQL數據庫的命令下演示多個節點參與的分佈式事務也是行不通的。一般來講,都是經過編程語言來完成分佈式事務的操做的。當前Java的JTA(Java Transaction API)能夠很好地支持MySQL的分佈式事務,須要使用分佈式事務應該認真參考其API

>> 最爲常見的內部XA事務存在於binlog與InnoDB存儲引擎之間

>> 對於不一樣語言的API,自動提交是不一樣的。MySQL C API默認的提交方式是自動提交,而MySQL Python API則會自動執行SET AUTOCOMMIT=0,以禁用自動提交。所以在選用不一樣的語言來編寫數據庫應用程序前,應該對鏈接MySQL的API作好研究。

>> 就像以前小節中所講到的,對事務的BEGIN、COMMIT和ROLLBACK操做應該交給程序端來完成,存儲過程須要完成的只是一個邏輯的操做,即對邏輯進行封裝。

>> 長事務(Long-Lived Transactions),顧名思義,就是執行時間較長的事務。好比,對於銀行系統的數據庫,每過一個階段可能須要更新對應帳戶的利息。若是對應帳號的數量很是大,例如對有1億用戶的表account,須要執行下列語句

>> 在執行過程當中,當數據庫或操做系統、硬件等發生問題時,從新開始事務的代價變得不可接受。數據庫須要回滾全部已經發生的變化,而這個過程可能比產生這些變化的時間還要長。所以,對於長事務的問題,有時能夠經過轉化爲小批量(mini batch)的事務來進行處理。當事務發生錯誤時,只須要回滾一部分數據,而後接着上次已完成的事務繼續進行

>> 上述代碼將一個須要處理1億用戶的大事務分解爲每次處理10萬用戶的小事務,經過批量處理小事務來完成大事務的邏輯。每完成一個小事務,將完成的結果存放在batchcontext表中,表示已完成批量事務的最大帳號ID。

 

第8章 備份與恢復

 

>> 按照備份後文件的內容,備份又能夠分爲:□ 邏輯備份□ 裸文件備份在MySQL數據庫中,邏輯備份是指備份出的文件內容是可讀的,通常是文本文件。內容通常是由一條條SQL語句,或者是表內實際數據組成。如mysqldump和SELECT*INTO OUTFILE的方法。這類方法的好處是能夠觀察導出文件的內容,通常適用於數據庫的升級、遷移等工做。但其缺點是恢復所須要的時間每每較長。裸文件備份是指複製數據庫的物理文件,既能夠是在數據庫運行中的複製(如ibbackup、xtrabackup這類工具),也能夠是在數據庫中止運行時直接的數據文件複製。這類備份的恢復時間每每較邏輯備份短不少。

>> 對於MySQL數據庫來講,官方沒有提供真正的增量備份的方法,大部分是經過二進制日誌完成增量備份的工做。這種備份較之真正的增量備份來講,效率仍是很低的

>> 最後,任什麼時候候都須要作好遠程異地備份,也就是容災的防範。只是同一機房的兩臺服務器的備份是遠遠不夠的。我曾經遇到的狀況是,公司在2008年的汶川地震中發生一個機房可能被淹的的狀況,這時遠程異地備份顯得就相當重要了。

>> replication的工做原理分爲如下3個步驟:1)主服務器(master)把數據更改記錄到二進制日誌(binlog)中。2)從服務器(slave)把主服務器的二進制日誌複製到本身的中繼日誌(relay log)中。3)從服務器重作中繼日誌中的日誌,把更改應用到本身的數據庫上,以達到數據的最終一致性。

>> 以前已經說過MySQL的複製是異步實時的,並不是徹底的主從同步。若用戶要想得知當前的延遲,能夠經過命令SHOW SLAVE STATUS和SHOW MASTER STATUS得知,如:

>> 假設當前應用採用了主從的複製架構,從服務器做爲備份。這時,一個初級DBA執行了誤操做,如DROP DATABASE或DROP TABLE,這時從服務器也跟着運行了。這時用戶怎樣從服務器進行恢復呢?所以,一個比較好的方法是經過對從服務器上的數據庫所在分區作快照,以此來避免誤操做對複製形成影響。當發生主服務器上的誤操做時,只須要將從服務器上的快照進行恢復,而後再根據二進制日誌進行point-in-time的恢復便可。

>> 還有一些其餘的方法來調整複製,好比採用延時複製,即間歇性地開啓從服務器上的同步,保證大約一小時的延時。這的確也是一個方法,只是數據庫在高峯和非高峯期間每小時產生的二進制日誌量是不一樣的,用戶很難精準地控制。另外,這種方法也不能徹底起到對誤操做的防範做用。

>> 建議在從服務上啓用read-only選項,這樣能保證從服務器上的數據僅與主服務器進行同步,避免其餘線程修改數據

 

第9章 性能調優

 

>> 另外一方面,閃存中的數據是不能夠更新的,只能經過扇區(sector)的覆蓋重寫,而在覆蓋重寫以前,須要執行很是耗時的擦除(erase)操做。擦除操做不能在所含數據的扇區上完成,而須要在刪除整個被稱爲擦除塊的基礎上完成,這個擦除塊的尺寸大於扇區的大小,一般爲128KB或者256KB。此外,每一個擦除塊有擦寫次數的限制。已經有一些算法來解決這個問題

>> 由於存在上述寫入方面的問題,閃存提供的讀寫速度是非對稱的。讀取速度要遠快於寫入的速度,所以對於固態硬盤在數據庫中的應用,應該好好利用其讀取的性能,避免過多的寫入操做。

>> 因爲將多個硬盤組合成爲一個邏輯扇區,RAID看起來就像一個單獨的硬盤或邏輯存儲單元,所以操做系統只會把它看成一個硬盤。

>> RAID 5具備和RAID 0相近似的數據讀取速度,只是多了一個奇偶校驗信息,寫入數據的速度至關慢,若使用Write Back可讓性能改善很多。

>> RAID 01比RAID 10有着更快的讀寫速度,不過也多了一些會讓整個硬盤組中止運轉的概率,由於只要同一組的硬盤所有損毀,RAID 01就會中止運做,而RAID 10能夠在犧牲RAID 0的優點下正常運做。RAID 10巧妙地利用了RAID 0的速度及RAID 1的安全(保護)兩種特性,它的缺點是須要較多的硬盤,由於至少必須擁有四個以上的偶數硬盤才能使用。

>> RAID Write Back功能是指RAID控制器可以將寫入的數據放入自身的緩存中,並把它們安排到後面再執行。這樣作的好處是,不用等待物理磁盤實際寫入的完成,所以寫入變得更快了

>> 對RAID卡進行配置能夠在服務器啓動時進入一個相似於BIOS的配置界面,而後再對其進行各類設置。此外,不少廠商都開發了各類操做系統下的軟件對RAID進行配置,若是用戶使用的是LSI公司生產提供的RAID卡,則能夠使用MegaCLI工具來進行配置。

>> 特別須要注意地是,當RAID卡的寫入策略從Write Back切換爲Write Through時,該更改當即生效。然而從Write Through切換爲Write Back時,必須重啓服務器才能使其生效。

>> 基準測試工具能夠用來對數據庫或操做系統調優後的性能進行對比。MySQL數據庫自己提供了一些比較優秀的工具,這裏將介紹另外兩款更爲優秀和經常使用的基準測試工具:sysbench和mysql-tpcc。

>> 對於MySQL數據庫的OLTP測試,和fileio同樣須要經歷prepare、run和cleanup階段。prepare階段會根據選項產生一張指定行數的表,默認表在sbtest架構下,表名爲sbtest(sysbench默認生成表的存儲引擎爲InnoDB)。例如建立一張8000W的表:

>> TPC(Transaction Processing Performance Council,事務處理性能協會)是一個用來評價大型數據庫系統軟硬件性能的非盈利組織。TPC-C是TPC協會制定的,用來測試典型的複雜OLTP(在線事務處理)系統的性能。目前在學術界和工業界廣泛採用TPC-C來評價OLTP應用的性能。

相關文章
相關標籤/搜索