上一部分一起学习了数据库中的ACID等特性。数据库面试系列第二部分分享数据库的调优常用步骤,索引底层原理,页机制等。总体思维导图如下所示。
1 定位
主要从三个方面进行定位,用户的反馈,日志记录以及服务器内网监控。
- 用户反馈
用户是最直接的反馈者,用户的反馈是推向系统进一步优化的重要一步。
- 服务器资源监控
尽量不要等着用户反馈的时候手忙脚乱。通常项目中都会有一套比较完整的服务端监控体系,所谓"监控不到位,领导两行泪"。那监控一般都是哪些内容呢?服务器本身CPU,IO等基础指标以外,通常会通过访问趋势表展示服务整体的访问量、响应供应时间情况,错误数量等。通过性能报表展示哪个资源或者服务出现问题。
监控指标
- 日志分析
除了Linux系统日志,还有数据库日志,根据他们定位问题所在。
Linux日志
数据调优的几个方向
- 数据库的选择
根据应用,业务的需要选择不同的数据库。是否考虑事务,行存储还是列存储等。
- 优化表
(1) 如果查询分析较多,可采用空间换时间的方式增加冗余字段提高查询效率。
(2) 不同字段的数据类型直接关系到查询效率的高低和存储的大小。
(3) 采用第三范式让结构更加清晰,减少冗余字段。
- 合理使用索引
(1) 不是索引越多越好,索引也需占用存储空间,同时也会增加筛选索引的计算时间。
(2) 数据的重复度过高不宜使用索引。
(3) 索引列处于不同的位置对索引影响比较大。比如在WHERE子句中,对索引字段进行计算会造成索引失效。
- 使用redis等作为缓存
缓存分为静态缓存,分布式缓存,热点缓存。"缓存"我的理解是解决不同硬件速度的差异性,协调且充分利用硬件的资源。使用缓存的案例无处不在,不管是Linux内核管理TLB,还是HTTP的缓存机制。总之很多地方都会通过使用缓存来提高访问速度,尽量减少和数据库的直接交互。
- 库级优化
(1) 在读写都比较多情况下,通过采用读写分离的方式降低数据库的负载。
(2) 数据库的分库分表。切分数据库到多台服务器。
2 索引的原理
索引看做字典的目录,根据目录能快速定位内容,不用从头到尾花费时间找。是不是加了索引就是起飞了?那是不一定的。
(1) 数据量小的情况,不加索引
(2) 数据流大的情况,考虑加索引
索引的种类
(1)普通索引
没有约束
(2)唯一性索引
增加唯一性约束,一张数据表可以多个唯一索引
(3)主键索引
在唯一性索引基础上,增加不为空的约束。
(4)全文索引
Mysql自带全文索引支持英文,通常使用ES等代替
聚集索引与非聚集索引
(1) 聚集索引
通过索引位置直接找到需要的值。
(2) 非聚集索引
索引项顺序存储,但是指向内容为随机。所以第一次找到索引,还需要第二次去找到索引对应的位置从而取出数据行。
(3) 两者不同点
- 聚集索引叶子节点存放数据值。非聚集索引叶子节点存放数据行的位置
- 一个表只能一个聚集索引但是可以有多个非聚集索引
- 聚集索引查询效率高,非聚集索引查询效率低
适合加索引
- 字段唯一性性质
我们知道唯一性索引和主键索引都具有唯一性的约束,如果某字段唯一则可以考虑
- 需要经常Group by和ORDER by的情况
- 索引是让数据按照某种顺序进行存储和检索。
- distinct字段需要创建索引
>
什么时候不需要创建索引
- WHERE条件中用不到的字段不需要创建索引
- 表记录太少
- 字段中大量重复
- 频繁更新的字段。更新字段也更新索引,索引多,更新索引的时候会成为负担。
失效的索引
- 使用表达式比如EXPLAIN查看表执行计划时索引会失效
EXPLAIN SELECT name from..
- 对索引使用函数也会失效
- 使用"like"进行模糊查询的时候不要使用"%",不然也会失效
二叉树
二分查找是一种高效的检索方式,时间复杂度为O(log2n),但是在特殊的情况退化为链表从而导致时间复杂度为O(n)。随后提出平衡二叉树的概念,但是二叉树中树的深度还是O(log2n),数据查询依赖于磁盘IO,从而改造了M叉树。比如B树,对于一个1000阶的B树,只需要三层就可以存储1000W的索引数据,因为高度比二叉树低很多。为了提高查询的稳定性,出现B 树。
这里也就出现一个面试题
B树和B 树
- B 树查询更稳定,因为在查询过程中都是在叶子节点才能找到数据。B树中非叶子节点也会存储数据
- B 树更矮胖,查询时所需磁盘IO更少。相同的磁盘页,B 树能存放更多节点关键字。
3 页结构
在数据库中,不管是读取一行还是多行都是将所在的页进行加载。页是数据库管理存储的空间的基本单位。
在数据库中存在页,区,段等概念,他们之间的关系如下图所示。
数据库页区段
从上图我们知道一个表空间存在多个段,其中一个段包含多个区,一个区存在多个页,每个页多行记录。那具体都是干啥的呢?
区
在Innodb中,一个区分配64连续的页,页大小默认为16KB,所以一个区大小为64*16KB=1MB
段
段是由多个区组成,不同数据库对象不同段。创建一张表的时候创建一个表段。创建索引则为索引段。
表空间
逻辑容器。其中包含很多段,但是一个段只能属于一个表空间。一个数据库由多个表空间组成,其中包含系统表空间,用户表空间等。
页
数据库IO操作最小单位为页。页的具体结构如下图所示。
为了知道页中各个字段是什么意思,总结了一个图表如下
上面的文件头和文件尾对页内容进行封装,通过校验的方式保证页的完整性。同时通过链表的方式将各个页连接在一起。如下图所示。
再看记录部分。其中记录部分包含了最大,最小记录和用户记录,另外还有可变的空闲空间方便灵活的分配新的记录。
索引部分
在页中记录按照单链表的方式存储。我们知道单链表的插入和删除方便,但是查找就不是很有好了。所以在此引入页目录,页目录提供二分查找的方式提高记录的检索效率。那具体是怎么样的呢?
- 先对记录分组,第一组只有一个记录,最后一组为最大记录
- 每一组最后一条记录存储一共多少条记录。
- 页目录存储最后一条记录的地址偏移量,也叫做槽,其指针指向组的最后一个记录。
假设查找键为6的用户,页目录下标从0开始,采用二分查找进行。 (1) mid=(low high)/2=1,此时取出槽1最大记录为4,4<6则在[mid,high]中寻找
(2) mid=(mid high)/2=2,此时取出槽2最大记录为8,8>6,直接在槽2查找,遍历取出即可。
3 悲观锁与乐观锁
锁运用到很多地方,我们熟知的多线程,线程同步等都可能用到锁,通过锁来调整运行顺序和保持一致性。在数据库中,按照粒度划分为行锁,表锁和页锁。
(1) 行锁
优点:锁粒度较小,锁冲突概率小,并发度较高。缺点:锁开销大,容易出现死锁
(2) 页锁
从前面总结我们知道页中包含行,那么页锁数据资源比行锁多。开销在行和表锁之间,会出现死锁。
(3) 表锁
优点:锁使用开销小,加锁快。缺点:锁定力度大,发生锁冲突概率大。
从数据库的管理角度来区分,分为共享锁和排它锁
(1) 共享锁
可以被用户读取,但是不能修改。
(2)排它锁
也叫做独占锁,写锁或者X 锁。只允许进行锁定的事务使用,其他事务无法对其修改或者查询。所以我们在使用更新操作的时候,为了防止其他事物的更改,就会使用排它锁。
从开发者角度分为乐观锁与悲观锁
乐观锁:通过自身采用时间戳或者版本机制进行控制。悲观锁:通过数据库自身机制保证数据操作的排他性
4 SQL分析常用步骤
(1) 检查有没有出现有没有周期性的规律,如果有可以考虑更新缓存的策略或者加缓存
(2) 如果不是,考虑是不是查询语句本身问题,从而分析查询语句。之前介绍的几种查询优化的方法都可以尝试。同时引入慢查询可以知道执行慢的语句有哪些
(3) 找到了执行慢的语句就可以引入explain查看SQL执行计划,通过expalin可以知道数据表读取顺序,实际使用的索引,被优化行的数量等。
(4) 最后使用show profile了解执行成本。默认是关着的,使用set 'profiling'="on"打开。
这一篇就到尾声了,谢谢大家的查看,也麻烦大家文末点个在看。再见,下一期常见面试题汇总见。
参考链接:
https://dev.mysql.com/doc/
https://time.geekbang.org/column/intro/139
http://www.redis.cn/
https://time.geekbang.org/column/intro/192
https://blog.csdn.net/gengkui9897/article/details/89294936