mysql核心知识(一)
一.存储引擎之MyISAM和InnoDB
使用语句可以查看mysql可以提供的存储引擎
show engines;
展示结果如下:
其中MyISAM是mysql5.5之前的版本默认的存储引擎,之后的版本默认使用InnoDB,为什么会替换成InnoDB呢?来对比下MyISAM与InnoDB的区别
- InnoDB能支持事务、外键,MyISAM不支持。
- InnoDB支持行级锁、MyISAM只支持表锁。
- 如果表使用InnoDB,将会在/data目录下生成的是*.ibd(mysql8只有一个),*.frm文件,如果是MyISAM则会生成三个文件,分别是
- *.frm:描述表结构、字段长度
- *.MYD(myDATA),存储数据信息
- *.MYI(MYindex):存储索引信息
- InnoDB写的效率要低于MYISAM,且占用的空间也会比MYISAM大。
- MyISAM索引只存储数据地址,不存储真实数据;InnoDB会存储真实数据,内存要去也会比MyISAM高
- MyISAM的索引是非聚簇索引,二InnoDB是聚簇索引+非聚簇索引。
总结:MyISAM主要处理非事务性的存储引擎,主要以读为主的业务。
二.索引篇
索引是一种数据结构,简单理解为排好序的快速查找数据结构 ,主要有如下作用:
- 可以降低数据库的IO,提高查询效率
- 通过唯一索引可保证数据的唯一性
- 加速表与表之间的连接,多表联合查询时提高查询效率
- 使用分组或排序时,减少分组排序时间,降低CPU的消耗
同时索引也有如下缺点:
- 占用磁盘空间
- 数据新增、删除时需要维护索引,降低了修改删除的效率。
B+树
B+树是mysql数据库索引默认的存储结构,叶子节点存储所有数据,且叶子节点通过双向链表排序。
索引分类
- 聚簇索引(聚集索引):一般根据表的主键生成的索引,每张表只有一个聚集索引。
如果表种没有主键,则会找一个唯一非空的字段替代,如果二则都没有则Mysql会自己生成一个隐式的聚簇索引。 - 辅助索引:叶子节点存储聚簇索引(主键值),非叶子节点存储索引值,一张表种可以有多个辅助索引
- 联合索引:
非主键索引的查询过程:
如:select * from user where name = ‘张’,name的索引是辅助索引,先根据辅助索引查询到主键索引,由于是select * ,所以
MyISAM和InnoDB中根据索引查询数据的过程
MyISAM索引结构
InnoDB索引结构
索引常见的面试题
为什么说B+树查询单行记录时,最多只需要1~3次磁盘IO?
如果主键是bitint类型的数据,bigint占用8个字节,那么非叶子节点存储的数据为主键值(8字节),向下的指针(4-8字节),mysql读取磁盘数据为按页读取,每页数据默认是16KB,大概每页存储的数据调试为:(1024*16)/(8+8)估算成1000,也就是说一个根节点能大概存储1000条主键+指针数据。那么第二层大概可以存储10002 条数据,则已经超过1000000条(100万)数据了,第三次数据条数10003估算能超过10亿调数据了。
实际情况中每个节点的数据可能不能填充满,因此B+树的实际高度一般都在2~4层
MYSQL设计将根节点缓存到内存中,所以实际查询时可能只需要1~3层IO
-- 查看每页读取数据大小
show variables like '%innodb_page_size%';
为什么要控制单行数据的大小
如果每条数据长度超过16KB,那么一个内存页就只能存储一条数据,这样效率就比较低,所以我们在设计数据库字段长度时就要注意不能使单条数据长度过长。
为什么数据库索引结构不能用hash、B树
B树与B+树的区别:
- B树在非叶子节点也会存储数据,所以搜索可能不需要到达叶子节点就能完成
- 由于B树的非叶子节点也存储了数据、那么叶子节点的数据就不像B+树一样是完整的,所以对排序、范围查找就不能很好的支持。
hash表是无序的,虽然查询效率高,但是不支持范围查找,如id>5,也不支持模糊查询,B+树可以支持‘xxx%’的模糊。
什么是最左前缀原则(模糊查询)
以最左边的为起点任何连续的索引都能匹配上。
如查询张姓的客户,查询语句where like ‘张%’,则在叶子节点种当发现一个为张的数据后直接再加载之后的数据(因为B+树已经排好序了),直到匹配不上以“张”开头的索引则查询结束,效率非常高。
如何查看索引建立是否合理
建立索引的基础应该是离散性越高越好,可以通过语句查询。
show index from [table];
如果cardinality的值越接近1,则越合理。
索引的设计原则
哪些情况适合建索引
- 数据又数值有唯一性的限制
- 频繁作为where条件的字段
- 经常使用group by 和order by的字段,既有group by 又有order by的字段时,建议建联合索引
- 经常作为update或delete条件的字段
- 经常需要
distinct
的字段- 多表连接时的字段建议创建索引,也有**
注意事项
**
- 连接表数量最好不要超过3张,每增加一张表就相当于增加了一次嵌套循环,数量级增长会非常快
- 对多表查询时的where条件创建索引
- 对连接字段创建索引,并且数据类型保持一致- 再确定数据范围的情况下尽量使用数据类型较小的,因为索引会也会占用空间
- 对字符串创建索引时建议使用字符串的前缀作为索引
这样做的好处是:1. 能节省索引的空间,2.虽然不能精确定位,但是能够定位到相同的前缀,然后通过主键查询完整的字符串,这样既能节省空间,又减少了字符串的比较时间,还能解决排序问题。- 区分度高(散列性高)的字段适合作为索引。
- 在多个字段需要创建索引的情况下,联合索引优先与单值索引。使用最频繁的列作为索引的最左侧 。
哪些情况下不需要使用索引
- 在where条件中用不到的字段不需要。
- 数据量小的不需要建索引,比如数据少于1000条。
- 由大量重复数据的列上不要建索引,比如性别字段中只有男和女时。
- 避免在经常更新的表或字段中创建过多的索引。
- 不建议主键使用无序的值作为索引,比如uuid。
- 不要定义冗余或重复的索引
例如:已经创建了联合索引key(id,name)后就不需要再单独建一个key(id)的索引
索引失效的场景
序号 | 场景描述 | 场景 |
---|---|---|
1 | 当like 内容中的百分号在前面时失效,具体原因参考第一张最左原则 |
LIKE |
2 | 当有一个不是索引时失效。如 a=‘1’ or b= ‘2’,a有索引,b没有索引 | OR |
3 | 联合索引时,没有按住首个字段索引查询时失效,如字段(a,b)是联合索引,查询 b=‘1’会失效 | 联合索引 |
4 | 对字段进行预算或函数时会失效。如:a+1 = ‘c’,date(a) = now() | 运算或函数 |
5 | 使用!= 或 not取反时会失效 | not 或 != |
6 | 当数据类型使用错误时会失效,例如a是int类型,查询时使用where a = '1’会失效 | 数据类型错误时 |
7 | 范围查询非聚簇索引时,不同的版本可能会导致索引失效,例如:a字段是非主键,where a > 6;mysql5.5版本之前可能会失效 原因见下面的章节《索引优化之MRR》 | 非主键范围查询 |
索引优化之MRR
例如有一张表user,主键id,普通字段age,为age创建非聚集索引,有一条查询语句select
*
user from table where age > 18;(注意查询语句中的结果是*)在Mysql5.5以及之前的版本中如何查询呢?
- 先通过非聚集索引查询到age>18的第一条数据,获取到了主键id
- 然后根据非聚集索引中的叶子节点存储的主键id去聚集索引中查询行数据
- 根据age>18的数据条数每次查询聚集索引,这个过程叫做回表。
上述的步骤有什么缺点呢?如何age>18的数据非常多,那么每次回表都需要经过3次IO(假设B+树的高度是3),那么会导致查询效率过低。
在Mysql5.6时针对上述问题进行了优化,优化器
- 先查询到age>18的所有数据的主键id
- 对所有主键的id进行排序,排序的结果缓存到read_rnd_buffer
- 然后通过排好序的主键在聚簇索引中进行查询
如果两个主键的范围相近,在同一个数据页中就可以之间按照顺序获取,那么磁盘io的过程将会大大降低。
这个优化的过程就叫做Multi Range Read(MRR) 多返回查询。
查看索引树的高度?
三.数据库存储结构:页
页(page)是mysql磁盘与内存交互的基本单位,innodb中默认大小是16kb,支持大小为2kb、4、8、16、32、64KB,一页中可以存储多行记录。页与页在物理结构上可以不相连,只要通过双向链表关联即可。每页内部的数据都会通过主键从小到大组成一个单项链表。
查看innodb中的每一页的大小
show variables like '%innodb_page_size%';