MySQL最与众不同的就是它的插件式存储引擎。这种架构将数据库查询处理、其他系统任务以及数据的存储提取相分离。这种数据存储和数据提取相分离的设计可以让我们使用时可以根据不同存储引擎的性能和特征选择数据存储的方式。要真正弄清楚这种设计的优缺点,我们先要了解MySQL的体系结构。
客户端:客户端是这个体系的最上面一层,这一层代表了各种可以通过MySQL连接协议连接到MySQL的客户端,比如:PHP、Java、C、.NET、ODBC、JDBC等。这一层并不是MySQL体系结构所特有的。大多数C/S设计都是采用这样一种体系结构。这一层主要是连接处理、授权认证、安全等方面的功能。每个连接到服务器的客户端都会在服务器上有一个线程。这个连接查询只会在这个单独线程中执行。这就是之前说的,每一个查询都只会用到一个CPU的核心。
MySQL服务层:大多数的MySQL核心服务都在这一层。这一层包括了:连接管理器、缓存查询器、查询解析器、查询优化器以及一系列的缓存,内置函数和SQL接口。所有跨存储引擎的功能都是在这一层实现的。在这一层实现了所有与存储引擎无关的特性。什么是与存储引擎无关的特性呢?举个栗子:比如说select语句,这个语句对于所有存储引擎来说,所实现的功能全都是一样的。获取存储在存储文件中的数据,并根据我们的过滤条件进行过滤,然后把数据显示出来。select功能就是在MySQL服务层实现的。而至于如何从存储文件中获取查询的数据,这个具体的实现方式则是由下一层存储引擎实现。
存储引擎层:MySQL同其他数据库区别最大的地方,就是存储引擎层。MySQL是一款非常优秀的开源数据库。其中定义了一系列的存储引擎接口,只要符合存储引擎接口的要求,我们就可以为MySQL开发出一款完全符合自己需要的存储引擎。比如我们现在比较常用的Innodb存储引擎,最初就是由第三方公司所开发的一款存储引擎。MySQL存储引擎有很多,比如:Innodb、myisam、XtraDB、CVS、Memory、MRG_MYISAM、archive等等。我们不难看出,这种插件是存储引擎的最大特点就是灵活。我们可以根据不同的应用特点选择不同的存储引擎。存储引擎是针对于表的,而不是针对于库的。
MyISAM存储引擎是MySQL5.5之前默认的存储引擎。MyISAM同时也是大部分MySQL系统表和临时表所使用的存储引擎。这里的临时 表不是通过create table 语句创建的临时表。使用create table创建的临时表我们可以使用任何存储引擎。这里的临时表是指:在排序、分组等操作中,当数量超过一定大小后,由查询优化器建立的临时表。
MyISAM存储引擎表由MYD和MYI组成。MyISAM存储引擎会将表存储在两个系统文件中,一个是数据文件,以MYD为扩展名。另一个是索引文件,以MYI为扩展名。下面这个表就是以MyISAM存储引擎存储的。可以看到对应的有三个文件,MYD和MYI文件是上面说到的。还有一个frm文件,这个文件不是MyISAM存储引擎所特有的一个文件,对于MySQL所有的存储引擎来说,都会有一个frm文件。这个文件适用于记录表的结构的。
并发性与锁级别
MyISAM使用的是表级锁而不是行级锁。这就意味着当对于表中数据进行修改时,就需要对整个表进行加锁。而对表中的数据进行读取时也要加共享锁。使用MyISAM的时候,读取和写入是互斥的。当然,在一些情况下,我们进行读取的时候也可以在末尾插入一些数据。从上面可以看出,MyISAM对于读写混合的并发性不是很好。如果是只读的情况下,并发性还是可以的。因为共享锁不会阻塞共享锁。
表损坏修复
MyISAM支持对由于任意意外关闭而损坏的MyISAM表进行检查和修复操作。在这里所说的修复操作并不是事务恢复。MyISAM并不是一种事务型的存储引擎。对于MyISAM表进行数据恢复可能会有些数据的丢失。
我们可以通过 check table tablename
命令对表进行检查,发现问题后我们可以使用 repair table tablename
命令对表进行修复
MyISAM表支持的索引类型
MyISAM表支持全文索引,并且是在MySQL5.7之前唯一原生就支持全文索引的官方的存储引擎。另外,MyISAM表还支持对TEXT或者BLOB等字段建立前500个字符的这种前缀索引。
MyISAM表支持数据压缩
如果MyISAM表是一张很大的只读表的话,就是说我们创建完导入数据后就不进行写入操作,只进行读取操作。我们可以进行压缩操作,这样可以减少磁盘的I/O。MyISAM表也是支持数据压缩的。
我们可以使用 myisampack
命令来压缩表中的数据。由于表中的数据是独立进行压缩的,所以在读取单行数据时,不比对整个表进行解压。
在MySQL5.0之前版本MyISAM表单表最大文件为4G。如果想要存储 超过4G,需要修改 MAX_Rows和 AVG_ROW_LENGTH这两个参数。这两个参数的乘积就是表能达到的最大的大小。对于大表来讲,修改这两个参数会导致表的重建,会需要一些时间。
MySQL5.0之后的版本默认支持为256TB,这已经足够我们所使用了。
MySQL5.5之后,Innodb成为了默认的存储引擎。Innodb是支持事务的存储引擎,另外Innodb使用表空间进行数据存储。
Innodb有自己的表空间的概念,表中的数据是存储在表空间之中的,具体存储在什么样的表空间之中,由 innodb_file_per_table
这个参数决定。如果这个参数为 ON,则会为每个Innodb表建一个ibd为后缀的文件 tablename.ibd 。如果这个参数为OFF时,则会把数据存储到系统的共享表空间,也就是 ibdataX 中,这个X是指的一个数字,它是从1开始的一个数字。
系统表空间无法简单的收缩文件大小
如果使用系统表空间会发现一些问题,在一个繁忙的体统中,我们会发现系统表空间会不断的增长。但是一旦我们的磁盘空间不足,我们为了释放磁盘空间,不得不在系统中删除大量的无效的数据,我们删除了数据后系统表空间并不会缩小。另外我们通过复制文件的方式对数据库进行备份,由于资源 删除了数据,但表空间的文件大小并不会改变。这就意味着每次删除都会浪费很大的空间。
而想要收缩系统表空间的唯一方法,就是把系统表空间所有的Innodb表导出后,删除Innodb相关的文件,再重启MySQL服务器,进行表空间的重建,然后再导入数据。这个过程也是很复杂的,也相当耗时。
使用系统表空间带来的问题就是无法很容易的收缩系统文件,造成大量的空间浪费,并且产生大量的磁盘碎片。
使用独立表空间可以通过optimize table 命令收缩系统文件
使用独立表空间就很容易解决上面的问题。我们对一个大表进行清理之后,可以很方便的对这一个表进行optimize table 操作,这实际上也会对这个表进行重构,但是对于整个系统重建要快的多。而且不需要重启服务器,不会影响到数据的访问。从这一点来看,使用独立表空间比系统表空间要好的多
完全支持事务的ACID特性(原子性、一致性、隔离性、持久性),实现的方式是由Redo Log和Undo Log 实现。
行级锁在写操作时所用的资源更少,行级锁可以最大程度的支持并发。
行级锁是在存储引擎层面实现的
锁是数据库系统区别于文件系统的一个重要特性。锁的主要作用是管理共享资源的并发访问。并发访问一直都是让人头疼的问题,对于任何串行环境下运行良好的系统,一旦涉及到并发的情况就会出现各种各样的问题。锁的另一个作用是用于实现事务的隔离性。Innodb通过Redo Log和Undo Log实现了事务的原子性、一致性和持久性,而隔离性就需要锁来实现。对于未提交的事务,锁定的数据是无法被其他事务查询到的。
读锁 | 写锁 | |
---|---|---|
读锁 | 不兼容 | 不兼容 |
写锁 | 不兼容 | 兼容 |
锁的粒度,就是指被加锁资源的最小单位。在行上面加锁,那么锁的粒度就是行,锁就是行级锁。如果是在表上面加锁,那么锁的粒度就是表,这个锁就是表级锁。我们可以通过减小粒度而提高共享资源的并发性。最理想的情况就是对需要修改的数据进行精确的锁定 。任何时候都是锁定的资源越小,并发性就越高,只要相互之间不产生阻塞就可以了。
关于锁还有两个常见的概念,阻塞和死锁。很多人也会混淆这两个概念,这里也进行一下阐述。
阻塞
阻塞是因为不同锁之间的兼容性关系。在有些时刻,一个事务中的锁需要等待另一个锁的释放。这就形成了阻塞。阻塞是为了可以使并发可以正常的运行,但如果一个系统中出现了大量的阻塞,就意味着系统出现了问题。也许是出现慢查询、表备份等耗时操作。大量的阻塞会导致大量连接堆积,浪费系统资源,导致性能的下降。
死锁
死锁是指两个或两个以上的事务在执行过程中,相互占用了对方等待的资源,而产生的一种异常。从定义上看,阻塞只是阻塞的事务占用了被阻塞事务等待的资源,而死锁是多个事务互相占用了对方等待的资源,导致一直不能执行。这就是阻塞和死锁最大的区别。还有一个不同就是,数据库会自动监测出来死锁,并在多个死锁的事务中找一个资源占用最少的事务来进行回滚操作,这样就可以使其他事务正常运行了。死锁是可以由系统自动处理的,如果有少量的死锁,并不会对系统造成什么影响。但是如果一个系统中频繁的出现大量的死锁,这时就需要留意了。
Innodb还有一个特性就是提供了一个独特的性能监视工具。这个工具就是show engin innodb status
命令。
innodb存储引擎在show engine innodb status(老版本对应的是show innodb status)输出中,显示除了大量的内部信息,它输出就是一个单独的字符串,没有行和列,内容分为很多小段,每一段对应innodb存储引擎不同部分的信息,其中有一些信息对于innodb开发者来说非常有用,但是,许多信息,如果你尝试去理解,并且应用到高性能innodb调优的时候,你会发现它们非常有趣,甚至是非常有必要的。
输出内容中包含了一些平均值的统计信息,这些平均值是自上次输出结果生成以来的统计数,因此,如果你正在检查这些值,那就要确保已经等待了至少30s的时间,使两次采样之间的积累足够长的统计时间并多次采样,检查计数器变化从而弄清其行为,并不是所有的输出都会在一个时间点上生成,因而也不是所有的显示出来的平均值会在同一时间间隔里重新再计算。而且,innodb有一个内部复位间隔,而它是不可预知的,各个版本也不一样。
从上面的特点可以看到,Innodb可以适应大多数的OLTP应用场景中。在MySQL5.7之前的版本,Innodb不支持全文索引和空间函数。所以说在MySQL5.7之前,如果想要使用这些内容,比较适合用MyISAM。在5.7之后Innodb也支持这些操作,所以在5.7之后也可以使用Innodb进行存储了。
MyISAM和Innodb是MySQL中最常用的两种存储引擎。另外还有一些特殊功能的存储引擎,在一些场景下会使用到这些存储引擎。比如CSV、Federated、Memory、Archive等。之后再写文章说明