深度解析慢查询优化:教你一招掌握高效慢SQL调优技巧
一文助你掌握如何改善慢SQL
改善慢SQL的核心策略包含以下几点:
理解SQL执行的根本顺序:
FROM和JOIN:首要明确表间关联,获取原始数据。WHERE:筛选数据,根据条件进行挑选。GROUP BY:对数据进行分类,但不进行筛选。HAVING:在分类后筛选或聚合数据。SELECT:选定要展示的字段。DISTINCT:去重处理,确保结果唯一。ORDER BY和LIMIT:对结果进行排序,可能伴随分页的LIMIT。
把握SQL语句的关键词顺序与执行顺序:
关键词顺序:SELECT... FROM... WHERE... GROUP BY... HAVING... ORDER BY... LIMIT。执行顺序:FROM> WHERE> GROUP BY> HAVING> SELECT字段> DISTINCT> ORDER BY> LIMIT。
深入剖析执行计划:
执行计划是改善SQL的基础,揭示了数据访问方式和索引使用等关键信息。使用EXPLAIN语句获取执行计划,分析其中的字段,如ID、select_type等。
索引优化的注意事项:
索引需遵循最左前缀原则,避免计算、类型转换、null值等因素导致索引失效。考虑排序稳定性、force_index的使用和ORDER BY对索引的影响。
优化案例分析:
通过具体的查询案例,使用EXPLAIN语句分析查询结构和优化策略。关注查询中的ID、select_type、table、type等列,了解数据来源、连接查询性能和索引使用情况。
总之,改善慢SQL需要深入理解SQL的执行顺序和原理,掌握执行计划的分析方法,以及注意索引的使用和优化策略。通过实践和学习,可以显著提高数据库性能。
MySQL某个表拥有千万数据,CRUD操作较慢,如何改善
数据量高达千万,占用的存储空间也较大,可以想象它不会存储在一块连续的物理空间上,而是以链式存储在多个碎片的物理空间上。可能对于长字符串的比较,就需要更多的时间查找与比较,这就导致用更多的时间。
可以执行表拆分,减少单表字段数量,优化表结构。
在保证主键有效的情况下,检查主键索引的字段顺序,使得查询语句中条件的字段顺序和主键索引的字段顺序保持一致。
主要有两种拆分:垂直拆分,水平拆分。
垂直分表
也就是“大表拆小表”,基于列字段进行的。一般是表中的字段较多,将不常用的,数据较大,长度较长(比如text类型字段)的拆分到“扩展表”。一般是针对那种几百列的大表,也避免查询时,数据量太大造成的“跨页”问题。
垂直分库针对的是一个系统中的不同业务进行拆分,比如用户User一个库,商品Product一个库,订单Order一个库。切分后,要放在多个服务器上,而不是一个服务器上。为什么?我们想象一下,一个购物网站对外提供服务,会有用户,商品,订单等的CRUD。没拆分之前,全部都是落到单一的库上的,这会让数据库的单库处理能力成为瓶颈。按垂直分库后,如果还是放在一个数据库服务器上,随着用户量增大,这会让单个数据库的处理能力成为瓶颈,还有单个服务器的磁盘空间,内存,tps等非常吃紧。所以我们要拆分到多个服务器上,这样上面的问题都解决了,以后也不会面对单机资源问题。
数据库业务层面的拆分,和服务的“治理”,“降级”机制类似,也能对不同业务的数据分别的进行管理,维护,监控,扩展等。数据库往往最容易成为应用系统的瓶颈,而数据库本身属于“有状态”的,相对于Web和应用服务器来讲,是比较难实现“横向扩展”的。数据库的连接资源比较宝贵且单机处理能力也有限,在高并发场景下,垂直分库一定程度上能够突破IO、连接数及单机硬件资源的瓶颈。
水平分表
针对数据量巨大的单张表(比如订单表),按照某种规则(RANGE,HASH取模等),切分到多张表里面去。但是这些表还是在同一个库中,所以库级别的数据库操作还是有IO瓶颈。不建议采用。
水平分库分表
将单张表的数据切分到多个服务器上去,每个服务器具有相应的库与表,只是表中数据集合不同。水平分库分表能够有效的缓解单机和单库的性能瓶颈和压力,突破IO、连接数、硬件资源等的瓶颈。
水平分库分表切分规则
1. RANGE
从0到10000一个表,10001到20000一个表;
2. HASH取模
一个商场系统,一般都是将用户,订单作为主表,然后将和它们相关的作为附表,这样不会造成跨库事务之类的问题。取用户id,然后hash取模,分配到不同的数据库上。
3.地理区域
比如按照华东,华南,华北这样来区分业务,七牛云应该就是如此。
4.时间
按照时间切分,就是将6个月前,甚至一年前的数据切出去放到另外的一张表,因为随着时间流逝,这些表的数据被查询的概率变小,所以没必要和“热数据”放在一起,这个也是“冷热数据分离”。
分库分表后面临的问题
事务支持
分库分表后,就成了分布式事务了。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价;如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担。
跨库join
只要是进行切分,跨节点Join的问题是不可避免的。但是良好的设计和切分却可以减少此类情况的发生。解决这一问题的普遍做法是分两次查询实现。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求得到关联数据。
跨节点的count,order by,group by以及聚合函数问题
这些是一类问题,因为它们都需要基于全部数据集合进行计算。多数的代理都不会自动处理合并工作。解决方案:与解决跨节点join问题的类似,分别在各个节点上得到结果后在应用程序端进行合并。和join不同的是每个结点的查询可以并行执行,因此很多时候它的速度要比单一大表快很多。但如果结果集很大,对应用程序内存的消耗是一个问题。
这些属于一类问题,因为它们都需要依据整体数据集进行运算。大多数代理都不会自动完成合并任务。对策:与解决跨节点join问题相似,分别在各个节点上获取结果后,在应用端进行汇总。与join不同的是,每个节点的查询可以并行执行,因此很多时候它的速度要比单一的大表快得多。但若结果集较大,对应用程序内存的消耗将成问题。
数据迁移、容量规划、扩容等问题
由淘宝综合业务平台团队提出,它利用对2的倍数取余具有向前兼容的特性(例如对4取余得1的数对2取余也是1)来分配数据,避免了行级别的数据迁移,但仍然需要进行表级别的迁移,同时对扩容规模和分表数量都有一定的限制。总的来说,这些方案都不是十分完美,多少都存在一些不足,这也从侧面反映出了Sharding扩容的复杂性。
ID问题
一旦数据库被分割到多个物理节点上,我们就不能再依赖数据库本身的主键生成机制。一方面,某个分区数据库自生成的ID无法保证在全局范围内是唯一的;另一方面,应用程序在插入数据之前需要先获取ID,以便进行SQL路由。
一些常见的主键生成策略
UUID
使用UUID作为主键是最简单的方案,但其缺点也十分明显。由于UUID非常长,除了占用大量存储空间外,最主要的问题是在索引上,在建立索引和基于索引进行查询时都存在性能问题。
Twitter的分布式自增ID算法Snowflake
在分布式系统中,需要生成全局UID的场景还是比较多的,Twitter的Snowflake解决了这种需求,实现起来也很简单,除了配置信息外,核心代码就是毫秒级时间41位机器ID 10位毫秒内序列12位。
跨分片的排序分页
通常情况下,分页时需要按照指定字段进行排序。当排序字段就是分片字段时,我们通过分片规则可以比较容易地定位到指定的分片,而当排序字段非分片字段时,情况就会变得比较复杂。为了确保最终结果的准确性,我们需要在不同的分片节点中对数据进行排序并返回,然后将不同分片返回的结果集进行汇总和再次排序,最后再返回给用户。