线上SQL优化

最近在线上发现很多性能有问题的sql,开发写sql语句的时候,没充分考虑是否用上索引了,所以这个坑得DBA来填,好了,废话不多说,把一些线上的优化经验跟大家分享。

由于是线上的表,所以就不公开具体的表结构了,请大家体谅,我会模拟一个类似的表来说明当时的性能问题:

当时的表结构类似此表:

mysql> show create table test\G
*************************** 1. row ***************************
       Table: test
Create Table: CREATE TABLE `test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `aa_id` int(11) DEFAULT NULL,
  `dealername` varchar(45) DEFAULT NULL,
  `dealertype` int(2) DEFAULT NULL,
  `bb_id` int(11) NOT NULL,
  `membername` varchar(45) DEFAULT NULL,
  `createat` datetime DEFAULT NULL,
  `creator_id` int(11) DEFAULT NULL,
  `name` varchar(45) DEFAULT NULL,
  `comp_id` int(11) DEFAULT NULL,
  `companyname` varchar(45) DEFAULT NULL,
  `cc_id` int(11) DEFAULT NULL,
  `level_id` int(2) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `shopmember_unique` (`aa_id`,`bb_id`,`cc_id`) USING BTREE
) ENGINE=MyISAM AUTO_INCREMENT=301554 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> 

当时查看Lepus 的慢查询监控,看到大量的这类SQL语句,而且消耗时长有点长:

大量类似以下的SQL语句:

select aa_id,dealername,dealertype,membername from  test where level_id <=4 order by aa_id limit 243000, 100;

下面我们看一下SQL语句的执行计划:

mysql> explain select aa_id,dealername,dealertype,membername from  test where level_id <=4 order by aa_id limit 243000, 100;
+----+-------------+-------+------+---------------+------+---------+------+--------+-----------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra                       |
+----+-------------+-------+------+---------------+------+---------+------+--------+-----------------------------+
|  1 | SIMPLE      | test  | ALL  | NULL          | NULL | NULL    | NULL | 301508 | Using where; Using filesort |
+----+-------------+-------+------+---------------+------+---------+------+--------+-----------------------------+
1 row in set (0.00 sec)

很多人一看表结构,发现在列level_id没索引,添加一个普通索引就完事啦!下面我们来试试:

mysql> alter table test add key (level_id);
Query OK, 301508 rows affected (3.71 sec)
Records: 301508  Duplicates: 0  Warnings: 0

mysql> explain select * from  test where level_id <=4 order by aa_id limit 243000, 100;
+----+-------------+-------+-------+---------------+----------+---------+------+--------+-----------------------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows   | Extra                       |
+----+-------------+-------+-------+---------------+----------+---------+------+--------+-----------------------------+
|  1 | SIMPLE      | test  | range | level_id      | level_id | 4       | NULL | 301393 | Using where; Using filesort |
+----+-------------+-------+-------+---------------+----------+---------+------+--------+-----------------------------+
1 row in set (0.00 sec)

mysql> 

添加索引后,可以看到用上索引了,但效果相对之前并没有很大的提升,还有些人可能会说,在level_id和aa_id添加组合索引,性能可能就好了,我们再来看下:

mysql> alter table test add key (level_id,aa_id);
Query OK, 301508 rows affected (3.75 sec)
Records: 301508  Duplicates: 0  Warnings: 0

mysql> explain select * from  test where level_id <=4 order by aa_id limit 243000, 100;
+----+-------------+-------+-------+---------------+----------+---------+------+--------+-----------------------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows   | Extra                       |
+----+-------------+-------+-------+---------------+----------+---------+------+--------+-----------------------------+
|  1 | SIMPLE      | test  | range | level_id      | level_id | 4       | NULL | 301218 | Using where; Using filesort |
+----+-------------+-------+-------+---------------+----------+---------+------+--------+-----------------------------+
1 row in set (0.00 sec)

mysql> 

可以看到,效果还是一样的差。为什么显示用上了索引,却还扫描了几十万行呢?

我们回顾一下不会用上索引的几种情况:(可以参考我的之前写的常用SQL语句优化

• 两个表关联字段类型不一样(也包括长度不一样)
• 通过索引扫描的记录数超过30%,变成全表扫描
• 联合索引中,第一个索引列使用范围查询
• 联合索引中,第一个查询条件不是最左索引列
• 模糊查询条件列最左以通配符 % 开始
• 内存表(HEAP 表)使用HASH索引时,使用范围检索或者ORDER BY
• 两个独立索引,其中一个用于检索,一个用于排序
• 使用了不同的 ORDER BY 和 GROUP BY 表达式

上面的SQL语句,符合了上面的联合索引中,第一个索引使用范围查询所以用不上索引,我们直接查询看看用时为多少:

mysql> select * from test where level_id <=4 order by aa_id limit 243000, 100;
100 rows in set (1.63 sec)

没用上索引,那我们应该怎么优化它呢?我们应该用延迟关联,把sql语句修改为如下:

mysql> reset query cache;
Query OK, 0 rows affected (0.00 sec)

mysql> explain SELECT a.* FROM  test a,(select id from test where level_id <=4 order by aa_id limit 243000, 100) b where a.id=b.id ;
+----+-------------+------------+--------+---------------+----------+---------+------+--------+-----------------------------+
| id | select_type | table      | type   | possible_keys | key      | key_len | ref  | rows   | Extra                       |
+----+-------------+------------+--------+---------------+----------+---------+------+--------+-----------------------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL          | NULL     | NULL    | NULL |    100 |                             |
|  1 | PRIMARY     | a          | eq_ref | PRIMARY       | PRIMARY  | 4       | b.id |      1 |                             |
|  2 | DERIVED     | test       | range  | level_id      | level_id | 4       | NULL | 301218 | Using where; Using filesort |
+----+-------------+------------+--------+---------------+----------+---------+------+--------+-----------------------------+
3 rows in set (0.30 sec)

mysql> SELECT a.* FROM  test a,(select id from test where level_id <=4 order by aa_id limit 243000, 100) b where a.id=b.id ;

100 rows in set (0.30 sec)

可以看到速度快了几倍,现在数据量只有几十万,如果几百万,效果会更明显,为什么这样写会比之前的效果好呢?因为通过覆盖索引返回所需数据行的主键,再通过主键获取所需数据,所以速度比之前快上不少。

优化案例二:

表结构是:

mysql> show create table test2\G
*************************** 1. row ***************************
       Table: test2
Create Table: CREATE TABLE `test2` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(45) DEFAULT NULL,
  `code` varchar(32) DEFAULT NULL,
  `url` varchar(255) DEFAULT NULL,
  `status` int(2) DEFAULT ‘1‘,
  `createat` datetime DEFAULT NULL,
  `write_id` int(11) DEFAULT NULL,
  `creator_id` int(11) DEFAULT NULL,
  `dealer_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `code_index` (`code`) USING BTREE,
  KEY `dealer_id` (`dealer_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7014142 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> 

slowlog里有大量这样的查询:

select count( id ) from `test2` where createat between ‘2015-05-26 00:00:00‘ and ‘2015-05-26 23:59:59‘  and status not in(7) and creator_id=8774 and write_id=925;          

查看下执行计划:

mysql> explain select count( id ) from `test2` where createat between ‘2015-05-26 00:00:00‘ and ‘2015-05-26 23:59:59‘  and status not in(7) and creator_id=8774 and write_id=925;
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | test2 | ALL  | NULL          | NULL | NULL    | NULL | 5135067 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------+
1 row in set (0.00 sec)

mysql> 

没有索引,做了全表扫描,有些开发人员创建表的时候考虑得不周到,导致频繁出现影响性能的sql,我们添加组合索引看看效果(这里要注意一下,在线上如果是5.6以下的版本,对于一些大数据的表,别直接添加索引,因为这个过程会阻塞DML操作的,如果添加索引需要的时间是几个小时或者更多,这是很悲剧的一件事情,个人经验,小数据的表发现没索引,或者索引设置的不合理,直接alter修改,大数据的表,就要用pt工具了。5.6版本的MySQL虽然支持了Online DDL,但也添加索引的时候,要考虑是否处于业务的高峰期,尽量选择业务量不繁忙的时候添加):

mysql> alter table test2 add key (createat,status,creator_id,write_id);
Query OK, 0 rows affected (1 min 36.27 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select count( id ) from `test2` where createat between ‘2015-05-26 00:00:00‘ and ‘2015-05-26 23:59:59‘  and status not in(7) and creator_id=8774 and write_id=925;
+----+-------------+-------+-------+---------------+----------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+----------+---------+------+------+--------------------------+
|  1 | SIMPLE      | test2 | range | createat      | createat | 14      | NULL |    1 | Using where; Using index |
+----+-------------+-------+-------+---------------+----------+---------+------+------+--------------------------+
1 row in set (0.00 sec)

mysql> 
时间: 2024-10-12 12:05:23

线上SQL优化的相关文章

Nginx、Tomcat线上环境优化配置

 Nginx.Tomcat线上环境优化配置 Nginx优化: Nginx安全方面的优化: 1. nginx安全优化,在nginx配置文件http标签段内添加"server_tokens  off"即可隐藏访问或者报错时提示web版本号信息. 2. server_tokens参数可以在http,server,location的位置添加 3. 还可以修改nginx的3个源码文件 4. 如还需要安全优化更改端口.用户. nginx 性能优化: 对于nginx配置文件中对优化比较有作用的一般为

2020.04.10 线上性能优化以及Linux下NIO/Epoll模型全解--实战

1.支付宝模拟线上优化实战 2.手写JUC工具与提升tomcat吞吐量 3.网络通信BIO设计与缺陷   -- accept()  和 read()阻塞 4.单线程解决高并发NIO精髓解读 5.OS内核下Epoll与Selete源码解读 第一部分: 性能优化 问题:如何在高并发场景下实现支付宝用户登录页面的信息获取?如用户信息,金额,积分等 浏览器  ---- Spring MVC ---- controller ----- service ---- 用户模块.余额模块.积分模块等 -- 调用多

线上mysql内存持续增长直至内存溢出被killed分析

来新公司前,领导就说了,线上生产环境Mysql库经常会发生日间内存爆掉被killed的情况,结果来到这第一天,第一件事就是要根据线上服务器配置优化配置,同时必须找出现在mysql内存持续增加爆掉的原因,虽然我主业已经不是数据库更不是dba了. 这个业务上基本山算是OLTP,盘中都是很简单的SQL,所以性能上虽然有些SQL有些慢,但看过slow-log和performance_schema,可以忽略不计. 初步了解,应用是java开发的,但是应用端没有出现过OOM的情况,也不见卡死或者越来越慢的情

转://从一条巨慢SQL看基于Oracle的SQL优化

http://mp.weixin.qq.com/s/DkIPwbDKIjH2FMN13GkT4w 本次分享的内容是基于Oracle的SQL优化,以一条巨慢的SQL为例,从快速解读SQL执行计划.如何从执行计划中找到SQL执行慢的Root Cause.统计信息与cardinality问题.探索性能杀手Filter操作.如何进行逻辑重写让SQL起飞等多个维度进行解析,最终优化巨慢SQL语句,希望能够抛砖引玉,和大家一起探讨SQL优化方法. 另外,还简单介绍了两种解决疑难SQL优化问题的工具:1005

关于线上优化服务器视频笔记1-----快速部署线上服务器

线上linux服务器优化经验 2.线上服务器网络安全配置与系统登录安全配置 2.1.授权用户登录与sudo的设定 /etc/sudoers 文件 <user list> <host list> = <operator list> <tag list> <command list> 常见配置: Mbb ALL=(ALL) NOPASSWD:ALL 2.2.ssh登陆登陆经验 备份sshd配置文件 Cp /etc/ssh/sshd_config /e

关于线上优化服务器视频笔记1-----调优线上服务器

linux服务器调优的经验 目录: 1.系统故障排除思路 重视报错信息 永远不要忘记日志文件 分析.定位.解决问题 2.影响linux性能的因素 服务器硬件因素 操作系统的相关因素 程序因素 3.系统性能优化工具 Cpu性能优化工具 vmstat,iosta,sar 内存性能检测工具 free,top,sar,pidstat 磁盘性能评估工具 iostat,sar 网络性能分析工具 ping,mtr,netstat 4.系统性能分析与标准 5.性能调优的思路与技巧分享 几个故障鼓励案例和性能优化

《跨 界 之SQL、PL/SQL优化指南》目录上

优化新作,<跨 界 之SQL.PL/SQL优化指南>详细目录. 目    录 一.理论篇....................................................................10    1.1). SQL的处理过程.......................................................10    1.2). 连接方式(JOIN METHODS)................................

一个SQL注释引发的线上问题

最近开始服务拆分,时间将近半个月.测试阶段也非常顺利,没有什么问题. 但上线之后的第二天,产品就风风火火的来找我们了,一看就是线上有什么问题.我们也不敢说,我们也不敢问,线上的后台商品忽然无法上架了,导致运营的同学删除商品后无法上架新的商品,导致APP的部分商品暂时不可见. 线上有问题,那么大家就开始迅速排查起来了.这里有一点要说一下,在上线前夕,产品临时添加一个新的需求,商品的搜索状态不可判断这个条件去掉,这个由于紧急而且对于我们来说也就是SQL中的一个条件的问题,也就没有经过测试,直接上线了

大厂面试经:说一下你们线上JVM是如何优化的?

JVM(Java虚拟机)简单来说就是运行Java代码的解释器,作为螺丝钉程序员JVM其实了解下就差不多啦,不懂JVM内部细节照样能写出优质的代码!但是一到造火箭.飞机的场景(面试)不懂JVM的你,会被面试官虐的体无完肤,本期内容列举常见的JVM面试题: 说一JVM的内存模型是什么样子的? 什么时候对象可以被收回? 常见的垃圾回收器算法有哪些,各有什么优劣? 什么时候对象会进入老年代? 什么是空间分配担保策略? 如何优化减少Full GC? 面对这一大波JVM面试题,你真的Hold住吗?文章有点长