MYSQL一次慢查询优化,不要被“索引“蒙蔽了双眼

2024-03-26 16:10

本文主要是介绍MYSQL一次慢查询优化,不要被“索引“蒙蔽了双眼,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近光衰趋势的excel表格一直不能正常发送。手工执行,发现某sql执行后返回查询结果要15分钟。判定是数据库原因导致,急需优化。

定位元凶,慢查询SQL:
select ifnull(t2.tx_dbm,‘0’) from devops.pvs_switch_port t1
left join devops.pvs_sfpshow t2 on (t1.ip=t2.ip and t1.port=t2.port) and t2.inserttime=202208282310 order by t1.ip,t1.port+0;

观察sql,有用到join,sort。目测可能需优化的地方,下面给出了各项配置数据:
1、 innodb_pool_size = 32G 【服务器内存为64G】
2、 慢查询索引,经过explain分析,发现都已经走索引了,无需添加
3、 SQL中有join,order ,检查 join_buffer_size,sort_buffer_size参数分表为16,32M。
4、 pvs_sfpshow表有4200W数据

由此:可以优化的地方:
1、 加大innnodb_pool_size =50G 【64*80%】
2、 Sfp_show 分表

测试:
1、 查询去掉order ,查询时间差不多,此项忽略
2、 将where条件放入t2中先执行,然后查询时间为,15.6分钟,时间长没变化
3、 将一天数据单独插入一个表中(create table XXX select XXX),然后查询。0.016秒。查询极快,以此判断大概率是数据量问题导致。要么分区,要么建临时表,要么优化索引

遇到难题:
1、 inserttime是varchar类型的,不支持range分区
2、 将inserttime改为bigint后,要求range字段为主键或者主键字段之一。
3、 遂更改主键为id,inserttime

由此:
1、 加大内存配置
2、 Sfpshow表做分区
ALTER TABLE pvs_sfpshow PARTITION BY RANGE( inserttime ) (PARTITION p20220829 VALUES LESS THAN (202208292359))
3、 加定时任务,每天自动分区
4、 以上更改后,虽然能执行,但是发现仍15分钟。仔细查看索引,发现并没有自己预想的索引,故此怀疑索引字段不够。遂将索引字段修改为inserttime,ip,port,查询变成了0.3秒

在这里插入图片描述

总结:此SQL虽然走了索引,但索引字段不够,查询仍缓慢,需将where中的字段都修改为索引字段,查询优化成功

以下附上分区脚本:(脚本复用zabbix的分区脚本,做了简单修改)


#!/bin/bash#配置环境变量
DB_USER="devops"
DB_PWD="dddd"
DB_NAME="devops"
DB_PORT="3306"
DB_HOST="127.0.0.1"
MYSQL_BIN="/home/mysql-8.0.25/bin/mysql"#历史数据保留时间,单位天
HISTORY_DAYS=180#趋势数据保留时间,单位月
TREND_MONTHS=12HISTORY_TABLE="pvs_sfpshow"
TREND_TABLE="trends"#MYSQL连接命令
MYSQL_CMD=$(echo ${MYSQL_BIN} -u${DB_USER} -p${DB_PWD} -P${DB_PORT} -h${DB_HOST} ${DB_NAME})function create_partitions_history() {#给历史表创建分区for PARTITIONS_CREATE_EVERY_DAY in $(date +"%Y%m%d") $(date +"%Y%m%d" --date='1 days') $(date +"%Y%m%d" --date='2 days') $(date +"%Y%m%d" --date='3 days')  $(date +"%Y%m%d" --date='4 days') $(date +"%Y%m%d" --date='5 days') $(date +"%Y%m%d" --date='6 days') $(date +"%Y%m%d" --date='7 days')doprintf "$PARTITIONS_CREATE_EVERY_DAY"TIME_PARTITIONS=${PARTITIONS_CREATE_EVERY_DAY}2359printf "$TIME_PARTITIONS"for TABLE_NAME in ${HISTORY_TABLE}doSQL1=$(echo "show create table ${TABLE_NAME};")RET1=$(${MYSQL_CMD} -e "${SQL1}"|grep "PARTITION BY RANGE"|wc -l)#表结构中的表分区不存在,则创建if [ "${RET1}" == "0" ];thenSQL2=$(echo "ALTER TABLE $TABLE_NAME PARTITION BY RANGE( inserttime ) (PARTITION p${PARTITIONS_CREATE_EVERY_DAY}  VALUES LESS THAN (${TIME_PARTITIONS}));")printf "$SQL2"RET2=$(${MYSQL_CMD} -e "${SQL2}")printf "$SQL2"if [ "${RET2}" != "" ];thenecho  ${RET2}echo "${SQL2}"elseprintf "table %-12s create partitions p${PARTITIONS_CREATE_EVERY_DAY}\n" ${TABLE_NAME}ficontinuefi#表结构中的表分区已经存在,则创建分区if [ "${RET1}" != "0" ];thenSQL3=$(echo "show create table ${TABLE_NAME};")RET3=$(${MYSQL_CMD} -e "${SQL3}"|grep "p${PARTITIONS_CREATE_EVERY_DAY}"|wc -l)if [ "${RET3}" == "0" ];thenTIME_PARTITIONS=${PARTITIONS_CREATE_EVERY_DAY}2359SQL4=$(echo "ALTER TABLE $TABLE_NAME  ADD PARTITION (PARTITION p${PARTITIONS_CREATE_EVERY_DAY} VALUES LESS THAN (${TIME_PARTITIONS}));")printf "$SQL4"RET4=$(${MYSQL_CMD} -e "${SQL4}")if [ "${RET4}" != "" ];thenecho  ${RET4}echo "${SQL4}"elseprintf "table %-12s create partitions p${PARTITIONS_CREATE_EVERY_DAY}\n" ${TABLE_NAME}fififidonedone
}function drop_partitions_history() {#删除历史表分区for PARTITIONS_DELETE_DAYS_AGO in $(date +"%Y%m%d" --date="${HISTORY_DAYS} days ago")dofor TABLE_NAME in ${HISTORY_TABLE}doSQL=$(echo -e  "show create table ${TABLE_NAME};")RET=$(${MYSQL_CMD} -e "${SQL}"|grep "p${PARTITIONS_DELETE_DAYS_AGO}"|wc -l)if [ "${RET}" == "1" ];thenSQL=$(echo "ALTER TABLE ${TABLE_NAME} DROP PARTITION p${PARTITIONS_DELETE_DAYS_AGO};")RET=$(${MYSQL_CMD} -e "${SQL}")if [ "${RET}" != "" ];thenecho  ${RET}echo "${SQL}"elseprintf "table %-12s drop partitions p${PARTITIONS_DELETE_DAYS_AGO}\n" ${TABLE_NAME}fifidonedone
}function create_partitions_trend() {#创建趋势表分区for PARTITIONS_CREATE_EVERY_MONTHS in $(date +"%Y%m") $(date +"%Y%m" --date='1 months') $(date +"%Y%m" --date='2 months') $(date +"%Y%m" --date='3 months') $(date +"%Y%m" --date='4 months') $(date +"%Y%m" --date='5 months')doTIME_PARTITIONS=$(date -d "$(echo ${PARTITIONS_CREATE_EVERY_MONTHS}01 00:00:00)" +%s)for TABLE_NAME in ${TREND_TABLE}doSQL1=$(echo "show create table ${TABLE_NAME};")RET1=$(${MYSQL_CMD} -e "${SQL1}"|grep "PARTITION BY RANGE"|wc -l)#表结构中的表分区不存在,则创建if [ "${RET1}" == "0" ];thenSQL2=$(echo "ALTER TABLE $TABLE_NAME PARTITION BY RANGE( clock ) (PARTITION p${PARTITIONS_CREATE_EVERY_MONTHS}  VALUES LESS THAN (${TIME_PARTITIONS}));")RET2=$(${MYSQL_CMD} -e "${SQL2}")if [ "${RET2}" != "" ];thenecho  ${RET2}echo "${SQL2}"elseprintf "table %-12s create partitions p${PARTITIONS_CREATE_EVERY_MONTHS}\n" ${TABLE_NAME}ficontinuefi#表结构中的表分区已经存在,则创建分区if [ "${RET1}" != "0" ];thenSQL3=$(echo "show create table ${TABLE_NAME};")RET3=$(${MYSQL_CMD} -e "${SQL3}"|grep "p${PARTITIONS_CREATE_EVERY_MONTHS}"|wc -l)if [ "${RET3}" == "0" ];thenSQL4=$(echo "ALTER TABLE ${TABLE_NAME}  ADD PARTITION (PARTITION p${PARTITIONS_CREATE_EVERY_MONTHS} VALUES LESS THAN (${TIME_PARTITIONS}));")RET4=$(${MYSQL_CMD} -e "${SQL4}")if [ "${RET4}" != "" ];thenecho  ${RET4}echo "${SQL4}"elseprintf "table %-12s create partitions p${PARTITIONS_CREATE_EVERY_MONTHS}\n" ${TABLE_NAME}fififidonedone
}function drop_partitions_trend() {#删除趋势表分区for PARTITIONS_DELETE_MONTHS_AGO in $(date +"%Y%m" --date="${TREND_MONTHS} months ago")dofor TABLE_NAME in ${TREND_TABLE}doSQL=$(echo "show create table ${TABLE_NAME};")RET=$(${MYSQL_CMD} -e "${SQL}"|grep "p${PARTITIONS_DELETE_MONTHS_AGO}"|wc -l)if [ "${RET}" == "1" ];thenSQL=$(echo "ALTER TABLE ${TABLE_NAME} DROP PARTITION p${PARTITIONS_DELETE_MONTHS_AGO};")RET=$(${MYSQL_CMD} -e "${SQL}")if [ "${RET}" != "" ];thenecho  ${RET}echo "${SQL}"elseprintf "table %-12s drop partitions p${PARTITIONS_DELETE_MONTHS_AGO}\n" ${TABLE_NAME}fifidonedone
}function main() {create_partitions_history#create_partitions_trenddrop_partitions_history#drop_partitions_trend
}main

这篇关于MYSQL一次慢查询优化,不要被“索引“蒙蔽了双眼的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/849069

相关文章

MyBatis分页查询实战案例完整流程

《MyBatis分页查询实战案例完整流程》MyBatis是一个强大的Java持久层框架,支持自定义SQL和高级映射,本案例以员工工资信息管理为例,详细讲解如何在IDEA中使用MyBatis结合Page... 目录1. MyBATis框架简介2. 分页查询原理与应用场景2.1 分页查询的基本原理2.1.1 分

MySQL的JDBC编程详解

《MySQL的JDBC编程详解》:本文主要介绍MySQL的JDBC编程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、前置知识1. 引入依赖2. 认识 url二、JDBC 操作流程1. JDBC 的写操作2. JDBC 的读操作总结前言本文介绍了mysq

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

MySQL中On duplicate key update的实现示例

《MySQL中Onduplicatekeyupdate的实现示例》ONDUPLICATEKEYUPDATE是一种MySQL的语法,它在插入新数据时,如果遇到唯一键冲突,则会执行更新操作,而不是抛... 目录1/ ON DUPLICATE KEY UPDATE的简介2/ ON DUPLICATE KEY UP

MySQL分库分表的实践示例

《MySQL分库分表的实践示例》MySQL分库分表适用于数据量大或并发压力高的场景,核心技术包括水平/垂直分片和分库,需应对分布式事务、跨库查询等挑战,通过中间件和解决方案实现,最佳实践为合理策略、备... 目录一、分库分表的触发条件1.1 数据量阈值1.2 并发压力二、分库分表的核心技术模块2.1 水平分

Python与MySQL实现数据库实时同步的详细步骤

《Python与MySQL实现数据库实时同步的详细步骤》在日常开发中,数据同步是一项常见的需求,本篇文章将使用Python和MySQL来实现数据库实时同步,我们将围绕数据变更捕获、数据处理和数据写入这... 目录前言摘要概述:数据同步方案1. 基本思路2. mysql Binlog 简介实现步骤与代码示例1

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

Python实战之SEO优化自动化工具开发指南

《Python实战之SEO优化自动化工具开发指南》在数字化营销时代,搜索引擎优化(SEO)已成为网站获取流量的重要手段,本文将带您使用Python开发一套完整的SEO自动化工具,需要的可以了解下... 目录前言项目概述技术栈选择核心模块实现1. 关键词研究模块2. 网站技术seo检测模块3. 内容优化分析模

使用shardingsphere实现mysql数据库分片方式

《使用shardingsphere实现mysql数据库分片方式》本文介绍如何使用ShardingSphere-JDBC在SpringBoot中实现MySQL水平分库,涵盖分片策略、路由算法及零侵入配置... 目录一、ShardingSphere 简介1.1 对比1.2 核心概念1.3 Sharding-Sp