Buffer Pool:缓冲池,简称BP。其作用是用来缓存表数据与索引数据,减少磁盘IO操作,提升效率。
Buffer Pool由缓存数据页(Page)和 对缓存数据页进行描述的控制块 组成, 控制块中存储着对应缓存页的所属的 表空间、数据页的编号、以及对应缓存页在Buffer Pool中的地址等信息。
Buffer Pool默认大小是128M, 以Page页为单位,Page页默认大小16K,而控制块的大小约为数据页的5%,大概是800字节。
注意: Buffer Pool大小为128M指的就是缓存页的大小,控制块则一般占5%,所以每次会多申请6M的内存空间用于存放控制块
MySQl中有一个哈希表数据结构,它使用 表空间号+数据页号,作为一个key,然后缓冲页对应的控制块作为value。
1、当需要访问某个页的数据时,先从哈希表中根据表空间号+页号看看是否存在对应的缓冲页。
2、如果有,则直接使用;如果没有,就从free链表中选出一个空闲的缓冲页,然后把磁盘中对应的页加载到该缓冲页的位置 。
BP的底层采用链表数据结构管理Page。在InnoDB访问表记录和索引时会在Page页中缓存,以后使用可以减少磁盘IO操作,提升效率。
Page根据状态可以分为三种类型:
free page : 空闲page,未被使用。
clean page:被使用page,数据没有被修改过。
dirty page:脏页,被使用page,数据被修改过,Page页中数据和磁盘的数据产生了不一致。
针对上面所说的三种page类型,InnoDB通过三种链表结构来维护和管理
1. free list:表示空闲缓冲区,管理free page
free链表是把所有空闲的缓冲页对应的控制块作为一个个的节点放到一个链表中,这个链表便称之为free链表
基节点: free链表中只有一个基节点是不记录缓存页信息(单独申请空间),它里面就存放了free链表的头节点的地址,尾节点的地址,还有free链表里当前有多少个节点。
2、flush list: 表示需要刷新到磁盘的缓冲区,管理dirty page,内部page按修改时间排序。
InnoDB引擎为了提高处理效率,在每次修改缓冲页后,并不是立刻把修改刷新到磁盘上,而是在未来的某个时间点进行刷新操作. 所以需要使用到flush链表存储脏页,凡是被修改过的缓冲页对应的控制块都会作为节点加入到flush链表。
flush链表的结构与free链表的结构相似。
3、lru list:表示正在使用的缓冲区,管理clean page和dirty page,缓冲区以midpoint为基点,前面链表称为new列表区,存放经常访问的数据,占63%;后面的链表称为old列表区,存放使用较少数据,占37%
Change Buffer:写缓冲区,是针对二级索引(辅助索引) 页的更新优化措施。
作用: 在进行DML操作时,如果请求的辅助索引(二级索引)没有在缓冲池中时,并不会立刻将磁盘页加载到缓冲池,而是在CB记录缓冲变更,等未来数据被读取时,再将数据合并恢复到BP中。
写缓冲区,仅适用于 非唯一普通索引页,为什么?
如果在索引设置唯一性,在进行修改时,InnoDB必须要做唯一性校验,因此必须查询磁盘,做一次IO操 作。会直接将记录查询到BufferPool中,然后在缓冲池修改,不会在ChangeBuffer操作。
LRU = Least Recently Used(最近最少使用): 就是末尾淘汰法,新数据从链表头部加入,释放空间时从末尾淘汰.
1. 当要访问某个页时,如果不在Buffer Pool,需要把该页加载到缓冲池,并且把该缓冲页对应的控制块作为节点添加到LRU链表的头部。
2. 当要访问某个页时,如果在Buffer Pool中,则直接把该页对应的控制块移动到LRU链表的头部
3. 当需要释放空间时,从最末尾淘汰
优点:
所有最近使用的数据都在链表表头,最近未使用的数据都在链表表尾,保证热数据能最快被获取到。
缺点:
如果发生全表扫描(比如:没有建立合适的索引 or 查询时使用select * 等),则有很大可能将真正的热数据淘汰掉.
由于MySQL中存在预读机制,很多预读的页都会被放到LRU链表的表头。如果这些预读的页都没有用到的话,这样,会导致很多尾部的缓冲页很快就会被淘汰。
改进型LRU:将链表分为new和old两个部分,加入元素时并不是从表头插入,而是从中间midpoint位置插入(就是说从磁盘中新读出的数据会放在冷数据区的头部),如果数据很快被访问,那么page就会向new列表头部移动,如果数据没有被访问,会逐步向old尾部移动,等待淘汰。
索引就是排好序的,帮助我们进行快速查找的数据结构.
简单来讲,索引就是一种将数据库中的记录按照特殊形式存储的数据结构。通过索引,能够显著地提高数据查询的效率,从而提升服务器的性能.
索引的优势与劣势
1、优点
- 提高数据检索的效率,降低数据库的IO成本
- 通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗
2、缺点
- 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加
- 索引需要占物理空间,除了数据表占用数据空间之外,每一个索引还要占用一定的物理空间
- 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度
3、创建索引的原则
- 在经常需要搜索的列上创建索引,可以加快搜索的速度;
- 在作为主键的列上创建索引,强制该列的唯一性和组织表中数据的排列结构;
- 在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
- 在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;
- 在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
- 在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。
页结构整体上可以分为三大部分,分别为通用部分(文件头、文件尾)、存储记录空间、索引部分。
通用部分 : 主要指文件头和文件尾,将页的内容进行封装,通过文件头和文件尾校验的CheckSum方式来确保页的传输是完整的。
其中比较重要的是在文件头中的 `FIL_PAGE_PREV` 和 `FIL_PAGE_NEXT` 字段,通过这两个字段,我们可以找到该页的上一页和下一页,实际上所有页通过两个字段可以形成一条双向链表。
页的主要作用是存储记录,所以“最小和最大记录”和“用户记录”部分占了页结构的主要空间。另外空闲空间是个灵活的部分,当有新的记录插入时,会从空闲空间中进行分配用于存储新记录
数据页中行记录按照主键值由小到大顺序串联成一个单链表(页中记录是以单向链表的形式进行存储的),且单链表的链表头为最小记录,链表尾为最大记录。并且为了更快速地定位到指定的行记录,通过 Page Directory实现目录的功能,借助 Page Directory使用二分法快速找到需要查找的行记录。
聚集索引与非聚集索引的区别是:叶节点是否存放一整行记录
聚簇索引: 将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据.
非聚簇索引:将数据与索引分开存储,索引结构的叶子节点指向了数据对应的位置.
InnoDB 主键使用的是聚簇索引,MyISAM 不管是主键索引,还是二级索引使用的都是非聚簇索引。
在InnoDB引擎中,主键索引采用的就是聚簇索引结构存储。
1、聚簇索引是一种数据存储方式,InnoDB的聚簇索引就是按照主键顺序构建 B+Tree结构。B+Tree 的叶子节点就是行记录,行记录和主键值紧凑地存储在一起。 这也意味着 InnoDB 的主键索引就是数据表本身,它按主键顺序存放了整张表的数据,占用的空间就是整个表数据量的大小。通常说的主键索引就是聚集索引。
2、InnoDB的表要求必须要有聚簇索引:
* 如果表定义了主键,则主键索引就是聚簇索引
* 如果表没有定义主键,则第一个非空unique列作为聚簇索引
* 否则InnoDB会从建一个隐藏的row-id作为聚簇索引
3、辅助索引
InnoDB辅助索引,也叫作二级索引,是根据索引列构建 B+Tree结构。但在 B+Tree 的叶子节点中只存了索引列和主键的信息。二级索引占用的空间会比聚簇索引小很多, 通常创建辅助索引就是为了提升查询效率。一个表InnoDB只能创建一个聚簇索引,但可以创建多个辅助索引。
与InnoDB表存储不同,MyISM使用的是非聚簇索引, 非聚簇索引的两棵B+树看上去没什么不同 ,节点的结构完全一致只是存储的内容不同而已,主键索引B+树的节点存储了主键,辅助键索引B+树存储了辅助键。
表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别。由于 索引树是独立的,通过辅助键检索无需访问主键的索引树
1. 当你需要取出一定范围内的数据时,用聚簇索引也比用非聚簇索引好。
2. 当通过聚簇索引查找目标数据时理论上比非聚簇索引要快,因为非聚簇索引定位到对应主键时还要多一次目标记录寻址,即多一次I/O。
3. 使用覆盖索引扫描的查询可以直接使用页节点中的主键值。
1. 插入速度严重依赖于插入顺序 。
2. 更新主键的代价很高,因为将会导致被更新的行移动 。
3. 二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据。
1、普通索引
这是最基本的索引类型,基于普通字段建立的索引,没有任何限制。
2、唯一索引
与"普通索引"类似,不同的就是:索引字段的值必须唯一,但允许有空值 。
3、主键索引
它是一种特殊的唯一索引,不允许有空值。在创建或修改表时追加主键约束即可,每个表只能有一个主键。
4、复合索引
用户可以在多个列上建立索引,这种索引叫做组复合索引(组合索引)。复合索引可以代替多个单一索引,相比多个单一索引复合索引所需的开销更小。
复合索引使用注意事项:
何时使用复合索引,要根据where条件建索引,注意不要过多使用索引,过多使用会对更新操作效率有很大影响。
如果表已经建立了(col1,col2),就没有必要再单独建立(col1);如果现在有(col1)索引,如果查询需要col1和col2条件,可以建立(col1,col2)复合索引,对于查询有一定提高。
5、全文索引
查询操作在数据量比较少时,可以使用like模糊查询,但是对于大量的文本数据检索,效率很低。如果使用全文索引,查询速度会比like快很多倍。
在MySQL 5.6 以前的版本,只有MyISAM存储引擎支持全文索引,从MySQL 5.6开始MyISAM和InnoDB存储引擎均支持。
全文索引方式有自然语言检索 `IN NATURAL LANGUAGE MODE`和布尔检索 `IN BOOLEAN MODE`两种
和常用的like模糊查询不同,全文索引有自己的语法格式,使用 match 和 against 关键字,
全文索引使用注意事项:
* 全文索引必须在字符串、文本字段上建立。
* 全文索引字段值必须在最小字符和最大字符之间的才会有效。
最佳左前缀法则: 如果创建的是联合索引,就要遵循该法则. 使用索引时,where后面的条件需要从索引的最左前列开始使用,并且不能跳过索引中的列使用。
* 场景1: 按照索引字段顺序使用,三个字段都使用了索引,没有问题。
EXPLAIN SELECT * FROM users WHERE user_name = 'tom' AND user_age = 17 AND user_level = 'A';
* 场景2: 直接跳过user_name使用索引字段,索引无效,未使用到索引。
EXPLAIN SELECT * FROM users WHERE user_age = 17 AND user_level = 'A';
* 场景3: 不按照创建联合索引的顺序,使用索引
EXPLAIN SELECT * FROM users WHERE user_age = 17 AND user_name = 'tom' AND user_level = 'A';
where后面查询条件顺序是 `user_age`、`user_level`、`user_name`与我们创建的索引顺序 `user_name`、`user_age`、`user_level`不一致,为什么还是使用了索引,原因是因为MySql底层优化器对其进行了优化。
MySQL创建联合索引的规则是: 首先会对联合索引最左边的字段进行排序( 例子是 `user_name` ), 在第一个字段的基础之上 再对第二个字段进行排序 ( 例子中是 `user_age` ) .