mysql中百萬級數據查詢sql優化


1.在做項目的時候遇到這樣的問題就是:當數據達到百萬級的時候分頁查詢的速率非常慢,下面個給直觀的現象截圖:測試數據為500W條數據

  平時在分頁的時候這么查詢總數的:但是當數據達到百萬級的時候會發現致命問題

SELECT COUNT(*) from test 

  

 可以直觀看到查詢時間達到近乎20S,啥意思你懂的,客戶點一下要等待這么長的時間直接導致超時,這是不能容忍的,但是加一點就可以提高10倍查詢速率,下面截圖可以很清晰看到,所以以后在這么寫的要注意別給自己挖坑,當然有其他條件在后面加就可以了

 

2.本文不是要優化這個問題,而是再次基礎上更加優化,正常分頁都是先查詢數據然后再查詢總數,要查2次,后面介紹查詢一次就可以解決:

 

 

 (1)建立自己的表,表中達到500W數據左右即可,添加方法很多,我是在idea中寫的代碼加進去的,挺慢的,有好的方法歡迎提出來;

(2)表建立完成后就開始寫代碼了:

  本次的優化在於mysql的 SQL_CALC_FOUND_ROWS函數,這個是什么可以自行查閱下相關資料,下面基於mybatias,springboot上代碼講述;

(3)開始之前需要注意,需要在配置文件application.yml的數據源加上 &allowMultiQueries=true如下,加這個是可以執行多條sql不然會報錯!!!

 
        
jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true 

(4)然后就是xml文件,主要這么寫

  <sql id="whereCaus">
        <if test="name != null">
            and name like '%${name}%'
        </if>
    </sql>
  <resultMap id="count" type="java.lang.Integer">
        <result column="count" />
    </resultMap>
    <resultMap id="BaseResultMap" type="com.example.demo.model.Test">
        <result column="id" jdbcType="INTEGER" property="id" />
        <result column="name" jdbcType="VARCHAR" property="name" />
    </resultMap>

    <select id="getListData" resultMap="BaseResultMap,count">
           SELECT SQL_CALC_FOUND_ROWS name,id
            FROM test
        <where>
            <include refid="whereCaus"/>
        </where>
        order by id
            LIMIT #{startOff},#{pageSize};
           SELECT FOUND_ROWS() as count;
    </select>
    

    <select id="getListData2" resultType="com.example.demo.model.Test">
        SELECT name,id
        FROM test
        <where>
          <include refid="whereCaus"/>
        </where>
        order by id
        LIMIT #{startOff},#{pageSize};
    </select>

    <select id="getTotal" resultType="java.lang.Integer">
       select count(id) from test where id >0
        <include refid="whereCaus"/>
    </select>
<select id="getTotal2" resultType="java.lang.Integer">
select count(id) from test
<where>
<include refid="whereCaus"/>
</where>
</select>

  dao層:(getTotal2這邊就不做測試了,前面試過了執行時間達到20S可能更久肯定不行的,可以自行試試)

List<?> getListData(ParamsDto paramsDto);//返回類型必須這么寫,否則會出問題
int getTotal(String name); 
List<Test> getListData2(ParamsDto paramsDto);

  controller層會解析數據,ParamsDto參數對象的話就3個參數private String name;private Integer pageSize;private Integer startOff;自行構建即可

package com.example.demo.controller;


import com.example.demo.dto.ParamsDto;
import com.example.demo.mapper.TestMapper;
import com.example.demo.model.Test;
import com.example.demo.run.RunScan;
import com.example.demo.utils.JsonResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author lxp
 * @since 2021-12-29
 */
@RestController
@RequestMapping("/test")
public class TestController {
    @Autowired
    private TestMapper testMapper;
    @RequestMapping("/v1.do")
    public  JsonResult test1(ParamsDto paramsDto) {
        Integer currentPage = paramsDto.getStartOff();
        //每頁顯示數量
        Integer pageSize = paramsDto.getPageSize();
        paramsDto.setName(paramsDto.getName());
        paramsDto.setPageSize(pageSize);
        paramsDto.setStartOff((currentPage-1)*pageSize);
        long startTime = System.currentTimeMillis();    //獲取開始時間
        List<?> listData = testMapper.getListData(paramsDto);
        long endTime = System.currentTimeMillis();    //獲取結束時間
        //接收count數據
        Integer totalCount = ((List<Integer>) listData.get(1)).get(0);
        Integer totalPage = totalCount % pageSize == 0 ?
                totalCount / pageSize :
                totalCount / pageSize + 1;
        List<Test> studentManageVoList = (List<Test>)listData.get(0);

        Map map = new HashMap();
        map.put("listData",studentManageVoList);
        map.put("totalPage",totalPage);
        map.put("finishTime",(endTime - startTime) + "ms");
        return JsonResult.successResult(map);
    }
    @RequestMapping("/v2.do")
    public JsonResult test2(ParamsDto paramsDto){
        Integer currentPage = paramsDto.getStartOff();
        //每頁顯示數量
        Integer pageSize = paramsDto.getPageSize();
        paramsDto.setName(paramsDto.getName());
        paramsDto.setPageSize(pageSize);
        paramsDto.setStartOff((currentPage-1)*pageSize);
        long startTime = System.currentTimeMillis();    //獲取開始時間
        List<Test> listData2 = testMapper.getListData2(paramsDto);
        int totalCount = testMapper.getTotal(paramsDto.getName());
        long endTime = System.currentTimeMillis();    //獲取結束時間

        Integer totalPage = totalCount % pageSize == 0 ?
                totalCount / pageSize :
                totalCount / pageSize + 1;
        Map map = new HashMap();
        map.put("listData",listData2);
        map.put("totalPage",totalPage);
        map.put("finishTime",(endTime - startTime) + "ms");
        return JsonResult.successResult(map);

    }
}

可以對比下這兩個方法的優劣,測試的結果顯而易見/test/v1.do的接口速度比/test/v2.do速度要快,差不多2倍速度

下面實驗下對比這兩個接口訪問時間:

 

 

 

 

 

 

 

 

 

 

 就此可以得到簡單結論,使用SELECT SQL_CALC_FOUND_ROWS * .... ;SELECT FOUND_ROWS() as count;相對於平時使用select count(*)查詢總數+查詢數據效率上快上快1倍的速度,分頁到后面的越明顯,分頁靠前的可能差不多,測試的時候可以自行實驗,當然2S的時間要是還能在優化就更好了,小伙伴有更加好的建議可以一起探討,感謝觀看!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM