MySQL存儲引擎MyISAM與InnoDB區別

前言

MySQL在V5.1之前默認存儲引擎是MyISAM;在此之後默認存儲引擎是InnoDB

MyISAM:它是基於傳統的ISAM類型,ISAM是Indexed Sequential Access Method (有索引的順序訪問方法) 的縮寫,它是存儲記錄和文件的標準方法。不是事務安全的,而且不支持外鍵,如果執行大量的select,insert MyISAM比較適合。

InnoDB:支持事務安全的引擎,支持外鍵、行鎖、事務是他的最大特點。如果有大量的update和insert,建議使用InnoDB,特別是針對多個併發和QPS較高的情況。

一、表鎖差異

MyISAM:

myisam只支持表級鎖,用戶在操作myisam表時,select,update,delete,insert語句都會給表自動加鎖,如果加鎖以後的表滿足insert併發的情況下,可以在表的尾部插入新的數據。也可以通過lock table命令來鎖表,這樣操作主要是可以模仿事務,但是消耗非常大,一般只在實驗演示中使用。

InnoDB

  • Innodb支持事務和行級鎖,是innodb的最大特色。

  • 事務的ACID屬性:atomicity,consistent,isolation,durable。

  • 併發事務帶來的幾個問題:更新丟失,髒讀,不可重複讀,幻讀。

  • 事務隔離級別:未提交讀(Read uncommitted),已提交讀(Read committed),可重複讀(Repeatable read),可序列化(Serializable)

四種隔離級別的比較

隔離級別 讀數據一致性 髒讀 不可重複讀 幻讀
爲提交讀(read uncommitted) 最低級別,不讀物理上順壞的數據
已提交讀(read committed) 語句級
可重複讀(Repeatable red) 事務級
可序列化(Serializable) 最高級別,事務級

查看MySQL的默認事務隔離級別

mysql> show global variables like 'tx_isolation'; 

Innodb的行鎖模式有以下幾種:共享鎖,排他鎖,意向共享鎖(表鎖),意向排他鎖(表鎖),間隙鎖。

注意:當語句沒有使用索引,innodb不能確定操作的行,這個時候就使用的意向鎖,也就是表鎖

關於死鎖

什麼是死鎖?當兩個事務都需要獲得對方持有的排他鎖才能完成事務,這樣就導致了循環鎖等待,也就是常見的死鎖類型。

解決死鎖的方法

  • 數據庫參數

  • 應用中儘量約定程序讀取表的順序一樣

  • 應用中處理一個表時,儘量對處理的順序排序

  • 調整事務隔離級別(避免兩個事務同時操作一行不存在的數據,容易發生死鎖)

二、數據庫文件差異

MyISAM

  • MyISAM屬於堆表

  • MyISAM在磁盤存儲上有三個文件,每個文件名以表名開頭,擴展名指出文件類型。

  • .frm 用於存儲表的定義

  • .MYD 用於存放數據

  • .MYI 用於存放表索引

MyISAM表還支持三種不同的存儲格式

  • 靜態表(默認,但是注意數據末尾不能有空格,會被去掉)

  • 動態表

  • 壓縮表

InnoDB

  • InnoDB屬於索引組織表
  • InnoDB有兩種存儲方式,共享表空間存儲和多表空間存儲
  • 兩種存儲方式的表結構和MyISAM一樣,以表名開頭,擴展名是.frm。

如果使用共享表空間,那麼所有表的數據文件和索引文件都保存在一個表空間裏,一個表空間可以有多個文件,通過innodb_data_file_pathinnodb_data_home_dir參數設置共享表空間的位置和名字,一般共享表空間的名字叫ibdata1-n

如果使用多表空間,那麼每個表都有一個表空間文件用於存儲每個表的數據和索引,文件名以表名開頭,以.ibd爲擴展名。

三、索引差異

1.關於自動增長

MyISAM引擎的自動增長列必須是索引,如果是組合索引,自動增長可以不是第一列,他可以根據前面幾列進行排序後遞增。

MyISAM引擎的自動增長咧必須是索引,如果是組合索引也必須是組合索引的第一列。

2.關於主鍵

  • MyISAM允許沒有任何索引和主鍵的表存在,
  • MyISAM的索引都是保存行的地址。
  • InnoDB引擎如果沒有設定主鍵或者非空唯一索引,就會自動生成一個6字節的主鍵(用戶不可見)
  • InnoDB的數據是主索引的一部分,附加索引保存的是主索引的值。

3.關於count()函數

MyISAM保存有表的總行數,如果select count(*) from table;會直接取出出該值

InnoDB沒有保存表的總行數,如果使用select count(*) from table;就會遍歷整個表,消耗相當大,但是在加了wehre條件後,MyISAM和InnoDB處理的方式都一樣。

4、全文索引

MyISAM支持FULLTEXT類型的全文索引

InnoDB不支持FULLTEXT類型的全文索引,但是InnoDB可以使用sphinx插件支持全文索引,並且效果更好。(sphinx 是一個開源軟件,提供多種語言的API接口,可以優化MySQL的各種查詢)

5.delete from table

使用這條命令時,InnoDB不會從新建立表,而是一條一條的刪除數據,在InnoDB上如果要清空保存有大量數據的表,最好不要使用這個命令。(推薦使用truncate table,不過需要用戶有drop此表的權限)

6.索引保存位置

MyISAM的索引以表名+.MYI文件分別保存。

InnoDB的索引和數據一起保存在表空間裏。

四、開發的注意事項

  • 可以用 show create table tablename 命令看錶的引擎類型。
  • 對不支持事務的表做start/commit操作沒有任何效果,在執行commit前已經提交。
  • 可以執行以下命令來切換非事務表到事務(數據不會丟失),innodb表比myisam表更安全:alter table tablename type=innodb;或者使用 alter table tablename engine = innodb;
  • 默認InnoDB是開啓自動提交的,如果你按照MyISAM的使用方法來編寫代碼頁不會存在錯誤,只是性能會很低。如何在編寫代碼時候提高數據庫性能呢?
    1.儘量將多個語句綁到一個事務中,進行提交,避免多次提交導致的數據庫開銷。
    2.在一個事務獲得排他鎖或者意向排他鎖以後,如果後面還有需要處理的sql語句,在這兩條或者多條sql語句之間程序應儘量少的進行邏輯運算和處理,減少鎖的時間。
    3.儘量避免死鎖
    4.sql語句如果有where子句一定要使用索引,儘量避免獲取意向排他鎖。
    5.針對我們自己的數據庫環境,日誌系統是直插入,不修改的,所以我們使用混合引擎方式,ZION_LOG_DB照舊使用MyISAM存儲引擎,只有ZION_GAME_DBZION_LOGIN_DBDAUM_BILLING使用InnoDB引擎

五、該怎麼選擇

下面先讓我們回答一些問題

  • 你的數據庫有外鍵嗎?
  • 你需要事務支持嗎?
  • 你需要全文索引嗎?
  • 你經常使用什麼樣的查詢模式?
  • 你的數據有多大?

MyISAM只有索引緩存

InnoDB不分索引文件數據文件 innodb buffer

MyISAM只能管理索引,在索引數據大於分配的資源時,會由操作系統來cache;
數據文件依賴於操作系統的cache。InnoDB不管是索引還是數據,都是自己來管理

思考上面這些問題可以讓你找到合適的方向,但那並不是絕對的。如果你需要事務處理或是外鍵,那麼InnoDB 可能是比較好的方式。如果你需要全文索引,那麼通常來說 MyISAM是好的選擇,因爲這是系統內建的,然而,我們其實並不會經常地去測試兩百萬行記錄。所以,就算是慢一點,我們可以通過使用Sphinx從InnoDB中獲得全文索引。

數據的大小,是一個影響你選擇什麼樣存儲引擎的重要因素,大尺寸的數據集趨向於選擇InnoDB方式,因爲其支持事務處理和故障恢復。數據庫的在小決定了故障恢復的時間長短,InnoDB可以利用事務日誌進行數據恢復,這會比較快。而MyISAM可能會需要幾個小時甚至幾天來幹這些事,InnoDB只需要幾分鐘。

操作數據庫表的習慣可能也會是一個對性能影響很大的因素。比如: COUNT() 在 MyISAM 表中會非常快,而在InnoDB 表下可能會很痛苦。而主鍵查詢則在InnoDB下會相當相當的快,但需要小心的是如果我們的主鍵太長了也會導致性能問題。大批的inserts 語句在 MyISAM下會快一些,但是updates 在InnoDB下會更快一些——尤其在併發量大的時候。

所以,到底你檢使用哪一個呢?根據經驗來看,如果是一些小型的應用或項目,那麼MyISAM 也許會更適合。當然,在大型的環境下使用 MyISAM 也會有很大成功的時候,但卻不總是這樣的。如果你正在計劃使用一個超大數據量的項目,而且需要事務處理或外鍵支持,那麼你真的應該直接使用 InnoDB方式。但需要記住InnoDB 的表需要更多的內存和存儲,轉換100GB 的MyISAM 表到InnoDB 表可能會讓你有非常壞的體驗。

對於支持事務的InnoDB類型的表,影響速度的主要原因是AUTOCOMMIT默認設置是打開的,而且程序沒有顯式調用BEGIN 開始事務,導致每插入一條都自動Commit,嚴重影響了速度。可以在執行sql前調用begin,多條sql形成一個事務(即使autocommit打開也可以),將大大提高性能。

InnoDB:
InnoDB 給 MySQL 提供了具有事務(commit)、回滾(rollback)和崩潰修復能力 (crash recovery capabilities)的事務安全(transaction-safe (ACID compliant))型表。 InnoDB 提供了行鎖(locking on row level),提供與 Oracle 類型一致的不加鎖讀取(non- locking read in SELECTs)。這些特性均提高了多用戶併發操作的性能表現。在InnoDB表中不需要擴大鎖定 (lock escalation),因爲 InnoDB 的列鎖定(row level locks)適宜非常小的空間。 InnoDB 是 MySQL 上第一個提供外鍵約束(FOREIGN KEY constraints)的表引擎。

InnoDB 的設計目標是處理大容量數據庫系統,它的 CPU 利用率是其它基於磁盤的關係數據庫引擎所不能比的。在技術上,InnoDB 是一套放在 MySQL 後臺的完整數據庫系統,InnoDB 在主內存中建立其專用的緩衝池用於高速緩衝數據和索引。 InnoDB 把數據和索引存放在表空間裏,可能包含多個文件,這與其它的不一樣,舉例來說,在 MyISAM 中,表被存放在單獨的文件中。InnoDB 表的大小隻受限於操作系統的文件大小,一般爲 2 GB。
InnoDB所有的表都保存在同一個數據文件 ibdata1 中(也可能是多個文件,或者是獨立的表空間文件),相對來說比較不好備份,免費的方案可以是拷貝數據文件、備份 binlog,或者用 mysqldump。

MyISAM:
MyISAM 是MySQL缺省存貯引擎 .
每張MyISAM 表被存放在三個文件 。frm 文件存放表格定義。 數據文件是MYD (MYData) 。 索引文件是 MYI (MYIndex) 引伸。
因爲MyISAM相對簡單所以在效率上要優於InnoDB…小型應用使用MyISAM是不錯的選擇.
MyISAM表是保存成文件的形式,在跨平臺的數據轉移中使用MyISAM存儲會省去不少的麻煩

以下是一些細節和具體實現的差別

  • InnoDB不支持FULLTEXT類型的索引。
  • InnoDB 中不保存表的具體行數,也就是說,執行select count() from table時,InnoDB要掃描一遍整個表來計算有多少行,但是MyISAM只要簡單的讀出保存好的行數即可。注意的是,當count()語句包含 where條件時,兩種表的操作是一樣的。
  • 對於AUTO_INCREMENT類型的字段,InnoDB中必須包含只有該字段的索引,但是在MyISAM表中,可以和其他字段一起建立聯合索引。
  • DELETE FROM table時,InnoDB不會重新建立表,而是一行一行的刪除。
  • LOAD TABLE FROM MASTER操作對InnoDB是不起作用的,解決方法是首先把InnoDB表改成MyISAM表,導入數據後再改成InnoDB表,但是對於使用的額外的InnoDB特性(例如外鍵)的表不適用。

另外,InnoDB表的行鎖也不是絕對的,如果在執行一個SQL語句時MySQL不能確定要掃描的範圍,InnoDB表同樣會鎖全表,例如 update table set num=1 where name like "%aaa%"

任何一種表都不是萬能的,只用恰當的針對業務類型來選擇合適的表類型,才能最大的發揮MySQL的性能優勢。

六、總結

1.MyISAM不支持事務,InnoDB是事務類型的存儲引擎,當我們的表需要用到事務支持的時候,那肯定是不能選擇MyISAM了

2.MyISAM只支持表級鎖,BDB支持頁級鎖和表級鎖默認爲頁級鎖,而InnoDB支持行級鎖和表級鎖默認爲行級鎖

表級鎖 :直接鎖定整張表,在鎖定期間,其他進程無法對該表進行寫操作,如果設置的是寫鎖,那麼其他進程讀也不允許

MyISAM是表級鎖定的存儲引擎,它不會出現死鎖問題

對於write,表鎖定原理如下

如果表上沒有鎖,在其上面放置一個寫鎖,否則,把鎖定請求放在寫鎖隊列中。

對於read,表鎖定原理如下

如果表上沒有寫鎖定,那麼把一個讀鎖放在其上面,否則把鎖請求放在讀鎖定隊列中

當一個鎖定被釋放時,表可被寫鎖定隊列中的線程得到,然後纔是讀鎖定隊列中的線程。這意味着,如果你在一個表上有許多更新,那麼你的SELECT語句將等到所有的寫鎖定線程執行完。

行級鎖:只對指定的行進行鎖定,其他進程還是可以對錶中的其他行進行操作的。

行級鎖是MySQL粒度最小的一種鎖,它能大大的減少數據庫操作的衝突,但是粒度越小實現成本也越大。

行級鎖可能會導致“死鎖”,那到底是怎麼導致的呢,分析原因:MySQL行級鎖並不是直接鎖記錄,而是鎖索引。索引分爲主鍵索引和非主鍵索引兩種,如果一條sql語句操作了主鍵索引,那麼MySQL就會鎖定這個主鍵索引,如果sql語句操作的是非主鍵索引,那麼MySQL會先鎖定這個非主鍵索引,再去鎖定主鍵索引。

UPDATEDELETE操作時MySQL不僅會鎖定所有WHERE條件掃描過得索引,還會鎖定相鄰的鍵值。

死鎖舉例分析

Test: (ID,STATE,TIME) 主鍵索引:ID 非主鍵索引:STATE

當執行UPDATE STATE =1011 WHERE STATE=1000 語句的時候會鎖定STATE索引,由於STATE 是非主鍵索引,所以MySQL還會去請求鎖定ID索引

當另一個SQL語句與語句1幾乎同時執行時:UPDATE STATE=1010 WHERE ID=1對於語句2 ,MySQL會先鎖定ID索引,由於語句2操作了STATE字段,所以MySQL還會請求鎖定STATE索引。這時。彼此鎖定着對方需要的索引,又都在等待對方釋放鎖定。所以出現了死鎖的情況

行級鎖的優點

  • 有許多線程訪問不同的行時,只存在少量的衝突。
  • 回滾時只有少量的更改
  • 可以長時間鎖定單一的行

行級鎖缺點

  • 相對於頁級鎖和表級鎖來說佔用了更多的內存
  • 當表的大部分行在使用時,比頁級鎖和表級鎖慢,因爲你必須獲得更多的鎖
  • 當在大部分數據上經常使用GROUP BY操作,肯定會比表級鎖和頁級鎖慢

頁級鎖

  • 表級鎖速度快,但是衝突多;
  • 行級鎖速度慢,但衝突少;
  • 頁級鎖就是他倆折中的,一次鎖定相鄰的一組記錄。

3.MyISAM引擎不支持外鍵,InnoDB支持外鍵

4.MyISAM引擎的表在大量高併發的讀寫下會經常出現表損壞的情況

我們以前做的項目就遇到這個問題,表的INSERTUPDATE操作很頻繁,原來用的MyISAM引擎,導致表隔三差五就損壞,後來更換成了InnoDB引擎。

其他容易導致表損壞原因

  • 服務器突然斷電導致數據文件損壞,強制關機(mysqld未關閉情況下)導致表損壞
  • mysqld進程在寫入操作的時候被殺掉
  • 磁盤故障

表損壞常見症狀

  • 查詢表不能返回數據或返回部分數據

  • 打開表失敗: Can’t open file: ‘×××.MYI’ (errno: 145) 。

  • Error: Table ‘p’ is marked as crashed and should be repaired 。

  • Incorrect key file for table: ‘…’. Try to repair it

MySQL表的恢復

對於MyISAM表的恢復:

可以使用MySQL自帶的myisamchk工具: myisamchk -r tablename 或者 myisamchk -o tablename(比前面的更保險) 對錶進行修復

5.對於count()查詢來說MyISAM更有優勢

因爲MyISAM存儲了表中的行數記錄,執行SELECT COUNT() 的時候可以直接獲取到結果,而InnoDB需要掃描全部數據後得到結果。

但是注意一點:對於帶有WHERE條件的 SELECT COUNT()語句兩種引擎的表執行過程是一樣的,都需要掃描全部數據後得到結果

6. InnoDB是爲處理巨大數據量時的最大性能設計,它的CPU效率可能是任何其它基於磁盤的關係數據庫引擎所不能匹敵的

7.MyISAM支持全文索引(FULLTEXT),InnoDB不支持

8.MyISAM引擎的表的查詢、更新、插入的效率要比InnoDB高

測試方法:連續提交10個query, 表記錄總數:38萬 , 時間單位 S

引擎類型 MyISAM InnoDB 性能相差
count 0.0008357 3.0163 3609
查詢主鍵 0.005708 0.1574 27.57
查詢非主鍵 24.01 80.37 3.348
更新主鍵 0.008124 0.8183 100.7
更新非主鍵 0.004141 0.02625 6.338
插入 0.004188 0.3694 88.21
  • 加了索引以後,對於MyISAM查詢可以加快:4 206.09733倍,對InnoDB查詢加快510.72921倍,同時對MyISAM更新速度減慢爲原來的1/2,InnoDB的更新速度減慢爲原來的1/30。要看情況決定是否要加索引,比如不查詢的log表,不要做任何的索引。

  • 如果你的數據量是百萬級別的,並且沒有任何的事務處理,那麼用MyISAM是性能最好的選擇。

  • InnoDB表的大小更加的大,用MyISAM可省很多的硬盤空間。

在我們測試的這個38w的表中,表佔用空間的情況如下

引擎類型 MyISAM InnoDB
數據 53,924 KB 58,976 KB
索引 13,640 KB 21,072 KB
佔用總空間 67,564 KB 80,048 KB

另外一個176W萬記錄的表, 表佔用空間的情況如下

引擎類型 MyISAM InnoDB
引擎類型 MyIsam InnorDB
數據 56,166 KB 90,736 KB
索引 67,103 KB 88,848 KB
佔用總空間 123,269 KB 179,584 KB

七、性能對比

測試的版本是mysql Ver 14.14 Distrib 5.1.49, for debian-linux-gnu (i686),使用的是Innodb plugin 1.0.8(官方稱比built-in版本性能更好)和默認的MyISAM。

測試機器是筆記本,配置如下:Intel 酷睿2雙核 P8600,2G*2 DDR3 1066內存,320G硬盤5400轉。

測試一
數據插入性能測試,這裏我分別對innodb_flush_log_at_trx_commit參數打開和關閉都測了了一下,每次測試都是運行40s,表中數字都是實際插入條數。

線程 MyISAM Innodb (打開) Innodb (關閉)
單線程,逐個插入 120000 60000 60000
4線程,逐個插入 40000*4 20000*4 40000*4
單線程,批量100條/次插入 3600*100 800*100 3000*100
單線程,批量200條/次插入 1800*200 400*200 1600*200

可以發現批量插入的性能遠高於單條插入,但是一次批量的大小對性能影響不大。每條記錄是否都刷新日誌的參數對innodb性能的影響巨大。總體上來說,MyISAM性能更優一點。這裏有一點需要注意,在插入測試過程中,我對系統資源進行了監控,發現MyISAM對系統資源佔用很低,但是Innodb對磁盤佔用卻很高,應該是對事務控制多了很多需要記錄的日誌。

測試二
數據讀取性能測試。每次隨機讀取1000條記錄,反覆進行讀取。

線程 MyISAM Innodb
單線程,200次讀取 5.7s 16.7s
4線程,200次讀取 12s 40.8s

可以看出MyISAM的讀取性能非常恐怖,性能差距在3倍的樣子。

以上兩個測試發現MyISAM在無事務的需求下幾乎完勝,但是要知道它是表鎖,Innodb是行鎖,那麼在併發讀寫同時存在的情況下,那結果會是怎麼樣呢?!

測試三:兩個線程併發寫入,2個線程併發讀取。

線程 MyISAM Innodb
逐個插入 寫入40s:10000*2
讀取200次*2:14s 寫入40s:60000*2
讀取200次*2:50s

批量100條/次插入 寫入`40s:10001002 讀取200次2:10s 寫入40s:15001002 讀取200次2:50s

這下立刻顯示出Innodb在併發情況下強勁的性能,幾乎沒有什麼性能衰減。而MyISAM單條插入速度變得非常慢,批量插入也下降了40%性能。

總結一下,在寫多讀少的應用中還是InnoDB插入性能更穩定,在併發情況下也能基本,如果是對讀取速度要求比較快的應用還是選MyISAM。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章