行格式
Mysql一条记录实际上是分两部分存储的:
重点需要知道在 记录的额外信息 中有一个两字节的称为 next_record 指针,通过该指针各条记录可以组成一个单向链表:
单向链表中的记录的主键值是按照从小到大的顺序排序的,也就是说这个单向链表是有序的。
## Innodb数据页结构
InnoDB是以页为单位从磁盘上加载数据的。页面可以配置成 4KB、8KB、16KB、32KB、64KB几种大小,不过默认是16KB的。
、
下边假设某个页中存储了16条用户插入的记录,该数据页的效果图如下所示:
其中 `Infimum记录` 和 `Supremum记录` 是InnoDB给我们自动生成的两个伪记录,并且规定:
`Infimum记录` 作为本页面中最小的记录
`Supremum记录` 作为本页面中最大的记录
为了解决快速搜索的问题,设计InnoDB的大叔引入了一个称作 页目录 的东西。具体的做法就是将单向链表中的记录分成若干个组
然后将把每个组最大的那条记录在页面中的地址(就是距离页面第0个字节处的偏移量)取出来单
独放到页面中的一个部分。每个地址占用2个字节,多个地址就可以组成一个数组结构,如下图所
示:
可见, `页目录` 本质上就是一个指针数组,指针指向的记录是有序的,我们就可以针对这
个 `页目录` 进行二分搜索。比方说我们想找主键值为6的记录,那就可以通过页目录先进行二分查
找,定位到主键值6其实是在第3组里,然后再遍历第3组中的记录,就可以定位到具体的主键值6
的记录在哪里了。
索引用的什么数据结构,为什么选B+不用B为什么不用跳表
B+树非叶子节点不会存储所有节点,层数低
B+树叶子结点有双向链表,可以遍历
b+树是在平衡树的基础上做了矮胖化处理,降低了树高,将一部分随机io转化为顺序io,扫的数据很多,时间复杂度其实是变大了,但是因为磁盘顺序io比随机io快太多了,所以可以牺牲一点时间复杂度来提高性能,但是redis都是在内存里面,不存在随机io和顺序io的区别,也就只需要考虑时空复杂度,用跳表肯定更好,同时跳表的实现相对来说简单一点
什么情况下索引失效
最佳左前缀法则 严禁左模糊或者全模糊 %*%
列 带了表达式
类型转换导致索引失效 123 ‘123’
慢SQL分析与优化
优化方式
SQL 语句执行效率的主要因素
- 数据量
-
- SQL 执行后返回给客户端的数据量的大小;
- 数据量越大需要扫描的 I/O 次数越多,数据库服务器的 IO 更容易成为瓶颈。
- 取数据的方式
-
- 数据在缓存中还是在磁盘上;
- 是否能够通过全局索引快速寻址;
- 是否结合谓词条件命中全局索引加速扫描。
- 数据加工的方式
-
- 排序、子查询、聚合、关联等,一般需要先把数据取到临时表中,再对数据进行加工;
- 对于数据量比较多的计算,会消耗大量计算节点的 CPU 资源,让数据加工变得更加缓慢;
- 是否选择了合适的 join 方式
优化思路
- 减少数据扫描(减少磁盘访问)
-
- 尽量在查询中加入一些可以提前过滤数据的谓词条件,比如按照时间过滤数据等,可以减少数据的扫描量,对查询更友好;
- 在扫描大表数据时是否可以命中索引,减少回表代价,避免全表扫描。
- 返回更少数据(减少网络传输或磁盘访问)
- 减少交互次数(减少网络传输)
-
- 将数据存放在更快的地方
- 某条查询涉及到大表,无法进一步优化,如果返回的数据量不大且变化频率不高但访问频率很高,此时应该考虑将返回的数据放在应用端的缓存当中或者 Redis 这样的缓存当中,以提高存取速度。
- 减少服务器 CPU 开销(减少 CPU 及内存开销)
- 避免大事务操作
- 利用更多资源(增加资源)
优化案例
数据分页优化
select * from table_demo where type = ? limit ?,?;
优化方式一:偏移 id
lastId = 0 or min(id) do { select * from table_demo where type = ? and id > {#lastId} limit ?; lastId = max(id) }while (isNotEmpty)