武汉音游科技有限公司
约 2031 字大约 7 分钟
2026-04-04
mybatis默认反射设置属性慢
服务器无法处理请求,nginx代理其中一台服务器处理不了请求,直接访问也打不开页面
1.日志查看出现GC
jmap -dump 查看是否内存溢出
发现某个对象频繁创建!!!2.查看进程占用高达1400%
top -Hp
printf %\n
jstack 线程 获取堆栈信息
发现某个方法一直在执行,分析可能是数据量大/数据库死锁这个方法是统计报表用到,每个月底会执行,数据每天20w作业,统计实验室的温度,湿度;获取数据湖的数据
本地debug 直接卡死,GC
多线程并行分批处理,发现一天一天执行时间还是10分钟左右
排查sql问题,sql执行1-3s
方法里面打印执行时间,发现查询数据慢
可能是sql字段过多,映射实体较慢
修改xml指定具体字段,换指定jdbc类型和java类型转换
发现问题还是没有解决
可能是mybatis默认的反射(运行时动态解析)填充字段慢
修改为构造器(编译时,调用类的构造函数)方式映射
修改完执行时间减少到1-2分钟(每一天的数据)
LinkedBlockingQueue阻塞队列实现事件调度,如果数据量过大,导致设置的队列长度满了,有没有方法解决
拒绝策略
使用当前线程处理
使用新线程处理
记录信息,发送消息提醒,手动重试
动态调整队列容量
使用带超时的offer方法,超时之后(丢弃当前事件、记录日志、 触发报警)
增加消费者线程数量
分布式锁除了通过key加锁(SETNX + Lua 脚本),还有没有其它的方式
使用 SET 命令(带 NX/EX 参数)。
Redis 的 SET 命令在 2.6.12 版本后支持 NX 和 EX 参数,可以直接在一条命令中完成加锁操作,避免了 SETNX 和 EXPIRE 分开执行带来的原子性问题。
实现原理:
使用 SET key value NX PX timeout 命令尝试加锁。
NX :只有当键不存在时才设置键。
PX :设置键的过期时间(毫秒)。
如果命令返回 OK ,则表示加锁成功;如果返回 nil ,则表示锁已被占用。
import redis.clients.jedis.Jedis;
public class RedisLockExample {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
String lockKey = "lock_key";
String requestId = UUID.randomUUID().toString();
int timeout = 10000; // 锁超时时间,单位为毫秒
String result = jedis.set(lockKey, requestId, "NX", "PX", timeout);
if ("OK".equals(result)) {
try {
// 执行业务逻辑
} finally {
// 释放锁
if (requestId.equals(jedis.get(lockKey))) {
jedis.del(lockKey);
}
}
} else {
// 获取锁失败,重试或返回
}
}
}使用 RedLock 算法,提高锁的高可用性和一致性。
RedLock 是 Redis 的作者提出的分布式锁算法,通过多个独立的 Redis 实例来实现锁的高可用性和一致性。实现原理:
• 客户端尝试在多个独立的 Redis 实例上加锁。
• 只有在大多数实例(超过半数)上成功加锁,才认为获取锁成功。\
• 锁的总耗时必须小于锁的有效时间。
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
public class RedLockExample {
public static void main(String[] args) {
Config config = new Config();
config.useSingleServer().setAddress("redis://localhost:6379");
RedissonClient redisson = Redisson.create(config);
RLock lock = redisson.getLock("myLock");
try {
if (lock.tryLock(10, 10, TimeUnit.SECONDS)) {
try {
// 执行业务逻辑
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}使用 Lua 脚本,确保操作的原子性。
Lua 脚本可以确保加锁和解锁操作的原子性,避免竞态条件。
实现原理:
• 使用 Lua 脚本将多个 Redis 命令封装在一起,确保操作的原子性。
• 例如,加锁时可以检查键是否存在,如果不存在则设置键并设置过期时间。
import redis.clients.jedis.Jedis;
public class LuaLockExample {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
String lockKey = "lock_key";
String requestId = UUID.randomUUID().toString();
int timeout = 10000; // 锁超时时间,单位为毫秒
// 加锁 Lua 脚本
String lockScript = "if redis.call('exists', KEYS[1]) == 0 then " +
"redis.call('set', KEYS[1], ARGV[1]);" +
"redis.call('pexpire', KEYS[1], ARGV[2]);" +
"return 1;" +
"else return 0; end";
// 释放锁 Lua 脚本
String unlockScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]);" +
"else return 0; end";
// 加锁
Long result = jedis.eval(lockScript, Collections.singletonList(lockKey), Arrays.asList(requestId, String.valueOf(timeout)));
if (result == 1L) {
try {
// 执行业务逻辑
} finally {
// 释放锁
jedis.eval(unlockScript, Collections.singletonList(lockKey), Collections.singletonList(requestId));
}
} else {
// 获取锁失败,重试或返回
}
}
}使用 Redis 的事务特性,实现简单的分布式锁。
虽然 Redis 的事务本身不支持回滚,但可以通过事务的原子性来实现简单的分布式锁。
实现原理:
• 使用 MULTI 和 EXEC 命令将加锁和设置过期时间的操作封装在一个事务中。
• 如果事务执行成功,则表示加锁成功。
import redis.clients.jedis.Jedis;
public class RedisTransactionLockExample {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
String lockKey = "lock_key";
String requestId = UUID.randomUUID().toString();
int timeout = 10000; // 锁超时时间,单位为毫秒
jedis.watch(lockKey); // 监视锁键
if (jedis.get(lockKey) == null) {
jedis.multi();
jedis.set(lockKey, requestId);
jedis.expire(lockKey, timeout / 1000);
List<Object> results = jedis.exec();
if (results != null && results.size() == 2) {
try {
// 执行业务逻辑
} finally {
jedis.del(lockKey);
}
}
} else {
// 获取锁失败,重试或返回
}
}
}防止多个用户操作同一数据造成数据不一致的问题
使用version,如果用户修改了数据还没提交,数据库挂了,有没有什么方法,保证修改了的数据不丢失
接口有重发机制,如果多次点击重发,怎么限制
前端限制
禁用按钮:在用户点击按钮后,立即禁用按钮,直到请求完成再重新启用
防抖(Debounce):确保在用户停止操作一段时间后才执行请求,适用于需要延迟触发的场景。
节流(Throttle):确保在指定时间内只执行一次操作,适用于需要限制操作频率的场景。后端限制
使用 Redis 分布式锁:确保在指定时间内只处理一次请求
使用 Redis 的 INCR 命令:实现计数限制,适用于需要限制请求频率的场景多线程countdownlatch并行查询,如果有线程报错或者执行失败,出现死锁,怎么解决
可设置超时机制,如果长时间执行,可以try catch手动让信号量减1
用到的设计模式
门面(提供统一接口,简化与多个子系统交互步骤),模板(定义业务骨架),建造者(构建和表示分离),策略(优化if else)
职业发展,5年经验需要会什么
https://note-1259190304.cos.ap-chengdu.myqcloud.com/noteimage-20241229205657540.png
遇到需求变更,或者leader和你的技术方案不同,你怎么解决
理解需求变更和技术分歧是项目开发过程中的一部分,保持开放的心态,愿意倾听和理解他人的观点。
在提出自己的观点之前,先花时间了解需求变更或技术分歧的背景和原因。
沟通是解决分歧的关键。主动与领导或相关团队成员进行沟通,表达自己的观点和担忧,同时也要倾听他们的意见。
在沟通的基础上,尝试找到双方都能接受的解决方案。
如果经过沟通和讨论后,最终决定采用领导的方案,要学会灵活调整自己的思路,积极配合项目的推进。即使自己的方案没有被采纳,也要理解这是项目决策的一部分,保持积极的工作态度。
无论最终结果如何,都要对这次经历进行记录和总结。
始终要从项目的整体目标出发,而不是单纯地坚持自己的技术方案。如果领导的方案更符合项目的短期目标或资源限制,即使技术上不是最优解,也可以接受。反之,如果自己的方案对项目的长期发展更有利,可以通过持续沟通和展示其潜在价值来说服领导。
贡献者
更新日志
fb8bc-更新为vuepress于