当前位置: 首页 > 科技观察

SQL:为什么我慢,你不知道吗?

时间:2023-03-23 01:35:55 科技观察

SQL语句执行慢的原因在面试中经常被问到,也是服务端开发必须要注意的问题。在生产环境中,缓慢的SQL执行是一个严重的事件。那么如何定位慢SQL,慢的原因,如何防患于未然。接下来,让我们带着这些问题开始这段旅程吧!-思维导图-写操作作为后端开发,日常数据库操作最常用的操作是写操作和读操作。我们将在下面讨论读取操作。在这个分类中,我们主要看SQL为什么在写操作的时候变慢。脏页的定义如下:当内存数据页与磁盘数据页不一致时,则称内存数据页为脏页。那为什么会出现脏页,刷脏页怎么会导致SQL变慢呢?那么我们需要看看写操作的过程是什么样子的。对于一个SQL写操作,执行过程涉及写日志、内存、同步磁盘等几种情况。-Mysql架构图-这里提到了一个日志文件,即redolog,它位于存储引擎层,用于存储物理日志。在写操作的过程中,存储引擎(这里讨论的是Innodb)会将记录写入redolog并更新缓存,这样就完成了更新操作。后续操作存储引擎会在合适的时候将操作记录同步到磁盘。看到这里,你可能会有疑问。重做日志不是日志文件吗?日志文件存储在磁盘上。写的时候不是很慢吗?其实写redolog的过程是顺序写入磁盘的,磁盘顺序写入减少了寻道等时间,速度比随机写入快很多(类似于Kafka的存储原理),所以redolog的写入速度非常快。好了,让我们回到最初的问题,为什么会出现脏页,为什么脏页会拖慢SQL。仔细想想,redolog的大小是固定的,而且是循环写的。在高并发场景下,redolog很快被填满,但是数据来不及同步到磁盘。这时候就会产生脏页,阻塞后续的写操作。SQL执行自然会慢一些。锁写操作时SQL慢的另一种情况是可能遇到锁,这个很好理解。比如你和别人合租一个房间,只有一个厕所,你们同时想去,但是对方比你早了一点点。那么这个时候你只能在对方出来之后才能进去。对应Mysql,当某个SQL要更改的行刚好被锁住,那么只有释放锁后才能进行后续操作。但是还有一种极端的情况,你的室友一直在霸占厕所,这时候怎么办,尿裤子都尿不出来,多尴尬啊。对应Mysql,出现死锁或者锁等待的情况。这个时候怎么处理呢?Mysql提供了一种查看当前锁情况的方式:通过执行命令行图中的语句,可以查看当前的事务情况。下面是查询结果中的一些重要参数:当前事务如果等待时间过长或者发生死锁,可以通过“killthreadID”来释放当前锁。这里的线程ID是指表中的trx_mysql_thread_id参数。读操作说完写操作,大家可能对读操作比较熟悉了。SQL慢导致读操作慢的问题在工作中经常会涉及到。慢查询在说读操作慢的原因之前,我们先来看看如何定位慢SQL。Mysql中有个东西叫做慢查询日志,用来记录超过指定时间的SQL语句。默认是关闭的,慢查询日志只能通过定位手动配置开启。具体配置方法如下:查看慢查询日志当前状态:启用慢查询日志(临时):注意这里只是暂时启用慢查询日志,重启mysql后失效。可以在my.cnf中配置使其永久化。存在的原因既然我们知道了如何检查SQL执行慢,那么我们来看看为什么查询慢是由读操作引起的。(1)缺少索引SQL查询慢的原因之一就是可能缺少索引。为什么使用索引可以使查询更快以及使用时的注意事项网上已经有很多文章,这里不再赘述。(2)脏页的另外一个问题就是我们上面提到的刷脏页的情况,但是和写操作不同的是脏页是在读的时候刷的。别着急,听我说:Innodb存储引擎为了避免在读写数据时增加每次访问磁盘的IO开销,将相应的数据页和索引页加载到内存缓冲池(bufferpool)中以提高读写速度。然后将缓存的数据按照最近最少使用原则保存在缓冲池中。那么当要读取的数据页不在内存中时,就需要在缓冲池中申请一个数据页,但是缓冲池中的数据页是一定的。数据页从内存中逐出。但是如果脏页被淘汰了,那么就需要把脏页刷到磁盘上以供重用。你看,又回到了刷脏页的情况。您可以理解读取操作的速度变慢,对吗?知其所以然,如何避免或缓解这种情况呢?首先来看下索引缺失的情况:不知道大家有没有在Mysql中使用explain的习惯。不管怎样,我每次都用它来检查当前的SQL命中索引。避免它带来的一些未知的隐患。下面简单介绍一下它的用法。在执行的SQL前加上explain可以分析当前的SQL执行计划:执行后结果对应字段的概要说明如下图所示:这里需要重点关注以下字段:1.type表示MySQL在表中查找所需行的方式。常用的类型有:ALL,index,range,ref,eq_ref,const,system,NULL这些类型从左到右,性能逐渐提升。ALL:Mysql遍历整个表,寻找匹配的行;index:与ALL的区别在于索引类型只遍历索引树;range:只检索给定范围内的行,并使用索引来选择行;ref:表示以上表的连接匹配条件,使用哪些列或常量在索引列上查找值;eq_ref:与ref类似,区别在于是否为唯一索引。对于每个索引键值,表中只有一条记录匹配。简单来说就是在多表连接中使用主键或者唯一键作为关联条件;const,system:当Mysql优化查询的某一部分,将其转化为常量时,使用这些类型进行访问。如果主键放在where列表中,Mysql可以将查询转化为常量。system是const类型的特例。当查询表只有一行时,使用system;NULL:Mysql在优化过程中对语句进行分解。它在执行时甚至不需要访问表或索引,例如,从索引列中选取最小值可以通过单独的索引查找来完成。2、查询possible_keys时可能用到的索引(但不一定用到,没有索引会显示为NULL)。3.key实际使用的索引。4.rows估计找到相应记录所需的行数。5、Extra比较常见的类型有以下几种:Usingindex:表示使用覆盖索引,不需要回表;使用where:不需要读取表中的所有信息,只需要通过索引就可以得到需要的数据。当表的所有请求列都是同一个索引的一部分时,意味着mysql服务器将在存储引擎检索到这些行后对其进行过滤;Usingtemporary:表示MySQL需要使用一张临时表来存储结果集,常见于排序、分组查询,常见的groupby、orderby;Usingfilesort:当Query中包含orderby操作时,使用索引无法完成的排序操作称为“文件排序”。对于刷脏页的情况,我们需要控制脏页的比例,不要经常逼近75%。同时需要控制redolog的写入速度,通过设置innodb_io_capacity参数告诉InnoDB你的磁盘容量。写操作总结当redolog满了,脏页会被flush,此时写操作会终止,那么SQL执行自然会变慢。当要修改的数据行或表被加锁时,需要等待锁被释放后再进行后续操作,SQL执行也会变慢。读操作读操作慢的常见原因是没有命中索引,导致全表扫描。你可以通过explain方法来分析SQL语句。另一个原因是读操作时,要读取的数据页不在内存中,需要清除脏页申请新的数据页,导致执行速度变慢。