对一个26万数据MYSQL表的Yii2程序优化实战之一 【去糟粕、加缓存】

这是一篇真实案例,并非理论课,阿北将同步个人整个优化之路,优化之路慢慢长,对你们抛砖引玉已达目的,若你也有一些优化思路,请跟贴。php

当前系统状况

项目是年前一个朋友作的,客户也是个人一个朋友,因此如今来帮忙优化,系统很简单,就是一个菜单页面,客户下单,而后打印机出小票,整个系统使用yii2基础版 + MySQL5.6.29驱动。html

客户店里天天大约走1.5-2w的流水,如今最大的表有26w数据,我切图你们先看下。web

当前数据表状况
当前数据表状况

从图例看可能要优化的地方数据库

  • 表引擎使用了MyISAM问题
  • order、order_box数据表瓶颈问题

我使用的工具

  • 本地环境MAMP
  • yii2-debug 神器小强
  • yii2的各类缓存

优化原则

代码的修改最小化,尽可能不动核心代码以防止引入bug,最后在进行数据库和服务器的优化。缓存

那咱就开始吧~服务器

客户说后台登录慢

当我第一次听客户这样说的时候,就已经知道慢的绝对不是登录,这个系统没有权限、没有不少日志、没有登录后的事件、仅仅就是一个登录而已。微信

那极可能就是慢在登录后进入的第一个页面而给客户的感受是登录慢。yii2

那就看看这个页面app

登录后的页面
登录后的页面

页面逻辑很简单,就是一个销售图表,天天的销售额曲线,那么问题最可能出如今这个图表上,毕竟销售额的计算看代码都是从订单实时分析出来的。yii

用yii2-debug看一看

数据库39次呀~~~
数据库39次呀~~~

果不其然,yii2-debug告诉我此action整个响应时间为2.652秒,而数据库就用了2.518秒,查询次数39次...

看看每一个查询,每一天执行一个SQL语句,每一个都用了90毫秒左右,毕竟是26w数据中拿数据。

彷佛问题明朗了,解决这个数据库查询就解决了这个页面,先看看这个图表的代码实现

$y = date('Y',time()); //年
$m = date('m',time()); //月
$days_num = date('t',time()); //当月天数

$days = '';
$moneys = '';
for($i=1; $i<=$days_num; $i++){
    $days .= $i.',';
    $begin_time = strtotime($y.'-'.$m.'-'.$i) + Yii::$app->params['business_hour']['begin_time'];
    $end_time = strtotime($y.'-'.$m.'-'.$i) + Yii::$app->params['business_hour']['end_time'];
    $money = Order::find()
        ->where(['>','dish_id',0])
        ->andWhere(['pay_state'=>'pay'])
        ->andWhere(['>=','pay_time',$begin_time])
        ->andWhere(['<=','pay_time',$end_time])
        ->andWhere(['store_id'=>Yii::$app->admin->identity->store_id])
        ->sum('money');
    $moneys .= ($money > 0 ? $money : 0).',';
}复制代码

优化的步骤很简单:首先看能不能减小查询次数,若是不能就加速查询,若是还不能就缓存结果。

在这段代码中,不管今日是几号,都进行了整月天数的查询,我先来去掉不应进行的查询。

修改及其简单,只是增长了3行代码

for($i=1; $i<=$days_num; $i++){
    $days .= $i.',';
    if($i > date('d',time())){
        $moneys .= "0,";
        continue;
    }
    ........
}复制代码

可是经过减小没必要要的查询的结果是

9次查询被干掉了
9次查询被干掉了

数据库检索从39减小到30次,耗时从2.518秒减小到1.982秒。

对于加速查询无外乎表类型选择及索引的添加,本着表是全部action的表,苍老师是世界的苍老师,我先不动,只是记录下dish_id、pay_state、pay_time、store_id四个字段,后面对order表进行改造的时候再考虑他们。

而后对于这种统计类数据,没有必要每次都实时读取,我应该加一个缓存。

加个文件类型缓存

若是你不会玩Yii2缓存,请移步到 Yii2缓存系列 先学习。

看这个图表和当下代码,个人缓存能够按照月份来,可是还要保证当天销量的实时性,因此我缓存的是本月今天以前的数据。

首先去配置文件 config/web.php 设置

// conf/web.php
...
'cache' => [
    'class' => 'yii\caching\FileCache',
],
...复制代码

采用默认的就好,代码进行一点小手术(将今天以前的代码单独处理),核心逻辑不变。

$cache = Yii::$app->cache;
$cacheKey = "month-report-{$m}";

$days = '';
$moneys = '';

// 今天以前的数据,也是咱们要缓存的数据
$monthDatBeforeToday = $cache->get($cacheKey);
if ($monthDatBeforeToday === false) {
    for($i = 1;$i < date('d',time());$i++){
        $days .= $i.',';
        $begin_time = strtotime($y.'-'.$m.'-'.$i) + Yii::$app->params['business_hour']['begin_time'];
        $end_time = strtotime($y.'-'.$m.'-'.$i) + Yii::$app->params['business_hour']['end_time'];
        $money = Order::find()
            ->where(['>','dish_id',0])
            ->andWhere(['pay_state'=>'pay'])
            ->andWhere(['>=','pay_time',$begin_time])
            ->andWhere(['<=','pay_time',$end_time])
            ->andWhere(['store_id'=>Yii::$app->admin->identity->store_id])
            ->sum('money');
        $moneys .= ($money > 0 ? $money : 0).',';
    }
    $monthDatBeforeToday = ['days'=>$days,'moneys'=>$moneys];
    $cache->set($cacheKey,$monthDatBeforeToday,7200);
}

$days = $monthDatBeforeToday['days'];
$moneys = $monthDatBeforeToday['moneys'];

for($i=date('d',time());$i<=$days_num; $i++){
    $days .= $i.',';
    if($i > date('d',time())){
        $moneys .= "0,";
        continue;
    }

    $begin_time = strtotime($y.'-'.$m.'-'.$i) + Yii::$app->params['business_hour']['begin_time'];
    $end_time = strtotime($y.'-'.$m.'-'.$i) + Yii::$app->params['business_hour']['end_time'];
    $money = Order::find()
        ->where(['>','dish_id',0])
        ->andWhere(['pay_state'=>'pay'])
        ->andWhere(['>=','pay_time',$begin_time])
        ->andWhere(['<=','pay_time',$end_time])
        ->andWhere(['store_id'=>Yii::$app->admin->identity->store_id])
        ->sum('money');
    $moneys .= ($money > 0 ? $money : 0).',';
}复制代码

固然,从编写上讲,这段代码能够继续优化,可是在原理上已经完成了,我将今天以前的数据都缓存下来,今天的实时读取,这样就知足了这个和原来同样的结果,为防止其余地方对订单的修改,换成我2个小时更新一次。

用yii2-debug看看结果。

天呀
天呀

你没看错

  • 数据库从39次到10次,耗时从2.518秒到0.1秒
  • Action执行从2.652秒减小到0.212秒

初步使用咱们Yii2优化三原则中的两条,Action执行提升了12倍、数据库耗时减小了23倍。

优化的步骤很简单:首先看能不能减小查询次数,若是不能就加速查询,若是还不能就缓存结果。

记住它,一点点来,万物都可优化。

还有不少

这仅仅是对统计数据采用缓存小试牛刀,若是对于订单列表这种检索那就要用到第二条原则了(如何让查询语句更快)。

下一篇将为你讲解 对一个26万数据的MYSQL表Yii2程序优化实战(真实例子)之二 【开刀数据表】

#更新记录
第一篇 对一个26万数据MYSQL表的Yii2程序优化实战之一 【去糟粕、加缓存】
juejin.im/post/594a7a…

第二篇 对一个26万数据MYSQL表的Yii2程序优化实战之二 【开刀数据表】juejin.im/post/594cf5…

(完)

本文原创发布于微信公众号 北哥小报 , 严谨的原创技术文,还有一些其余研究。

微信扫码能够关注
微信扫码能够关注
相关文章
相关标签/搜索