mysql就是一个应用,和你电脑上的任何应用一样,没有特别之处。只不过它的作用是存储和检索数据,原始数据存在磁盘文件上,利用一些硬件和软件技术来提升处理效率,仅此而已。
mysql中有很多存储引擎,而一般开发者用到的都是面向OLTP事务的innodb。在工作中,我们经常会遇到大表优化、慢查询等问题,这些问题并不是innodb本身的性能瓶颈,绝大部分原因都是由于开发人员对innodb的理解不到位,从而在使用层面引发的那些问题。
下面我们从内部实现原理入手,进而一步步的揭示innodb应该怎么使用。
逻辑存储
innodb的所有数据都被放在一个逻辑存储结构中,称作表空间(tablespace)。逻辑存储结构如下:
表空间由段(segment)、区(extent)、页(page)、行(row)组成。
段
常使用的段有数据段、索引段、回滚段。
-
数据段:B+数的叶子节点
-
索引段:B+数的非叶子节点
-
回滚段:
-
可以存在ibdata中
-
可以存在独立undo表空间中
-
可以存在ibtmp临时表空间中
-
innodb最多存在128个回滚段
-
每个回滚段需要单独的page维护其拥有的undo slot
-
区
在任何情况下,每个区的大小都是1MB,由连续的表空间组成。为了保证区中页的连续性,innodb每次从磁盘申请4-5个区。默认情况下,innodb一个页的大小为16K,所以,一个区中有64个连续的页。
页
页是innodb管理的最小单位,默认大小16K,可以通过innodb_page_size进行设置,但是设置后所有表中的页大小都变成innodb_page_size,不能再次修改。
常见的页类型如下:
-
数据页(B-tree Node)
-
undo页(undo log page)
-
系统页(system page)
-
事务数据页(transaction system page )
-
插入缓冲位图页(insert buffer bitmap)
-
插入缓冲空闲列表页(insert buffer free list)
-
未压缩的二进制大对象页(uncompressed BLOB page)
-
压缩的二进制大对象页(compressed BLOB page)
数据页结构如下:
由7部分组成:
-
File Header:头文件
-
Page Header:页头
-
Infimun + Supremum Records:用来限定记录边界
-
User Records:行记录
-
Free Space:空闲空间
-
Page Directory:页目录
-
File Trailer:文件尾信息
Infimun和Supremum Records
用来限定记录边界,Infimun记录比该页中任何主键都小,Supremum比任何该页中任何主键的值都大,这两个值在任何情况下都不会被删除,示意图如下:
Page Directory
存放记录的相对位置,存放这些记录指针的结构叫做槽(slot),需要知道的是,一个槽中记录了多条记录,所以innodb的查找过程是这样的,先根据B+树索引找到目标所在的页,将页加载到内存中,根据page directory进行二分查找,找到目标key所在的slot,再进行二分查找,进而找到目标。内存中二分查找时间可以忽略不计,耗时的主要操作是将页从磁盘读取到内存的过程。
行
innodb中数据的实际都是按照行的格式存储的,行也严格的格式,每个页能存放的行的数量也是有严格的规定,最多可以存放16K / 2 - 200 即7992行。先说一个结论:页中放的行越多,innodb性能越高。所以在mysql 5.0中引入了compact行记录格式。
compact行记录格式
目标就是为了高效的存储数据,比redundant存储空间减少20%
-
变长字段长度列表:变长列中存储多少字节数据是不固定的,所以在存储数据时候也需要把这些数据占用的字节数存储起来。varchar(M) M代表的是存储多少字符(mysql5.0.3之前是字节,之后是字符)。
-
NULL标志位:bit向量标识的null列
-
记录头信息:固定占用5个字节
-
隐藏列:每行数据除了用户定义的列之外还有可能三种隐藏列
-
事务id列:占用6个字节,标识当前列的事务id
-
回滚指针列:占用7个字节
-
行id:如果该表没有指定主键的话,会有占用6字节的行id列
-
综上,无论是char类型还是varchar类型,null值都不占用任何存储空间。
redundant行记录格式
mysql5.0之前的存储格式,为了是兼容之前版本的格式页。
和compact格式差不多,记录头信息占用6个字节,细节不再讨论。对于varchar类型的NULL值不占用空间,但是对于char类型的NULL值占用存储空间,占用空间的大小取决于所使用的字符集。
dynamic行记录格式
基于compact格式,提高存储容量,支持大索引(3071字节),用innodb_large_prefix
控制。
compressed行记录格式
基于dynamic格式,支持表和索引数据压缩。
行格式汇总
在 msyql 5.7.9 及以后版本,默认行格式由innodb_default_row_format
变量决定,它的默认值是DYNAMIC
行溢出数据
一个页中至少要放两行数据,否则B+树就成了链表,所以如果行数据超出一定长度,innodb就会将行数据放到溢出页中。
compact和redundant
如果列数据大于768字节,数据页只保存前768字节数据前缀,剩余数据保存于溢出页。
dynamic和compressed
数据页中只存放20字节的Off Page指针,实际数据都在溢出页。
物理存储
以上是innodb的逻辑存储结构,真实数据实际存储在外存储器中,分为机械磁盘和固态硬盘。
机械磁盘
磁盘构造如下:
盘面构造如下:
读写数据所花费的时间:
1、寻道时间:启动磁臂的时间+常数*所需移动的磁道数
2、旋转延迟:把扇区移动到磁头下面的时间
3、传输时间:读写的字节数/每秒转速*每扇区的字节数
当前服务器机械硬盘的寻道时间已经能够达到3ms,转速为15000RPM(rotate per minute),传统机械硬盘最大的问题在于读写磁头,读写磁头的设计使硬盘可以不再像磁带一样,只能进行顺序访问,而是可以随机访问。但是,机械硬盘的访问需要耗费长时间的磁头旋转和定位来查找,因此顺序访问的速度要远高于随机访问。传统关系数据库的很多设计也都是在尽量充分地利用顺序访问的特性。目前一次机械磁盘io用时大约0.02-0.04秒。
固态硬盘
固态硬盘不用磁头,可以做到随机读取,寻道时间几乎为0。所以,innodb_flush_neighbors
(innodb刷新邻接表的控制)在使用固态硬盘时候可以关闭。
但是固态硬盘提供的读写速度不是对称的,读取速度远远大于写入速度,所以如果使用固态硬盘做存储,尽量发挥其快速读取的能力,而避免过多的写入操作。
本文部分图片来自网络,侵权必删!若有不恰当之处,还望指教,谢谢