MySQL-存储引擎及InnoDB详解
MySQL存储引擎
一个库中可以有多个不同存储引擎的表。
InnoDB和MyISAM数据库的表的结构定义信息都存储在frm文件中。
InnoDB的数据和索引存储在ibd文件中(聚集),MyISAM索引存在MYI文件中,数据存储在MYD文件中(非聚集)。
Innodb
若一个主键被定义,该主键则作为密集索引;
若没有主键被定义,该表的第一个唯一非空索引则作为密集索引;
若不满足以上条件,innoDB内部会生成一个隐藏主键(rowid,密集索引,该主键是一个六字节的列,该列的值随着记录的插入而自增)。
即innoDB必须得有一个主键作为密集索引存在,innodb引擎里的主键一般都是聚集索引;
非主键索引都是非聚集索引,非主键索引存储相关键位和其对应的主键值,要进行两次查找(先通过非主键索引去查找主键索引,再通过主键索引去查找数据的物理地址)。
InnoDB表的行锁也不是绝对的,假如在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如updatetable set num=1 where name like “a%”,就是说在不确定的范围时,InnoDB还是会锁表的。
InnoDB的行锁,只是在WHERE的主键是有效的,非主键的WHERE都会锁全表的
MyISAM
MyISAM索引结构: MyISAM索引用的B+ tree来储存数据,MyISAM索引的指针指向的是键值的地址,地址存储的是数据。
B+Tree的数据域存储的内容为实际数据的地址,也就是说它的索引和实际的数据是分开的,只不过是用索引指向了实际的数据,这种索引就是所谓的非聚集索引。
主键索引和其他索引没有任何区别, 都是稀疏索引, 表数据存储在独立的地方MYD, 表数据MYD和索引MYI的分开的, 索引用地址指向表数据。
两者的对比:
- 是否支持行级锁;
- 是否支持事务和崩溃后的安全恢复
MyISAM强调的是性能,每次查询具有原子性,其执行速度比InnoDB类型更快,但是不提供事务支持。
InnoDB提供事务支持、外部键等高级数据库功能。 - 是否支持外键;
- 是否支持MVCC
仅InnoDB支持。
应对高并发事务,MVCC比单纯的加锁更高效;
MVCC只在READ COMMITTED
和REPEATABLE READ
两个隔离级别下工作;
MVCC可以使用乐观锁和悲观锁来实现;
各数据库中MVCC实现并不统一。
1 | 不要轻易相信“MyISAM比InnoDB快”之类的经验之谈,这个结论往往不是绝对的。在很多我们已知场景中,InnoDB的速度可以让MyISAM望尘莫及,尤其是用到了聚簇索引,或者需要访问的数据都可以放入内存的应用。 |
MySQL InnoDB存储引擎体系结构
InnoDB后台线程
InnoDB存储引擎是多线程的模型,有多个后台线程去负责处理不同的任务。
后台线程的主要作用是负责刷新内存池中的数据,保证缓冲池中缓存的是最近的数据。此外将已修改的数据文件刷新到磁盘文件,同时保证在数据库发生异常的情况下InnoDB能恢复到正常运行状态。
- Master Thread
该线程是一个非常核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘中,保证数据的一致性。包括脏页的刷新、合并插入缓冲、UNDO页回收等。- 脏页
由于InnoDB在进行表数据修改的时候,并不是直接修改磁盘上的数据文件,而是将磁盘上的页缓存到缓冲池中进行修改,之后再根据一定的机制和频率刷新回磁盘,这种还没有刷新回磁盘但是已经被修改的页称为脏页。 - 合并插入缓冲
当要插入非聚集索引的时候,将其放在缓冲内存中保存,根据一定的机制和频率将要插入的非聚集索引一起刷新到索引页中。
- 脏页
- IO Thread
在InnoDB存储引擎中使用了大量的AIO(Async IO)来处理写IO请求,这样可以极大的提高数据库的性能,而IO Thread的工作主要是负责这些IO请求的回调处理。 - Purge Thread
Purge Thread主要的作用是回收已经使用并分配的undo页。
事务被提交后,其所使用的undo log可能不再需要,因此需要Purge Thread来回收已经使用并分配的undo页。 - Page Cleaner Thread
作用就是刷新脏页,这个线程是1.2.x版本中引入的,将刷新脏页的操作放入单独的线程中完成,减轻原Master Thread的工作量,提高存储引擎的性能。
InnoDB内存结构
缓冲池
InnoDB可以有多个缓冲池实例(innodb_buffer_pool_instances
默认为1,最大为64),每个页根据哈希值平均分配到不同缓冲池实例中。
- 数据页
- 索引页
- 插入缓冲
- 自适应哈希索引
- 锁信息
- 数据字典信息
重做日志缓冲
InnoDB先将重做日志信息放到这个缓冲区,然后按一定频率刷新到重做日志文件。
一般每秒都会将缓冲刷新到日志,所以重做日志缓冲不需要很大。
额外的内存池
每个缓冲池中的帧缓冲(frame buffer)以及对应的缓冲控制对象,它们的内存需要从额外内存池中获取。