对SQL 优化,提升性能!

对SQL 进行优化能够有效提高SQL 语句的执行效率,降低系统资源开销,是开发者提高后端系统处理能力的首选方案。
 
新产品上线后,随着运营推广活动的开始,业务进入快速增长期,数据库作为后端系统唯一或者主要持久化组件,无论是存储的数据量还是事务请求次数都呈现指数级增长,数据库的处理能力逐渐成为整个系统的性能瓶颈。
 
提高物理规格配置虽然可以起到一定程度的缓解作用,但毕竟只是治标不治本,同时也会带来高额的硬件成本。分布式数据库虽然听起来高大上,但是系统改造成本以及开发运维成本又让一般的中小型团队望而却步。
 
SQL 优化能够有效提高SQL 语句的执行效率,降低系统资源开销,是开发者提高后端系统处理能力的首选方案。
 
SQL 优化是指通过采集SQL 执行的多项关键指标,分析SQL 的执行过程,根据数据库中表数据以及索引,对SQL 语句进行改写或者调整索引,最终达到SQL 语句按照最佳执行计划执行的目的。
 
接下来,我们就来介绍下SQL 优化的一般步骤。
 

第一步:收集SQL 语句

 
SQL 优化的起点是用户访问数据库的SQL 语句,尤其是问题SQL 语句。在数据库中,主要是访问超时的SQL 语句。
 
MySQL 数据库提供了慢SQL 日志功能,帮助开发者获取执行时间超过一定阈值的SQL 语句列表,通过参数 slow_query_log 开启。开启后,系统会将执行时间超过 long_query_time 的SQL 语句记入慢SQL 日志中,默认时长为10秒。
 
MySQL 支持两种慢SQL 日志保存格式:Table 和 File。Table的优势在于能够直接使用SQL进行查看,但是值得注意的是,因为默认使用的是mysiam引擎,如果使用Xtrabackup备份,慢SQL数量过多可能会导致实例长时间的不可写,所以要定时清理。
 

 
除了超时SQL 语句,没有被索引覆盖的SQL 语句同样值得开发者关注,通过long_queries_not_using_indexes,开发者可以在慢日志中捕获这些SQL。为了控制慢日志的写入频率,log_throttle_queries_not_using_indexes 规定了每分钟因为没有走索引而记入慢SQL 日志的查询数。远程控制软件
 
MySQL 还提供了参数 log_slow_admin_statements 控制管理类型的SQL 语句是否记入慢SQL 日志,例如 ALTER TABLE,ANALYZE TABLE 等。
 
另外,min_examined_row_limit 规定了所有记入慢日志的SQL 最小的行扫描记录数,只有扫描记录数大于该值的SQL 才会被记入日志。默认值为0,建议开发者将其设置为100,如果扫描记录数很少,说明该SQL 已经没有优化的空间了。
 
此外,慢SQL 作为衡量数据库访问速度的一个重要指标,可以通过执行 show global status like ‘Slow_queries’ 来监控其数量的变化。如果在某个时间段内,数据库的慢SQL 数量出现急剧增长,开发者就需要关注该时间段内的慢SQL 语句,进行SQL 语句排查。
 
除了慢SQL 语句,系统中执行次数最多、扫描记录数最多、执行时间最长的SQL 语句,都可以作为SQL 优化的对象。
 
在 MySQL 5.7 的 sys 库的 statement_analysis 视图中,整理了所有来自用户的SQL 语句。
 

 
由于SQL 的执行计划与SQL 包含的具体参数取值无关,所以通过将格式相同,仅参数取值不同的SQL 归类,可以提高开发者的检索效率。
 
按照 exec_count、latency、rows_examined 三个字段分别排序,即可获取执行次数最多、执行时间最长、扫描记录数最多的SQL 语句列表。

第二步:跟踪执行过程

 
明确了优化对象,接下来,就是追踪这些SQL 语句的执行过程,这需要我们采集如下关键指标:
 
○ 索引:通过执行 explain SQL 语句,获取SQL 的执行计划。key 字段标识了该SQL 语句是否通过索引扫描记录,如果该字段为 NULL,且扫描记录数较多,则预示可以通过新建索引来优化执行效率。
 

 
○ 扫描记录数:慢日志的 rows_examined 字段,标识了该SQL 语句扫描的记录数,扫描记录数越多,表示该SQL 语句执行花销越大,执行效率越低。
 
○ 持锁时间:慢日志的 lock_time 字段标识了该SQL 语句因为锁等待浪费的时间,如果该值较大,说明SQL 语句存在较多的等待和锁冲突。
 
○ 返回记录数:慢日志的 rows_sent 字段标识该条SQL 语句的返回记录条数,返回记录过多,对系统资源的消耗也越多。
 
除了上述几个关键指标外,在网易(c.163.com)维护的 MySQL 分支版本 InnoSQL 中,还增加了针对某条SQL 语句的 IO 开销统计。在慢日志中增加了 logical reads 和 physical reads 两个字段,分别表示该SQL 语句读取数据库缓存和产生实际硬盘 IO 的次数。

第三步:分析优化方案

 
掌握了这些关键运行指标,接下来就是进行分析优化的过程。
 
没有覆盖索引:对于没有被索引覆盖的SQL 语句的过滤条件涉及的字段,在区分度较大的字段上创建索引,如果涉及多个字段,尽量创建联合索引。需要注意的是,SQL 语句在一些情况下是无法使用索引的,例如使用!<>判断等,此时应修改SQL 语句逻辑。
 
SQL 语句被索引覆盖,但是扫描记录数非常多,返回记录数不多:此时要考虑索引是否高效。衡量索引效率的一个判断标准是索引的区分度,通过mysql 库下 innodb_index_stats 表的 stat_value 字段,我们可以知道该表在该索引涉及列上取值不同的记录数,然后与 n_rows 表中记录的该表总记录数相除,即可得到该表的区分度。越接近1,表示区分度越高,低于0.1,则说明区分度较差。开发者应该重新评估SQL 语句涉及的字段,选择区分度高的多个字段创建索引。
 
SQL 语句被索引覆盖,扫描记录数非常多,返回记录数也非常多:除了索引效率的问题,也有可能是因为SQL 语句本身过滤条件不强,导致返回的记录数过多。此时,开发者应该从业务层修改SQL 语句,增加SQL 过滤条件。
 
除了上述指标的特征外,由于索引的创建在插入、更新、删除数据时会带来索引维护的开销,所以我们必须尽可能地精简索引的数量,可以通过如下方式实现:
 
○ 去除冗余索引: 通过 MySQL sys 库下的 schema_redundant_indexes 视图,可以查看当前实例有哪些冗余索引。
 

 
○ 合并多个索引:对于已经创建的索引,如果多个索引涉及字段顺序一致,且索引的第一个字段相同,则可以组成一个联合索引。例如索引(a,c)和索引(a,d),可以组成联合索引(a,c,d)。值得注意的是,合并的两个索引的第一个字段必须相同,因为联合索引的第一个字段必须要出现在查询条件中,否则查询不能使用该索引。
 
○ 去除无效索引:通过 MySQL sys 库下的 schema_unused_indexes 视图,可以查看当前实例哪些索引从来没有被使用。
 
至此就完成了SQL 优化的整个过程。
 
经过上文的介绍大家可以看出,SQL 优化,尤其是执行计划分析的过程,十分依赖开发人员的数据库基础和调优经验,为了降低开发者的使用门槛,网易云基础服务集成网易10年的数据库开发以及优化经验,推出SQL 优化专家系统。

网易云基础服务 SQL 优化专家系统

 

 
慢SQL 功能可以实时监控数据库的慢SQL 数量变化,自动锁定超过阈值的时间范围,同时提供了对应时间范围内CPU 和IO 负载视图。
 

 
点击锁定的时间范围,检索该时间段内所有的SQL 语句,SQL 语句进行了格式化处理。
 

 
点击某条SQL 语句,专家系统会自动收集SQL 执行过程的关键指标,给出优化建议。
 

 
SQL 排行榜功能则从执行次数、响应时间和扫描记录数的角度整理了所有SQL。
 

 
热表功能是网易云基础服务推出的一个通过分析表的访问特点,针对表进行优化的功能。
 
举例来看,在电商的业务架构中,数据库有多种用途。对于存储商品信息的数据库表,读请求远远多于写请求,对于读多写少的业务,专家系统会推荐开发者在数据库前端增加缓存,或者创建只读从节点,通过读写分离技术来扩展数据库的读能力。对于存储交易流水、用户足迹的业务,插入数据较为集中,专家系统会建议用户使用批量插入的方式 insert into table values()()() 来提高插入效率,避免每次插入都需要数据库反复解析SQL 语句,生成执行计划的过程开销。对于一些要求实时插入的场景,专家系统会推荐开发者使用插入效率更高的 tokuDB 存储引擎作为 MySQL 数据存储的解决方案。
 

 
热表功能的另外一个作用就是预热。新建只读从节点,为了防止业务切入新建节点后,节点没有缓存热点数据,导致请求直接访问硬盘,访问延迟增加,开发者可以通过热表功能锁定热点数据,预先将这些数据扫描到内存中。

posted on 2018-08-17 13:30  时光平移  阅读(302)  评论(0编辑  收藏  举报

导航