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

千万级数据量真实业务场景SQL性能优化

时间:2023-03-15 20:15:58 科技观察

前言通过前面几篇文章的积累,我们的理论知识已经很扎实了。这时候我们就可以开始sql优化了。SQL优化非常重要。因为再好的MySQL设计架构,也无法处理一个频繁查询的垃圾SQL语句。关于sql的优化,我们也是有一定的原则和顺序的。下面用流程图来看一下大概的步骤:总体来说,大致可以分为以下几个步骤:(1)首先,我们要检查sql语句中是否有join语句,比如innerjoin查询内连接、外连接查询左连接右连接等;因为join语句一般都会涉及到跨表查询,所以首先我们要负责在join语句中进行连接,在两个表的字段上创建一个索引,这样可以利用索引来加速关联两张桌子之间。(2)接下来,我们再看一下sql语句中的where语句。我们可以根据当前表的数据量和where语句的过滤条件来判断查询结果的数据量是否会很大。如果数据量很大的话,查询速度肯定会很慢。因此,为了提高SQL语句的执行效率,我们不得不在where语句中为过滤字段创建单独的索引。(3)我们优化了join语句和where语句中的字段之后,我们可以看看其他一些细节。比如在sql语句中使用了聚合函数,或者对查询结果进行了排序,那么,一般我们建议为聚合函数中的字段和排序后的字段创建索引,这样这些操作可以更快的使用索引.在sql优化中,无论是where语句的优化,聚合函数的优化,还是排序操作的优化,都比较简单。为相应的字段创建一个合适的索引就足够了。不过join语句的优化涉及到一些比较重要的原则,我们还是要看一下。简单的说,如果在mysql中使用join语句关联两个表,比如执行这个sql:select*fromorder_infot1leftjoinorder_item_detailt2ont1.order_no=t2.order_no这时候,流程是怎样的加入协会查询是什么样的?关于什么?实际上,这取决于当前连接语句中使用的算法。join语句中有三种算法,其中最基本的是Simplenestedloop算法。接下来,我们一起来看看吧。简单嵌套循环算法简单嵌套循环算法,说白了,就是一种双for循环遍历算法。Simple嵌套循环算法的匹配过程如下:从左边的驱动表order_info中,每取出一条记录,都要遍历驱动表。order_item_detail,说白了就是一个双for循环。如果驱动表和从动表中都有100条数据,那么此时需要匹配100*100=10000次,可见效率很低。因此,MySQL并没有选择使用Simplenestedloop算法,而是使用OptimizedBlocknestedloop算法。块嵌套循环算法块嵌套循环算法优化了简单嵌套循环算法。它引入了连接缓冲区。joinbuffer主要用于优化无索引条件的join查询。它会缓存连接过程中用到的字段,从而有效减少匹配次数,像这样:可以看出Block嵌套循环的优化思路是减少驱动表的匹配次数.主要是在驱动表中一次性缓存多条数据,减少驱动表的数量。匹配的次数,从而达到提高性能的目的。需要注意的是,MySQL提供了一个参数joinbuffer_size,用于控制joinbuffer的大小,MySQL默认的join_buffer_size是256K,所以如果驱动表数据过多,默认的joinbuffer可能一次不适合所有数据。这时joinbuffer会使用分段缓存机制来缓存驱动表的数据,但是这种分段缓存方式的性能比一次性缓存所有数据要差。因此,我们可以通过join_buffer_size参数适当增加joinbuffer的大小,让joinbuffer一次性存储driver表中的所有数据,可以提高join的性能。索引嵌套循环算法最后还有一种索引嵌套循环算法:其优化思路主要是减少被驱动表数据的匹配次数,即驱动表直接匹配被驱动表的索引,使其不需要匹配驱动表的每个索引。记录比较。原来的匹配次数是:驱动表行数*从动表行数,现在变成:驱动表行数*从动表索引高度,大大减少了从动表的匹配次数。大大提高了join的性能。如果连接查询可以使用索引,MySQL会使用Indexnestedloop算法。如果不能使用Indexnestedloop算法,MySQL会默认使用Blocknestedloop算法。最后能用join吗?好了,我们刚刚了解了Simplenestedloop、Blocknestedloop、Indexnestedloop这三种算法,那么现在我们可以回答开头的问题了:join可以用吗?其实如果能在驱动表上使用索引,说白了,如果能使用Index嵌套循环算法,就可以使用join。但是如果使用Block嵌套循环算法,由于扫描的行数和比较的次数会比较多,会占用大量的系统资源,所以这种情况下,可以使用join或者join。我们平时使用explain优化SQL的时候,如果explain结果中的Extra字段中包含'Usingjoinbuffer(BlockNestedLoop)',说明此时使用的是Blocknestedloop算法。如果可以使用从动表上的索引,则仍然可以使用连接。这时候性能基本不会受到影响,那么为什么要优化这里的join呢?主要是因为两个原因。首先,我们有分库分表的方案,所以为了有更好的扩展性,我们会对join进行优化。其次,MySQL是专门用于数据存储的,所以尽量不要把业务相关的逻辑放在MySQL层面。所以基于这两个原因,我们会对单应用版本的加入进行优化。join关系查询优化实战驱动表order_no列未建立索引(一)join关系查询sql语句可以看到,在sql语句中,左边的join语句中,订单明细表是通过order_no字段与order表关联的,此时驱动表order_info的order_no被索引了,但是驱动表order_item_detail的order_no字段没有被索引(2)看查询时间。此时order_info中的数据量为2500万,而订单明细表order_item_detail中的数据量为10亿。可以看出,当驱动表order_item_detail不使用索引时,查询效率很低。优化:给从动表order_no列加索引(一)给从动表加索引现在我们给从动表order_item_detail的order_no加索引,添加索引sql如下:createindexinx_item_order_noonorder_item_detail(订单号);(2)再次查看join关联查询的时间此时我们发现,对驱动表order_item_detail的关联字段order_no进行索引后,查询效率明显提升。进一步优化:去掉join这时候我们需要优化join关联查询以获得更好的扩展性(1)查看join的优化代码:拆分join,改成单表查询,然后组装数据在记忆中。(2)看一下优化后的时间,我们可以看到,在对join查询进行优化之后,我们不仅可以获得更大的扩展性,而且查询性能也有很大的提升。从被动到主动,监控系统在sql优化的例子中诞生。这个问题是DBA同学发现的,然后DBA同学把问题反馈给我们。实际工作中,可能是产品同学发现订单信息查询页面有点慢,就把问题反馈给我们。不管是谁发现的,对于我们订单系统的开发者来说都是非常被动的,因为我们不能及时主动的发现问题,比如某个接口变慢了,我们不能及时知道,只能等着别人给我们反馈,这种被动的问题发现会在一定程度上扩大问题的影响。为了解决这个问题,我们建立了完整的监控系统。该监控系统可以添加多个监控面板。比如我们可以添加一个订单监控面板。订单监控面板中的核心指标包括:订单核心接口的请求次数、失败次数、TP50、TP99等。那么,为了及时发现问题,这套监控系统还集成了报警功能。说白了,我们会针对某个监控指标设置一个告警规则,比如每天某个时间段,在多少分钟内,失败的请求超过多少次,然后告警给相应的开发者。报警方式分为报警呼叫和消息推送(推送到公司内部办公聊天软件)两种。为了避免开发者的单点故障,一般会添加多个报警接收器。如果第一个人没有接听报警电话,那么第二个人会在稍后呼叫,这样才能最大程度及时发现问题,真正变被动为主动起来。