mysql从头学一 1.1存储引擎 MyISAM和 innoDB

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/insis_mo/article/details/83651689

各种存储引擎的特性

下面重点介绍几种常用的存储引擎,并对比各个存储引擎之间的区别,以帮助读者理解

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

表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 把内部数据词典和未作日志放在这个文件中。

猜你喜欢

转载自blog.csdn.net/insis_mo/article/details/83651689