1. 程式人生 > >mysql從頭學一 1.1儲存引擎 MyISAM和 innoDB

mysql從頭學一 1.1儲存引擎 MyISAM和 innoDB

各種儲存引擎的特性

下面重點介紹幾種常用的儲存引擎,並對比各個儲存引擎之間的區別,以幫助讀者理解

不同儲存引擎的使用方式。

表7-1                          常用儲存引擎的對比

特點

MyISAM

InnoDB

MEMORY

MERGE

NDB

儲存限制

64TB

沒有

事務安全

 

支援

 

 

 

鎖機制

表鎖

行鎖

表鎖

表鎖

行鎖

B 樹索引

支援

支援

支援

支援

支援

雜湊索引

 

 

支援

 

支援

全文索引

支援

 

 

 

 

叢集索引

 

支援

 

 

 

資料快取

 

支援

支援

 

支援

索引快取

支援

支援

支援

支援

支援

資料可壓縮

支援

 

 

 

 

空間使用

N/A

記憶體使用

中等

批量插入的速度

支援外來鍵

 

支援

 

 

 

 

下面將重點介紹最常使用的 4 種儲存引擎:MyISAM、InnoDB、MEMORY 和 MERGE。NDB

儲存引擎會在第 33 章 MySQL CLUSTER 中詳細介紹,這裡不再贅述。

7.2.1 MyISAM

MyISAM 是 MySQL 的預設儲存引擎。MyISAM 不支援事務、也不支援外來鍵,其優勢是訪問的速度快,對事務完整性沒有要求或者以 SELECT、INSERT 為主的應用基本上都可以使用這個引擎來建立表。

每個 MyISAM 在磁碟上儲存成 3 個檔案,其檔名都和表名相同,但副檔名分別是:

  1. .frm(儲存表定義);
  2. .MYD(MYData,儲存資料); l .MYI (MYIndex,儲存索引)。資料檔案和索引檔案可以放置在不同的目錄,平均分佈 IO,獲得更快的速度。要指定索引檔案和資料檔案的路徑,需要在建立表的時候通過 DATA DIRECTORY 和 INDEX DIRECTORY 語句指定,也就是說不同 MyISAM 表的索引檔案和資料檔案可以放置到不同的路徑下。檔案路徑需要是絕對路徑,並且具有訪問許可權。

 

MyISAM 型別的表可能會損壞,原因可能是多種多樣的,損壞後的表可能不能訪問,會提示需要修復或者訪問後返回錯誤的結果。MyISAM 型別的表提供修復的工具,可以用 CHECK TABLE 語句來檢查 MyISAM 表的健康,並用 REPAIR TABLE 語句修復一個損壞的 MyISAM 表。表損壞可能導致資料庫異常重新啟動,需要儘快修復並儘可能地確認損壞的原因。具體的操作步驟可以參見第 35 章應急處理。

MyISAM 的表又支援 3 種不同的儲存格式,分別是:

  1. 靜態(固定長度)表;
  2. 動態表; l 壓縮表。

其中,靜態表是預設的儲存格式。靜態表中的欄位都是非變長欄位,這樣每個記錄都是固定長度的,這種儲存方式的優點是儲存非常迅速,容易快取,出現故障容易恢復;缺點是佔用的空間通常比動態表多。靜態表的資料在儲存的時候會按照列的寬度定義補足空格,但是在應用訪問的時候並不會得到這些空格,這些空格在返回給應用之前已經去掉。

但是也有些需要特別注意的問題,如果需要儲存的內容後面本來就帶有空格,那麼在返回結果的時候也會被去掉,開發人員在編寫程式的時候需要特別注意,因為靜態表是預設的儲存格式,開發人員可能並沒有意識到這一點,從而丟失了尾部的空格。以下例子演示了插入的記錄包含空格時處理的情況:

mysql> create table Myisam_char (name char(10)) engine=myisam; Query OK, 0 rows affected (0.04 sec)

 

mysql> insert into Myisam_char values('abcde'),('abcde  '),('  abcde'),('  abcde  ');

Query OK, 4 rows affected (0.00 sec)

Records: 4  Duplicates: 0  Warnings: 0

 mysql> select name,length(name) from Myisam_char;

+---------+--------------+

| name    | length(name) |

+---------+--------------+

| abcde   | 5            |

| abcde   | 5            |

|   abcde | 7            |

|   abcde | 7            |

+---------+--------------+

4 rows in set (0.00 sec)

從上面的例子可以看出,插入記錄後面的空格都被去掉了,前面的空格保留。

 

動態表中包含變長欄位,記錄不是固定長度的,這樣儲存的優點是佔用的空間相對較少,但是頻繁地更新刪除記錄會產生碎片,需要定期執行 OPTIMIZE TABLE 語句或 myisamchk -r 命令來改善效能,並且出現故障的時候恢復相對比較困難。

壓縮表由 myisampack 工具建立,佔據非常小的磁碟空間。因為每個記錄是被單獨壓縮的,所以只有非常小的訪問開支。

7.2.2 InnoDB

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

下面將重點介紹 InnoDB 儲存引擎的表使用過程中不同於其他儲存引擎的特點。

1自動增長列

InnoDB 表的自動增長列可以手工插入,但是插入的值如果是空或者 0,則實際插入的將是自動增長後的值。下面定義新表 autoincre_demo,其中列 i 使用自動增長列,對該表插入記錄,然後檢視自動增長列的處理情況,可以發現插入 0 或者空實際插入的都將是自動增長後的值:

mysql> create table autoincre_demo      -> (i smallint not null auto_increment,

    -> name varchar(10),primary key(i)

    -> )engine=innodb;

Query OK, 0 rows affected (0.13 sec)

 

mysql> insert into autoincre_demo values(1,'1'),(0,'2'),(null,'3');

Query OK, 3 rows affected (0.04 sec)

Records: 3  Duplicates: 0  Warnings: 0

 mysql> select * from autoincre_demo;

+---+------+

| i | name |

+---+------+

| 1 | 1    |

| 2 | 2    |

| 3 | 3    |

+---+------+

3 rows in set (0.00 sec)

 

可以通過“ALTER TABLE *** AUTO_INCREMENT = n;”語句強制設定自動增長列的初識值,預設從 1 開始,但是該強制的預設值是保留在記憶體中的,如果該值在使用之前資料庫重新啟動,那麼這個強制的預設值就會丟失,就需要在資料庫啟動以後重新設定。

 

可以使用 LAST_INSERT_ID()查詢當前執行緒最後插入記錄使用的值。如果一次插入了多條記錄,那麼返回的是第一條記錄使用的自動增長值。下面的例子演示了使用 LAST_INSERT_ID() 的情況:

mysql> insert into autoincre_demo values(4,'4');

Query OK, 1 row affected (0.04 sec)

 

mysql> select LAST_INSERT_ID();

+------------------+

| LAST_INSERT_ID() |

+------------------+

| 2                |

+------------------+

1 row in set (0.00 sec)

 

mysql> insert into autoincre_demo(name) values('5'),('6'),('7');

Query OK, 3 rows affected (0.05 sec)

Records: 3  Duplicates: 0  Warnings: 0

 mysql> select LAST_INSERT_ID();

+------------------+

| LAST_INSERT_ID() |

+------------------+

| 5                |

+------------------+

1 row in set (0.00 sec)

對於 InnoDB 表,自動增長列必須是索引。如果是組合索引,也必須是組合索引的第一列,但是對於 MyISAM 表,自動增長列可以是組合索引的其他列,這樣插入記錄後,自動增長列是按照組合索引的前面幾列進行排序後遞增的。例如,建立一個新的 MyISAM 型別的表 autoincre_demo,自動增長列 d1 作為組合索引的第二列,對該表插入一些記錄後,可以發現自動增長列是按照組合索引的第一列 d2 進行排序後遞增的:

mysql> 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.03 sec)

 

mysql> 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.00 sec)

Records: 6  Duplicates: 0  Warnings: 0

 

mysql> 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)

2、外來鍵約束

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

下面是樣例資料庫中的兩個表,country 表是父表,country_id 為主鍵索引,city 表是子表,country_id 欄位對 country 表的 country_id 有外來鍵。

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;

 

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 `fk_city_country` FOREIGN KEY (country_id) REFERENCES country (country_id) ON

DELETE RESTRICT ON UPDATE CASCADE

)ENGINE=InnoDB DEFAULT CHARSET=utf8;

在建立索引的時候,可以指定在刪除、更新父表時,對子表進行的相應操作,包 RESTRICT、

CASCADE、SET NULL 和 NO ACTION。其中 RESTRICT 和 NO ACTION 相同,是指限制在子表有關聯記錄的情況下父表不能更新;CASCADE 表示父表在更新或者刪除時,更新或者刪除子表對應記錄;SET NULL 則表示父表在更新或者刪除的時候,子表的對應欄位被 SET NULL。選擇後兩種方式的時候要謹慎,可能會因為錯誤的操作導致資料的丟失。

例如對上面建立的兩個表,子表的外來鍵指定是 ON DELETE RESTRICT ON UPDATE CASCADE 方式的,那麼在主表刪除記錄的時候,如果子表有對應記錄,則不允許刪除,主表在更新記錄的時候,如果子表有對應記錄,則子表對應更新:

mysql> 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)

 mysql> 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)

 

mysql> 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)

 

mysql> 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

 mysql> 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)

 mysql> 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)

 

當某個表被其他表建立了外來鍵參照,那麼該表的對應索引或者主鍵禁止被刪除。

在匯入多個表的資料時,如果需要忽略表之前的匯入順序,可以暫時關閉外來鍵的檢查;同樣,在執行 LOAD DATA 和 ALTER TABLE 操作的時候,可以通過暫時關閉外來鍵約束來加快處理的速度,關閉的命令是“SET FOREIGN_KEY_CHECKS = 0;”,執行完成之後,通過執行“SET FOREIGN_KEY_CHECKS = 1;”語句改回原狀態。

對於 InnoDB 型別的表,外來鍵的資訊通過使用 show create table 或者 show table status 命令都可以顯示。

mysql> show table status like 'city' \G

*************************** 1. row ***************************

           Name: city

         Engine: InnoDB

        Version: 10

     Row_format: Compact

           Rows: 427

 Avg_row_length: 115

    Data_length: 49152 Max_data_length: 0

   Index_length: 16384

      Data_free: 0

 Auto_increment: 601

    Create_time: 2007-07-17 09:45:33

    Update_time: NULL

     Check_time: NULL

      Collation: utf8_general_ci

       Checksum: NULL  Create_options: 

        Comment: InnoDB free: 0 kB; (`country_id`) REFER `sakila/country`(`country_id`) ON

UPDATE

1 row in set (0.00 sec)

 

        Engine: InnoDB

        Version: 10

     Row_format: Compact

           Rows: 427

 Avg_row_length: 115

    Data_length: 49152 Max_data_length: 0

   Index_length: 16384

      Data_free: 0

 Auto_increment: 601

    Create_time: 2007-07-17 09:45:33

    Update_time: NULL

     Check_time: NULL

      Collation: utf8_general_ci

       Checksum: NULL  Create_options: 

        Comment: InnoDB free: 0 kB; (`country_id`) REFER `sakila/country`(`country_id`) ON

UPDATE

1 row in set (0.00 sec)

 

3、儲存方式

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

  1. 使用共享表空間儲存,這種方式建立的表的表結構儲存在.frm 檔案中,資料和索引儲存在 innodb_data_home_dir 和 innodb_data_file_path 定義的表空間中,可以是多個檔案。
  2. 使用多表空間儲存,這種方式建立的表的表結構仍然儲存在.frm 檔案中,但是每個表的資料和索引單獨儲存在.ibd 中。如果是個分割槽表,則每個分割槽對應單獨的.ibd 檔案,檔名是“表名+分割槽名”,可以在建立分割槽的時候指定每個分割槽的資料檔案的位置,以此來將表的 IO 均勻分佈在多個磁碟上。

要使用多表空間的儲存方式,需要設定引數 innodb_file_per_table,並重新啟動服務後才可以生效,對於新建的表按照多表空間的方式建立,已有的表仍然使用共享表空間儲存。如果將已有的多表空間方式修改回共享表空間的方式,則新建表會在共享表空間中建立,但已有的多表空間的表仍然儲存原來的訪問方式。所以多表空間的引數生效後,只對新建的表生效。

多表空間的資料檔案沒有大小限制,不需要設定初始大小,也不需要設定檔案的最大限制、擴充套件大小等引數。

對於使用多表空間特性的表,可以比較方便地進行單表備份和恢復操作,但是直接複製.ibd 檔案是不行的,因為沒有共享表空間的資料字典資訊,直接複製的.ibd 檔案和.frm 檔案恢復時是不能被正確識別的,但可以通過以下命令:

ALTER TABLE tbl_name DISCARD TABLESPACE; 

ALTER TABLE tbl_name IMPORT TABLESPACE; 

將備份恢復到資料庫中,但是這樣的單表備份,只能恢復到表原來在的資料庫中,而不能恢復到其他的資料庫中。如果要將單表恢復到目標資料庫,則需要通過 mysqldump 和 mysqlimport 來實現。

注意:即便在多表空間的儲存方式下,共享表空間仍然是必須的,InnoDB 把內部資料詞典和未作日誌放在這個檔案中。