Mysql优化和原理

一.性能监控

        1.mySQL逻辑架构图

Mysql优化和原理_第1张图片

  • 第一层为客户端的连接认证,C/S都有此架构
  • 第二层为服务器层,包含MySQL的大多数核心服务功能
  • 第三层包含了存储引擎,服务器通过API与其通信,API规避了不同存储引擎的差异,不同存储引擎也不会互相通信,另外存储引擎不会去解析SQL(InnoDB是例外,它会解析外键定义,因为服务器本身没有实现该功能)

2.mysql性能监控

  • show profile

 select @@have_profiling来显示当前mysql是否支持profile:

通过 select @@profiling查看profile是否开启

如果为0未开启通过 set profiling =1 开启

show profile的常用查询参数。

①ALL:显示所有的开销信息。

②BLOCK IO:显示块IO开销。

③CONTEXT SWITCHES:上下文切换开销。

④CPU:显示CPU开销信息。

⑤IPC:显示发送和接收开销信息。

⑥MEMORY:显示内存开销信息。

⑦PAGE FAULTS:显示页面错误开销信息。

⑧SOURCE:显示和Source_function,Source_file,Source_line相关的开销信息。

⑨SWAPS:显示交换次数开销信息。

日常开发需注意的结论。

①converting  HEAP to MyISAM:查询结果太大,内存不够,数据往磁盘上搬了。

②Creating tmp table:创建临时表。先拷贝数据到临时表,用完后再删除临时表。

③Copying to tmp table on disk:把内存中临时表复制到磁盘上,危险!!!

④locked。

如果在show profile诊断结果中出现了以上4条结果中的任何一条,则sql语句需要优化。

  • performance_schema 

--语句事件记录表,这些表记录了语句事件信息,当前语句事件表events_statements_current、历史语句事件表events_statements_history和长语句历史事件表events_statements_history_long、以及聚合后的摘要表summary,其中,summary表还可以根据帐号(account),主机(host),程序(program),线程(thread),用户(user)和全局(global)再进行细分)
show tables like '%statement%';

--等待事件记录表,与语句事件类型的相关记录表类似:
show tables like '%wait%';

--阶段事件记录表,记录语句执行的阶段事件的表
show tables like '%stage%';

--事务事件记录表,记录事务相关的事件的表
show tables like '%transaction%';

--监控文件系统层调用的表
show tables like '%file%';

--监视内存使用的表
show tables like '%memory%';

--动态对performance_schema进行配置的配置表
show tables like '%setup%';
  • performance_schema实践操作

--1、哪类的SQL执行最多?
SELECT DIGEST_TEXT,COUNT_STAR,FIRST_SEEN,LAST_SEEN FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--2、哪类SQL的平均响应时间最多?
SELECT DIGEST_TEXT,AVG_TIMER_WAIT FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--3、哪类SQL排序记录数最多?
SELECT DIGEST_TEXT,SUM_SORT_ROWS FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--4、哪类SQL扫描记录数最多?
SELECT DIGEST_TEXT,SUM_ROWS_EXAMINED FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--5、哪类SQL使用临时表最多?
SELECT DIGEST_TEXT,SUM_CREATED_TMP_TABLES,SUM_CREATED_TMP_DISK_TABLES FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--6、哪类SQL返回结果集最多?
SELECT DIGEST_TEXT,SUM_ROWS_SENT FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--7、哪个表物理IO最多?
SELECT file_name,event_name,SUM_NUMBER_OF_BYTES_READ,SUM_NUMBER_OF_BYTES_WRITE FROM file_summary_by_instance ORDER BY SUM_NUMBER_OF_BYTES_READ + SUM_NUMBER_OF_BYTES_WRITE DESC
--8、哪个表逻辑IO最多?
SELECT object_name,COUNT_READ,COUNT_WRITE,COUNT_FETCH,SUM_TIMER_WAIT FROM table_io_waits_summary_by_table ORDER BY sum_timer_wait DESC
--9、哪个索引访问最多?
SELECT OBJECT_NAME,INDEX_NAME,COUNT_FETCH,COUNT_INSERT,COUNT_UPDATE,COUNT_DELETE FROM table_io_waits_summary_by_index_usage ORDER BY SUM_TIMER_WAIT DESC
--10、哪个索引从来没有用过?
SELECT OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME FROM table_io_waits_summary_by_index_usage WHERE INDEX_NAME IS NOT NULL AND COUNT_STAR = 0 AND OBJECT_SCHEMA <> 'mysql' ORDER BY OBJECT_SCHEMA,OBJECT_NAME;
--11、哪个等待事件消耗时间最多?
SELECT EVENT_NAME,COUNT_STAR,SUM_TIMER_WAIT,AVG_TIMER_WAIT FROM events_waits_summary_global_by_event_name WHERE event_name != 'idle' ORDER BY SUM_TIMER_WAIT DESC
--12-1、剖析某条SQL的执行情况,包括statement信息,stege信息,wait信息
SELECT EVENT_ID,sql_text FROM events_statements_history WHERE sql_text LIKE '%count(*)%';
--12-2、查看每个阶段的时间消耗
SELECT event_id,EVENT_NAME,SOURCE,TIMER_END - TIMER_START FROM events_stages_history_long WHERE NESTING_EVENT_ID = 1553;
--12-3、查看每个阶段的锁等待情况
SELECT event_id,event_name,source,timer_wait,object_name,index_name,operation,nesting_event_id FROM events_waits_history_longWHERE nesting_event_id = 1553;

3.show processlist

查看当前连接;

连接池:druid alibaba

二.数据类型和schema优化

1.数据类型优化

更小的数据类型

应该尽量使用可以正确存储数据的最小数据类型,更小的数据类型通常更快,因为它们占用更少的磁盘、内存和CPU缓存,并且处理时需要的CPU周期更少,但是要确保没有低估需要存储的值的范围,如果无法确认哪个数据类型,就选择你认为不会超过范围的最小类型

简单数据类型

简单数据类型的操作通常需要更少的CPU周期,例如,
1、整型比字符操作代价更低,因为字符集和校对规则是字符比较比整型比较更复杂,
2、使用mysql自建类型而不是字符串来存储日期和时间
3、用整型存储IP地址

尽量避免null

如果查询中包含可为NULL的列,对mysql来说很难优化,因为可为null的列使得索引、索引统计和值比较都更加复杂,坦白来说,通常情况下null的列改为not null带来的性能提升比较小,所有没有必要将所有的表的schema进行修改,但是应该尽量避免设计成可为null的列

实际细则

1.整数类型:可以使用的几种整数类型:TINYINT,SMALLINT,MEDIUMINT,INT,BIGINT分别使用8,16,24,32,64位存储空间。尽量使用满足需求的最小数据类型

2.字符和字符串类型

  •     varchar根据实际内容长度保存数据

        1、使用最小的符合需求的长度。
        2、varchar(n) n小于等于255使用额外一个字节保存长度,n>255使用额外两个字节保存长度。
        3、varchar(5)与varchar(255)保存同样的内容,硬盘存储空间相同,但内存空间占用不同,是指定的大小 。
        4、varchar在mysql5.6之前变更长度,或者从255一下变更到255以上时时,都会导致锁表。
        应用场景
            1、存储长度波动较大的数据,如:文章,有的会很短有的会很长
            2、字符串很少更新的场景,每次更新后都会重算并使用额外存储空间保存长度
            3、适合保存多字节字符,如:汉字,特殊字符等

  •     char固定长度的字符串

        1、最大长度:255
        2、会自动删除末尾的空格
        3、检索效率、写效率 会比varchar高,以空间换时间
        应用场景
            1、存储长度波动不大的数据,如:md5摘要
            2、存储短字符串、经常更新的字符串

3.BLOB和TEXT类型

        MySQL 把每个 BLOB 和 TEXT 值当作一个独立的对象处理。两者都是为了存储很大数据而设计的字符串类型,分别采用二进制和字符方式存储。

4.datetime和timestamp
    datetime
        占用8个字节
        与时区无关,数据库底层时区配置,对datetime无效
        可保存到毫秒
        可保存时间范围大
        不要使用字符串存储日期类型,占用空间大,损失日期类型函数的便捷性
    timestamp
        占用4个字节
        时间范围:1970-01-01到2038-01-19
        精确到秒
        采用整形存储
        依赖数据库设置的时区
        自动更新timestamp列的值
    date
        占用的字节数比使用字符串、datetime、int存储要少,使用date类型只需要3个字节
        使用date类型还可以利用日期时间函数进行日期之间的计算
        date类型用于保存1000-01-01到9999-12-31之间的日期
5.使用枚举代替字符串类型
6.特殊类型数据

人们经常使用varchar(15)来存储ip地址,然而,它的本质是32位无符号整数不是字符串,可以使用INET_ATON()和INET_NTOA函数在这两种表示方法之间转换
案例:
select inet_aton('1.1.1.1')
select inet_ntoa(16843009)

2.合理使用范式和反范式

 Mysql优化和原理_第2张图片Mysql优化和原理_第3张图片

空间换时间,不用连表查。

3.主键的选择

代理主键
    与业务无关的,无意义的数字序列
自然主键
    事物属性中的自然唯一标识
推荐使用代理主键
    它们不与业务耦合,因此更容易维护,一个大多数表,最好是全部表,通用的键策略能够减少需要编写的源码数量,减少系统的总体拥有成本

4.字符集的选择

字符集直接决定了数据在MySQL中的存储编码方式,由于同样的内容使用不同字符集表示所占用的空间大小会有较大的差异,所以通过使用合适的字符集,可以帮助我们尽可能减少数据量,进而减少IO操作次数。utf8mb4

5.存储引擎的选择

Mysql优化和原理_第4张图片

6.适当的数据冗余

1.被频繁引用且只能通过 Join 2张(或者更多)大表的方式才能得到的独立小字段。
2.这样的场景由于每次Join仅仅只是为了取得某个小字段的值,Join到的记录又大,会造成大量不必要的 IO,完全可以通过空间换取时间的方式来优化。不过,冗余的同时需要确保数据的一致性不会遭到破坏,确保更新的同时冗余字段也被更新。

7.适当拆分

当我们的表中存在类似于 TEXT 或者是很大的 VARCHAR类型的大字段的时候,如果我们大部分访问这张表的时候都不需要这个字段,我们就该义无反顾的将其拆分到另外的独立表中,以减少常用数据所占用的存储空间。这样做的一个明显好处就是每个数据块中可以存储的数据条数可以大大增加,既减少物理 IO 次数,也能大大提高内存中的缓存命中率。
 

三.执行计划

使用explain+SQL语句来模拟优化器执行SQL查询语句
Column Meaning
id

select查询的序列号,包含一组数字,表示查询中执行select子句或者操作表的顺序

1、如果id相同,那么执行顺序从上到下

​ 2、如果id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行

​ 3、id相同和不同的,同时存在:相同的可以认为是一组,从上往下顺序执行,在所有组中,id值越大,优先级越高,越先执行

select_type

主要用来分辨查询的类型,是普通查询还是联合查询还是子查询

sample:简单的查询,不包含子查询和union
primary:查询中若包含任何复杂的子查询,最外层查询则被标记为Primary
union:若第二个select出现在union之后,则被标记为union
dependent union:跟union类似,此处的depentent表示union或union all联合而成的结果会受外部表影响
union result:从union表获取结果的
subquery:在select或者where列表中包含子查询
dependent subquery:subquery的子查询要受到外部表查询的影响
DERIVED: from子句中出现的子查询,也叫做派生类,
UNCACHEABLE SUBQUERY:表示使用子查询的结果不能被缓存
uncacheable union:表示union的查询结果不能被缓存:sql语句未验证
table

对应行正在访问哪一个表,表名或者别名,可能是临时表或者union合并结果集

type

type显示的是访问类型,访问类型表示我是以何种方式去访问我们的数据,最容易想的是全表扫描,直接暴力的遍历一张表去寻找需要的数据,效率非常低下,访问的类型有很多,效率从最好到最坏依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

一般情况下,得保证查询至少达到range级别,最好能达到ref

all:全表扫描,一般情况下出现这样的sql语句而且数据量比较大的话那么就需要进行优化。
index:全索引扫描这个比all的效率要好,主要有两种情况,一种是当前的查询时覆盖索引,即我们需要的数据在索引中就可以索取,或者是使用了索引进行排序,这样就避免数据的重排序
range:表示利用索引查询的时候限制了范围,在指定范围内进行查询,这样避免了index的全索引扫描,适用的操作符: =, <>, >, >=, <, <=, IS NULL, BETWEEN, LIKE, or IN() 
index_subquery:利用索引来关联子查询,不再扫描全表
unique_subquery:该连接类型类似与index_subquery,使用的是唯一索引
index_merge:在查询过程中需要多个索引组合使用,没有模拟出来
ref_or_null:对于某个字段即需要关联条件,也需要null值的情况下,查询优化器会选择这种访问方式
ref:使用了非唯一性索引进行数据的查找
eq_ref :使用唯一性索引进行数据查找
const:这个表至多有一个匹配行,
system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现
possible_keys

​ 显示可能应用在这张表中的索引,一个或多个,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用

key

​ 实际使用的索引,如果为null,则没有使用索引,查询中若使用了覆盖索引,则该索引和查询的select字段重叠。

key_len

表示索引中使用的字节数,可以通过key_len计算查询中使用的索引长度,在不损失精度的情况下长度越短越好。

ref

显示索引的哪一列被使用了,如果可能的话,是一个常数

rows 根据表的统计信息及索引使用情况,大致估算出找出所需记录需要读取的行数,此参数很重要,直接反应的sql找了多少数据,在完成目的的情况下越少越好
extra

包含额外的信息。

using filesort:说明mysql无法利用索引进行排序,只能利用排序算法进行排序,会消耗额外的位置

using temporary:建立临时表来保存中间结果,查询完成之后把临时表删除

using index:这个表示当前的查询时覆盖索引的,直接从索引中读取数据,而不用访问数据表。如果同时出现using where 表名索引被用来执行索引键值的查找,如果没有,表面索引被用来读取数据,而不是真的查找

using where:使用where进行条件过滤

四、通过索引进行优化

索引匹配方式

全值匹配
匹配最左前缀
匹配列前缀
匹配范围值
精确匹配某一列并范围匹配另外一列
只访问索引的查询

哈希索引

当需要存储大量的URL,并且根据URL进行搜索查找,如果使用B+树,存储的内容就会很大
也可以利用将url使用CRC32做哈希,可以使用以下查询方式:
select id fom url where url="" and url_crc=CRC32("")
此查询性能较高原因是使用体积很小的索引来完成查找

组合索引

组合索引a,b,c  最左匹配

Mysql优化和原理_第5张图片

聚簇索引与非聚簇索引

覆盖索引

1、如果一个索引包含所有需要查询的字段的值,我们称之为覆盖索引
2、不是所有类型的索引都可以称为覆盖索引,覆盖索引必须要存储索引列的值
3、不同的存储实现覆盖索引的方式不同,不是所有的引擎都支持覆盖索引,memory不支持覆盖索引

1、索引条目通常远小于数据行大小,如果只需要读取索引,那么mysql就会极大的较少数据访问量
2、因为索引是按照列值顺序存储的,所以对于IO密集型的范围查询会比随机从磁盘读取每一行数据的IO要少的多
3、一些存储引擎如MYISAM在内存中只缓存索引,数据则依赖于操作系统来缓存,因此要访问数据需要一次系统调用,这可能会导致严重的性能问题
4、由于INNODB的聚簇索引,覆盖索引对INNODB表特别有用

优化小细节

1.当使用索引列进行查询的时候尽量不要使用表达式,把计算放到业务层而不是数据库层

select actor_id from actor where actor_id+1=5;

2.尽量使用主键查询,而不是其他索引,因此主键查询不会触发回表查询

3.使用前缀索引

有时候需要索引很长的字符串,这会让索引变的大且慢,通常情况下可以使用某个列开始的部分字符串,这样大大的节约索引空间,从而提高索引效率,但这会降低索引的选择性,索引的选择性是指不重复的索引值和数据表记录总数的比值,范围从1/#T到1之间。索引的选择性越高则查询效率越高,因为选择性更高的索引可以让mysql在查找的时候过滤掉更多的行。

alter table citydemo add key(city(7));

4.使用索引扫描来排序

order by 里索引列 

5.union all,in,or都能够使用索引,但是推荐使用in

6.范围列可以用到索引  

        范围条件是:<、>

        范围列可以用到索引,但是范围列后面的列无法用到索引,索引最多用于一个范围列

7.强制类型转换会全表扫描

8.更新十分频繁,数据区分度不高的字段上不宜建立索引

9.创建索引的列,不允许为null,可能会得到不符合预期的结果

10.当需要进行表连接的时候,最好不要超过三张表,因为需要join的字段,数据类型必须一致

11.能使用limit的时候尽量使用limit

12.单表索引建议控制在5个以内
13.单索引字段数不允许超过5个(组合索引)
14.创建索引的时候应该避免以下错误概念
    索引越多越好
    过早优化,在不了解系统的情况下进行优化

查询优化

查询性能低下的主要原因是访问的数据太多,某些查询不可避免的需要筛选大量的数据,我们可以通过减少访问数据量的方式进行优化

是否向数据库请求了不需要的数据

五、隔离级别和事务原理

四种隔离级别,MySQL 全都支持。

  1. 读未提交(READ UNCOMMITTED)
  2. 读提交 (READ COMMITTED)
  3. 可重复读 (REPEATABLE READ)
  4. 串行化 (SERIALIZABLE)

脏读

脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据,这就是脏读。

可重复读

可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据都是一致的。通常针对数据更新(UPDATE)操作。

不可重复读

对比可重复读,不可重复读指的是在同一事务内,不同的时刻读到的同一批数据可能是不一样的,可能会受到其他事务的影响,比如其他事务改了这批数据并提交了。通常针对数据更新(UPDATE)操作。

幻读

幻读是针对数据插入(INSERT)操作来说的。假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。

Mysql优化和原理_第6张图片

 MySQL 的默认级别 REPEATABLE-READ,也就是可重复读

 ORACLE的默认级别 READ COMMITTED,也就是读已提交

MySQL 事务控制 本质上是read view生成时机不同 支持REPEATABLE-READ 和READ COMMITTED

先来看看sql在 Innodb执行过程

在这里插入图片描述 

mysql执行一条sql语句的完整过程,sql语句在mysql中的执行过程_知识分子_的博客-CSDN博客_mysql执行一条语句的全过程

undo日志:主要用于mvcc机制的实现,还有事务回滚也可直接从undo日志中取到原始值
redo日志:主要用于防止mysql宕机时,buff pool中的数据还未来得及写入磁盘,导致数据丢失。使用redo日志可在mysql宕机后再启动时,把数据恢复到buff pool中。第二点就是等待binlog日志发送commit标记后才提交事务,保证与binlog日志文件数据一致!
binlog日志:主要用于数据库数据误删恢复,主从复制
 

MVCC:MVCC主要是为Repeatable-Read事务隔离级别做的。

MVCC模块在MySQL中的具体实现是由三个隐式字段,undo日志、read view三个组件来实现的。

三个隐式字段:

        DB_TRX_ID

​        6字节,最近修改事务id,记录创建这条记录或者最后一次修改该记录的事务id

​        DB_ROLL_PTR

​        7字节,回滚指针,指向这条记录的上一个版本,用于配合undolog,指向上一个旧版本

​        DB_ROW_ID

​        6字节,隐藏的主键,如果数据表没有主键,那么innodb会自动生成一个6字节的row_id

Mysql优化和原理_第7张图片

Read View是事务进行快照读操作的时候生产的读视图,在该事务执行快照读的那一刻,会生成一个数据系统当前的快照,记录并维护系统当前活跃事务的id,事务的id值是递增的。

​        其实Read View的最大作用是用来做可见性判断的,也就是说当某个事务在执行快照读的时候,对该记录创建一个Read View的视图,把它当作条件去判断当前事务能够看到哪个版本的数据,有可能读取到的是最新的数据,也有可能读取的是当前行记录的undolog中某个版本的数据

​        Read View遵循的可见性算法主要是将要被修改的数据的最新记录中的DB_TRX_ID(当前事务id)取出来,与系统当前其他活跃事务的id去对比,如果DB_TRX_ID跟Read View的属性做了比较,不符合可见性,那么就通过DB_ROLL_PTR回滚指针去取出undolog中的DB_TRX_ID做比较,即遍历链表中的DB_TRX_ID,直到找到满足条件的DB_TRX_ID,这个DB_TRX_ID所在的旧记录就是当前事务能看到的最新老版本数据。

1、首先比较DB_TRX_ID < up_limit_id,如果小于,则当前事务能看到DB_TRX_ID所在的记录,如果大于等于进入下一个判断

​2、接下来判断DB_TRX_ID >= low_limit_id,如果大于等于则代表DB_TRX_ID所在的记录在Read View生成后才出现的,那么对于当前事务肯定不可见,如果小于,则进入下一步判断

3、判断DB_TRX_ID是否在活跃事务中,如果在,则代表在Read View生成时刻,这个事务还是活跃状态,还没有commit,修改的数据,当前事务也是看不到,如果不在,则说明这个事务在Read View生成之前就已经开始commit,那么修改的结果是能够看见的。
 

Read View是事务进行快照读操作的时候生产的读视图,在该事务执行快照读的那一刻,会生成一个数据系统当前的快照,记录并维护系统当前活跃事务的id,事务的id值是递增的。

​        其实Read View的最大作用是用来做可见性判断的,也就是说当某个事务在执行快照读的时候,对该记录创建一个Read View的视图,把它当作条件去判断当前事务能够看到哪个版本的数据,有可能读取到的是最新的数据,也有可能读取的是当前行记录的undolog中某个版本的数据

​        Read View遵循的可见性算法主要是将要被修改的数据的最新记录中的DB_TRX_ID(当前事务id)取出来,与系统当前其他活跃事务的id去对比,如果DB_TRX_ID跟Read View的属性做了比较,不符合可见性,那么就通过DB_ROLL_PTR回滚指针去取出undolog中的DB_TRX_ID做比较,即遍历链表中的DB_TRX_ID,直到找到满足条件的DB_TRX_ID,这个DB_TRX_ID所在的旧记录就是当前事务能看到的最新老版本数据。

在RC隔离级别下,是每个快照读都会生成并获取最新的Read View,而在RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View,之后的快照读获取的都是同一个Read View.

六、索引架构

mysql数据文件分为frm和ibd   

.frm文件:保存了每个表的元数据,包括表结构的定义等;

.ibd文件:InnoDB引擎开启了独立表空间(my.ini中配置innodb_file_per_table = 1)产生的存放该表的数据和索引的文件。

1. 数据文件本身就是索引文件

2. 表数据文件本身就是按B+Tree组织的一个索引结构文件

3. 聚集索引中叶节点包含了完整的数据记录

4. InnoDB表必须要有主键,并且推荐使用整型自增主键

InnoDB索引数据结构 --B+Tree

B+Tree是在BTree的基础之上做的一种优化,变化如下:

1、B+Tree每个节点可以包含更多的节点,这个做的原因有两个,第一个原因是为了降低树的高度,第二个原因是将数据范围变为多个区间,区间越多,数据检索越快,

2、非叶子节点存储key,叶子节点存储key和数据,每个叶子节点默认的大小是16KB

3、叶子节点两两指针相互连接(符合磁盘的预读特性),顺序查询性能更高

当新记录插入到InnoDB聚簇索引中时,如果按顺序插入索引记录(升序或降序),当达到叶子节点最大的容量时,下一条记录就会写到新的的页中。叶子节点可使用的容量为总容量的15/16,InnoDB会留1/16的空间,以备将来插入和更新索引记录时使用, 如果以随机顺序插入记录,则页面的容量为1/2到15/16之间。

你可以设置 innodb_page_size 来调整页的大小,支持 64KB, 32KB, 16KB (默认), 8KB, 和4KB。

页结构

这就是一个page内的存储,共16K的空间,要放的所有东西。

分为几个大部分,文件管理头信息、页面头信息、页面尾信息、最小记录最大记录、用户记录、可重用空间、未使用空间、页面槽信息。

从名字就能看出来,用户记录就是行数据,可重用就是曾经被分配过数据后来被删了,未使用就是没分配过的空间
 

Mysql优化和原理_第8张图片

 覆盖索引与回表

这先要从InnoDB的索引实现说起,InnoDB有两大类索引:

  • 聚集索引(clustered index)

  • 普通索引(secondary index)

InnoDB聚集索引和普通索引有什么差异?

InnoDB聚集索引的叶子节点存储行记录,因此, InnoDB必须要有,且只有一个聚集索引:

(1)如果表定义了PK,则PK就是聚集索引;

(2)如果表没有定义PK,则第一个not NULL unique列是聚集索引;

(3)否则,InnoDB会创建一个隐藏的row-id作为聚集索引;

画外音:所以PK查询非常快,直接定位行记录。

InnoDB普通索引的叶子节点存储主键值。

Mysql优化和原理_第9张图片

粉红色路径,需要扫码两遍索引树:

(1)先通过普通索引定位到主键值id=5;

(2)在通过聚集索引定位到行记录;

这就是所谓的回表查询,先定位主键值,再定位行记录,它的性能较扫一遍索引树更低。

 

如何实现索引覆盖?解决回表

常见的方法是:将被查询的字段,建立到联合索引里去。

场景:

场景1:全表count查询优化

场景2:列查询回表优化

场景3:分页查询

你可能感兴趣的:(mysql,mysql优化)