一、存储引擎
在文件系统中,MySQL将每个数据库(或者叫Schema)保存为数据目录下的一个子目录。创建表是,MySQL会在数据库子目录下创建一个和表同名的.frm文件保持表的定义。不同存储引擎保存数据和索引的方式是不同的,但表的定义则是在MySQL服务器层统一处理。
InnoDB
InnoDB是MySQL的默认事务型引擎,也是最重要、使用最广泛的存储引擎。它被设计用来处理大量的短期(short-lived)事务,短期事务大部分情况是正常提交的,很少会被回滚。
- InnoDB的数据存储在表空间(tablespace)中,表空间是由InnoDB管理的一个黑盒子,有一系列的文件组成。
- InnoDB采用“MVCC”来支持高并发,并且实现了4个标准的隔离级别。其中默认级别是REPEATABLE READ(可重复读),并且通过间隙锁(next-key locking)策略来防止幻读的出现。间隙锁使得InnoDB不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定,已防止幻影行的插入
- InnoDB表是基于聚簇索引建立的
InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
MyISAM
MyISAM提供了大量的特性,包括全文索引、压缩、空间函数(GIS)等,但MyISAM不支持“事务”和“行级锁”,而且有一个重要的缺陷就是崩溃后无法安全恢复。但由于MyISAM数据以紧密格式存储,所以在某些场景下的性能很好。
MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
---Innodb与MyISAM的区别
1. 存储结构
MyISAM存储表分为三个文件frm(表结构)、MYD(表数据)、MYI(表索引),而Innodb如上文所说,根据存储方式不同,存储结构不同。
2. 事务支持
MyISAM不支持事务,而Innodb支持事务,具有事务、回滚和恢复的事务安全。
- 外键和主键
MyISAM不支持外键,而Innodb支持外键。MyISAM允许没有主键,但是Innodb必须有主键,若未指定主键,会自动生成长度为6字节的主键。
4. 锁
MyISAM只支持表级锁,而Innodb支持行级锁,具有比较好的并发性能,但是行级锁只有在where子句是对主键筛选才生效,非主键where会锁全表
5. 索引
MyISAM使用B+树作为索引结构,叶节点保存的是存储数据的地址,主键索引key值唯一,辅助索引key可以重复,二者在结构上相同。Innodb也是用B+树作为索引结构,数据表本身就是按照b+树组织,叶节点key值为数据记录的主键,data域为完整的数据记录,辅助索引data域保存的是数据记录的主键。
Archive引擎
Archive 不支持事务,支持表级别锁定,不支持 B-tree、Hash、Full-text 等索引;
Blackhole引擎
CSV引擎
Federated引擎
Memory引擎
如果需要快速地访问数据,并且这些数据不会被修改,重启以后丢失也没有关系,那边使用Memory表也是非常有用的。常用于:
用于查找(lookup)或者映射(mapping)表
用于缓存周期性聚合数据(periodically aggregated data)的结果
-
用户保存数据分析中产生的中间数据
另外,虽然Memory表的读取速度很快,但它只支持表级锁,因此并发写入的性能较低。
Memory 不支持事务,支持表级别锁定,支持 B-tree、Hash 等索引,不支持 Full-text 索引;
NDB集群引擎
NDB 支持事务,支持行级别锁定,支持 Hash 索引,不支持 B-tree、Full-text 等索引;
二、索引
索引介绍
索引在mysql中也做“键”,是存储引擎用于快速找到记录的一种数据结构。在一般的应用系统中大多是读多写少,而且在数据量大的时候,数据读取的性能问题尤为显著。在没有设置索引的时候,数据是“顺序读取”,这样在1000万条数据检索某一条数据,可能要遍历1000万次,时间复杂度为o(n), 当使用索引后,速度提高了好几个数量级。一个是采用了“二分查找”,时间复杂度为o(logn), 第二在索引中的数据,不需要存放每条数据的全部内容,这样使得每个块存放的数据更多,减少了磁盘io,从而大大提高了查询效率。
索引原理
1、 索引原理
索引的目的在于提高查询效率,与我们查阅图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个小节,然后找到页数。相似的例子还有:查字典,查火车车次,飞机航班等。
本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。
数据库也是一样,但显然要复杂的多,因为不仅面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查询(or)等等。数据库应该选择怎么样的方式来应对所有的问题呢?我们回想字典的例子,能不能把数据分成段,然后分段查询呢?最简单的如果1000条数据,1到100分成第一段,101到200分成第二段,201到300分成第三段......这样查第250条数据,只要找第三段就可以了,一下子去除了90%的无效数据。但如果是1千万的记录呢,分成几段比较好?稍有算法基础的同学会想到搜索树,其平均复杂度是lgN,具有不错的查询性能。但这里我们忽略了一个关键的问题,复杂度模型是基于每次相同的操作成本来考虑的。而数据库实现比较复杂,一方面数据是保存在磁盘上的,另外一方面为了提高性能,每次又可以把部分数据读入内存来计算,因为我们知道访问磁盘的成本大概是访问内存的十万倍左右,所以简单的搜索树难以满足复杂的应用场景。
2、 磁盘IO与预读
考虑到磁盘IO是非常高昂的操作,计算机操作系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,因为局部预读性原理告诉我们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操作系统有关,一般为16k或8k,也就是我们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计非常有帮助。
索引分类
按照命名分类:
一共三类: 单值索引(普通索引、 唯一索引、 主键索引),组合索引,全文索引。
普通索引
用表中的普通列构建的索引,没有任何限制
唯一索引
唯一索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
主键索引
根据主键建立索引,不允许重复,不允许空值;
全文索引
仅可用于MyISAM表,针对较大的数据,生成全文索引非常的消耗时间和空间(在生成FULLTEXT索引时,会为文本生成一份单词的清单,在索引时及根据这个单词的清单来索引)。
组合索引
又叫联合索引。用多个列组合构建的索引,这多个列中的值不允许有空值。可以在创建表的时候指定,也可以修改表结构。
按照存储结构分类:
“聚集索引”在一个表中只有一个,一般是表中的主键字段,存储特点是在叶子节点存放了每条数据全部内容,“聚集索引”之外的都是“非聚集索引”,每条数据在叶子节点只存放了索引字段和其对应的主键,所以查询通过“非聚集索引”查询整行数据时,会造成“回表”。“覆盖索引”是一种组合索引,当查询的字段和索引的字段相同,我们称这种组合索引为“覆盖索引”,这时候虽然索引的叶子节点未存放整行数据,但是查询的时候也不需要“回表”,所以有些特定场景下,可以设置覆盖索引来避免回表操作。
聚集索引
定义:数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引。
非聚集索引
定义:该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同,一个表中可以拥有多个非聚集索引。
索引数据结构分类:
用于提高读写效率的数据结构有很多,这里先介绍常见的3种,分别是:
哈希索引
哈希表是一种以键-值(key-value)的方式存储数据的结构,我们只要输入待查找的值(即key),就可以找到其对应的值(即Value)。哈希的思路很简单,把值放在数组里,用一个哈希函数把key换算成一个确定的位置,然后把value放在数组的这个位置,即idx = Hash(key)。如果出现哈希冲突,就采用拉链法解决。
因为哈希表中存放的数据不是有序的,因此不适合做区间查询,适用于只有等值查询的场景。
有序数组
有序数组在等值查询和范围查询场景中的性能都非常优秀。用二分法就可以快速找到(时间复杂度为O(logN))。但是如果要往中间插入一条数据,则必须挪动后面的所有记录,成本较高。因此,有序数组只适用于静态存储引擎,即数据表一旦建立后不再会修改。
搜索树
搜索树分为B树和B+树,在mysql的MyISAM和Innodb中都是使用的B+树数据结构作为索引,在mongdb中使用的B树数据结构作为索引。下面会有章节具体描述B树和B+树的区别。
MyISAM存储引擎--B+索引
磁盘存储
MyISAM在磁盘存储上有三个文件,每个文件名以表名开头,扩展名指出文件类型。
- .frm:用于存储表的定义。
- .MYI:用于存放表索引。
- .MYD:用于存放数据。
索引结构
MyISAM引擎使用“B+树”作为索引结果,叶节点的data域存放的是数据记录的地址。在结构上没有任何区别,通过主键列作为索引是是主键索引,通过其他列作为索引的时候是辅助索引(辅助索引可以重复),叶子节点都是包含索引列和指向数据行的地址。
=================================================================
Innodb存储引擎----B+索引
MySQL5.5开始支持InnoDB引擎,并将其作为默认数据库引擎。
磁盘存储
Innodb有两种存储方式,“共享表空间”存储和“多表空间”存储,Innodb只有表结构文件和数据文件,“表结构文件”和MyISAM一样,以表名开头,扩展名是.frm,“数据文件”与存储方式有关。
- 如果使用“共享表空间”,那么所有表的数据文件和索引文件都保存在一个表空间里,一个表空间可以有多个文件,通过innodb_data_file_path和innodb_data_home_dir参数设置共享表空间的位置和名字,一般共享表空间的名字叫ibdata1-n。
- 如果使用“多表空间”,那么每个表都有一个表空间文件用于存储每个表的数据和索引,文件名以表名开头,以.ibd为扩展名。
索引结构
Btree查询过程
B树的搜索,从根结点开始,对结点内的关键字(有序)序列进行二分查找,如果命中则结束,否则进入查询关键字所属范围的儿子结点;重复,直到所对应的是叶子结点。
查找文件29的过程:
- 根据根结点指针找到文件目录的根磁盘块1,将其中的信息导入内存(在内存中遍历)。(磁盘IO操作1次)
- 此时内存中有两个文件名17,35和三个存储其他磁盘页面地址的数据。根据算法我们发现17<29<35,因此我们找到指针p2。
- 根据p2指针,我们定位到磁盘块3,并将其中的信息导入内存。(磁盘IO操作2次)
- 此时内存中有两个文件名26,30和三个存储其他磁盘页面地址的数据。根据算法我们发现26<29<30,因此我们找到指针p2。
- 根据p2指针,我们定位到磁盘块8,并将其中的信息导入内存。(磁盘IO操作3次)
- 此时内存中有两个文件名28,29。根据算法我们查找到文件29,并定位了该文件内存的磁盘地址。
B+tree特点
1、 B+tree是基于btree优化的一种搜索树,只有叶子节点存放“数据”,非叶子节点只存“键(也就是索引列的值)”,且叶子节点由指针串连起来,当查询到叶子节点的值不是目标值,且目标值在返回的叶子节点所在的磁盘块,可以通过指针顺序遍历到目标值,无需再从跟节点重新遍历(如果不在一个磁盘块,那么还是要重新遍历的)。根据叶子节点的存放数据的完整性不同,将索引分为“聚集”和“非聚集”两种类型,聚集索引中存放了主键key和整行的数据,非聚集索引中存放了索引列和对应的主键列,如果通过非聚集索引按*检索,则会产生回表操作(如上图)。b+tree的查询过程跟上面的b树基本一致(除了叶子节点指针和回表两个区别之外)。
2、通过上面的分析,我们知道IO次数取决于b+数的高度h,假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N,当数据量N一定的情况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。这就是为什么每个数据项,即索引字段要尽量的小,比如int占4字节,要比bigint8字节少一半。这也是为什么B-tree要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高。当数据项等于1时将会退化成线性表。
3、当B-tree的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,B-tree会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,B-tree就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,B-tree可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性。
4、如果定义了主键,Innodb会选择主键作为聚集索引;如果没有定义主键,Innodb会选择不包含NULL值的唯一索引作为聚集索引;如果也没有这样的唯一索引列,Innodb会选择内置6字节长的rowID作为隐含的聚集索引,这里的RowId会随着记录的写入而主键自增,但是它是不可引用和查看的,是数据库引擎内部的使用。
5、 如果我们使用自增主键,那么每次插入的新纪录都在原先记录的尾部按照顺序,添加到当前节点的索引后面,当一页快写满的时候,就会开辟一个新的页。数据记录本身就存与主索引的叶子节点上,B+tree的树。这就要求每一个叶子节点内的各条数据记录按主键顺序存放,因此每当有一条新的记录插入的时候,MYSQL会根据其主键将其插入到合适的节点和位置上,如果页面达到装载因子(INNODB默认为15/16),则开辟新的页面(节点)。
6、如果使用非自增主键(如果身份证号或学号等),由于每次插入主键的值近似于随机,因此每次新纪录都要被插到现有索引页得中间某个位置,此时MySQL不得不为了将新记录插到合适位置而移动数据,甚至目标页面可能已经被回写到磁盘上而从缓存中清掉,此时又要从磁盘上读回来,这增加了很多开销,同时频繁的移动、分页操作造成了大量的碎片,得到了不够紧凑的索引结构,后续不得不通过OPTIMIZE TABLE来重建表并优化填充页面。
7、红黑树或者平衡二叉树的其他树结构,
- h明显要深的多,执行效率低。
- 逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,
- 每个节点存储的数据量太小了,对磁盘空间造成浪费,带来频繁的IO操作。
8、下面的表总结了何时使用聚集索引或非聚集索引(很重要):
|
动作描述
|
使用聚集索引
|
使用非聚集索引
|
|
列经常被分组排序
|
应
|
应
|
|
返回某范围内的数据
|
应
|
不应
|
|
一个或极少不同值
|
不应
|
不应
|
|
频繁更新的列
|
不应
|
应
|
|
外键列
|
应
|
应
|
|
主键列
|
应
|
应
|
|
频繁修改索引列
|
不应
|
应
|
B树和B+树索引的区别
B树的特性
- 叶子节点和非叶子节点都存放数据,且每个节点存放了键(索引列)和数据,所有的磁盘块加起来代表索引的全集,没有聚集和非聚集索引的区分。
- 任何一个关键字出现且只出现在一个节点中。
- 搜索有可能在非叶子结点结束。
- 其搜索性能等价于在关键字全集内做一次二分查找。
- 虽然通过二分查找提高了查询时间复杂度,但是还是对范围查询支持的不好,只能查一个节点,而不能查一个磁盘块内的相邻节点。
B+树的特性
- 只有叶子节点才存放数据,非叶子节点只存放键(索引列的值)。
- 任何一个关键字可能出现在多个节点中。
- 搜索只会在叶子节点结束。(这个是mongdb选择b树的原因)
- 叶子节点由指针串连起来,方便顺序查找,如果查找的值在一个磁盘块中,可以避免回表。
- 内部节点并没有指向关键字具体信息的指针,节省了内部节点的存储空间。
- 分为“聚集索引”和“非聚集索引”,区别是叶子节点是否存放完整行数据。
==================================================================
--最左前缀
ALTER TABLE 'table_name' ADD INDEX index_name('col1','col2','col3');
为了更多的提高mysql效率可建立组合索引,遵循”最左前缀“原则。创建复合索引时应该将最常用(频率)作限制条件的列放在最左边,依次递减。示例的组合索引相当于建立了col1;col1col2;col1col2col3三个索引,而col2或者col3是不能使用索引的,也就是顺序的使用索引,索引才会生效。
组合索引在where、order by、group by中最左原则体现形式,先判断完where,然后判断group by,最后判断order by, 它们之间是有传递性的,与select什么字段没关系。比如select * from T where col1='Q' and col3='P' order by col2;这时候查询过程中只有col1生效,因为col3中间少了col2;然后排序的时候,col2在排序时是生效的。
组合索引和单列索引相比是对多个列创建索引。组合索引本质上也是一颗B+树,只不过它的键值是大于等于2的,好比('col1','col2')的值有(1,2)、(2,4)、(2,1)、(1,3)、(2,3),那么它也是按照顺序进行存放的:(1,2)、(1,3)、(2,1)、(2,3)、(2,4),例如对于SELECT * FROM TABLE col1=xxx AND col2=xxx,这个明显是可以使用('col1','col2')这个组合索引,而且对于SELECT * FROM TABLE col1=xxx ,但是对于SELECT * FROM TABLE col2=xxx就不能使用('col1','col2')的组合索引了,因为可以看到,对于col2本来说,2,3,1,3,4并不是排序的。这也就是组合索引的最左前缀问题。
①最佳左前缀法则。
1.在等值查询时,更改索引列顺序,并不会影响explain的执行结果,因为mysql底层会进行优化。
2.在使用order by时,注意索引顺序、常量,以及可能会导致Using filesort的情况。
②group by容易产生Using temporary。
③通俗理解口诀:
全值匹配我最爱,最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
LIKE百分写最右,覆盖索引不写星;
不等空值还有or,索引失效要少用。
-- 回表
在使用非聚集索引查询过滤数据的时候,如果返回值中包含非索引列,那么需要通过叶子节点的主键字段去主键索引中再遍历一遍,这个过程称为回表操作。
-- 覆盖索引
覆盖索引是一个逻辑上的概念,当查询返回列与索引列相同,查询不需要回表,我们称这时候的索引为覆盖索引,他可能是单值索引,也可能是组合索引。有时候表中的列很多,数据量也很大,而我们查询返回的列很少,为了不使用主键索引,也为了不回表,我们会构建组合索引,包含所有返回的列,这样就可以避免回表产生。
覆盖查询失效
- select选择的字段中含有不在索引中的字段 ,即索引没有覆盖全部的列。
- 所有使索引失效的情况。
--索引下推(组合索引下的查询优化)
MySQL 5.6引入了索引下推优化,可以在索引遍历过程中,对索引中包含的字段先做判断,过滤掉不符合条件的记录,减少回表字数。
需求:index(name,age)
mysq> select * from tuser where name like '张 %' and age=10 and sex=‘男’;
根据前面说的“最左前缀原则”,该语句在搜索索引树的时候,只能匹配到名字第一个字是‘张’的记录(即记录ID3),接下来是怎么处理的呢?当然就是从ID3开始,逐个回表,到主键索引上找出相应的记录,再比对age和ismale这两个字段的值是否符合。、
下面图1、图2分别展示这两种情况。
图 1 中,在 (name,age) 索引里面我特意去掉了 age 的值,这个过程 InnoDB 并不会去看 age 的值,只是按顺序把“name 第一个字是’张’”的记录一条条取出来回表。因此,需要回表 4 次。
图 2 跟图 1 的区别是,InnoDB 在 (name,age) 索引内部就判断了 age 是否等于 10,对于不等于 10 的记录,直接判断并跳过。在我们的这个例子中,只需要对 ID4、ID5 这两条记录回表取数据判断,就只需要回表 2 次。
总结:如果没有索引下推优化(或称ICP优化),当进行索引查询时,首先根据索引来查找记录,然后再根据where条件来过滤记录;在支持ICP优化后,MySQL会在取出索引的同时,判断是否可以进行where条件过滤,也就是说提前执行where的部分过滤操作,在某些场景下,可以大大减少回表次数,从而提升整体性能。
没有索引下推优化的时候,是先通过第一个索引找到所有的符合条件的辅助索引项,然后逐条回表找到对应的主键索引,然后在主键索引中按where进行其他条件的率选。优化后,会先在辅助索引项中,对所有的索引项进行过滤(name,age),然后再回表,在主键索引中对其他非索引字段过滤(sex),这样就减少了回表操作。
--order by
下面是查询city=‘苏州’的包含city,name,age三个字段的user信息,并将查询的结果按照name进行排序的过程。 查询出辅助索引对应的索引块,回表,得到需要返回的所有列,将返回的数据在内存中排序,然后返回。
但这个算法有一个问题,就是如果查询要返回的字段很多的话,那么sort_buffer里面要放的字段数太多,这样内存里能够同时放下的行数很少,要分成很多个临时文件,排序的性能会很差。
全字段排序 VS rowid 排序
- 如果 MySQL 实在是担心排序内存太小,会影响排序效率,才会采用 rowid 排序算法,这样排序过程中一次可以排序更多行,但是需要再回到原表去取数据。
- 如果 MySQL 认为内存足够大,会优先选择全字段排序,把需要的字段都放到 sort_buffer 中,这样排序后就会直接从内存里面返回查询结果了,不用再回到原表去取数据。
- 这也就体现了 MySQL 的一个设计思想:如果内存够,就要多利用内存,尽量减少磁盘访问。
- 对于 InnoDB 表来说,rowid 排序会要求回表多造成磁盘读,因此不会被优先选择。
CREATE TABLE student (
id INT NOT NULL,
age INT, NAME VARCHAR ( 50 )
) ENGINE = INNODB;
insert into student VALUES (1,12,'xiaoming');
insert into student VALUES (3,13,'huahua');
insert into student VALUES (1,11,'jianjian');
insert into student VALUES (2,15,'huihui');
insert into student VALUES (1,10,'linlin');
alter table student add key(id);
alter table student add key(id,age);
show index from student;
可以看到查询的结果为我们分别创建了单列索引id和组合索引(id,age)接着我们查询一下,可以看到可以使用的possible_keys有id,id_2两种,但是优化器执行的是id,也就是单列索引,这是因为相对于组合索引来说,单列索引它的B+树的叶子结点只包含单个键值,所以理论上来说一个页可以存放的记录应该更多。
接着我们继续进行实验:
这次优化器选择了id_2,也就是组合索引,这是因为order by必须进行排序,而对于组合索引id_2来说在创建的时候已经进行过排序了,所以优化器选择了使用id_2组合索引,当然id的单列索引也是可以使用的,下面我们强制使用索引id,可以看到出现了Using temporary; Using filesort,这个说明MySQL进行了一次额外排序,这样性能会下降很多,而直接使用就组合索引id_2就不会出现这种情况了。
Mongdb----索引
MongoDB不是传统的关系性数据库,而是以Json格式作为存储的nosql,目的就是高性能,高可用,易扩展。首先它摆脱了关系模型,所以范围查询和遍历查询的需求就没那么强烈了,其次Mysql由于使用B+树,数据都在叶节点上,每次查询都需要访问到叶节点,而MongoDB使用B-树,所有节点都有Data域,只要找到指定索引就可以进行访问。
三、缓存
如何判断缓存命中
MySQL判断缓存命中的方法很简单:缓存存放在一个引用表中,通过一个哈希值引用,这个哈希值包括了如下因数:查询本身、当前要查询的数据库、客户端协议的版本等一些其他可能会影响返回结果的信息。当判断缓存是否命中,MySQL不会解析查询语句、而是直接使用SQL语句和客户端发送过来的原始信息。任何字符上的不同,如空格、注释等,都会导致缓存不命中。
当查询语句中有一些不确定的数据时,则不会被缓存。例如包含函数NOW()等。另外,如果查询中包含任何用户自定义的函数、存储函数、用户变量、临时表、mysql库中的系统表,或者任何不包含列级别权限表,都不会缓存。(另外,如果查询中包含一个不确定的函数,MySQL则不会检查查询缓存。这个说法是不正确的)
如何配置和维护缓存
- query_cache_type,是否打开缓存。可以设置成OFF、ON或DEMAND。DEMAND表示只有在查询语句中明确些了SQL_CACHE的语句才放入缓存,另外这个变量可以是会话级别或者全局级别的。
- query_cache_size,查询缓存使用的总内存空间,单位是字节。这个值必须是1024的整数倍,否则MySQL实际分配的数据会和你指定的策略不同
- query_cache_min_res_unit,在查询缓存中分配内存块时的最小单位(类似于JVM)
- query_cache_limit,MySQL能够缓存的最大查询结果。如果查询结果大于这个值,则不会被缓存。因为查询缓存在数据生成的时候就开始缓存,所以只有当结果全部返回后,MySQL才知道是否超出限制,这时才判断这个查询数据是否从缓存中去掉
- query_cache_wlock_invalidate,如果某个数据表被其他的连接锁住,是否仍然从缓存中返回结果。这个参数默认是OFF
InnoDB和查询缓存
因为InnoDB有自己的MVCC机制,所以相比其他存储引擎,InnoDB和查询缓存的交互要更加复杂。MySQL4.0版本中,在事务处理中查询缓存是被禁止的,从4.1和更新的InnoDB版本开始,InnoDB会控制在一个事务中是否使用查询缓存,InnoDB会同时控制对查询缓存的对和写操作。
事务是否可以访问查询缓存取决于当前事务ID,以及对于的数据表上是否有锁。每一个InnoDB表的内存数据字典都保存了一个事务ID,如果当前事务ID小于该事务ID,则无法访问查询缓存。如果表上有任何的锁,那么对这个表的任何查询都是无法被缓存。
参考:
mysql索引原理与查询优化
https://www.cnblogs.com/bypp/p/7755307.html
组合索引-最左前缀
https://www.cnblogs.com/developer_chan/p/9223671.html
b+tree
https://www.cnblogs.com/vianzhang/p/7922426.html
组合索引,覆盖索引
https://blog.csdn.net/Hollake/article/details/93481372
order by
https://www.toutiao.com/i6816147801047564813/
https://www.cnblogs.com/developer_chan/p/9225638.html