北上广深程序员,在北京年 20w,回郑州要降薪还不到15w。笑哭,回老家, 这么难嘛?到底是谁在和我竞争啊!
刚刷到个吐槽帖,给我看沉默了。
说北上广深的程序员,在北京一年20w,结果想回郑州,薪资直接往下砍,别说20了,15w都费劲。笑哭,这回老家也不是回,是重新投胎啊。
最扎心的是,你以为自己从一线回来,多少算个“降维打击”。结果一看岗位,要求没少,工资少一截,面试还挺挑。你会的他们都要,你不会的他们也写上,最后给的价格像在招实习生加强版。
然后评论区更真实:不是没人要你,是大家都想回去。大厂出来的、小厂熬过的、远程干不动的、家里催结婚买房的,全挤到那几个坑里。
所以问题来了,到底是谁在和我竞争?
接口压测时最难看的不是 CPU 飙高,是日志里一排这样的东西:
query user profile cost=18432ms, ids=10000
SQLSyntaxErrorException: too many parameters
这类问题我一般不先看业务逻辑。先看查询是不是一口气把所有 id 塞进去了。
批处理查询这题,表面是算法题,实际线上经常撞到数据库、RPC、ES、缓存这些东西的限制。一次查太少,网络来回耗死;一次查太多,SQL 直接炸,或者把数据库拖慢。
笨写法大概长这样:
List<UserInfo> users = userMapper.selectByIds(userIds);
看着干净,风险全藏在里面。
如果 userIds 只有几十个,没事。 如果是后台导入、定时补偿、报表回刷,一来就是几千上万,这行代码就不太可信了。
我一般会把它拆成小批次,每批 300 到 1000,看库的情况调,不写死在业务方法里。
publicclassBatchUserQuery{
privatefinal UserMapper userMapper;
publicBatchUserQuery(UserMapper userMapper){
this.userMapper = userMapper;
}
public List<UserInfo> queryUsers(List<Long> rawIds, int batchSize){
if (rawIds == null || rawIds.isEmpty()) {
return Collections.emptyList();
}
List<Long> ids = rawIds.stream()
.filter(Objects::nonNull)
.distinct()
.toList();
Map<Long, UserInfo> userMap = new HashMap<>(ids.size() * 2);
for (int left = 0; left < ids.size(); left += batchSize) {
int right = Math.min(left + batchSize, ids.size());
List<Long> part = ids.subList(left, right);
long start = System.currentTimeMillis();
List<UserInfo> rows = userMapper.selectAliveUsers(part);
long cost = System.currentTimeMillis() - start;
if (cost > 800) {
System.out.println("slow batch query, size=" + part.size() + ", cost=" + cost);
}
for (UserInfo row : rows) {
userMap.put(row.getUserId(), row);
}
}
List<UserInfo> result = new ArrayList<>(rawIds.size());
for (Long id : rawIds) {
UserInfo user = userMap.get(id);
if (user != null) {
result.add(user);
}
}
return result;
}
}
SQL 也别写得太飘:
select user_id, name, level
from user_info
where deleted = 0
and user_id in (...)
这里有几个细节容易被忽略。
第一,先去重。 同一个 id 查十次,数据库不会因为你诚恳就少干活。
第二,返回结果最好按原始 id 顺序组回去。 很多批处理后面还要和 Excel 行号、任务明细、消息列表对应,顺序一乱,排查起来很烦。
第三,别把 batchSize 写成 10000。 那不叫批处理,那叫换个姿势硬怼数据库。
第四,慢批次要打日志。 不是所有慢查询都会被你第一时间抓到,有时候就是某一批 id 命中了坏数据、冷数据、或者执行计划抽风。
我更喜欢这样的日志:
slow batch query, size=500, cost=1260
比一句“查询失败”有用多了。
这题的关键不在切数组,而在控制边界。 一次查多少,失败怎么重试,结果顺序要不要保留,空数据怎么处理,这些才是线上代码和刷题代码拉开差距的地方。
批处理查询写好以后,系统不会突然变快得离谱,但至少不会因为一批 2 万个 id,把数据库打得半天缓不过来。