浅析MySql语句优化

package beetlsql;

import org.beetl.sql.core.*;
import org.beetl.sql.core.db.DBStyle;
import org.beetl.sql.core.db.MySqlStyle;
import org.beetl.sql.core.query.Query;
import org.beetl.sql.ext.DebugInterceptor;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

public class TestBeetSql {
    public static void main(String[] args) {
        ConnectionSource source = ConnectionSourceHelper.getSimple("com.mysql.jdbc.Driver", "jdbc:mysql://10.202.4.25:3306/whht?useUnicode=true&characterEncoding=UTF8&useSSL=true", "osms", "osms");
        DBStyle mysql = new MySqlStyle();
        // sql语句放在classpagth的/sql 目录下
        SQLLoader loader = new ClasspathLoader("/sql");
        // 数据库命名跟java命名一样,所以采用DefaultNameConversion,还有一个是UnderlinedNameConversion,下划线风格的,
        NameConversion nc = new MyConversion();
        // 最后,创建一个SQLManager,DebugInterceptor 不是必须的,但可以通过它查看sql执行情况
        SQLManager sqlManager = new SQLManager(mysql, loader, source, nc, new Interceptor[]{new DebugInterceptor()});
        //1.查询大数据量变慢时
        //分页查询是最常用的场景之一,但也通常也是最容易出问题的地方。比如对于下面简单的语句,
        // 一般 想到的办法是在字段上加组合索引。这样条件排序都能有效的利用到索引,性能迅速提升。
        //SELECT * FROM tt_router WHERE STATUS = '1' LIMIT 500000,10;  //1.1
        //但当 LIMIT 子句变成 “LIMIT 500000,10” 时,我只取10条记录为什么还是慢
        //要知道数据库也并不知道第1000000条记录从什么地方开始,即使有索引也需要从头计算一次。
        //在前端数据浏览翻页,或者大数据分批导出等场景下,是可以将上一页的最大值(id或者其他字段)当成参数作为查询条件的。SQL 重新设计如下:
        //SELECT * FROM tt_router WHERE STATUS = '1' and id>=500712 ORDER BY id ASC  LIMIT 0,10; //0.002
        //2.关联更新、删除
        //比如下面 UPDATE 语句,MySQL 实际执行的是循环/嵌套子查询(DEPENDENT SUBQUERY),其执行时间可想而知。
        //在慢查优化都反复强调过 explain 的重要性,但有时候肉眼看不出 explain 结果如何指导优化,这时候还需要有一些其他基础知识的佐助,
        // 甚至需要了解 MySQL 实现原理,如子查询慢查优化。
        //看到 SQL 执行计划中 select_type 字段中出现“ DEPENDENT SUBQUERY”时,要打起精神了!
        //EXPLAIN UPDATE tt_router r SET r.status = '0'
        //WHERE r.sf_waybill IN ( SELECT sf_waybill FROM (SELECT t.sf_waybill FROM tt_router t WHERE t.OPERATOR_ID = 'test89003424') t )

        //优化
        //重写为 JOIN 之后,子查询的选择模式从 DEPENDENT SUBQUERY 变成 DERIVED,执行速度大大加快
        //EXPLAIN UPDATE tt_router r
        //JOIN (SELECT t.sf_waybill FROM tt_router t WHERE t.OPERATOR_ID = 'test89003424') t
        //ON r.sf_waybill = t.sf_waybill
        //SET r.status = '0';

        //3.混合排序**
        //MySQL 不能利用索引进行混合排序。但在某些场景,还是有机会使用特殊方法提升性能的。
        //EXPLAIN SELECT * FROM tt_router ORDER BY STATUS ASC , bar_time DESC LIMIT 0,20; //0.3
        //执行计划显示为全表扫描:
        //由于 STATUS 只有0,1,2三种状态,我们按照下面的方法重写后,提升效率
        //EXPLAIN SELECT * FROM ((SELECT * FROM tt_router WHERE STATUS = '0' ORDER BY  bar_time DESC LIMIT 0,20  ) UNION ALL
        //(SELECT * FROM tt_router WHERE STATUS = '1' ORDER BY bar_time DESC LIMIT 0,20 ) UNION ALL
        //(SELECT * FROM tt_router WHERE STATUS = '2' ORDER BY bar_time DESC LIMIT 0,20 )) t
        //ORDER BY t.STATUS ASC ,t.bar_time DESC LIMIT 20; //0.002

        //4.EXISTS
        //MySQL 对待 EXISTS 子句时,仍然采用嵌套子查询的执行方式
        //EXPLAIN SELECT * FROM tt_router r WHERE EXISTS(SELECT 1  FROM tse_epiemp_abroad_etl e WHERE r.`operator_id` = e.`EPIEMP_CODE`) //2.2 秒
        //去掉 exists 更改为 join,能够避免嵌套子查询
        //EXPLAIN SELECT * FROM tt_router r LEFT JOIN tse_epiemp_abroad_etl e ON r.`operator_id` = e.`EPIEMP_CODE` //0.002秒

        //5.条件下推
        //外部查询条件不能够下推到复杂的视图或子查询的情况有:
        //1、聚合子查询; 2、含有 LIMIT 的子查询; 3、UNION 或 UNION ALL 子查询; 4、输出字段中的子查询;
        //如下面的语句,从执行计划可以看出其条件作用于聚合子查询之后:
        //EXPLAIN SELECT * FROM (SELECT operator_id,COUNT(*) FROM tt_router GROUP BY operator_id) t WHERE t.operator_id = '01331817';
        //确定从语义上查询条件可以直接下推后,重写如下:
        //EXPLAIN SELECT operator_id,COUNT(*) FROM tt_router WHERE operator_id = '01331817'  GROUP BY operator_id;

        //6.提前缩小范围
        //SELECT * FROM tt_router r
        //LEFT JOIN tse_epiemp_abroad_etl e ON r.`operator_id` = e.`EPIEMP_CODE`
        //LEFT JOIN tt_branch b ON r.net_num = b.BRANCH_CODE
        //WHERE r.status = '0'
        //ORDER BY r.bar_time DESC
        //LIMIT 0,20;
        //该SQL语句原意是:先做一系列的左连接,然后排序取前15条记录。从执行计划也可以看出,最后一步估算排序记录数为50万,时间消耗为5秒。
        //优化
        //由于最后 WHERE 条件以及排序均针对最左主表,因此可以先对 my_order 排序提前缩小数据量再做左连接。SQL 重写后如下

        //SELECT * FROM (SELECT * FROM tt_router r WHERE r.status='0' ORDER BY r.bar_time DESC LIMIT 0,20) r
        //LEFT JOIN tse_epiemp_abroad_etl e ON r.`operator_id` = e.`EPIEMP_CODE`
        //LEFT JOIN tt_branch b ON r.net_num = b.BRANCH_CODE
        //ORDER BY r.bar_time DESC LIMIT 0,20;


        //7.中间结果集下推
        //EXPLAIN SELECT * FROM
        //(SELECT * FROM  tse_epiemp_abroad_etl t WHERE STATUS = '1' ORDER BY EPIEMP_CODE LIMIT 20) t
        //LEFT JOIN
        //(SELECT * FROM tt_branch b GROUP BY BRANCH_CODE) c
        //ON t.GC_ORG_NETCODE = c.BRANCH_CODE
        //tt_branch 为全表扫描

        //修改
        //SELECT * FROM
        //(SELECT * FROM  tse_epiemp_abroad_etl t WHERE STATUS = '1' ORDER BY EPIEMP_CODE LIMIT 20) t
        //LEFT JOIN
        //(SELECT BRANCH_CODE FROM tt_branch b,(SELECT GC_ORG_NETCODE FROM  tse_epiemp_abroad_etl t WHERE STATUS = '1' ORDER BY EPIEMP_CODE LIMIT 20) c
        //WHERE b.BRANCH_CODE = c.GC_ORG_NETCODE
        //GROUP BY b.BRANCH_CODE ) c
        //ON t.GC_ORG_NETCODE = c.BRANCH_CODE

        //案例,对查询变慢时优化
        Map<String, Object> param = new HashMap<>();
        param.put("order", "id asc");
        param.put("id", 0);
        recursion(param, sqlManager);
    }

    private static void recursion(Map<String, Object> param, SQLManager sqlManager) {
        List<TtRouter> ttRouters = search(sqlManager, param, 10);
        ttRouters.stream().map(TtRouter::getId).forEach(System.out::println);
        TtRouter ttRouter = ttRouters.get(ttRouters.size() - 1);
        param.put("id", ttRouter.getId());
        recursion(param, sqlManager);
    }

    private static List<TtRouter> search(SQLManager sqlManager, Map<String, Object> param, int pageSize) {
        return query(q -> {
            if (param.get("order") != null) {
                q.orderBy(param.get("order").toString());
            }
            if (param.get("id") != null) {
                q.andGreat("id", param.get("id").toString());
            }
            q.limit(1, pageSize);
        }, sqlManager, TtRouter.class).select();
    }

    private static <T> Query<T> query(Consumer<Query<T>> queryConsumer, SQLManager sqlManager, Class<T> clazz) {
        Query<T> query = sqlManager.query(clazz);
        if (queryConsumer != null) {
            queryConsumer.accept(query);
        }
        return query;
    }
}

class TtRouter implements Serializable {
    private Integer id;
    private String netNum;
    private String operatorId;
    //发送状态(0,成功 1,失败 2,待发送)
    private Integer status;
    private Date barDate;
    private Date barTime;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getNetNum() {
        return netNum;
    }

    public void setNetNum(String netNum) {
        this.netNum = netNum;
    }

    public String getOperatorId() {
        return operatorId;
    }

    public void setOperatorId(String operatorId) {
        this.operatorId = operatorId;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public Date getBarDate() {
        return barDate;
    }

    public void setBarDate(Date barDate) {
        this.barDate = barDate;
    }

    public Date getBarTime() {
        return barTime;
    }

    public void setBarTime(Date barTime) {
        this.barTime = barTime;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值