mysql
2023-03-27 16:14:10 0 举报
AI智能生成
登录查看完整内容
mysql
作者其他创作
大纲/内容
锁是计算机协调多个进程或线程并发访问某一资源的机制(保证系统数据访问的一致性)
InnoDB支持行锁,MyISAM不支持行锁
开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高
行锁
InnoDB和MyISAM都支持表锁
开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低
表锁
只有BDB存储引擎支持页锁,页锁就是在页的粒度上进行锁定,锁定的数据资源比行锁要多,因为一个页中 可以有多个行记录。当我们使用页锁的时候,会出现数据浪费的现象,但这样的浪费最多也就是一个页上的 数据行。页锁的开销介于表锁和行锁之间,会出现死锁。锁定粒度介于表锁和行锁之间,并发度一般。
页锁
数据操作粒度区分
适合写操作比较多的场景,表锁,行锁,页锁都是悲观锁
悲观锁
版本控制或者cas加重试,乐观锁适合读操作比较多的场景,如果在写操作比较多的场景使用乐观锁可能会导致重试次数过多影响性能
乐观锁
性能区分
读锁(共享锁,S锁(Shared)):针对同一份数据,多个读操作可以同时进行而不会互相影响
读锁
写锁(排它锁,X锁(eXclusive)):当前写操作没有完成前,它会阻断其他写锁和读锁,数据修改操作都 会加写锁,查询也可以通过for update加写锁
写锁
数据库操作类型区分
锁介绍
原子性(Actomicity):一个事务是一个原子操作单元,要么全部执行成功,要么执行失败(由undo_log保证)
一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态(最终共同保证数据的一致性)
隔离性(Isolation):数据库系统提供了一定的隔离机制,保证事务不受外界并发操作的影响“独立”执行(由锁机制保证)
持久性(Durable):事务完成之后,对于数据的修改是永久性的,即使出现系统故障也不会发生改变(由redo_log保证)
支持事务(ACID)
脏读(Dirty Read):A事务在执行过程中,B事务读取了A事务修改后的数据;但是由于某些原因,A事务进行了RollBack,则B事务所读取的数据就是脏数据
不可重复读(non-repeatable Read):B事务读取了两次数据,在这两次读取过程中A事务修改了数据,造成B事务两次读取的数据不一样,即不可重复读
幻读:B事务读取了两次数据,在这两次读取过程中A事务添加了数据,造成B事务这两次读取出来的集合不一样,即幻读
问题
上述问题都是有关于数据库读一致性的问题,可以通过事务的隔离机制来保证;
数据库的事务隔离级别越高,并发副作用就越小,但付出的代价也就越大
因为事务隔离本质上就是使事务在一定程度上串行化,需要根据具体的业务需求来决定使用哪种隔离级别
问题分析
read uncommitted(读未提交):允许一个事务读取另一个事务未提交的数据,可能会发生脏读,幻读,不可重复读(不建议使用)
read committed(读已提交):一个事务只能读取另一个事务已经提交的数据,可以避免脏读;可能发生幻读、不可重复读
repeatable read(可重复读):一个事务可以多次执行某一查询操作,并且每次的返回结果相同;可以避免脏读、不可重复读,可能发生幻读
serializable(串行化):每个事务都有序的进行,事务之间互相不干扰,可以防止脏读,幻读,不可重复读,但是会影响系统的效率。
隔离级别
并发事务
mysql InnoDB引擎默认的修改数据语句:update、delete、insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁
可以使用select …for update(加排他锁),使用select … lock in share mode(加共享锁);所以加过排他锁的数据行在其他事务中是不能修改的,也不能通过for update或lock in share mode的方式查询数据,但可以直接通过select …from…查询数据,因为普通查询没有任何锁机制
又称读锁;获取行数据的共享锁的事务可以进行行数据的读取,但是不能修改;其他事物也可以获取该行数据的共享锁,但是不能获取排他锁
事务a对数据对象data加了S锁,则事务a可以读data但不能修改data;其他事务只能再对data加S锁,而不能加X锁,直到事务a释放data上的S锁;这保证了其他事务可以读data,但在事务a释放data上的S锁之前不能对data做任何修改
共享锁(S)
又称写锁;允许获取排他锁的事务进行读写数据,阻止其他事务获取相同数据集的读锁和写锁
若事务a对数据对象data加上X锁,事务a可以读data也可以修改data;其他事务不能再对data加任何锁,直到a释放data上的锁为止
排他锁(X)
\tInnoDB的行锁是通过给索引项加锁实现的,这种行锁的实现特点意味着:只有通过索引条件检索数据,InnoDB才会使用行级锁,否则,InnoDB将使用表锁
1、创建无索引的表
set autocommit=0;
2、关闭自动提交事务
线程a: select * from tab1 where id = 1; -- 查询成功
线程b:select * from tab1 where id = 2; -- 查询成功
①不加锁
线程a: select * from tab1 where id = 1 for update; -- 查询成功
线程b: select * from tab1 where id = 2 for update; -- 查询堵塞
b线程查询id=2被阻塞,说明无索引加的表锁,a线程提交事务b才能返回结果
②加写锁
3、两个线程操作
alter table tab1 add index idx_id(id);
4、给tab1的id列加索引
线程b: select * from tab1 where id = 2 for update; -- 查询成功
a,b线程分别查询不同记录加锁,查询成功,说明有索引加的行锁
加写锁
5、两个线程操作
结论:查询列带索引,加行锁;不带锁,加表锁。
案例一
线程a:select * from tab1 where id=1 lock in share mode; --操作成功
线程b:select * from tab1 where id=1 lock in share mode; --操作成功
某一线程持有一数据行的读锁,其他线程也可以对该数据行加读锁
①读锁
线程a:select * from tab1 where id=1 for update; --操作成功
select * from tab1 where id=1 for update; --阻塞等待
select * from tab1 where id=1 lock in share mode; --阻塞等待
select * from tab1 where id=2 for update; --操作成功
线程b
某一线程持有一数据行的写锁,其他线程对该数据行加锁(读锁,写锁)都会进行阻塞等待,可以操作其他数据
②写锁
select * from tab1 where id=1 and name='hhhh' for update; --操作成功
线程a
select * from tab1 where id=1 and name='kkkk' for update; --阻塞等待
虽然查询的不是同一数据,但是查询使用的是同一索引,对索引加锁,所以线程b会阻塞等待
③对索引加锁再理解
线程a:select * from tab1 where id=1 lock in share mode; --操作成功
线程a:select * from tab1 where id=1 for update; --操作成功
先加读锁,再加写锁
先加写锁,再加读锁
同一线程可以对同一数据反复加锁(读锁、写锁)
④同一线程加锁
案例二
锁
指定大小的内存来缓存数据和索引,最大可以设置为物理内存的80%
innodb_buffer_pool_size
主要控制innodb将log buffer中的数据写入日志文件并flush磁盘的时间点,值分别为0,1,2
innodb_flush_log_at_trx_commit
设置innodb线程的并发数,默认为0表示不受限制,如果要设置建议跟服务器的cpu核心数一致或者是cpu核心数的两倍
innodb_thread_concurrency
此参数确定日志文件所用的内存大小,以M为单位
innodb_log_buffer_size
此参数确定数据日志文件的大小,以M为单位
innodb_log_file_size
以循环方式将日志文件写到多个文件中
innodb_log_files_in_group
mysql读入缓冲区大小,对表进行顺序扫描的请求将分配到一个读入缓冲区
read_buffer_size
mysql随机读的缓冲区大小
read_rnd_buffer_size
此参数确定为每张表分配一个新的文件
innodb_file_per_table
mysql数据页的大小(16K)
font color=\
配置
事务对应的数据库操作正在执行过程中时,我们就说该事务处在活动的状态。
活动的(active)
当事务中的最后一个操作执行完成,但由于操作都在内存中执行,所造成的影响并没有刷新到磁盘时,我们就说该事务处在部分提交的状态。
部分提交的(partially committed)
当事务处在活动的或者部分提交的状态时,可能遇到了某些错误(数据库自身的错误、操作系统错误或者直接断电等)而无法继续执行,或者人为的停止当前事务的执行,我们就说该事务处在失败的状态
失败的(failed)
如果事务执行了半截而变为失败的状态,要撤销失败事务对当前数据库造成的影响。书面一点的话,我们把这个撤销的过程称之为回滚。当回滚操作执行完毕时,也就是数据库恢复到了执行事务之前的状态,我们就说该事务处在了中止的状态。
中止的(aborted)
当一个处在部分提交的状态的事务将修改过的数据都同步到磁盘上之后,我们就可以说该事务处在了提交的状态。
提交的(committed)
状态流程图
事务的状态
事务
锁机制
Multi-Version Concurrency Control,多版本并发控制
MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存
MVCC在MySQL InnoDB中的实现主要是为了提高数据库的并发能力,用更好的方式去处理读写冲突,在有读写冲突时,即使不加锁也能进行非阻塞的并发读操作
MVCC介绍
为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
当前读
像不加锁的select操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读
之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即MVCC;
快照读可能读到的数据并不一定是最新版本的,有可能是之前的历史版本
快照读可以认为MVCC是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;
快照读
准确的说,MVCC多版本并发控制指的是 “维持一个数据的多个版本,使得读写操作没有冲突” 这一个概念;这仅仅是一个理想概念
在MySQL中,快照读就是MySQL实现MVCC理想模型的一个具体非阻塞读功能的落地实现
快照读本身也是一个抽象概念,MVCC模型在MySQL中的具体实现则是由 3个隐式字段,undo日志 ,Read View 等去完成的
与MVCC的关系
当前读与快照读
不存在问题,不需要进行并发控制
读-读
存在并发安全问题,会产生数据一致性问题,发生脏读、幻读、不可重复读
读-写
存在并发安全问题,会产生数据一致性问题,数据更新丢失
写-写
数据库并发场景
多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照数据
在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能
同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决数据更新丢失问题
MVCC带来的好处
认识MVCC
MVCC的目的就是多版本并发控制,在数据库中的实现,就是为了解决读写冲突,它的实现原理主要是依赖记录中的 3个隐式字段,undo日志 ,Read View 来实现的
6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID
DB_TRX_ID
6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引
DB_ROW_ID
7byte,回滚指针,用于配合undo日志,指向上一个旧版本(存储于rollback segment里)
DB_ROLL_PTR
三个字段
insert undo log
事务在进行update或delete时产生的undo log; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除
update undo log
为了实现InnoDB的MVCC机制,更新或者删除操作都只是设置一下老记录的deleted_bit,并不真正将过时的记录删除。
为了节省磁盘空间,InnoDB有专门的purge线程来清理deleted_bit为true的记录。
为了不影响MVCC的正常工作,purge线程自己也维护了一个read view(这个read view相当于系统中最老活跃事务的read view);
如果某个记录的deleted_bit为true,并且DB_TRX_ID相对于purge线程的read view可见,那么这条记录一定是可以被安全清除的。
purge
undo log
Read View
实现原理
undo log是为了实现事务的原子性,是innodb存储引擎级别的日志,用undo Log来实现多版本并发控制(MVCC)
在写数据之前,先将数据备份到undo log中,然后再进行写操作,如果出现了错误或者执行了ROLLBACK,系统可以利用undo log将数据恢复到事务开始之前的状态
可以理解为:当delete时,undo log中会记录对应的insert;当insert时,undo log中会记录对应的delete;当update时,undo log中记录对应相反的update
那么如何保证这个持久性呢?一个很简单的做法就是在事务提交完成之前把该事务所修改的所有页面都刷新到磁盘,但是这个简单粗暴的做法有些问题刷新一个完整的数据页太浪费了有时候我们仅仅修改了某个页面中的一个字节,但是我们知道在InnoDB中是以页为单位来进行磁盘IO的,也就是说我们在该事务提交时不得不将一个完整的页面从内存中刷新到磁盘,我们又知道一个页面默认是16KB大小,只修改一个字节就要刷新16KB的数据到磁盘上显然是太浪费了。随机IO刷起来比较慢一个事务可能包含很多语句,即使是一条语句也可能修改许多页面,倒霉催的是该事务修改的这些页面可能并不相邻,这就意味着在将某个事务修改的Buffer Pool中的页面刷新到磁盘时,需要进行很多的随机IO,随机IO比顺序IO要慢,尤其对于传统的机械硬盘来说。
redo log是为了实现事务的持久性,是innodb存储引擎级别的日志
当发生数据修改的时候,innodb引擎会先将记录写到redo log中,并更新内存,此时更新就算是完成了,同时innodb引擎会在合适的时机将记录更新到磁盘中
redo log是固定大小的,是循环写的过程; 有了redo log之后,innodb就可以保证即使数据库发生异常重启,之前的记录也不会丢失,叫做crash-safe
redo log的保存分为两个阶段(prepare和commit)
redo log
binlog是MySQL server服务端的日志级别,主要记录mysql功能层面的日志
redo log是innodb独有的,binlog是所有引擎都可以使用的
redo log是物理日志,记录的是在某个数据页上做了什么修改;binlog是逻辑日志,记录的是这个语句的原始逻辑
redo log是循环写的,会覆盖旧的信息,空间会用完;binlog是可以追加写的,不会覆盖之前的日志信息
与redo log的区别
binlog中会记录所有的逻辑,并且采用追加写的方式,一般在企业中数据库会有备份系统,可以定期执行备份,备份的周期可以自己设置
恢复数据的过程:找到最近一次的全量备份数据;从备份的时间点开始,将备份的binlog取出来,重放到要恢复的那个时刻
binlog
1、执行器先从引擎中找到数据,如果在内存中直接返回,如果不在内存中,查询后返回
2、执行器拿到数据之后会先修改数据,然后调用引擎接口重新吸入数据
3、引擎将数据更新到内存,同时写数据到redo log中,此时处于prepare阶段,并通知执行器执行完成,随时可以操作
4、执行器生成这个操作的binlog
5、执行器调用引擎的事务提交接口,引擎把刚刚写完的redo log改成commit状态,更新完成
若在commit没有发生,redo log和binlog都没有提交成功,保证了数据恢复时的一致性
数据更新的流程
日志
MVCC
具备外键支持功能的事务存储引擎
InnoDB
主要的非事务处理存储引擎
MyISAM
用于数据存档(行被插入后不能再修改)
ARCHIVE
丢弃写操作,读操作会返回空内容
BLACKHOLE
在存储数据时,以逗号分隔各个数据项
CSV
用来访问远程表
FEDERATED
置于内存的表
MEMORY
用来管理多个MyISAM表构成的表集合
MERGE
MySQL集群专用存储引擎
NDB
修改表的存储引擎:ALTER TABLE engine_demo_table ENGINE = InnoDB;
建表时指定存储引擎:Create Table: CREATE TABLE `engine_demo_table` ( `i` int(11) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8
存储引擎
B+树索引叶子结点从小到大存储,非叶子结点都是目录节点,一般情况下,我们用到的B+树都不会超过4层,那我们通过主键值去查找某条记录最多只需要做4个页面内的查找(查找3个目录项页和一个用户记录页),又因为在每个页面内有所谓的Page Directory(页目录),所以在页面内也可以通过二分法实现快速定位记录,这是真的很快!
索引的存储方式
假设,假设所有存放用户记录的叶子节点代表的数据页可以存放100条用户记录,所有存放目录项记录的内节点代表的数据页可以存放1000条目录项记录,那么:如果B+树只有1层,也就是只有1个用于存放用户记录的节点,最多能存放100条记录。如果B+树有2层,最多能存放1000×100=100000条记录。如果B+树有3层,最多能存放1000×1000×100=100000000条记录。如果B+树有4层,最多能存放1000×1000×1000×100=100000000000条记录。
B+索引能存储的数据量
聚簇索引:使用主键值进行记录和页的排序且存储的树完整的数据记录,我们把具有这两种特性的B+树称为聚簇索引,所有完整的用户记录都存放在这个聚簇索引的叶子节点处。这种聚簇索引并不需要我们在MySQL语句中显式的使用INDEX语句去创建,InnoDB存储引擎会自动的为我们创建聚簇索引。另外有趣的一点是,在InnoDB存储引擎中,聚簇索引就是数据的存储方式(所有的用户记录都存储在了叶子节点),也就是所谓的索引即数据,数据即索引。
二级索引:非聚簇索引就是二级索引,二级索引里存储的数据包括索引列加上主键,没有索引覆盖时(查询的列超过索引的列),需要通过二级索引树先定位到主键,再通过主键到聚簇索引里定位记录,这步被称为回表
B+树索引类型
这个是显而易见的,每建立一个索引都要为它建立一棵B+树,每一棵B+树的每一个节点都是一个数据页,一个页默认会占用16KB的存储空间,一棵很大的B+树由许多数据页组成,那可是很大的一片存储空间呢。
空间的代价
每次对表中的数据进行增、删、改操作时,都需要去修改各个B+树索引。而且我们讲过,B+树每层节点都是按照索引列的值从小到大的顺序排序而组成了双向链表。不论是叶子节点中的记录,还是内节点中的记录(也就是不论是用户记录还是目录项记录)都是按照索引列的值从小到大的顺序而形成了一个单向链表。而增、删、改操作可能会对节点和记录的排序造成破坏,所以存储引擎需要额外的时间进行一些记录移位,页面分裂、页面回收啥的操作来维护好节点和记录的排序。如果我们建了许多索引,每个索引对应的B+树都要进行相关的维护操作,这还能不给性能拖后腿么?所以说,一个表上索引建的越多,就会占用越多的存储空间,索引越多增删改记录的时候性能就越差,要对多个索引树操作。
时间上的代价
索引的代价
全值匹配匹配左边的列匹配范围值精确匹配某一列并范围匹配另外一列用于排序用于分组
索引的使用场景
只为用于搜索、排序或分组的列创建索引(加快查询效率)为列的基数大的列创建索引(基数小的建了没意义,还得一条条数据筛选)索引列的类型尽量小(每个索引会建一颗B+树)可以只对字符串值的前缀建立索引(让索引占的空间更小)只有索引列在比较表达式中单独出现才可以适用索引(才能用到索引)为了尽可能少的让聚簇索引发生页面分裂和记录移位的情况,建议让主键拥有AUTO_INCREMENT属性。(减少性能损耗,AUTO_INCREMENT的主键太有规律可以使用雪花算法生成自增的主键ID)定位并删除表中的重复和冗余索引(减少空间和时间上的性能损耗)尽量使用覆盖索引进行查询,避免回表带来的性能损耗。span style=\
索引使用的注意事项
当我们使用左或者左右模糊匹配的时候,也就是 like %xx 或者 like %xx%这两种方式都会造成索引失效;当我们在查询条件中对索引列使用函数,就会导致索引失效。当我们在查询条件中对索引列进行表达式计算,也是无法走索引的。MySQL 在遇到字符串和数字比较的时候,会自动把字符串转为数字,然后再进行比较。如果字符串是索引列,而条件语句中的输入参数是数字的话,那么索引列会发生隐式类型转换,由于隐式类型转换是通过 CAST 函数实现的,等同于对索引列使用了函数,所以就会导致索引失效。联合索引要能正确使用需要遵循最左匹配原则,也就是按照最左优先的方式进行索引的匹配,否则就会导致索引失效。在 WHERE 子句中,如果在 OR 前的条件列是索引列,而在 OR 后的条件列不是索引列,那么索引会失效。联合索引ASC、DESC混用
索引失效的场景
在使用idx_name_birthday_phone_number索引进行查询时大致可以分为这两个步骤:从索引idx_name_birthday_phone_number对应的B+树中取出name值在Asa~Barlow之间的用户记录。由于索引idx_name_birthday_phone_number对应的B+树用户记录中只包含name、birthday、phone_number、id这4个字段,而查询列表是*,意味着要查询表中所有字段,也就是还要包括country字段。这时需要把从上一步中获取到的每一条记录的id字段都到聚簇索引对应的B+树中找到完整的用户记录,也就是我们通常所说的回表,然后把完整的用户记录返回给查询用户。由于索引idx_name_birthday_phone_number对应的B+树中的记录首先会按照name列的值进行排序,所以值在Asa~Barlow之间的记录在磁盘中的存储是相连的,集中分布在一个或几个数据页中,我们可以很快的把这些连着的记录从磁盘中读出来,这种读取方式我们也可以称为顺序I/O。根据第1步中获取到的记录的id字段的值可能并不相连,而在聚簇索引中记录是根据id(也就是主键)的顺序排列的,所以根据这些并不连续的id值到聚簇索引中访问完整的用户记录可能分布在不同的数据页中,这样读取完整的用户记录可能要访问更多的数据页,这种读取方式我们也可以称为随机I/O。一般情况下,顺序I/O比随机I/O的性能高很多,所以步骤1的执行可能很快,而步骤2就慢一些。所以这个使用索引idx_name_birthday_phone_number的查询有这么两个特点:会使用到两个B+树索引,一个二级索引,一个聚簇索引。访问二级索引使用顺序I/O,访问聚簇索引使用随机I/O。需要回表的记录越多,使用二级索引的性能就越低,甚至让某些查询宁愿使用全表扫描也不使用二级索引。比方说name值在Asa~Barlow之间的用户记录数量占全部记录数量90%以上,那么如果使用idx_name_birthday_phone_number索引的话,有90%多的id值需要回表,这不是吃力不讨好么,还不如直接去扫描聚簇索引(也就是全表扫描)。那什么时候采用全表扫描的方式,什么时候使用采用二级索引 + 回表的方式去执行查询呢?这个就是传说中的查询优化器做的工作,查询优化器会事先对表中的记录计算一些统计数据,然后再利用这些统计数据根据查询的条件来计算一下需要回表的记录数,需要回表的记录数越多,就越倾向于使用全表扫描,反之倾向于使用二级索引 + 回表的方式。当然优化器做的分析工作不仅仅是这么简单,但是大致上是个这个过程。一般情况下,限制查询获取较少的记录数会让优化器更倾向于选择使用二级索引 + 回表的方式进行查询,因为回表的记录越少,性能提升就越高
上边的讨论对回表这个词儿多是一带而过,可能大家没啥深刻的体会,索引为idx_name_birthday_phone_number看下边这个查询SELECT * FROM person_info WHERE name > 'Asa' AND name < 'Barlow'
回表的代价
InnoDB快速查询的秘籍:B+树索引
一条条查询语句在经过MySQL查询优化器的各种基于成本和规则的优化会后生成一个所谓的执行计划,这个执行计划展示了接下来具体执行查询的方式,比如多表连接的顺序是什么,对于每个表采用什么访问方法来具体执行查询等等。EXPLAIN语句可以帮助我们查看某个查询语句的具体执行计划
explain的使用
EXPLAIN语句输出的各个列的作用
mysql> EXPLAIN FORMAT=JSON SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key2 WHERE s1.common_field = 'a'\\G*************************** 1. row ***************************EXPLAIN: { \"query_block\": { \"select_id\
我们上边介绍的EXPLAIN语句输出中缺少了一个衡量执行计划好坏的重要属性 —— 成本。在EXPLAIN单词和真正的查询语句中间加上FORMAT=JSON。这样我们就可以得到一个json格式的执行计划,里边儿包含该计划花费的成本
Json格式的执行计划
SHOW WARNINGS展示出来的信息有三个字段,分别是Level、Code、Message。我们最常见的就是Code为1003的信息,当Code值为1003时,Message字段展示的信息类似于查询优化器将我们的查询语句重写后的语句。比如我们上边的查询本来是一个左(外)连接查询,但是有一个s2.common_field IS NOT NULL的条件,s着就会导致查询优化器把左(外)连接查询优化为内连接查询,从SHOW WARNINGS的Message字段也可以看出来,原本的LEFT JOIN已经变成了JOIN。但是大家一定要注意,我们说Message字段展示的信息类似于查询优化器将我们的查询语句重写后的语句,并不是等价于,也就是说Message字段展示的信息并不是标准的查询语句,在很多情况下并不能直接拿到黑框框中运行,它只能作为帮助我们理解查MySQL将如何执行查询语句的一个参考依据而已。
我们使用EXPLAIN语句查看了某个查询的执行计划后,紧接着还可以使用SHOW WARNINGS语句查看与这个查询的执行计划有关的一些扩展信息
Extented EXPLAIN
explain详解
mysql
0 条评论
回复 删除
下一页