昨晚收到客服MM电话,一用户反馈数据库响应非常慢,手机收到load异常报警,登上主机后发现大量sql执行非常慢,有的执行时间超过了10s
优化点一:
SELECT * FROM `sitevipdb`.`game_shares_buy_list` WHERE price>='2.00′ ORDER BY tran_id DESC LIMIT 10;
表结构为:
CREATE TABLE `game_shares_buy_list` ( `tran_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `………..' PRIMARY KEY (`tran_id`), KEY `ind_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=3144200 DEFAULT CHARSET=utf8;
执行计划:
root@127.0.0.1 : sitevipdb 09:10:22> explain SELECT * FROM `sitevipdb`.`game_shares_buy_list` WHERE price>='2.00′ ORDER BY tran_id DESC LIMIT 10; +—-+————-+———————-+——-+—————+———+———+——+——+————-+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +—-+————-+———————-+——-+—————+———+———+——+——+————-+ | 1 | SIMPLE | game_shares_buy_list | index | NULL | PRIMARY | 4 | NULL | 10 | Using where | +—-+————-+———————-+——-+—————+———+———+——+——+————-+ 1 row in set (0.00 sec)
分析该sql的执行计划,由于tran_id是表的主键,所以查询根据主键降序顺序扫描,这样就可以不用排序,
然后在过滤条件price>2.00的记录,看上去这个执行计划貌似非常好,如果查询扫描到了满足条件的10条记录,就会停止扫描;
但是这里有个问题,如果表中有大量的记录是不符合2.00的,意味查询就需要扫描非常多的记录,才能找到符合条件的10条:
root@127.0.0.1 : sitevipdb 09:17:23> select price,count(*) as cnt from `game_shares_buy_list` group by price order by cnt desc limit 10; +——-+——-+ | price | cnt | +——-+——-+ | 1.75 | 39101 | | 1.68 | 38477 | | 1.71 | 34869 | | 1.66 | 34849 | | 1.72 | 34718 | | 1.70 | 33996 | | 1.76 | 32527 | | 1.69 | 27189 | | 1.61 | 25694 | | 1.25 | 25450 |
可以看到表中有大量的记录不是2.00的,所以这个时候不能在根据主键顺序扫描,在过滤记录;
那么是否需要在price建立一个索引:
root@127.0.0.1 : sitevipdb 09:09:01> select count(*) from `game_shares_buy_list` where price>'2′; +———-+ | count(*) | +———-+ | 4087 | +———-+ root@127.0.0.1 : sitevipdb 09:17:31> select count(*) from `game_shares_buy_list` ; +———-+ | count(*) | +———-+ | 1572100 |
从上面price的数据分布可以看出,price的分布相对还是比较集中的,如果在price建立索引,mysql也有可能认为由于需要回表的记录过多,
同时需要额外的排序,而不选择在price上的索引:
root@127.0.0.1 : sitevipdb 09:24:53> alter table game_shares_buy_list add index ind_game_shares_buy_list_price(price); Query OK, 0 rows affected (5.79 sec)
可以看到优化器虽然注意到了我们新加的索引,但是最终还是选择了primary来扫描;
所以这个时候我们加上去的索引没有产生效果,数据库负载依然很高,如果强制走price上的索引,效果会这样:
root@127.0.0.1 : sitevipdb 09:35:38> SELECT * FROM `sitevipdb`.`game_shares_buy_list` WHERE price>='2.0′ ORDER BY tran_id DESC LIMIT 10; 。。。。。 10 rows in set (7.06 sec) root@127.0.0.1 : sitevipdb 09:36:00> SELECT * FROM `sitevipdb`.`game_shares_buy_list` force index(ind_game_shares_buy_list_price) WHERE price>='2.0′ ORDER BY tran_id DESC LIMIT 10; 。。。。 10 rows in set (1.01 sec)
可以看到如果强制走索引,时间已经明显下降了,但是还是有些慢,能不能在快一点?其实我们需要扫描的记录只有10条,但查询在取得这10条记录的时候需要扫描大量无效的记录
怎么降低这个数据:其实只要改写一下sql就可以,我们先从索引中得到满足条件的10个id,在回表进行关联:
root@127.0.0.1 : sitevipdb 09:44:45> select * from game_shares_buy_list t1, -> ( SELECT tran_id FROM sitevipdb.game_shares_buy_list WHERE price>='2.0′ ORDER BY tran_id DESC LIMIT 10) t2 -> where t1.tran_id=t2.tran_id; 10 rows in set (0.00 sec)
可以看到执行时间已经不在秒级别了,和客户电话沟通后,很愿意这样改写sql。
—这里看到是order by tran_id是要额外排序的,索引也可以这样来建立消除排序(tran_id,price)这样可以消除排序,同时可以利用order by desc/asc +limit M,N的优化。
优化点二:
CREATE TABLE `game_session` ( `session_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT , `session_expires` int(10) unsigned NOT NULL DEFAULT '0′, `client_ip` varchar(16) DEFAULT NULL, `session_data` text, ……………………. PRIMARY KEY (`session_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
查询为select `session_data`, `session_expires` from `game_session` where session_id='xxx'出现大量等待情况
同时该表的insert,也有等待的现象;
可以看到这个表结构设计是有些问题的,咨询了客户后,可以改为下面结构:
CREATE TABLE `game_session` ( id int auto_increment, `session_id` varchar(30) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT , `session_expires` int(10) unsigned NOT NULL DEFAULT '0′, `client_ip` varchar(16) DEFAULT NULL, `session_data` varchar(200), PRIMARY KEY (id), key ind_session_id(session_id,session_data, session_expires) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
小结:
本文向大家介绍Mysql使用索引实现查询优化,包括了Mysql使用索引实现查询优化的使用技巧和注意事项,需要的朋友参考一下 索引的目的在于提高查询效率,可以类比字典,如果要查“mysql”这个单词,我们肯定需要定位到m字母,然后从下往下找到y字母,再找到剩下的sql。如果没有索引,那么你可能需要把所有单词看一遍才能找到你想要的。 1.索引的优点 假设你拥有三个未索引的表t1、t2和t3,每个表都分
本文向大家介绍Mysql性能优化案例研究-覆盖索引和SQL_NO_CACHE,包括了Mysql性能优化案例研究-覆盖索引和SQL_NO_CACHE的使用技巧和注意事项,需要的朋友参考一下 场景 产品中有一张图片表pics,数据量将近100万条,有一条相关的查询语句,由于执行频次较高,想针对此语句进行优化 表结构很简单,主要字段: 一个用户会有多条图片记录,现在有一个根据user_id建立的索引:u
本文向大家介绍Mysql性能优化案例 - 覆盖索引分享,包括了Mysql性能优化案例 - 覆盖索引分享的使用技巧和注意事项,需要的朋友参考一下 场景 产品中有一张图片表,数据量将近100万条,有一条相关的查询语句,由于执行频次较高,想针对此语句进行优化 表结构很简单,主要字段: 一个用户会有多条图片记录 现在有一个根据user_id建立的索引:uid 查询语句也很简单:取得某用户的图片集合 优化前
本文向大家介绍mysql性能优化之索引优化,包括了mysql性能优化之索引优化的使用技巧和注意事项,需要的朋友参考一下 作为免费又高效的数据库,mysql基本是首选。良好的安全连接,自带查询解析、sql语句优化,使用读写锁(细化到行)、事物隔离和多版本并发控制提高并发,完备的事务日志记录,强大的存储引擎提供高效查询(表记录可达百万级),如果是InnoDB,还可在崩溃后进行完整的恢复,优点非常多
本文向大家介绍做过哪些MySQL索引相关优化?相关面试题,主要包含被问及做过哪些MySQL索引相关优化?时的应答技巧和注意事项,需要的朋友参考一下 尽量使用主键查询: 聚簇索引上存储了全部数据, 相比普通索引查询, 减少了回表的消耗. MySQL5.6之后引入了索引下推优化, 通过适当的使用联合索引, 减少回表判断的消耗. 若频繁查询某一列数据, 可以考虑利用覆盖索引避免回表. 联合索引将高频字段
本文向大家介绍MySQL如何优化索引,包括了MySQL如何优化索引的使用技巧和注意事项,需要的朋友参考一下 1. MySQL如何使用索引 索引用于快速查找具有特定列值的行。如果没有索引,MySQL必须从第一行开始,然后遍历整个表以找到相关的行。表越大,花费越多。如果表中有相关列的索引,MySQL可以快速确定要在数据文件中间查找的位置,而不必查看所有数据。这比顺序读取每一行要快得多。 大多数MyS