MySQL几个简单SQL的优化

最近在作项目的时候,遇到了一些大数据量的操做,有大批量的CRUD的操做,一开始的实现的方案通过性能测试,发现性能并非很好,而后开始审查代码,对相关能够提高性能的操做进行了优化,这里分享给你们。sql

原则

首先我这里不讲索引相关的内容以及数据库相应参数的优化,这里假设你对索引已经有了相关的了解了,我总结了下我此次的优化,主要两个原则:数据库

  • 一些特定的场景,尽可能用批处理处理数据,好比批量添加数据,批量修改数据;
  • 结合业务尽可能减小SQL的执行次数和查询没必要要的数据;

场景实践

为模拟运行场景,我这里建了一个表,并往里面添加了300w条数据,表结构以下:网络

CREATE TABLE `tb_big_data` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `weixin_id` varchar(64) NOT NULL,
 `openid` varchar(64) NOT NULL,
 `status` int(3) NOT NULL,
 `gmt_create` datetime NOT NULL,
 `gmt_modified` datetime NOT NULL,
 PRIMARY KEY (`id`),
 KEY `weixin_id_gmt_create_openid` (`weixin_id`,`gmt_create`,`openid`)
) ENGINE=InnoDB AUTO_INCREMENT DEFAULT CHARSET=utf8

1.分页查询小优化

分页查询老生常谈,网上各类优化方法都不少,这里就不说起了,这里只是分享一个小技巧:性能

如何在使用最普通的limit的时候提升性能?测试

假设咱们如今有一条这样的SQL:大数据

SELECT * FROM `tb_big_data` where weixin_id ='gh_266a30a8a1f6' and gmt_create > '2017-10-10 00:00:00' order by id asc limit 800000, 100;

执行时间:100 rows in set (1.53 sec)

假如咱们如今不能进行其余优化,好比传入最小id,分表查询等策略,以及不进行SQL预热,怎么提升这条SQL的速度呢?
其实很简单咱们只须要一个in操做便可:优化

SELECT * FROM `tb_big_data` t1 where t1.id in ( 
    SELECT tt.id FROM ( 
        SELECT id FROM `tb_big_data` t2 where weixin_id = 'gh_266a30a8a1f6' and gmt_create > '2017-10-10 00:00:00' order by t2.id asc limit 800100, 100
        ) as tt);

执行时间:100 rows in set (1.17 sec)

能够看出只需稍加修改,SQL的效率能够提升30%~40%,并且在单条数据记录越大的状况下效果越好,固然这不是最好的分页方法,这只是一个小技巧;3d

2.减小SQL查询

如今有一个需求咱们如今有一个用户的列表(用户的惟一标识为openid)而后咱们须要判断用户在当天是否有过相应的记录;code

这是问题其实很简单,咱们首先一想到的操做就是循环这个列表一个一个判断,很简单也很好实现,可是真正测试的时候发现性能却不好,尤为在数据量大的状况下,倍数级增加,这里有有网络数据传输消耗的时间和SQL自己的执行时间;索引

假设咱们如今执行一条如下的SQL:

SELECT * FROM `tb_big_data` WHERE weixin_id ='gh_266a30a8a1f6' and gmt_create > '2017-10-13 00:00:00' and openid='2n6bvynihm5bzgyx';

执行时间:1 row in set (0.95 sec)

如今若是咱们执行100次,不敢想象会是什么状况,庆幸本身发现了这个问题,由于在数据量少的状况下,这个问题表现的并非那么严重,其实咱们稍加改变就能以另外一种高效的方式解决这个问题:

SELECT * FROM `tb_big_data` WHERE weixin_id ='gh_266a30a8a1f6' and gmt_create > '2017-10-13 00:00:00' and openid in ('2n6bvynihm5bzgyx','1stbvdnl63de2q37','3z8552gxzfi3wy27'...);

执行时间:100 row in set (1.05 sec)

发现了没有,仍是用in,并且执行时间几乎与单条查询的时间同样,可见只是单一这一部分处理就能够提高了很大的性能。

3.特定场景使用SQL的批处理

这个跟上一点有一个类似点,那就是减小SQL执行,上面只是查询而已,而当出现大批量的CUD的操做时,执行每条SQL,数据库都会进行事务处理,这将会消耗大量的时间,并且极端状况下会引发大批量SQL等待没法执行,致使业务出错,正是由于这些缘由,咱们在一些适当的状况下可使用批处理来解决这个问题。

(1)批量插入

批量插入比较简单,也比较经常使用,这里就给一下基本语法:

INSERT INTO table_name (field1,filed2,...) values (value11,value12,...),(value21,value22,...),...
(2)批量更新

我先举个简单的例子,咱们如今来根据一些条件来更新数据,具体SQL以下:

update `tb_big_data` set status = 2 WHERE weixin_id ='gh_266a30a8a1f6' and gmt_create > '2017-10-13 00:00:00' and openid = '2n6bvynihm5bzgyx';

Query OK, 1 row affected (2.28 sec)
Rows matched: 1  Changed: 1  Warnings: 0

很惊讶,咱们只是更新了一条记录,并且更新条件上是有复合索引的,没想到速度还那么慢,能够想象若是咱们批量更新数据,那得耗时多少;

可是咱们看一下另外一条SQL:

update `tb_big_data` set status = 1 WHERE id = 900098;

Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

上面的id值为以前条件筛选出来的记录的id,是否是很惊讶,怎么这条SQL执行的时间几乎不须要什么时间,因此咱们能够利用这个特色和批量查询简化批量更新,虽然这种方式不能让性能到最优,可是也能提高很大了,我进行了一个测试,根据相应条件批量更新100条数据:

方式 直接批量更新 先批量查主键再批量更新
耗时 289.12s 1.342s

能够看出这种方式相对对于普通方式来讲,性能提高巨大,具体执行的时候咱们也能够将这些SQL放在一个事务提交,减小数据库事务次数,但只这是一种在代码层面上的优化;

另外咱们能够利用MySQL提供的特殊语法进行批量更新,具体语法为:

#语法
INSERT INTO table_name (id,field1,field2,...) VALUES  (id1,value11,value12,...),(id1,value11,value12,...),... on duplicate key update  field = VAULES(field);

#使用例子

INSERT INTO `tb_big_data` (id,weixin_id,openid,gmt_create,status) values  (1,'gh_266a30a8a1f6','w9q8fmodytjgppsr','2017-10-13 12:00:00',3),(2,'gh_266a30a8a1f6','bu1flmch4i8eegzf','2017-10-13 12:00:00',3) on duplicate key update status = VAULES(status);

通过测试这种方式在数据量小的状况下与上述方式效率差很少,可是随着数据量愈来愈大,性能也愈来愈好,缺点的话主要传输的数据量很大,不须要更新的字段也须要传输。

另外也不推荐大量数据的批量更新,一次不要超过1000条为好。

总结

总的来讲,SQL优化是一门细心的学问,须要不断去尝试,测试,找到最优方式,另外还有一点就是要结合实际状况,综合考虑选择合适的方式。

相关文章
相关标签/搜索