mysql 5.6.50,5.7 版本 索引碎片化导致服务器cpu骤增问题

在二开和运维途中将一张数据200多万的表的一个无用字段修改了类型和名称之后发现在job+rabbitmq 大批量对这个表进行查询修改增加的时候出现了服务器cpu骤增的情况

我的服务器配置是4核16GB的Ubuntu 20

这个问题让博主也是头疼了两天,不知道改如何解决这个问题。

我先是修改了线上数据库mysql5.6.50的配置文件

增加了innodb的缓存 innodb_buffer_pool_size 1024M->2048M
innodb_log_file_size 512M->1024M和innodb_log_buffer_size 128M->256M也对应增加了一倍,还将innodb_thread_concurrency :8 调整到了16

配置修改如下:

sql 复制代码
innodb_buffer_pool_size = 2048M
innodb_log_file_size = 1024M
innodb_log_buffer_size = 256M
innodb_thread_concurrency = 16
然后修改了对应的job在给队列推送消息的时候每次从数据库取1000条 延时500ms再发送下一条,目前增加了对应的List.clear防止for中List多创建
java 复制代码
 int pageCount = total / BATCH_SIZE + (total % BATCH_SIZE != 0 ? 1 : 0);
        Page<ReadMeterParamsVo> page = new Page<>();
        page.setSize(BATCH_SIZE);
        for (int i = 1; i <= pageCount; i++) {
            page.setCurrent(i);
            IPage<ReadMeterParamsVo> meterIPage = customerService.getReadMeterList(page, queryWrapper);
            for (ReadMeterParamsVo readMeterParamsVo : meterIPage.getRecords()) {
                rabbitMqClient.sendMessage(READ_METER_CONSUME_QUEUE,readMeterParamsVo);
            }
            log.info("本次加入队列数(第{}批):{}", i,meterIPage.getRecords().size());
            // 这样就不会每次创建一个IPage对象了,而是复用一个IPage对象
            meterIPage.getRecords().clear();
            if(i < pageCount){
                try {
                    Thread.sleep(BATCH_INTERVAL);
                } catch (InterruptedException e) {
                    log.error("线程休眠异常",e);
                }
            }
        }

我的消费者设置了12-16个根据消息的多少4个消费者设置的都是 concurrency = "3-4",取文件是prefetch 4,没有修改定时任务所对应表字段的时候,抄表时mysql cpu占比是4.6%,修改之后cpu占比是99%,并且我的sql慢查询日志也没有

然后又想到了mysql索引碎片化,如是使用下面脚本查询一下数据库对应表的碎片率和空闲空间

sql如下

sql 复制代码
-- 查询数据库所有表的
SELECT 
  TABLE_NAME,
  DATA_FREE/1024/1024 AS DATA_FREE_MB,  -- 空闲空间(MB)
  (DATA_FREE/(DATA_LENGTH+INDEX_LENGTH))*100 AS FRAGMENT_RATE  -- 碎片率(%)
FROM INFORMATION_SCHEMA.TABLES 
WHERE TABLE_SCHEMA = 'smart_water';
sql 复制代码
-- 查询出索引碎片化大于10%的表
SELECT 
  TABLE_NAME,
  -- 保留2位小数,显示空闲碎片空间(MB)
  ROUND(DATA_FREE/1024/1024, 2) AS DATA_FREE_MB,
  -- 保留2位小数,计算碎片率,避免除以0
  ROUND(IF((DATA_LENGTH+INDEX_LENGTH)=0, 0, (DATA_FREE/(DATA_LENGTH+INDEX_LENGTH))*100), 2) AS FRAGMENT_RATE
FROM INFORMATION_SCHEMA.TABLES 
WHERE TABLE_SCHEMA = 'smart_water' 
  -- 筛选碎片率>10%的表,兼容5.6
  AND IF((DATA_LENGTH+INDEX_LENGTH)=0, 0, (DATA_FREE/(DATA_LENGTH+INDEX_LENGTH))*100) > 10
  -- 排除视图/空表,仅保留InnoDB/MyISAM表
  AND TABLE_TYPE = 'BASE TABLE' 
  AND DATA_LENGTH > 0;
对碎片化大于30%的表使用OPTIMIZE TABLE重建所有索引 + 整理表碎片,对于小一点的使用 ANALYZE TABLE更新索引统计信息(如行数、字段分布)

下面是两个sql的区别

操作 核心作用 锁表情况 耗时(百万行表) 适用场景
ANALYZE TABLE 更新索引统计信息(如行数、字段分布) 无锁/极低锁(几乎不阻塞业务) 几秒 1. 修改字段后; 2. 数据大量增删后; 3. 优化器选错索引
OPTIMIZE TABLE 重建所有索引 + 整理表碎片 短时间锁表(InnoDB 5.6+支持在线) 几十秒~几分钟 1. 索引碎片率极高(>30%); 2. 表数据频繁删除后空间浪费; 3. 索引损坏

使用就是 OPTIMIZE TABLE 表名,表名 和 ANALYZE TABLE 表名,表名

目前数据库配置和索引碎片化已经整理了,就期待明天的定时抄表任务cpu占比效率了

如果大佬还有其他的方法能解决这个mysql cpu突然飙升的问题,欢迎评论区留言

相关推荐
无限码力12 小时前
华为OD技术面真题 - Mysql相关 - 4
mysql·华为od·华为od技术面真题·华为od技术面八股·华为od技术面八股文·华为od技术面mysql相关
Wasim40412 小时前
【渗透测试】SQL注入
网络·数据库·sql
laplace012313 小时前
Claude Code 逆向工程报告 笔记(学习记录)
数据库·人工智能·笔记·学习·agent·rag
2401_8365631813 小时前
用Python读取和处理NASA公开API数据
jvm·数据库·python
2301_8187320613 小时前
项目启动报错,错误指向xml 已解决
xml·java·数据库·后端·springboot
Austindatabases13 小时前
<span class=“js_title_inner“>DBA 未来的是末日还是希望 -- 国外专家博主分析谈发展趋势 (翻译 分析)</span>
数据库·dba
难得的我们13 小时前
超越Python:下一步该学什么编程语言?
jvm·数据库·python
青衫码上行14 小时前
NoSql数据库简介 + Redis概述
数据库·redis·nosql
码农阿豪14 小时前
Oracle 到金仓数据库迁移实战:一次真正“落地”的国产替代之旅
java·数据库·oracle
TDengine (老段)14 小时前
通过云服务 快速体验 TDengine
大数据·数据库·物联网·时序数据库·tdengine·涛思数据·iotdb