在二开和运维途中将一张数据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突然飙升的问题,欢迎评论区留言
