一、InnoDB存储原理
1. 记录存储方式:页
内外存交换的基本单位:Mysql将数据从外存读入内存不是以记录为单位,这样消耗太大,是以页为单位,每个页里填充记录。每页大小为16KB。
- 页头:记录页面的控制信息,包括页的左右兄弟页面指针、页面空间使用情况等。
- 虚记录:
- 最大虚记录:比页内最大主键还大
- 最小虚记录:比页内最小主键还小
- 记录堆:行记录存储区,分为有效记录和已删除记录两种
- 自由空间链表:已删除记录组成的链表
- 未分配空间
- 页尾:存储页面校验信息
2.页内记录维护
页内的数据是按照主键的顺序有序存储的。
(1)顺序保证
页内数据的组织用的是逻辑连续,不需要移动数据。页和页之间用双向指针连接。
(2)插入策略
插入时优先插入自由空间链表,满了再插入未分配空间。
(3)页内查询
由于页内使用逻辑连续,所以类似一个链表。如果要使用遍历那效率太低,这时用到了页内的slot区。
slot将一条链表分为了多个段,每次查询时只需要先用二分查找定位到数据所在段对应的slot,再从子链表中遍历查找数据。
二、InnoDB索引实现
索引原理
1.聚簇索引
聚簇索引就是将数据和索引存在一起,数据是按主键的顺序存储的。在InnoDB中的主键索引就是聚簇索引。
2.二级索引
非聚簇索引也叫二级索引。叶子结点的数据域不是行数据,而是主键的值。
通过二级索引查找数据时,需要用主键值回表查询。
3.联合索引
key包含多列,排序时先比较第一列,第一列相等再比较第二列。
最左匹配原则:如果不是按照最左开始查找,无法使用索引。不能跳过中间列。某列使用范围查询,后面的列不能使用索引。
假如建立索引(a,b,c),查询条件为b=xx,a=xx,c=xx时,数据库会进行优化,依然可以使用联合索引
索引使用优化
1.存储空间
存储空间的直观表现就是索引文件大小。
字段大小决定着页内节点个数,从而决定页的层数。
2.主键选择
- 自增主键:写入磁盘利用率高,每次查询走二级索引。(因为主键没有意义)
- 随机主键:写入磁盘利用率低,每次查询走二级索引。
- 联合主键:影响索引大小,不宜维护,不建议使用。
可以自定义一种业务主键,有意义且递增,那写入、查询磁盘利用率都高。可以使用一级索引。
3.联合索引使用
建立索引时,按照索引的区分度排序,因为这样可以过滤更多的数据。
索引覆盖:如果要查询的数据都被联合索引覆盖,那么就不需要回表查询了。
比如建立联合索引(a,b),那么以下查询语句就不需要回表查询:
select a,b from table where a = x1 and b = x2;
4.字符串索引
字符串索引要设置合理长度,比如一个512字节的字段,可以只取前10个字节建立索引,否则效率太低。
不支持%开头的模糊查询。(会全表扫描)
三、InnoDB内存管理
1.内存池
使用内存池,在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。
2.内存页面管理
(1)页面分类
空闲页、数据页、脏页。脏页是指这一页的数据被修改,需要刷新回磁盘。
(2)页面淘汰
算法
LRU:Least Recently Used的缩写,即最近最少使用。
存在问题
全表扫描对内存的影响:假如要进行全表扫描,内存完全不够用,此时的LRU近乎失效。所有的热数据都会从内存中被替换出去,那该怎么办?
思路:
- 访问时间+频率。(Redis这么做)
- 两个LRU表:有一个热数据专用LRU表,存在晋升与降级
Mysql解决措施
将LRU表分为冷热两段。当有新的页被加载进内存时,会放在LRU_old的头部。
- old -> new:old区中存活时间大于innodb_old_blocks_time的页,有机会进入new区(放到new的头部)。
- new -> old:当向new区放新的页时,将指针前移让尾部的页进入old区。