目前项目上使用kafka+redis+mysql进行高速公路行驶车辆抓拍流水的存储,之前一直运行正常,但周五上午10点kafka突然出现消息积压,并且无缓解趋势。
二、排查过程1、检查实时数据处理程序运行状况,发现单条数据处理时间很不稳定,从几毫秒到几秒。
2、检查kafka中间件服务器状态及kafka日志,无异常情况,且kafka中其它队列消费情况正常。
3、检查redis缓存服务器状态,使用info命令查看redis连接及内存使用,无异常情况。
4、通过对实时数据处理过程的日志分析,确定问题出在数据入库环节,检查连接池最大连接数,无异常情况。
5、通过show processlist查看数据库连接状态,连接数正常,也不存在慢SQL情况。
6、检查数据库服务器CPU及内存使用状态,情况正常。
7、iostat -x 1 10 检查数据库服务器磁盘IO情况,发现数据盘一直100%满负荷状态。
8、安装 iotop 进行监测,发现磁盘写入正常,但读IO一直保持在300M-400M的状态,导致磁盘一直处于满负荷状态。
9、由于实时数据处理主要是做数据insert入库操作,不应该存在大量的数据读取,为确定数据读取操作的来源,将数据入库服务暂时关闭,发现磁盘读IO立即下降,基本可确定是数据入库服务导致的磁盘读IO满负载。
10、SET GLOBAL general_log = 'ON' 开启mysql日志输出功能,经过对执行日志的分析,发现存在大量根据id字段select流水明细数据表的操作。
11、对数据入库服务代码进行检查,发现程序在使用JPA方式保存数据时手动设置了主键id,而JPA的save方法在检测到实体主键存在时会先根据主键去数据库做一次数据查询,若数据存在则进行update操作,否则才进行insert操作,所以产生了大量的数据库读命令,导致磁盘读IO满负载。
12、对代码进行优化,数据insert之前不再进行select操作,磁盘IO恢复正常,消息积压问题解决。
@JsonIgnoreProperties(ignoreUnknown = true) @Entity(name = "t_vehicle_waste") public class VehicleWaste implements Persistable{ @Id @JsonProperty("id") private String id; @JsonProperty("vlp") private String vlp; ... @Override public boolean isNew() { return true; } }
13、问题解决过程中发现mysql数据库在启动之前未进行配置优化,顺便进行了部分参数的优化。
## 设置数据库缓存 SET GLOBAL innodb_buffer_pool_size = 8589934592; ## 设置数据库缓存实例个数(无法动态更新,在数据库配置文件中设置) SET GLOBAL innodb_buffer_pool_instances = 8; ## 设置临时表空间大小(查询排序等会写入临时表) SET GLOBAL tmp_table_size = 2147483648; ## 设置binlog写入方式,0:不主动写入,依赖操作系统;1:每次提交事务写入;N:提交N次事务后写入 SET GLOBAL sync_binlog = 1000; ## 提交事务时根据策略把 redo 日志从 redo log buffer 里刷入到磁盘文件里去 ## 0:提交事务的时候,不立即把 redo log buffer 里的数据刷入磁盘文件的,而是依靠 InnoDB 的主线程每秒执行一次刷新到磁盘,若mysql宕机内存里的数据全部丢失 ## 1:提交事务时把 redo log 从内存刷入到磁盘文件里去,只要事务提交成功,那么 redo log 就必然在磁盘里 ## 2:提交事务时把 redo 日志写入磁盘文件对应的 os cache 缓存里去,而不是直接进入磁盘文件,可能 1 秒后才会把 os cache 里的数据写入到磁盘文件里去 SET GLOBAL innodb_flush_log_at_trx_commit = 2;三、存在的疑惑
这个问题应该一直存在,但不知道为何现在才暴露出来,春运期间车流量最大的时候反倒没有出现消息积压现象。猜测可能是由于流水表数据量变大,导致表数据和表索引变大,查询时占用的磁盘IO也提高了。



