插件式存储引擎是 MySQL 数据库最重要的特性之一,用户可以根据应用的需要选择如何存储和索引数据、是否使用事务等。MySQL 默认支持多种存储引擎,以适应与不同的领域的数据库应用需要。
MySQL 5.x 支持的存储引擎包括 MyISAM、InnoDB、BDB、MEMORY、MERGE、EXAMPLE、NDB Cluster、ARCHIVE、CSV、BLACKHOLE、FEDERATED 等,其中 InnoDB 和 BDB 提供事务安全表。
创建表时如果不指定存储引擎,那么系统就会使用默认存储引擎,MySQL 的默认引擎是 InnoDB。如果要修改默认的存储引擎,可以在参数文件中设置 default_storage_engine。查看当前的默认存储引擎,可以使用下面的命令:
mysql> show variables like '%storage_engine%';
+----------------------------------+--------+
| Variable_name | Value |
+----------------------------------+--------+
| default_storage_engine | InnoDB |
| default_tmp_storage_engine | InnoDB |
| disabled_storage_engines | |
| internal_tmp_disk_storage_engine | InnoDB |
+----------------------------------+--------+
4 rows in set (0.00 sec)
可以通过下面的方法查询当前数据库支持的存储类型:
mysql> show engines \G
*************************** 1. row ***************************
Engine: InnoDB
Support: DEFAULT
Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES
XA: YES
Savepoints: YES
*************************** 2. row ***************************
Engine: MRG_MYISAM
Support: YES
Comment: Collection of identical MyISAM tables
Transactions: NO
XA: NO
Savepoints: NO
*************************** 3. row ***************************
Engine: MEMORY
Support: YES
Comment: Hash based, stored in memory, useful for temporary tables
Transactions: NO
XA: NO
Savepoints: NO
...
在创建表的时候,可以通过增加 ENGINE 关键字设置新建表的存储引擎,例如:
mysql> create table ai(
-> i bigint(20) NOT NULL AUTO_INCREMENT,
-> PRIMARY KEY(i)
-> )ENGINE = MyISAM DEFAULT charset = gbk;
Query OK, 0 rows affected (0.00 sec)
mysql> create table country(
-> country_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
-> conutry VARCHAR(50) NOT NULL,
-> last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-> PRIMARY KEY(country_id)
-> )ENGINE=InnoDB DEFAULT charset=gbk;
Query OK, 0 rows affected (0.01 sec)
可以使用 alter table 将一个已经存在的表修改成其他的存储引擎:
mysql> alter table ai engine = innodb;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show create table ai \G
*************************** 1. row ***************************
Table: ai
Create Table: CREATE TABLE `ai` (
`i` bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`i`)
) ENGINE=InnoDB DEFAULT CHARSET=gbk
1 row in set (0.00 sec)
特点 | MyISAM | InnoDB | MEMORY | MEGRE | NDB |
---|---|---|---|---|---|
存储限制 | 有 | 64TB | 有 | 没有 | 有 |
事务安全 | 支持 | ||||
锁机制 | 表锁 | 行锁 | 表锁 | 表锁 | 行锁 |
B 树索引 | 支持 | 支持 | 支持 | 支持 | 支持 |
哈希索引 | 支持 | 支持 | |||
全文索引 | 支持 | ||||
集群索引 | 支持 | ||||
索引缓存 | 支持 | 支持 | 支持 | 支持 | 支持 |
数据可压缩 | 支持 | ||||
空间使用 | 低 | 高 | N/A | 低 | 低 |
内存使用 | 低 | 高 | 中等 | 低 | 高 |
批量插入的速度 | 高 | 低 | 高 | 高 | 高 |
支持外键 | 支持 |
MyISAM 不支持事务、也不支持外键,其优势是访问的速度快,对事务完整性没有要求或者以 SELECT、INSERT 为主的应用基本上都可以使用这个引擎来创建表。
每个 MyISAM 在磁盘上存储成 3 个文件,其文件名都和表名相同,但是扩展名分别是:
数据文件和索引文件可以放在不同的目录,平均分布 I/O,获得更快的速度。
要指定索引文件和数据文件的路径,需要在创建表的时候通过 DATA DIRECTORY 和 INDEX DIRECTORY 语句指定。
MyISAM 类型的表可能会损坏,原因有很多种,损坏后的表可能不能被访问,会提示需要修复或者访问后返回错误的结果。MyISAM 类型的表提供修复的工具,可以用 CHECK TABLE 语句来检查 MyISAM 表的健康,并用 REPAIRE TABLE 语句来修复一个损坏的表。
MyISAM 的表还支持 3 中不同的存储格式:
其中,静态表是默认存储格式。静态表中的字段都是非变长字段,这样每个记录都是固定长度的,这种存储方式的优点是存储非常快,容易缓存,出现故障容易恢复;缺点是占用的空间通常比动态表多。静态表的数据在存储时会按照列的宽度定义补足空格,但是在应用访问的时候并不会得到这些空格,这些空格在返回给应用之前已经去掉。
mysql> create table Myisam_char(name char(10))engine=MyISAM;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into Myisam_char values('abcde'),('abcde '),(' abcde'),(' abcde ');
Query OK, 4 rows affected (0.01 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 | 6 |
+---------+--------------+
4 rows in set (0.00 sec)
有上面的例子可知,插入记录后面的空格都被去掉了,前面的空格保留了。
动态表中包含变长的字段,记录不是固定的长度,这样存储的优点是占用的空间较少,但是频繁地更新和删除记录会产生碎片,需要定期执行 OPTIMIZE TABLE 语句或 myisamchk -r 命令来改善性能,并且在出现故障时恢复相对比较困难。
压缩表由 myisampack 工具创建,占据非常小的磁盘空间。
InnoDB 存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是对比 MyISAM 的存储引擎,InnoDB 写的处理效率会差一些,并且会占用更多的磁盘空间以保留数据和索引。
下面重点介绍存储引擎 InnoDB 的表在使用过程中不同于使用其他存储引擎的表的特点:
自动增长的列,可以手动插入值,但插入 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.00 sec)
mysql> insert into autoincre_demo values(1,"1"),(0,'2'),(null,'3'),(5,'4'),(0,'5');
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> select * from autoincre_demo;
+---+------+
| i | name |
+---+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 5 | 4 |
| 6 | 5 |
+---+------+
5 rows in set (0.00 sec)
可以通过 “alter table 表名 auto_increment=n;” 语句强制设置自动增长列的初始值,默认从 1 开始,但是该初始值是保留在内存中,如果该值在使用之前数据库重启,那么这个强制的默认值就会丢失。
可以使用 last_insert_id() 查询当前线程最后插入记录使用的值。如果一次插入了多条记录,那么返回的是第一条记录使用的自动增长值。
mysql> insert into autoincre_demo values(7,"7");
Query OK, 1 row affected (0.00 sec)
mysql> select LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 2 |
+------------------+
1 row in set (0.00 sec)
mysql> insert into autoincre_demo(name) values('8'),('9');
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 8 |
+------------------+
1 row in set (0.00 sec)
对于 InnoDB 表,自动增长列必须是索引。如果是组合索引,也必须是组合索引的第一列。但是对于 MyISAM 表,自动增长列也可以是组合索引的其他列,这样插入数据后,自动增长列是按照组合索引的前面几列进行排序后递增的。例如:
mysql> create table autoincre_myisam(
-> d1 smallint not null auto_increment,
-> d2 smallint not null,
-> name varchar(10),
-> index(d2,d1) #自动增长列作为了组合索引的第二列
-> )engine = myisam;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into autoincre_myisam(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_myisam;
+----+----+------+
| 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)
mysql> create table autoincre_innodb(
-> d1 smallint not null auto_increment,
-> d2 smallint not null,
-> name varchar(10),
-> index(d2,d1)
-> )engine = innodb;
ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key #提示自动增长列必须是组合索引的第一列
MySQL 支持外键的存储引起只有 InnoDB,在创建外键的时候,要求父表必须有对应的索引,子表在创建外键的时候也会自动创建对应的索引。
下面的例子:数据库中有两个表,country 是父表,country_id 为主键索引,city 为子表,country_id 字段为外键,对应于 country 表的主键 country_id。
mysql> 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),
-> foreign key (country_id) references country (country_id) on delete restrict on update cascade
-> )engine=InnoDB default charset=utf8;
Query OK, 0 rows affected (0.00 sec)
在创建索引时,可以指定在删除、更新父表时,对子表进行的相应操作,包括 RESTRICT、CASCADE、SET NULL、NO ACTION。其中 Restrict 和 NO ACTION 相同,是指限制在子表有关联记录的情况下父表不能更新;CASCADE 表示父表在更新或删除时,更新或者删除自表对应的记录;SET NULL 则表示父表在更新或删除时,子表的对应字段被 SET NULL。
InnoDB 存储表和索引表有以下两种存储方式:
MEMORY 存储引擎使用存在于内存中的内容来创建表。每个 MEMORY 表只实际对应一个磁盘文件,格式为 .frm。MEMORY 类型的表访问非常的快,因为它的数据是放在内存中的,并且默认使用 HASH 索引,但是一旦服务关闭,表中的数据就会丢失。
mysql> create table tab_memory engine = memory
-> select city_id ,city,country_id
-> from city group by city_id;
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> select count(*) from tab_memory;
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
mysql> show table status like 'tab_memory' \G
*************************** 1. row ***************************
Name: tab_memory
Engine: MEMORY
Version: 10
Row_format: Fixed
Rows: 0
Avg_row_length: 155
Data_length: 0
Max_data_length: 16252835
Index_length: 0
Data_free: 0
Auto_increment: NULL
Create_time: 2020-08-19 08:24:33
Update_time: NULL
Check_time: NULL
Collation: utf8_general_ci
Checksum: NULL
Create_options:
Comment:
1 row in set (0.00 sec)