MySQL存储引擎
功能 | MylSAM | MEMORY | InnoDB | Archive |
---|---|---|---|---|
存储限制 | 256TB | RAM | 64TB | None |
支持事务 | No | No | Yes | No |
支持全文索引 | Yes | No | No | No |
支持树索引 | Yes | Yes | Yes | No |
支持哈希索引 | No | Yes | No | No |
支持数据缓存 | No | N/A | Yes | No |
支持外键 | No | No | Yes | No |
一、MyISAM
MyISAM拥有较高的插入、查询速度,但不支持事物和外键。
1、MyISAM在磁盘上存储成三个文件。
1).frm文件:存储表的定义
2).MYD文件:存储数据
3).MYI文件:存储索引
MyISAM 数据文件和索引文件可以在创建表的时候通过 DATA DIRECTORY 和 INDEX DIRECTORY 放置在不同的目录,文件需要绝对路径,并且具有访问权限,从而平均分布 IO ,获得更快的速度。
#创建 myisam 表并制定数据和索引文件的路径
create table myisam_tab(name char(10)) DATA DIRECTORY='/home/mysql/tmp/tmpmyisam/data/' INDEX DIRECTORY='/home/mysql/tmp/tmpmyisam/index/' engine=myisam;
#检查是否开启使用符号链接
mysql> SHOW VARIABLES LIKE 'have_symlink';
+---------------+----------+
| Variable_name | Value |
+---------------+----------+
| have_symlink | DISABLED |
+---------------+----------+
1 row in set (0.01 sec)
#开启符号链接支持 vi my.cnf,修改如下,并重启MySQL
symbolic-links=1
#检查是否开启使用符号链接
mysql> SHOW VARIABLES LIKE 'have_symlink';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| have_symlink | YES |
+---------------+-------+
1 row in set (0.00 sec)
#创建 myisam 表并制定数据和索引文件的路径
mysql> create table myisam_tab(name char(10)) DATA DIRECTORY='/home/mysql/tmp/tmpmyisam/data/' INDEX DIRECTORY='/home/mysql/tmp/tmpmyisam/index/' engine=myisam;
Query OK, 0 rows affected (0.01 sec)
2、MyISAM 的表支持 3 种不同的存储格式
1) 静态(固定长度)表:默认的存储格式,这种方式的优点在于存储速度非常迅速,容易缓存,而且表发生损坏后也容易修复。缺点是占空间。
静态表的数据的存储是会按照列的宽度定义补足空格,但是在应用访问的时候并不会得到这些空格,这些空格在返回给应用之前已经去掉。
注意:如果保存的内容后面本来就带有空格,那么在返回结果的时候也会被去掉。
mysql> insert into myisam_tab values('abcd'),(' abcd'),('ab cd'),('abcd '),(' abcd ');
Query OK, 5 rows affected (0.00 sec)
mysql> select name,length(name) from myisam_tab;
+--------+--------------+
| name | length(name) |
+--------+--------------+
| abcd | 4 |
| abcd | 6 |
| ab cd | 6 |
| abcd | 4 |
| abcd | 5 |
+--------+--------------+
5 rows in set (0.00 sec)
2) 动态表:优点是节省空间,但是频繁的更新和删除记录会产生碎片,需要定期执行 OPTIMIZE TABLE 语句或 myisamchk -r 命令来改善性能,并且一旦出错恢复起来比较麻烦。
3) 压缩表:压缩表由 myisampack 工具创建,占用非常小的磁盘空间。因为每个记录是被单独压缩的,所以只要非常小的访问开销。
二、InnoDB
1、自动增长列
InnoDB 表的自动增长列可以手工插入,但是插入的值如果是空,则实际插入的将是自动增长后的值。可以通过 ALTER TABLE TABNAME AUTO_INCREMENT=num; 语句来强制设置自动增长列的初始值。
注意:在 MySQL 8.0 之前,对于 InnoDB 存储引擎来说,AUTO_INCREMENT 只保留在内存中,如果数据库重新启动,那么这个值就会丢失,数据库会自动将 AUTO_INCREMENT 重置为自增列当前存储的最大值 +1,这可能会导致在数据库重启后,自增列记录的值和预期不一致,从而导致数据冲突。这种情况可能导致在某些历史数据归档或者复制环境中发生数据冲突。
#创建测试表
mysql> create table emp_auto_incre(
-> id int(11) not null AUTO_INCREMENT,
-> name varchar(10) default null,
-> primary key(id)
-> )engine=InnoDB;
Query OK, 0 rows affected (0.31 sec)
#修改 AUTO_INCREMENT=20
mysql> alter table emp_auto_incre AUTO_INCREMENT=20;
Query OK, 0 rows affected (0.00 sec)
#插入数据验证
mysql> insert into emp_auto_incre values(null,'abc');
Query OK, 1 row affected (0.00 sec)
mysql> select * from emp_auto_incre;
+----+------+
| id | name |
+----+------+
| 20 | abc |
+----+------+
1 row in set (0.00 sec)
mysql> alter table emp_auto_incre AUTO_INCREMENT=100;
Query OK, 0 rows affected (0.00 sec)
#查看表结构
mysql> show create table emp_auto_incre \G;
*************************** 1. row ***************************
Table: emp_auto_incre
Create Table: CREATE TABLE `emp_auto_incre` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
ERROR:
No query specified
mysql>
#重启 MySQL,再次查看表结构
mysql> show create table emp_auto_incre \G;
*************************** 1. row ***************************
Table: emp_auto_incre
Create Table: CREATE TABLE `emp_auto_incre` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
ERROR:
No query specified
#插入数据验证
mysql> insert into emp_auto_incre values(null,'zh');
Query OK, 1 row affected (0.00 sec)
mysql> select * from emp_auto_incre;
+----+------+
| id | name |
+----+------+
| 20 | abc |
| 21 | zh |
+----+------+
2 rows in set (0.00 sec)
对于 InnoDB 表,自动增长列必须被索引。如果是组合索引,必须是组合索引的第一列。
对于 MyISAM 表,自动增长列可以是组合索引的其他列。
2、外键约束
MySQL 支持外键的常用存储引擎只有 InnoDB,子表在创建外键的时候,要求父表必须有对应的索引,子表在创建外键的时候也会自动创建对应的索引。
子表在创建外键时,可以指定在删除、更新、父表时,对子表进行相应的操作。
-
RESTRICT:表示限制在子表有关联记录的情况下父表不能更新。(如果未指定on delete或on update的动作,则on delete或on update的默认动作就为restrict)
-
CASCADE:表示父表在更新或者删除时,更新或者删除子表对应记录。
-
SET NULL:表示父表在更新或者删除时,子表的对应字段被 SET NULL。
-
NO ACTION:同 RESTRICT,表示限制在子表有关联记录的情况下父表不能更新。
#创建父表
mysql> create table emp_for_fa(
-> id int(10) not null AUTO_INCREMENT,
-> name varchar(10),
-> primary key(id)
-> )engine=InnoDB;
Query OK, 0 rows affected (0.30 sec)
#创建子表方式一
mysql> create table emp_for_son1(
-> id int(10) not null AUTO_INCREMENT,
-> name varchar(10),
-> foreign key (id) references emp_for_fa(id) on delete cascade on update cascade
-> )engine=InnoDB;
Query OK, 0 rows affected (0.02 sec)
#创建子表方式二
mysql> create table emp_for_son2(
-> id int(10) not null AUTO_INCREMENT,
-> name varchar(10),
-> key idx_fk_id(id),
-> constraint idx_fk_id foreign key (id) references emp_for_fa(id) on update cascade on delete NO ACTION
-> )engine=InnoDB;
Query OK, 0 rows affected (0.01 sec)
子表外键指定的 on update cascade on delete NO ACTION 方式,那么在主表删除记录时,如果子表有对应的记录,则不允许删除;主表在更新记录时,如果子表有对应的记录,则子表对应更新。
#在表存在的情况下创建外键
-- 为表添加外键,并指定外键名
alter table test.emp_for_son1 add constraint idx_fk_id foreign key (id) references emp_for_fa(id);
-- 为表添加外键,不指定外键名,由数据库自动生成外键名
alter table test.emp_for_son1 add foreign key (id) references emp_for_fa(id);
#关闭外键约束
set foreign_key_checks=0;
#打开外键约束
set foreign_key_checks=1;
注意:1、当某个表被其他表创建了外键参照,那么该表的对应索引或者主键禁止被删除。
2、外键一旦使用不当,可能带来性能下降或者数据不一致问题,在 OLTP 类型的应用中,需要谨慎使用外键。
3、主键和索引
1) InnoDB 的数据文件本身就是一聚簇索引的形式保存的,这个聚簇索引也被称为主索引,并且也是 InnoDB 表的主键, InnoDB 表的每行数据都保存在主索引的叶子节点上。因此,所有 InnoDB 表都必须包含主键,如果创建表时候,没有显示指定主键,那么 InnoDB 存储引擎会自动创建一个长度为 6 个字节的 long 类型隐藏字段作为主键。
2) 在 InnoDB 表上,除了主键之外的其他索引都叫作辅助索引或者二级索引,二级索引会指向主索引,并通过主索引获取最终数据。因此,主键是否合理的创建,会对所有索引的效率产生影响。
3) 主键应该按照以下原则来选择:
①满足唯一和非空约束
②优先考虑使用最频繁被当作查询条件的字段或者自增字段
③字段值基本不会被修改
④使用尽可能短的字段
4、存储方式
1) 使用共享表空间存储:这种方式创建的表的表结构保存在 .frm 文件中,数据和索引保存在 innodb_data_home_dir 和 innodb_data_file_path 定义的表空间中,可以是多个文件。
2) 使用多表空间存储:表结构保存在 .frm 文件中,表的数据和索引单独保存字 .ibd 中。多表空间的数据文件没有大小限制。
注意:在多表空间的存储方式下,共享表空间仍然是必须的,InnoDB 把内部数据词典和在线重做日志放在这个文件中。
#将表从共享表空间改为多表空间
set global innodb_file_per_table=1;
alter table tabname engine=InnoDB;
三、MEMORY
MEMORY 存储引擎将表中的数据存储到内存中,每个 MEMORY 表实际对应一个磁盘文件 .frm。每个 MEMORY 表中可以放置的数据量的大小,受到 max_heap_table_size 系统变量的约束,初始值为 16MB,当内存表中的数据大于max_heap_table_size设定的容量大小时,MySQL 会转换超出的数据存储到磁盘上,因此这是性能就大打折扣了 。在定义表的时候还可以使用 MAX_ROWS 子句指定表的最大行数。
当不再需要 MEMORY 表的内容时,要释放被MEMORY表使用的内存,应该执行DELETE FROM或TRUNCATE TABLE,或者删除整个表(使用DROP TABLE)
MEMORY 类型的存储引擎主要用于内容变化不频繁的代码表,或者作为统计操作的中间结果表。
#创建内存表
create table emp_mem(a int(10),b int(10)) ENGINE=MEMORY MAX_ROWS=1000;
#给内存表创建索引时,可以指定使用 HASH 索引还是 BTREE 索引
create index emp_mem_hash using hash on emp_mem(a);
#删除索引
drop index emp_mem_hash on emp_mem;
create index emp_mem_btree using btree on emp_mem(a);
四、MERGE
MERGE 存储引擎也被称为 MRG_MyISAM,是一组 MyISAM 表的组合。这些 MyISAM 表结构必须完全相同,MERGE 表本身没有数据,对 MERGE 类型的表进行查询、更新、删除操作实际上是对内部的 MyISAM 表进行的。对于 MERGE 类型表的插入操作,是通过 INSERT_METHOD 子句定义插入的表,可以有 3 个不同的值。
1) FIRST : 插入操作作用在第一个表上
2) LAST : 插入操作作用在最后一个表上
3) NO 或空 : 表示不能对这个 MERGE 表执行插入操作
对 MERGE 表进行 DROP 操作,只是删除 MERGE 的定义,对内部的表没有任何影响。
MERGE 表在磁盘上保留两个文件,文件名以表的名字开始,一个 .frm 文件存储表定义,一个 .MRG 文件包含组合表的信息,包括 MERGE 表有哪些表组成、插入新的数据时的依据。可以同过修改 .MGR 文件来修改 MERGE 表,但是修改后要通过 FLUSH TABLES 刷新。
#创建测试表
mysql> create table emp_merge_1(a int(10),b int(10)) ENGINE=myisam;
Query OK, 0 rows affected (0.01 sec)
mysql> create table emp_merge_2(a int(10),b int(10)) ENGINE=myisam;
Query OK, 0 rows affected (0.05 sec)
mysql> create table emp_merge_all(
-> a int(10),
-> b int(10)
-> )engine=merge union=(emp_merge_1,emp_merge_2) INSERT_METHOD=LAST;
Query OK, 0 rows affected (0.01 sec)
#插入数据
mysql> insert into emp_merge_1 values(1,1),(1,2),(1,3);
Query OK, 3 rows affected (0.00 sec)
mysql> insert into emp_merge_2 values(2,1),(2,2),(2,3);
Query OK, 3 rows affected (0.00 sec)
#查看三个表的记录
mysql> select * from emp_merge_1;
+------+------+
| a | b |
+------+------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
+------+------+
3 rows in set (0.01 sec)
mysql> select * from emp_merge_2;
+------+------+
| a | b |
+------+------+
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
+------+------+
3 rows in set (0.00 sec)
mysql> select * from emp_merge_all;
+------+------+
| a | b |
+------+------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
+------+------+
6 rows in set (0.00 sec)
#向 merge 表中插入一条记录,由于 INSERT_METHOD=LAST ,所以向最后一个表中插入记录。
mysql> insert into emp_merge_all values(999,999);
Query OK, 1 row affected (0.01 sec)
mysql> select * from emp_merge_all;
+------+------+
| a | b |
+------+------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 999 | 999 |
+------+------+
7 rows in set (0.00 sec)
mysql> select * from emp_merge_1;
+------+------+
| a | b |
+------+------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
+------+------+
3 rows in set (0.00 sec)
mysql> select * from emp_merge_2;
+------+------+
| a | b |
+------+------+
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 999 | 999 |
+------+------+
4 rows in set (0.00 sec)
五、TokuDB
TokuDB 是一个高性能、支持事务处理的存储引擎,有着出色的数据压缩功能。
参考链接:https://www.biaodianfu.com/tokudb.html
TokuDB 特别适用以下几种场景
1) 日志数据,因为日志通常插入频繁且存储量大
2) 历史数据,通常不会再有写操作,可以利用 TokuDB 的高压缩特性进行存储
3) 在线 DDL 叫频繁的场景,适用 TokuDB 可以大大增加系统的可用性。