您好,欢迎访问三七文档
当前位置:首页 > 临时分类 > mysql存储引擎innodb底层存储结构
Mysqlinnodb底层存储目录Contents01Part.02Part.04Part.03Part.扇区、磁盘块和数据页B+树存储结构索引组织表数据页结构和行记录扇区、磁盘块和数据页01Part.扇区扇区磁盘数据的最小单元,大小为512字节,在物理层面上是真实存在的;扇区的划分可以让硬盘更好的管理空间。磁盘块由于扇区太小,文件系统按照扇区去读取数据会相当慢,所以有了block(磁盘块)的概念。磁盘块由1个或者连续几个(2^n)相邻的扇区组成,是操作系统与磁盘交互的最小单位,操作系统可以把2^n个扇区数据一次性加载到内存进行快速读取。一个块大小=一个扇区大小*2的n次方,n是可以修改的,由操作系统决定n的大小;数据页数据页=2^n磁盘块,数据页是Mysql操作的最小单位,Innodb里面一般为16KB,lKB=1024字节=2个扇区,不过也可能是8KB、32KB或者其他值,这跟MySQL的存储引擎对数据的存储方式有很大的关系,innodb存储引擎页大小为16KB,16x1024=16384字节。可以存储英文字符16384个,中文8192个。innodb存储引擎页大小为16KB,16x1024=16384字节。可以存储英文字符16384个,中文8192个。拿常见的表举例,一般一行数据占用字节数在100~300个字节。一个数据页在剔除100个字节(存储其它信息)之后,(16384-100)/300=54.28行数据。考虑大多业务场景下(除开日志记录、大字段记录等),y一个数据页正常存储的行数量在16~60之间。表空间、段、区表空间:Innodb存储引擎按照表空间方式对数据进行存放,默认情况下,innodb建立共享的表空间文件,用于存储表所有的有关存储数据,也允许启动innodb_file_per_table为每个表设立自身的表空间,表的数据、索引和缓存BitMap页都放在自身表的表空间文件,但其它信息,如回滚信息、缓冲索引页还是放置在共享表空间。段:多个数据页的集合,innodb的预读比较倾向于对段范围进行预读;区:由连续的页组成,区的大小为1MB,innodb存储引擎会一次预先申请4~5个区;B+树数据结构02Part.B+树B+树是一种树数据结构,通常用于数据库和操作系统的文件系统中。B+树的特点是能够保持数据稳定有序,其插入与修改拥有较稳定的对数时间复杂度。B+树元素自底向上插入,这与二叉树恰好相反。查询数据拥有稳定的对数时间复杂;能更精确的进行范围查询;B+树由B树(自平衡树)演进而来。但由于B树的节点既存储路由(索引),也存储数据,所以相对于B树,B+树更适合范围查询。为什么用B+树而不是B树B+演进同一层级节点按大小顺序排序;节点数满则进行分裂;索引组织表03Part.Innodb索引组织表的数据插入Innodb表本质上是索引组织表(区别于堆表),以索引为驱动指引和操纵数据。换句话说,innodb就是按照B+树的方式去放置数据。对表插入数据,表数据在数据页中按照链表方式进行逻辑顺序排放,数据页空间满则进行依照B+树原则进行数据页分裂,索引键值自下而上成为中间节点或根节点。索引组织表Innodb表本质上是索引组织表(区别于堆表),表数据都按照主键进行顺序存放(逻辑顺序)。在innodb存储引擎表中,每个表都必须要有一个主键,如果没有显式的声明主键,则会按照以下方式进行创建:如果表存在非空唯一索引,如果存在,则使用该列作为主键;如果不存在非空唯一索引,Innodb存储引擎会自动创建一个6字节的指针作为主键,该主键对用户不可见;堆表:区别于索引组织表的顺序插入,堆表的数据在插入时是随机插入,因此从效率上,堆表的插入效率更高,因为省去了插入位置的判断;但索引组织表的查询效率更好,特别是范围查询。Innodb的B+树在实际业务情况下能存储多少的数据量?一个数据页大小为16K,16x1024=16384。索引页中,主要存储了键值和指针,键值以bigint为例,占8个字节,指针占6个字节,则8+6=14,能存储的键值和指针的组合数位16384/14=1170.28571。叶子节点存储着行记录,假如行记录以1k为例,1k已经能满足绝大部分业务场景。那么一页能存储的行数量为16k/1k=16。以一颗三层B+树为例,则可以计算:1171x1171x16=21,939,856。若以一颗四层B+树为例,则可以计算:1171x1171x1171x16=(200亿)。在日常使用过程中,三层的B+树能满足绝大多数的场景。聚集索引和非聚集索引聚集索引聚集索引就是按照表数据的主键构建一颗B+树,叶子节点既存放了表的行记录。因此,表数据也是索引的一部分。一个表通常只有一个聚集索引(既主键索引)。非聚集索引除开聚集索引之外的其它索引,叶子节点除开存放索引列外,还存放着寻找目标行记录的指示信息,mysql需根据指示信息进行二次查找才能找到行记录。包括我们额外创建的普通索引、唯一索引、联合索引。非聚集索引Innodb:标签存储着聚集索引的键值;MyISAM:标签存储着指向行数据的地址,如:(文件号、页号、槽号)。行记录物理地址改变时,索引需要跟着维护Innodb根据非聚集索引列找寻行数据,需在非聚集索引上找到标签,再根据标签找寻聚集索引,通过树查找定位到行数据。例如:右边非聚集索引为3层,聚集索引树也为3层,则需要经过6次IO(抛开预读)才能定位到行数据。在Innodb查询优化器中,会优先考虑聚集索引。数据页和行记录04Part.行记录行记录的逻辑存储顺序行数2行数1行数3行结构包含了下一行的相对位置。在一个数据页里,行记录的存储顺序并非是物理顺序,而是基于链表的逻辑顺序。但由于mysql在查找行数据时,把整个页都加载在内存中,所以即便是链表查找,依然有非常高的效率。另外,针对具体的目标数据,innodb还能使用页目录(槽)缩小查找范围,在相对较小的范围里再使用链表进行定位目标数据。随机读取时则无法使用页目录,只能进行随机性的链表遍历。理想的顺序实际的顺序单数据页单数据页1)新建表2)插入数据行记录内容举例数据从c078开始隐藏列事务id下条记录的偏移量Char剩余字节使用cx20(空格来填充)00000000额外分配6字节主键插入第二行数据非空标识,06转换成二进制为00000110,倒序第2、3位为1,说明第2、3列空值;问题1:变长列表为什么要倒序?顺序排列无法区分当列长度超过255,譬如511,换成16进制为01ff,那么当mysql读取到两个字节时,无法区分是两个列的长度(1字节和255字节)或者是一个列255字节。但是逆序就没问题,假如要表达255,则可以使用ff00,表达511使用ff01,无论如何,遇到ff则默认带上后面字节。(个人猜测,仅供参考)问题2:非空标识只占一个字节,能表示8个列的非空情况,那么第9个列怎么表示?非空标识的8位字节并不是固定的,每超过8位,innodb都会往非空标识的左边增加一个字节,并且表结构会把canbenull字段的个数记录下来,作为辅助作用来区分字节的非空状态。问题3:varchar存储时,只使用了实际存储内容的空间,那为什么不能定的长点呢?Mysql在进行排序或者多维计算时,会在根据表定义申请固定空间来创建内存临时表,varchar过长容易浪费计算内存。所以实际业务建议只申请真正需要的空间。256+255=511行溢出•当varchar数据内容过长时,innodb只会存储varchar内容的前缀部分,剩余数据会存储到溢出页。类型截取长度varchar截取768字节的前缀BLOB截取768字节的前缀text未知,预估跟varchar一样,大多文档中提示text可以当做是大的varchar行溢出会增加查询的IO,在实际业务中,应避免行溢出的设计。行删除在innodb存储引擎中,使用Deletefromtable删除数据并不能真正的回收空间,(MyISAM会释放)只是被删除的行空间能够被复用,但空间并不能被回收。要想回收空间,可以通过:drop/truncatetable;OPTIMEZE释放表空间,但该命令会锁住整表。日常在表主键自增的场景下,建议使用逻辑删除。数据页数据页-双向链表头信息包含了上一页和下一页的偏移量,说明innodb的叶子衔接是双向链表。双向驱动,能更快速的定位数据;满足范围查询的正序和倒序。双向链表数据页查询优化-槽Innodb的槽是一个键值目录,能优化单页里目标记录的查询速度。当innodb要找寻目标行记录,把页从磁盘加载到内存,如果没有其它的辅助信息,那么只能随机的根据链表元素的下一个指向来遍历数据。这里的槽就是用于优化单页中目标数据的查询,innodb根据槽内容使用二分查询,能大大缩小查询的范围。为什么说是大大缩小,因为这里的槽是一个稀疏目录,能让innodb把数据的查询定位到相对较小的范围,而不是定位到具体的行数据,仍需按照下一个指向的原则进行遍历查找。假如,单页里有2000行数据,从1~2000,那么槽目录可能是【1,500,1000,1500】,使用该槽目录,一下子使搜索范围缩小了3倍。当数据变更的时候,需要对该槽进行维护。页分裂数据从页中间分裂的弊端:容易造成空间的浪费,每次分裂都造成了单页有一半的物理空间被浪费,除非主键的生成方式为随机生成,这样空闲的空间还有可能被使用,如果主键的生成方式为自增,空闲的空间会造成浪费。分裂造成单页有一半的数据都迁移到别的数据页,这一半数据均会发生物理地址的改变,加大服务器的运行压力。页分裂PageHeader里面存在三个方便页分裂的属性:•PAGF_LAST_INSERT最后插入记录的位置•PAGE_DERECTION最后插入的方向•PAGE_N_DERECTION一个方向连续插入数据的数量1.若插入是随机的(单方向插入不足5),则直接取中间数据;2.若往一个方向连续插入的数据数量是5,则取定位数据的后面第三个作为分裂点,右边部分移到右新数据页,若向右数据不足三个,则插入数据就是分裂数据;页分裂在实际业务中,建议使用auto_increment,innodb对该属性有“特殊照顾”。从空间利用率和插入效率上比较:auto_increment日期类的自增主键uuid类的主键Thanks.
本文标题:mysql存储引擎innodb底层存储结构
链接地址:https://www.777doc.com/doc-8532335 .html