MySQL 存儲引擎

前言

  • 和大多數數據庫不一樣,MySQL 中有一個存儲引擎的概念,針對不一樣的存儲需求能夠選擇最優的存儲引擎。

一、MySQL 存儲引擎概述

  • 插件式存儲引擎是 MySQL 數據庫最重要的特性之一,用戶能夠根據應用的須要選擇如何存儲和索引數據、是否使用事務等。MySQL 默認支持多種存儲引擎,以適用於不一樣領域的數據庫應用須要,用戶能夠經過選擇使用不一樣的存儲引擎提升應用的效率,提供靈活的存儲,用戶甚至能夠按照本身的須要定製和使用本身的存儲引擎,以實現最大程度的可定製性。mysql

  • MySQL 5.0 支持的存儲引擎包括 MyISAM、InnoDB、BDB、MEMORY、MERGE、EXAMPLE、NDB Cluster、ARCHIVE、CSV、BLACKHOLE、FEDERATED 等,其中 InnoDB 和 BDB 提供事務安全表,其餘存儲引擎都是非事務安全表。sql

  • 建立新表時若是不指定存儲引擎,那麼系統就會使用默認存儲引擎,MySQL 5.5 以前的默認存儲引擎是 MyISAM,5.5 以後改成了 InnoDB。數據庫

1.1 經常使用存儲引擎對比

  • 經常使用存儲引擎對比緩存

    特色 MyISAM InnoDB MEMORY MERGE NDB
    存儲限制 64 TB 沒有
    事物安全 支持
    鎖機制 表鎖 行鎖 表鎖 表鎖 行鎖
    B 樹索引 支持 支持 支持 支持 支持
    哈希索引 支持 支持
    全文索引 支持
    集羣索引 支持
    數據緩存 支持 支持 支持
    索引緩存 支持 支持 支持 支持 支持
    數據可壓縮 支持
    空間使用 N/A
    內存使用 中等
    批量插入的速度
    支持外鍵 支持

1.2 MySQL 存儲引擎設置

  • 1)查詢當前數據庫支持的存儲引擎安全

    >  show engines;
    
        +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
        | Engine             | Support | Comment                                                        | Transactions | XA   | Savepoints |
        +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
        | ARCHIVE            | YES     | Archive storage engine                                         | NO           | NO   | NO         |
        | BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears) | NO           | NO   | NO         |
        | MRG_MYISAM         | YES     | Collection of identical MyISAM tables                          | NO           | NO   | NO         |
        | FEDERATED          | NO      | Federated MySQL storage engine                                 | NULL         | NULL | NULL       |
        | MyISAM             | YES     | MyISAM storage engine                                          | NO           | NO   | NO         |
        | PERFORMANCE_SCHEMA | YES     | Performance Schema                                             | NO           | NO   | NO         |
        | InnoDB             | DEFAULT | Supports transactions, row-level locking, and foreign keys     | YES          | YES  | YES        |
        | MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables      | NO           | NO   | NO         |
        | CSV                | YES     | CSV storage engine                                             | NO           | NO   | NO         |
        +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
        9 rows in set (0.00 sec)
    > show variables like 'have%';
    
        +------------------------+----------+
        | Variable_name          | Value    |
        +------------------------+----------+
        | have_compress          | YES      |
        | have_dynamic_loading   | YES      |
        | have_geometry          | YES      |
        | have_openssl           | YES      |
        | have_profiling         | YES      |
        | have_query_cache       | NO       |
        | have_rtree_keys        | YES      |
        | have_ssl               | YES      |
        | have_statement_timeout | YES      |
        | have_symlink           | DISABLED |
        +------------------------+----------+
        10 rows in set (0.00 sec)
  • 2)查看當前默認存儲引擎併發

    > show variables like 'table_type';
    
        +---------------+--------+
        | Variable_name | Value  |
        +---------------+--------+
        | table_type    | InnoDB |
        +---------------+--------+
        1 row in set (0.00 sec)
  • 3)設置新建表的存儲引擎app

    # create table 表名 (
          列名稱字段 數據類型(長度) 約束條件,
          列名稱字段 數據類型(長度) 約束條件
      ) engine = 存儲引擎名;
    > create table ai (
       i bigint(20) not null auto_increment,
       primary key(i)
      ) engine = MyISAM;
    
        Query OK, 0 rows affected (0.08 sec)
  • 4)修改已有表的存儲引擎ide

    # 修改已有表的存儲引擎
    # alter table 表名 engine = 存儲引擎名;
    > alter table ai engine = InnoDB;
    
        Query OK, 0 rows affected (0.09 sec)
        Records: 0  Duplicates: 0  Warnings: 0
  • 5)顯示錶的存儲引擎工具

    # 顯示錶的存儲引擎
    # show create table 表名;
    > show create table ai;
    
        +-------+-----------------------------------------------------------------------------------------+
        | Table | Create Table                                                                            |
        +-------+-----------------------------------------------------------------------------------------+
        | ai    | CREATE TABLE `ai` (
                                      `i` bigint(20) NOT NULL AUTO_INCREMENT,
                                      PRIMARY KEY (`i`)
                                    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci    |
        +-------+-----------------------------------------------------------------------------------------+
        1 row in set (0.01 sec)

二、MyISAM 存儲引擎

  • MyISAM 是 MySQL 默認的存儲引擎。MyISAM 不支持事務、也不支持外鍵,其優點是訪問的速度快,對事務完整性沒有要求或者以 SELECT、INSERT 爲主的應用基本上均可以使用這個引擎來建立表。性能

  • 若是應用是以讀操做和插入操做爲主,只有不多的更新和刪除操做,而且對事務的完整性、併發性要求不是很高,那麼選擇這個存儲引擎是很是適合的。MyISAM 是在 Web、數據倉儲和其餘應用環境下最常使用的存儲引擎之一。

2.1 存儲方式

  • 每一個 MyISAM 在磁盤上存儲成 3 個文件,其文件名都和表名相同,但擴展名分別是:

    • .frm(存儲表定義)
    • .MYD(MYData,存儲數據)
    • .MYI(MYIndex,存儲索引)
  • 數據文件和索引文件能夠放置在不一樣的目錄,平均分佈 IO,得到更快的速度。

  • 要指定索引文件和數據文件的路徑,須要在建立表的時候經過 DATA DIRECTORY 和 INDEX DIRECTORY 語句指定,也就是說不一樣 MyISAM 表的索引文件和數據文件能夠放置到不一樣的路徑下。文件路徑須要是絕對路徑,而且具備訪問權限。

  • MyISAM 類型的表可能會損壞,緣由多是多種多樣的,損壞後的表可能不能被訪問,會提示須要修復或者訪問後返回錯誤的結果。

  • MyISAM 類型的表提供修復的工具,能夠用 CHECK TABLE 語句來檢查 MyISAM 表的健康,並用 REPAIR TABLE 語句修復一個損壞的 MyISAM 表。

  • 表損壞可能致使數據庫異常從新啓動,須要儘快修復並儘量地確認損壞的緣由。

2.2 存儲格式

  • MyISAM 的表還支持 3 種不一樣的存儲格式,分別是:

    • 靜態(固定長度)表
    • 動態表
    • 壓縮表
  • 其中,靜態表是默認的存儲格式。靜態表中的字段都是非變長字段,這樣每一個記錄都是固定長度的,這種存儲方式的優勢是存儲很是迅速,容易緩存,出現故障容易恢復;缺點是佔用的空間一般比動態表多。靜態表的數據在存儲時會按照列的寬度定義補足空格,可是在應用訪問的時候並不會獲得這些空格,這些空格在返回給應用以前已經去掉。

    • 可是也有些須要特別注意的問題,若是須要保存的內容後面原本就帶有空格,那麼在返回結果的時候也會被去掉,開發人員在編寫程序的時候須要特別注意,由於靜態表是默認的存儲格式,開發人員可能並無意識到這一點,從而丟失了尾部的空格。
  • 動態表中包含變長字段,記錄不是固定長度的,這樣存儲的優勢是佔用的空間相對較少,可是頻繁地更新和刪除記錄會產生碎片,須要按期執行 OPTIMIZE TABLE 語句或 myisamchk-r 命令來改善性能,而且在出現故障時恢復相對比較困難。

  • 壓縮表由 myisampack 工具建立,佔據很是小的磁盤空間。由於每一個記錄是被單獨壓縮的,因此只有很是小的訪問開支。

三、InnoDB 存儲引擎

  • InnoDB 存儲引擎提供了具備提交、回滾和崩潰恢復能力的事務安全。可是對比 MyISAM 的存儲引擎,InnoDB 寫的處理效率差一些,而且會佔用更多的磁盤空間以保留數據和索引。

  • 用於事務處理應用程序,支持外鍵。若是應用對事務的完整性有比較高的要求,在併發條件下要求數據的一致性,數據操做除了插入和查詢之外,還包括不少的更新、刪除操做,那麼 InnoDB 存儲引擎應該是比較合適的選擇。InnoDB 存儲引擎除了有效地下降因爲刪除和更新致使的鎖定,還能夠確保事務的完整提交(Commit)和回滾(Rollback),對於相似計費系統或者財務系統等對數據準確性要求比較高的系統,InnoDB 都是合適的選擇。

3.1 自動增加列

  • 1)InnoDB 表的自動增加列能夠手工插入,可是插入的值若是是空或者 0,則實際插入的將是自動增加後的值。

    > create table autoincre_demo (
        i smallint not null auto_increment, 
        name varchar(10), primary key(i)
      ) engine = innodb;
    
        Query OK, 0 rows affected (0.04 sec)
    
    > insert into autoincre_demo values (1, '1'), (0, '2'), (null, '3');
    
        Query OK, 3 rows affected (0.09 sec)
        Records: 3  Duplicates: 0  Warnings: 0
    
    > select * from autoincre_demo;
    
        +---+------+
        | i | name |
        +---+------+
        | 1 | 1    |
        | 2 | 2    |
        | 3 | 3    |
        +---+------+
        3 rows in set (0.00 sec)
  • 2)能夠經過 alter table *** auto_increment = n; 語句強制設置自動增加列的初始值。

    • 默認從 1 開始。
    • 該強制的默認值是保留在內存中的,若是該值在使用以前數據庫從新啓動,那麼這個強制的默認值就會丟失,就須要在數據庫啓動之後從新設置。
  • 3)可使用 last_insert_id() 查詢當前線程最後插入記錄使用的值。若是一次插入了多條記錄,那麼返回的是第一條記錄使用的自動增加值。

    > insert into autoincre_demo values(4, '4');
    
        Query OK, 1 row affected (0.05 sec)
    
    > select last_insert_id();
    
        +------------------+
        | last_insert_id() |
        +------------------+
        |                2 |
        +------------------+
        1 row in set (0.01 sec)
    
    > insert into autoincre_demo (name) values ('5'), ('6'), ('7');
    
        Query OK, 3 rows affected (0.01 sec)
        Records: 3  Duplicates: 0  Warnings: 0
    
    > select last_insert_id();
    
        +------------------+
        | last_insert_id() |
        +------------------+
        |                5 |
        +------------------+
        1 row in set (0.00 sec)
  • 4)對於 InnoDB 表,自動增加列必須是索引。若是是組合索引,也必須是組合索引的第一列,可是對於 MyISAM 表,自動增加列能夠是組合索引的其餘列,這樣插入記錄後,自動增加列是按照組合索引的前面幾列進行排序後遞增的。

    > create table autoincre_demo (
        d1 smallint not null auto_increment, 
        d2 smallint not null, 
        name varchar(10), 
        index(d2, d1)
      ) engine = myisam; 
    
        Query OK, 0 rows affected (0.07 sec)
    
    > insert into 
        autoincre_demo 
        (d2, name) 
      values 
        (2, '2'), (3, '3'), (4, '4'), (2, '2'), (3, '3'), (4, '4');
    
        Query OK, 6 rows affected (0.01 sec)
        Records: 6  Duplicates: 0  Warnings: 0
    
    > select * from autoincre_demo;
    
        +----+----+------+
        | d1 | d2 | name |
        +----+----+------+
        |  1 |  2 | 2    |
        |  1 |  3 | 3    |
        |  1 |  4 | 4    |
        |  2 |  2 | 2    |
        |  2 |  3 | 3    |
        |  2 |  4 | 4    |
        +----+----+------+
        6 rows in set (0.00 sec)
    • 建立一個新的 MyISAM 類型的表 autoincre_demo,自動增加列 d1 做爲組合索引的第二列,對該表插入一些記錄後,能夠發現自動增加列是按照組合索引的第一列 d2 進行排序後遞增的。

3.2 外鍵約束

  • 1)MySQL 支持外鍵的存儲引擎只有 InnoDB,在建立外鍵的時候,要求父表必須有對應的索引,子表在建立外鍵的時候也會自動建立對應的索引。

    > create table country (
        country_id smallint unsigned not null auto_increment, 
        country varchar(50) not null, 
        last_update timestamp not null default current_timestamp on update current_timestamp, 
    
        primary key (country_id)
    
      ) engine = InnoDB default charset = utf8;
    
        Query OK, 0 rows affected, 1 warning (0.05 sec)
    
    > create table city (
        city_id smallint unsigned not null auto_increment, 
        city varchar(50) not null, 
        country_id smallint unsigned not null, 
        last_update timestamp not null default current_timestamp on update current_timestamp, 
    
        primary key(city_id), 
        key idx_fk_country_id(country_id),
    
        # constraint 約束名稱 foreign key(外鍵名稱) references 源表(主鍵名稱)
        constraint fk_city_country foreign key(country_id) references country(country_id) on delete restrict on update cascade 
    
      ) engine = InnoDB default charset = utf8;
    
        Query OK, 0 rows affected, 1 warning (0.07 sec)
    • country 表是父表,country_id 爲主鍵索引,city 表是子表,country_id 字段爲外鍵,對應於 country 表的主鍵 country_id。
  • 2)在建立索引時,能夠指定在刪除、更新父表時,對子表進行的相應操做,包括 restrict、cascade、set null 和 no action。選擇後兩種方式的時候要謹慎,可能會由於錯誤的操做致使數據的丟失。

    • restrict 和 no action 相同,是指限制在子表有關聯記錄的狀況下父表不能更新;
    • cascade 表示父表在更新或者刪除時,更新或者刪除子表對應記錄;
    • set null 則表示父表在更新或者刪除的時候,子表的對應字段被 set nullL。
    > select * from country where country_id = 1;
    
        +------------+-------------+---------------------+
        | country_id | country     | last_update         |
        +------------+-------------+---------------------+
        |          1 | Afghanistan | 2006-02-15 04:44:00 |
        +------------+-------------+---------------------+
        1 row in set (0.00 sec)
    
    > select * from city where country_id = 1;
    
        +---------+-------+------------+---------------------+
        | city_id | city  | country_id | last_update         |
        +---------+-------+------------+---------------------+
        |     251 | Kabul |          1 | 2006-02-15 04:45:25 |
        +---------+-------+------------+---------------------+
        1 row in set (0.00 sec)
    
    > delete from country where country_id=1;
    
        ERROR 1451 (23000): Cannot delete or update a parent row: 
        a foreign key constraint fails ('sakila/city', CONSTRAINT 'fk_city_country' 
        FOREIGN KEY ('country_id') REFERENCES 'country' ('country_id') ON UPDATE CASCADE)
    
    > update country set country_id = 10000 where country_id = 1;
    
        Query OK, 1 row affected (0.04 sec)
        Rows matched: 1 Changed: 1 Warnings: 0
    
    > select * from country where country = 'Afghanistan';
    
        +------------+-------------+---------------------+
        | country_id | country     | last_update         |
        +------------+-------------+---------------------+
        |      10000 | Afghanistan | 2007-07-17 09:45:23 |
        +------------+-------------+---------------------+
        1 row in set (0.00 sec)
    
    > select * from city where city_id = 251;
    
        +---------+-------+------------+---------------------+
        | city_id | city  | country_id | last_update         |
        +---------+-------+------------+---------------------+
        |     251 | Kabul |      10000 | 2006-02-15 04:45:25 |
        +---------+-------+------------+---------------------+
        1 row in set (0.00 sec)
    • 當某個表被其餘表建立了外鍵參照,那麼該表的對應索引或者主鍵禁止被刪除。
  • 3)在導入多個表的數據時,若是須要忽略表以前的導入順序,能夠暫時關閉外鍵的檢查;一樣,在執行 load data 和 alter table 操做的時候,能夠經過暫時關閉外鍵約束來加快處理的速度,關閉的命令是 set foreign_key_checks = 0;,執行完成以後,經過執行 set foreign_key_checks = 1; 語句改回原狀態。

  • 4)對於 InnoDB 類型的表,外鍵的信息經過使用 show create table 或者 show table status 命令均可以顯示。

    # show create table 表名;
    > show create table city;
    
        +-------+--------------------------------------------------------------------------------------------------------------------------------------+
        | Table | Create Table                                                                                                                         |
        +-------+--------------------------------------------------------------------------------------------------------------------------------------+
        | city  | CREATE TABLE `city` (
                                        `city_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
                                        `city` varchar(50) NOT NULL,
                                        `country_id` smallint(5) unsigned NOT NULL,
                                        `last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                                        PRIMARY KEY (`city_id`),
                                        KEY `idx_fk_country_id` (`country_id`),
                                        CONSTRAINT `fk_city_country` FOREIGN KEY (`country_id`) REFERENCES `country` (`country_id`) ON UPDATE CASCADE
                                      ) ENGINE=InnoDB DEFAULT CHARSET=utf8                                                                             |
        +-------+--------------------------------------------------------------------------------------------------------------------------------------+
        1 row in set (0.00 sec)
    
    # show table status like 表名;
    > show table status like 'city';
    
        +------+--------+---------+------------+------+----------------+-------------+-----------------+--------------+-----------+----------------+
        | Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment |
        +------+--------+---------+------------+------+----------------+-------------+-----------------+--------------+-----------+----------------+
        | city | InnoDB |      10 | Dynamic    |    0 |              0 |       16384 |               0 |        16384 |         0 |              1 |
        +------+--------+---------+------------+------+----------------+-------------+-----------------+--------------+-----------+----------------+
    
         ---------------------+-------------+------------+-----------------+----------+----------------+---------+
          Create_time         | Update_time | Check_time | Collation       | Checksum | Create_options | Comment |
         ---------------------+-------------+------------+-----------------+----------+----------------+---------+
          2018-06-21 18:58:55 | NULL        | NULL       | utf8_general_ci |     NULL |                |         |
         ---------------------+-------------+------------+-----------------+----------+----------------+---------+
    
        1 row in set (0.06 sec)

3.3 存儲方式

  • InnoDB 存儲表和索引有如下兩種方式。

    • 使用共享表空間存儲,這種方式建立的表的表結構保存在 .frm 文件中,數據和索引保存在 innodb_data_home_dirinnodb_data_file_path 定義的表空間中,能夠是多個文件。
    • 使用多表空間存儲,這種方式建立的表的表結構仍然保存在 .frm 文件中,可是每一個表的數據和索引單獨保存在 .ibd 中。若是是個分區表,則每一個分區對應單獨的 .ibd 文件,文件名是 「表名+分區名」,能夠在建立分區的時候指定每一個分區的數據文件的位置,以此來將表的 IO 均勻分佈在多個磁盤上。
  • 要使用多表空間的存儲方式,須要設置參數 innodb_file_per_table,而且從新啓動服務後才能夠生效,對於新建的表按照多表空間的方式建立,已有的表仍然使用共享表空間存儲。若是將已有的多表空間方式修改回共享表空間的方式,則新建表會在共享表空間中建立,但已有的多表空間的表仍然保存原來的訪問方式。因此多表空間的參數生效後,只對新建的表生效。

  • 多表空間的數據文件沒有大小限制,不須要設置初始大小,也不須要設置文件的最大限制、擴展大小等參數。

  • 對於使用多表空間特性的表,能夠比較方便地進行單表備份和恢復操做,可是直接複製 .ibd 文件是不行的,由於沒有共享表空間的數據字典信息,直接複製的 .ibd 文件和 .frm 文件恢復時是不能被正確識別的,但能夠經過如下命令將備份恢復到數據庫中,可是這樣的單表備份,只能恢復到表原來所在的數據庫中,而不能恢復到其餘的數據庫中。若是要將單表恢復到目標數據庫,則須要經過 mysqldump 和 mysqlimport 來實現。

    alter table tbl_name discard tablespace;
    alter table tbl_name import tablespace;
  • 即使在多表空間的存儲方式下,共享表空間仍然是必須的,InnoDB 把內部數據詞典和在線重作日誌放在這個文件中。

相關文章
相關標籤/搜索