查看MySQL提供的全部存儲引擎mysql
mysql> show engines;算法
查看MySQL提供的全部存儲引擎 (圖片來源網絡)sql
從上圖咱們能夠查看出 MySQL 當前默認的存儲引擎是InnoDB,而且在5.7版本全部的存儲引擎中只有 InnoDB 是事務性存儲引擎,也就是說只有 InnoDB 支持事務。數據庫
查看MySQL當前默認的存儲引擎segmentfault
mysql> show variables like '%storage_engine%';緩存
查看錶的存儲引擎安全
show table status like "table_name" ;網絡
MyISAM是MySQL的默認數據庫引擎(5.5版以前)。雖然性能極佳,並且提供了大量的特性,包括全文索引、壓縮、空間函數等,但MyISAM不支持事務和行級鎖,並且最大的缺陷就是崩潰後沒法安全恢復。不過,5.5版本以後,MySQL引入了InnoDB(事務性數據庫引擎),MySQL 5.5版本後默認的存儲引擎爲InnoDB。數據結構
大多數時候咱們使用的都是 InnoDB 存儲引擎,可是在某些狀況下使用 MyISAM 也是合適的好比讀密集的狀況下。(若是你不介意 MyISAM 崩潰回覆問題的話)。架構
二者的對比:
是否支持行級鎖 : MyISAM 只有表級鎖(table-level locking),而InnoDB 支持行級鎖(row-level locking)和表級鎖,默認爲行級鎖。
是否支持事務和崩潰後的安全恢復:MyISAM 強調的是性能,每次查詢具備原子性,其執行數度比InnoDB類型更快,可是不提供事務支持。可是InnoDB提供事務支持事務,外部鍵等高級數據庫功能。具備事務(commit)、回滾(rollback)和崩潰修復能力(crash recovery capabilities)的事務安全(transaction-safe (ACID compliant))型表。
是否支持外鍵: MyISAM不支持,而InnoDB支持。
是否支持MVCC :僅 InnoDB 支持。應對高併發事務, MVCC比單純的加鎖更高效;MVCC只在 READ COMMITTED 和 REPEATABLE READ 兩個隔離級別下工做;MVCC可使用 樂觀(optimistic)鎖 和 悲觀(pessimistic)鎖來實現;各數據庫中MVCC實現並不統一。推薦閱讀:MySQL-InnoDB-MVCC多版本併發控制https://segmentfault.com/a/1190000012650596
《MySQL高性能》上面有一句話這樣寫到:
不要輕易相信「MyISAM比InnoDB快」之類的經驗之談,這個結論每每不是絕對的。在不少咱們已知場景中,InnoDB的速度均可以讓MyISAM可望不可即,尤爲是用到了聚簇索引,或者須要訪問的數據均可以放入內存的應用。
通常狀況下咱們選擇 InnoDB 都是沒有問題的,可是某事狀況下你並不在意可擴展能力和併發能力,也不須要事務支持,也不在意崩潰後的安全恢復問題的話,選擇MyISAM也是一個不錯的選擇。可是通常狀況下,咱們都是須要考慮到這些問題的。
字符集指的是一種從二進制編碼到某類字符符號的映射。校對規則則是指某種字符集下的排序規則。MySQL中每一種字符集都會對應一系列的校對規則。
MySQL採用的是相似繼承的方式指定字符集的默認值,每一個數據庫以及每張數據表都有本身的默認值,他們逐層繼承。好比:某個庫中全部表的默認字符集將是該數據庫所指定的字符集(這些表在沒有指定字符集的狀況下,纔會採用默認字符集。
MySQL索引使用的數據結構主要有BTree索引 和 哈希索引 。對於哈希索引來講,底層的數據結構就是哈希表,所以在絕大多數需求爲單條記錄查詢的時候,能夠選擇哈希索引,查詢性能最快;其他大部分場景,建議選擇BTree索引。
MySQL的BTree索引使用的是B數中的B+Tree,但對於主要的兩種存儲引擎的實現方式是不一樣的。
MyISAM: B+Tree葉節點的data域存放的是數據記錄的地址。在索引檢索的時候,首先按照B+Tree搜索算法搜索索引,若是指定的Key存在,則取出其 data 域的值,而後以 data 域的值爲地址讀取相應的數據記錄。這被稱爲「非聚簇索引」。
InnoDB: 其數據文件自己就是索引文件。相比MyISAM,索引文件和數據文件是分離的,其表數據文件自己就是按B+Tree組織的一個索引結構,樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,所以InnoDB表數據文件自己就是主索引。這被稱爲「聚簇索引(或彙集索引)」。而其他的索引都做爲輔助索引,輔助索引的data域存儲相應記錄主鍵的值而不是地址,這也是和MyISAM不一樣的地方。在根據主索引搜索時,直接找到key所在的節點便可取出數據;在根據輔助索引查找時,則須要先取出主鍵的值,再走一遍主索引。 所以,在設計表的時候,不建議使用過長的字段做爲主鍵,也不建議使用非單調的字段做爲主鍵,這樣會形成主索引頻繁分裂。
執行查詢語句的時候,會先查詢緩存。不過,MySQL 8.0 版本後移除,由於這個功能不太實用
my.cnf加入如下配置,重啓MySQL開啓查詢緩存
query_cache_type=1 query_cache_size=600000
MySQL執行如下命令也能夠開啓查詢緩存
set global query_cache_type=1; set global query_cache_size=600000;
如上,開啓查詢緩存後在一樣的查詢條件以及數據狀況下,會直接在緩存中返回結果。這裏的查詢條件包括查詢自己、當前要查詢的數據庫、客戶端協議版本號等一些可能影響結果的信息。所以任何兩個查詢在任何字符上的不一樣都會致使緩存不命中。此外,若是查詢中包含任何用戶自定義函數、存儲函數、用戶變量、臨時表、MySQL庫中的系統表,其查詢結果也不會被緩存。
緩存創建以後,MySQL的查詢緩存系統會跟蹤查詢中涉及的每張表,若是這些表(數據或結構)發生變化,那麼和這張表相關的全部緩存數據都將失效。
緩存雖然可以提高數據庫的查詢性能,可是緩存同時也帶來了額外的開銷,每次查詢後都要作一次緩存操做,失效後還要銷燬。 所以,開啓緩存查詢要謹慎,尤爲對於寫密集的應用來講更是如此。若是開啓,要注意合理控制緩存空間大小,通常來講其大小設置爲幾十MB比較合適。此外,還能夠經過sql_cache和sql_no_cache來控制某個查詢語句是否須要緩存:
select sql_no_cache count(*) from usr;
事務是邏輯上的一組操做,要麼都執行,要麼都不執行。
事務最經典也常常被拿出來講例子就是轉帳了。假如小明要給小紅轉帳1000元,這個轉帳會涉及到兩個關鍵操做就是:將小明的餘額減小1000元,將小紅的餘額增長1000元。萬一在這兩個操做之間忽然出現錯誤好比銀行系統崩潰,致使小明餘額減小而小紅的餘額沒有增長,這樣就不對了。事務就是保證這兩個關鍵操做要麼都成功,要麼都要失敗。
圖片來源網絡
原子性: 事務是最小的執行單位,不容許分割。事務的原子性確保動做要麼所有完成,要麼徹底不起做用;
一致性: 執行事務先後,數據保持一致,多個事務對同一個數據讀取的結果是相同的;
隔離性: 併發訪問數據庫時,一個用戶的事務不被其餘事務所幹擾,各併發事務之間數據庫是獨立的;
持久性: 一個事務被提交以後。它對數據庫中數據的改變是持久的,即便數據庫發生故障也不該該對其有任何影響。
在典型的應用程序中,多個事務併發運行,常常會操做相同的數據來完成各自的任務(多個用戶對統一數據進行操做)。併發雖然是必須的,但可能會致使如下的問題。
髒讀: 當一個事務正在訪問數據而且對數據進行了修改,而這種修改尚未提交到數據庫中,這時另一個事務也訪問了這個數據,而後使用了這個數據。由於這個數據是尚未提交的數據,那麼另一個事務讀到的這個數據是「髒數據」,依據「髒數據」所作的操做多是不正確的。
丟失修改: 指在一個事務讀取一個數據時,另一個事務也訪問了該數據,那麼在第一個事務中修改了這個數據後,第二個事務也修改了這個數據。這樣第一個事務內的修改結果就被丟失,所以稱爲丟失修改。例如:事務1讀取某表中的數據A=20,事務2也讀取A=20,事務1修改A=A-1,事務2也修改A=A-1,最終結果A=19,事務1的修改被丟失。
不可重複讀: 指在一個事務內屢次讀同一數據。在這個事務尚未結束時,另外一個事務也訪問該數據。那麼,在第一個事務中的兩次讀數據之間,因爲第二個事務的修改致使第一個事務兩次讀取的數據可能不太同樣。這就發生了在一個事務內兩次讀到的數據是不同的狀況,所以稱爲不可重複讀。
幻讀: 幻讀與不可重複讀相似。它發生在一個事務(T1)讀取了幾行數據,接着另外一個併發事務(T2)插入了一些數據時。在隨後的查詢中,第一個事務(T1)就會發現多了一些本來不存在的記錄,就好像發生了幻覺同樣,因此稱爲幻讀。
不可重複度和幻讀區別:不可重複讀的重點是修改好比屢次讀取一條記錄發現其中某些列的值被修改,幻讀的重點在於新增或者刪除好比屢次讀取一條記錄發現記錄增多或減小了。
SQL 標準定義了四個隔離級別:
READ-UNCOMMITTED(讀取未提交): 最低的隔離級別,容許讀取還沒有提交的數據變動,可能會致使髒讀、幻讀或不可重複讀。
READ-COMMITTED(讀取已提交): 容許讀取併發事務已經提交的數據,能夠阻止髒讀,可是幻讀或不可重複讀仍有可能發生。
REPEATABLE-READ(可重複讀): 對同一字段的屢次讀取結果都是一致的,除非數據是被自己事務本身所修改,能夠阻止髒讀和不可重複讀,但幻讀仍有可能發生。
SERIALIZABLE(可串行化): 最高的隔離級別,徹底服從ACID的隔離級別。全部的事務依次逐個執行,這樣事務之間就徹底不可能產生干擾,也就是說,該級別能夠防止髒讀、不可重複讀以及幻讀。
隔離級別 | 髒讀 | 不可重複讀 | 幻影讀 |
---|---|---|---|
READ-UNCOMMITTED | √ | √ | √ |
READ-COMMITTED | × | √ | √ |
REPEATABLE-READ | × | × | √ |
SERIALIZABLE | × | × | × |
MySQL InnoDB 存儲引擎的默認支持的隔離級別是 REPEATABLE-READ(可重讀)。咱們能夠經過SELECT @@tx_isolation;命令來查看
+-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 1 row in set, 1 warning (0.00 sec)
這裏須要注意的是:與 SQL 標準不一樣的地方在於 InnoDB 存儲引擎在 REPEATABLE-READ(可重讀)事務隔離級別下使用的是Next-Key Lock 鎖算法,所以能夠避免幻讀的產生,這與其餘數據庫系統(如 SQL Server)是不一樣的。因此說InnoDB 存儲引擎的默認支持的隔離級別是 REPEATABLE-READ(可重讀) 已經能夠徹底保證事務的隔離性要求,即達到了 SQL標準的SERIALIZABLE(可串行化)隔離級別。
由於隔離級別越低,事務請求的鎖越少,因此大部分數據庫系統的隔離級別都是READ-COMMITTED(讀取提交內容):,可是你要知道的是InnoDB 存儲引擎默認使用 REPEATABLE-READ(可重讀)並不會有任何性能損失。
InnoDB 存儲引擎在 分佈式事務 的狀況下通常會用到SERIALIZABLE(可串行化)隔離級別。
MyISAM和InnoDB存儲引擎使用的鎖:
MyISAM採用表級鎖(table-level locking)。
InnoDB支持行級鎖(row-level locking)和表級鎖,默認爲行級鎖
表級鎖和行級鎖對比:
表級鎖: MySQL中鎖定 粒度最大 的一種鎖,對當前操做的整張表加鎖,實現簡單,資源消耗也比較少,加鎖快,不會出現死鎖。其鎖定粒度最大,觸發鎖衝突的機率最高,併發度最低,MyISAM和 InnoDB引擎都支持表級鎖。
行級鎖: MySQL中鎖定 粒度最小 的一種鎖,只針對當前操做的行進行加鎖。行級鎖能大大減小數據庫操做的衝突。其加鎖粒度最小,併發度高,但加鎖的開銷也最大,加鎖慢,會出現死鎖。
InnoDB存儲引擎的鎖的算法有三種:
Record lock:單個行記錄上的鎖
Gap lock:間隙鎖,鎖定一個範圍,不包括記錄自己
Next-key lock:record+gap 鎖定一個範圍,包含記錄自己
相關知識點:
innodb對於行的查詢使用next-key lock
Next-locking keying爲了解決Phantom Problem幻讀問題
當查詢的索引含有惟一屬性時,將next-key lock降級爲record key
Gap鎖設計的目的是爲了阻止多個事務將記錄插入到同一範圍內,而這會致使幻讀問題的產生
有兩種方式顯式關閉gap鎖:(除了外鍵約束和惟一性檢查外,其他狀況僅使用record lock) A. 將事務隔離級別設置爲RC B. 將參數innodb_locks_unsafe_for_binlog設置爲1
當MySQL單表記錄數過大時,數據庫的CRUD性能會明顯降低,一些常見的優化措施以下:
務必禁止不帶任何限制數據範圍條件的查詢語句。好比:咱們當用戶在查詢訂單歷史的時候,咱們能夠控制在一個月的範圍內;
經典的數據庫拆分方案,主庫負責寫,從庫負責讀;
根據數據庫裏面數據表的相關性進行拆分。 例如,用戶表中既有用戶的登陸信息又有用戶的基本信息,能夠將用戶表拆分紅兩個單獨的表,甚至放到單獨的庫作分庫。
簡單來講垂直拆分是指數據表列的拆分,把一張列比較多的表拆分爲多張表。 以下圖所示,這樣來講你們應該就更容易理解了。
數據庫垂直分區 (圖片來源網絡)
垂直拆分的優勢: 可使得列數據變小,在查詢時減小讀取的Block數,減小I/O次數。此外,垂直分區能夠簡化表的結構,易於維護。
垂直拆分的缺點: 主鍵會出現冗餘,須要管理冗餘列,並會引發Join操做,能夠經過在應用層進行Join來解決。此外,垂直分區會讓事務變得更加複雜;
保持數據表結構不變,經過某種策略存儲數據分片。這樣每一片數據分散到不一樣的表或者庫中,達到了分佈式的目的。水平拆分能夠支撐很是大的數據量
水平拆分是指數據錶行的拆分,表的行數超過200萬行時,就會變慢,這時能夠把一張的表的數據拆成多張表來存放。舉個例子:咱們能夠將用戶信息表拆分紅多個用戶信息表,這樣就能夠避免單一表數據量過大對性能形成影響。
數據庫水平拆分 (圖片來源網絡)
水平拆分能夠支持很是大的數據量。須要注意的一點是:分表僅僅是解決了單一表數據過大的問題,但因爲表的數據仍是在同一臺機器上,其實對於提高MySQL併發能力沒有什麼意義,因此 水平拆分最好分庫 。
水平拆分可以 支持很是大的數據量存儲,應用端改造也少,但 分片事務難以解決 ,跨節點Join性能較差,邏輯複雜。儘可能不要對數據進行分片,由於拆分會帶來邏輯、部署、運維的各類複雜度 ,通常的數據表在優化得當的狀況下支撐千萬如下的數據量是沒有太大問題的。若是實在要分片,儘可能選擇客戶端分片架構,這樣能夠減小一次和中間件的網絡I/O。