生产环境mysql服务器cpu100%,排查过程,后续优化思路
1、top命令查看cpu占用情况
2、执行 mysql -u root -p 进入mysql, 查看正在执行的sql
show processlist; //显示的sql不完整
show full processlist;
看到有多条相同的sql在查询,耗时较久
按照查询次数排序,导出前30条慢sql
mysqldumpslow -s c -t 30 /home/slow-20221002.log
主要参数:
A: -s, 是sort的意思,表示按照何种方式排序,c、t、l、r分别是按照记录次数、时间、查询时间、返回的记录数来排序,ac、at、al、ar,表示相应的倒序;
B: -t, 是top n的意思,即为返回前面多少条的数据;
C: -g, 是grep的意思,后边可以写一个正则匹配模式,大小写不敏感的。
找到最慢的一条sql,进行排查,最后定位到就是这条sql导致的问题。因为这条sql算是统计类的sql,要全表扫描,关联的表也比较大,group by字段多, count的字段不是聚集索引,计算量过大cpu一直下不来。
Count: 9 Time=267.50s (2407s) Lock=0.01s (0s) Rows=1.0 (9)
explain
去掉了大部分无用的字段,count字段改成主键,再看读取成本少了很多
sql优化完了效果其实并不明显,后面又加了redis缓存,使用同步锁
思路:请求接口时,先查缓存,如果没有缓存,先获取锁,获取到了查数据库,查到放入缓存,过期时间配置为1天
加锁其实就是为了解决缓存击穿,高并发访问这个key时,如果缓存失效,大量请求就不会立刻去查数据库。
@Cacheable(value = "xxxx", key = "#root.methodName+'_'+ #a0.pageNo +'_'+ #a0.pageSize", sync = true)
public Data getList(Page page){
//
return data;
}
public class MyCache implements Cache{
RedisTemplate redisTemplate;
public RedisTemplate<String, Object> getRedisTemplate() {
return redisTemplate;
}
public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public String getName() {
return key;
}
public void setName(String key) {
this.key = key;
}
@Override
public Object getNativeCache() {
return this.redisTemplate;
}
public String getKeyWarp(String key){
return getName() + key;
}
@Override
public ValueWrapper get(Object key) {
final String keyf = getKeyWarp(key.toString()) ;
Object object = redisTemplate.opsForValue().get(keyf);
return (object != null ? new SimpleValueWrapper(object) : null);
}
@Override
public <T> T get(Object key, Class<T> type) {
return null;
}
// 如果sync = true会走这段代码,这里的锁在分布式环境可以改成分布式锁
@Override
public <T> T get(Object key, Callable<T> valueLoader) {
ValueWrapper storeValue = get(key);
if (storeValue != null) {
return (T) storeValue.get();
} else {
synchronized(this) {
storeValue = get(key);
if (storeValue != null) {
return (T) storeValue.get();
} else {
Object value;
try {
value = valueLoader.call();
} catch (Throwable var8) {
throw new ValueRetrievalException(key, valueLoader, var8);
}
put(key, value);
return (T) value;
}
}
}
}
@Override
public void put(Object key, final Object value) {
String strKey = getKeyWarp(key.toString());
//临时缓存 1天过期
if(strKey.indexOf("xxxx") > -1 {
redisTemplate.opsForValue().set(strKey, value);
redisTemplate.expire(strKey, 1, TimeUnit.DAYS);
} else {
redisTemplate.opsForValue().set(strKey, value);
}
}
@Override
public void evict(Object key) {
final String keyf = getKeyWarp(key.toString());
redisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection connection)
throws DataAccessException {
return connection.del(keyf.getBytes());
}
});
}
@Override
public void clear() {
}
@Override
public ValueWrapper putIfAbsent(Object arg0, Object arg1) {
return null;
}
}
@Bean("cacheManager")
public SimpleCacheManager getSimpleCacheManager(RedisTemplate redisTemplate){
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
MyCache myCache= new MyCache();
mySpringCacheRedisConfig.setName("xxx");
mySpringCacheRedisConfig.setRedisTemplate(redisTemplate);
list.add(mySpringCacheRedisConfig);
simpleCacheManager.setCaches(list);
return simpleCacheManager;
}
参考链接