欢迎

记录技术思考与实践笔记

CPU 100% 故障定位指导手册

适用场景: 告警系统上报 CPU 使用率超过阈值, 需逐层定位至代码行级 前置知识: Linux 进程/线程模型, CPU 时间片调度, JVM 线程模型 一、为什么需要四层诊断模型 1.1 理论依据: 故障定位的"层级原则" 一个 CPU 告警可能由多个层面的问题引起, 每一层需要不同的工具来观测: 系统层 (kernel) → /proc/stat, vmstat — 看调度队列和 CPU 时间分配 进程层 (process) → top, /proc/<pid>/stat — 看进程级别的 CPU 消耗 线程层 (thread) → top -H, /proc/<pid>/task — 看线程级别的 CPU 消耗 代码层 (code) → jstack, perf, Arthas — 看具体的执行方法/指令 选择依据: 每一层只看上一层无法提供的信息。系统层告诉你"CPU 在忙", 但不告诉你是谁; 进程层告诉你"是 PID 172 在忙", 但如果你直接跳到代码层(比如盲目 jstack), 你拿到 30 个线程的栈, 却不知道该看哪一个——这就是为什么需要线程层: 先找到具体的线程 TID, 再精准定位。 ...

2026-06-13 · 陈曦

在Java中写一个正确的单例模式

本文参考极客时间每日一课《在Java中如何写一个正确的单例模式?》 1. 单例/单例设计模式 一个类只允许创建一个对象(实例),这个类就是一个单例类。这种设计模式叫做单例设计模式,简称单例模式。 实现一个单例类,需要关注: 构造函数私有,避免通过new关键字创建实例 创建时的线程安全问题 是否支持延迟加载 getInstance是否高性能(锁) 2. 单例模式的写法 2.1. 饿汉式 public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton() { } public static Singleton getInstance() { return INSTANCE; } } public class Singleton { private static Singleton instance; static { instance = new Singleton(); } private Singleton() {} public static Singleton getInstance() { return instance; } } 类加载时,静态实例就已经创建好并初始化好了。 ...

2021-11-19 · 陈曦

MySQL主从复制-异步复制

复制是MySQL数据库提供的一种高可用高性能解决方案,一般用来建立大型应用(《MySQL技术内幕:innodb存储引擎(第二版)》)。 常见的主从复制方案有:异步复制、半同步复制、组复制。本文主要介绍传统的异步复制方式。 1. 环境准备 操作系统:macOS Big Sur 11.0.1 MySQL: mysql 5.7 为方便操作,这里使用docker来进行配置。配置3个MySQL实例,用于后续进行主从复制实验(1主2从)。 1.1. 拉取MySQL 5.7的镜像 docker pull mysql:5.7 1.2. 启动MySQL实例 启动 master 实例 docker run -d \ -p 3307:3306 \ -e MYSQL_ROOT_PASSWORD=123456 \ -e TZ=Asia/Shanghai \ -v ~/tools/mysql/master/data:/var/lib/mysql \ -v ~/tools/mysql/master/conf.d:/etc/mysql/conf.d \ --name mysql-5.7-master \ mysql:5.7 将MySQL数据文件挂在到本地目录 ~/tools/mysql/master/data,这样数据文件就可以直接在本地查看。 将MySQL配置文件目录挂在到本地目录 ~/tools/mysql/master/conf.d,这样就可以在本地自定义配置文件,而不需要每次修改配置都登入容器内部。 自定义配置文件 my-custom.cnf 如下, [mysqld] server_id = 1 sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES log_bin=mysql-bin binlog-format=Row 启动 slave01 实例 docker run -d \ -p 3317:3306 \ -e MYSQL_ROOT_PASSWORD=123456 \ -e TZ=Asia/Shanghai \ -v ~/tools/mysql/slave01/data:/var/lib/mysql \ -v ~/tools/mysql/slave01/conf.d:/etc/mysql/conf.d \ --name mysql-5.7-slave01 \ mysql:5.7 自定义配置文件 my-custom.cnf 如下, ...

2021-06-17 · 陈曦

GC日志解读

为深入学习GC(Garbage Collection,垃圾回收),本文将使用一段测试代码来测试不同的GC策略下的执行情况,并对输出的GC日志做简要分析。 1. 测试环境 1.1. 操作系统及jdk版本 ➜ 01jvm git:(main) ✗ java -version java version "1.8.0_131" Java(TM) SE Runtime Environment (build 1.8.0_131-b11) Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode) 1.2. 测试代码 测试代码来源: https://github.com/JavaCourse00/JavaCourseCodes/blob/main/01jvm/GCLogAnalysis.java import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.LongAdder; /* 演示GC日志生成与解读 */ public class GCLogAnalysis { private static Random random = new Random(); public static void main(String[] args) { // 当前毫秒时间戳 long startMillis = System.currentTimeMillis(); // 持续运行毫秒数; 可根据需要进行修改 long timeoutMillis = TimeUnit.SECONDS.toMillis(1); // 结束时间戳 long endMillis = startMillis + timeoutMillis; LongAdder counter = new LongAdder(); System.out.println("正在执行..."); // 缓存一部分对象; 进入老年代 int cacheSize = 2000; Object[] cachedGarbage = new Object[cacheSize]; // 在此时间范围内,持续循环 while (System.currentTimeMillis() < endMillis) { // 生成垃圾对象 Object garbage = generateGarbage(100*1024); counter.increment(); int randomIndex = random.nextInt(2 * cacheSize); if (randomIndex < cacheSize) { cachedGarbage[randomIndex] = garbage; } } System.out.println("执行结束!共生成对象次数:" + counter.longValue()); } // 生成对象 private static Object generateGarbage(int max) { int randomSize = random.nextInt(max); int type = randomSize % 4; Object result = null; switch (type) { case 0: result = new int[randomSize]; break; case 1: result = new byte[randomSize]; break; case 2: result = new double[randomSize]; break; default: StringBuilder builder = new StringBuilder(); String randomString = "randomString-Anything"; while (builder.length() < randomSize) { builder.append(randomString); builder.append(max); builder.append(randomSize); } result = builder.toString(); break; } return result; } } 编译测试代码 ...

2021-06-05 · 陈曦

Redis持久化、主从复制、哨兵、集群配置,一步一步走向高可用

1. Redis数据持久化 2. Redis主从复制:从单机到多节点 3. Redis Sentinel主从切换:走向高可用 3.1. 配置主从 参考步骤2. 配置一主一从两个Redis节点,主节点为127.0.0.1:6379,从节点为127.0.0.1:6380。 3.2. 配置sentinel的配置文件 sentinel0.conf sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 10000 3.3. 启动一个sentinel节点 使用以下命令启动一个sentinel redis-sentinel ./conf/sentinel0.conf 启动之后控制台输出如下内容 5643:X 13 Mar 2021 21:40:56.054 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 5643:X 13 Mar 2021 21:40:56.054 # Redis version=6.0.9, bits=64, commit=00000000, modified=0, pid=5643, just started 5643:X 13 Mar 2021 21:40:56.054 # Configuration loaded 5643:X 13 Mar 2021 21:40:56.055 * Increased maximum number of open files to 10032 (it was originally set to 256). _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 6.0.9 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in sentinel mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 26379 | `-._ `._ / _.-' | PID: 5643 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 5643:X 13 Mar 2021 21:40:56.058 # Sentinel ID is d66c226c4532b8e5d5927e0a9cd441d6c4c8f805 5643:X 13 Mar 2021 21:40:56.058 # +monitor master mymaster 127.0.0.1 6379 quorum 2 5643:X 13 Mar 2021 21:40:56.058 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379 再次查看配置文件sentinel0.conf,会发现redis会自动将sentinel0.conf文件修改为如下内容 ...

2021-06-03 · 陈曦