在某个项目中,订单表每天会产生5-10万条记录。
先前对大数据查询没多少处理经验,碰到难以查动的统计数据通常是建一张表,定时统计一次。或者写存储过程,优化查询过程。
按照以往一贯思路,建表,加索引。
然而这次在表里只有十多万数据时,每日统计查询竟然查不动了,查一次要四五秒。
如果是百万级数据,我还可以甩锅为服务器性能问题,十万数据查不动,要是汇报让领导升级服务器,估计领导直接让我去跟人事结算工资了。
我是这么设想的:
我:“查不动是服务器性能问题。”领导:“现在服务器一百一个月,十万一百,一百万一千。这样吧,你跟人事结下工资,我用你的工资买个高配服务器。”
于是我开始了百度寻找数据优化的方法。
表结构如下(字段有删减,字段个数,数据量对查询速度有影响):
CREATE TABLE `myorder` (
`ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '数据id',
`CREATE_TIME` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`UPDATe_TIME` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '修改时间',
`sn` varchar(50) NOT NULL COMMENT '订单编号',
`client_id` int(11) NOT NULL COMMENT '供应商id',
`money` decimal(19,2) DEFAULT NULL COMMENT '订单金额',
PRIMARY KEY (`ID`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
ALTER TABLE myorder ADD INDEX index_mo_sn ( `sn` ) ;
ALTER TABLE myorder ADD INDEX index_mo_client_id ( `client_id` ) ;
SQL如下:
select client_id,sum(money)
from myorder
where CREATE_TIME>=CURRENT_DATE
group by client_id
去掉分组条件,查询很快。
经过尝试,第一次优化的结果便是给 时间 字段加上索引。
原以为问题解决了。某天晚上,一个电话把我吓得睡不着。领导发现每次统计查询要半分钟。
我纳闷了,不可能,昨天都没问题,怎么今天就萎了,四十万和五十万数据有很大区别?
于是我亲自去检阅了那个功能,确实查不动。然后我告诉领导:“服务器网络波动,我修复一下。”
经过测试,发现这天的数据量有点大,按时间查询都要十来秒,加上分组,时间更长。
过了十多分钟,领导问我:“怎么样了?”我:“服务商说他们网络被攻击了,正在修复。”我登录服务器,防火墙把80端口关了。
查看执行计划,查询没有走 时间 索引。最后不断写SQL测试发现,用时间做查询条件是行不通的。
静音了手机后,我细细思考,一度萌生了建个统计表定时统计的想法。但是一想这统计每次要十多秒,万一锁了表就很尴尬了。
经过一番思考,我想到了用ID查询,用前一天最后一条数据的ID做统计条件
SQL如下:
select client_id,sum(money)
from myorder
where id>(select max(id) from myorder where CREATE_TIME<CURRENT_DATE)
group by client_id
经测试,百万数据查询时间控制在1秒内。
随之还发现了一个小问题。因为分页查询分两步,第一步查询总条数,第二步查分页数据。在查总条数时,不加任何条件,查询速度有点慢,数据量越大速度越慢。
SQL如下:
select count(1)
from pay_order
100w数据查了2秒
查看执行计划可以发现,查询没走索引,让他走索引即可。
SQL如下:
select count(1)
from pay_order
where id is not null
加上后只用了0.2秒
重新部署项目,给领导回拨了个电话。领导:“为什么不接我电话?”我:“刚临时有事出门,没带手机。”领导:“服务商告诉我他们网络没被攻击。”我诡辩道:“不可能,我刚什么都没做,要是没被攻击,它怎么自己好了。”领导语塞。
有一天,领导突然叫我去聊天。领导:“我发现现在查询很慢,特别是点最后一页。”我解释道:“数据量大了,查的就慢了。”领导:“我朋友公司那个开发月薪4K,百万级数据查询速度和你的差不多,你要对得起你8K的工资,快一倍要求不高吧?”我愕然,没想到还能这样比:“好像有那么点道理……我觉得可以升级一下服务器。”领导一脸不快:“要我给你配个天河一号?”我:“……”
首先看下最后一页查询速度慢的问题
按照百度到的方法,改写查询条件:
原SQL:
select *
from myorder
limit 1000000,100
改写后:
select *
from myorder
where id >= (SELECt id FROM myorder LIMIT 1000000, 1)
limit 100
看着好像是没什么问题,遂部署上线。过了两天,领导突然找我。领导:“为什么倒序排序的数据不管哪一页都是一样的?”我一看,还真是,灵机一动:“服务商的数据库默认配置有问题,我处理一下。”
在实际中,我们的where后面会带很多查询条件,还会有多个字段组合升序降序的排序。有个好办法,禁用排序(尽可能的减少order by),禁用搜索。
经过多次测试发现,如果结果字段只有id这一列,查询速度会非常快。查看执行计划,结果列多加一个字段,就不走索引了。where和order by用到的字段都要测一遍,查看执行计划。
既然如此,那就把上面的SQL拆成两个查询
SELECt id FROM myorder where ... order by ... LIMIT 1000000, 100
select * from myorder where id in (1,2,3,4...)
先查出id,将id拼成字符串,再用in查询,查询速度没什么影响,数据准确
某一天,领导:“服务器怎么访问不了了?”我查看日志,发现有人试图导出几十万数据,内存溢出,应用崩了:“服务器网络波动。”没多久,领导:“服务商说网络没问题,还给我提了个建议。”我很好奇:“什么建议?”领导:“建议我开除你。”