HR “人类观察实验室”:7月最后一天的候选人行为艺术大赏

一、对数据库的查询性能优化,有什么策略?

1.SQL语句层优化

根据慢查询日志,找出需要优化的一些语句。

启动慢查询日志方法:
1‌.开启慢查询日志‌
  1.1使用SQL命令设置系统变量:SET GLOBAL slow_query_log = ‘ON’;,开启日志记录功能
  1.2默认日志文件通常位于 /var/lib/mysql/ 目录下(具体路径可通过 SHOW VARIABLES LIKE ‘slow_query_log_file’; 查询) ‌
2.配置执行时间阈值‌
  2.1设置 long_query_time 参数(单位:秒):例如 SET GLOBAL long_query_time = 2;,表示超过2秒的SQL将被记录 ‌
  2.2默认阈值为10秒,但生产环境建议设为1秒或更低以提高敏感度 ‌
3.手动查看日志内容‌
  3.1通过命令行工具(如 tail)实时监控日志:tail -f /var/lib/mysql/slow.log,可动态显示新增条目 ‌
  3.2或使用文本编辑器(如 vi)静态查看:vi /var/lib/mysql/slow.log ‌
日志包含SQL语句、执行时间等细节,示例:执行时间超过阈值的SQL会明确标注(如 Query_time: 12) ‌
4.分析慢查询原因‌
  4.1使用 EXPLAIN 工具解析SQL执行计划:针对日志中的慢SQL运行 EXPLAIN [SQL语句],检查索引使用情况(如 type 扫描方式、rows 扫描行数) ‌
  4.2正则表达式清洗日志:替换查询条件变量为占位符(如将具体值改为 X),聚合相同业务SQL以统计平均耗时 ‌
  4.3推荐工具:MySQL自带的 mysqldumpslow 或第三方工具(如 Percona Toolkit)解析日志,快速定位高频慢SQL ‌

常见优化方向:
1.避免SELECT *,只查询必要的字段
2.避免在SQL中进行函数计算等操作,是的无法命中索引
3.避免使用%LIKE,导致全表扫描
4.注意联合索引需要满足最左匹配原则
5.不要对无索引字段进行排序操作
6.链表查询时需要注意不同字段的字符集是否一致,不一致也会导致全表扫描

2.数据库表设计优化

1.合理的表结构:比如选择合适的数据类型,例如能用int就不要用bigint,还有varchar和char的选择等
2.合理冗余字段:在适当的情况下进行反范式化设计,冗余部分数据,减少关联查询
3.索引优化:根据查询频率和条件,创建合适的索引,删除不必要的索引,因为索引维护也需要成本。建议使用EXPLAIN分析查询执行计划,确认是否用上索引,是否用对索引
4.分库分表:对于超大规模的数据库系统,可以采用分库分表策略,将数据拆分到多个数据库或表中,提高读写性能和扩展性。

二、假如数据库中有一张表,存储的是现场施工人员的定位信息,以坐标为编码,有一个轨迹编码的字段。例如施工人员从A地走到B地,中间所有的坐标都会连接成一条线,A到B的距离归为一条轨迹,如果有百万级的数据量,针对所有的施工人员,查询出人员每天轨迹的矩阵图,展示在地图上,有什么方式使得查询的效率更高?

1.核心优化策略
  • 空间索引加速查询
    a. PostGIS(PostgreSQL插件):对坐标字段创建GIST索引
CREATE INDEX idx_location ON worker_trajectory USING GIST (coordinate);

       b. MySQL 8+:使用SPATIAL INDEX

ALTER TABLE worker_trajectory ADD SPATIAL INDEX(coordinate);

       c. 轨迹数据预聚合

-- 按天+人员+轨迹ID聚合为LineString
CREATE TABLE daily_trajectory AS
SELECT 
  worker_id,
  trajectory_id,
  DATE(timestamp) AS date,
  ST_MakeLine(coordinate ORDER BY timestamp) AS path  -- PostGIS函数
FROM raw_trajectory
GROUP BY worker_id, trajectory_id, DATE(timestamp);

       d. 时空分区表

-- PostgreSQL 按日期分区
CREATE TABLE trajectory_2025 (
  PARTITION OF worker_trajectory
  FOR VALUES FROM ('2025-01-01') TO ('2026-01-01')
);
2.查询优化技巧

2.1 简化轨迹点数(降低传输量)

-- PostGIS 使用Douglas-Peucker算法压缩
SELECT ST_Simplify(path, 0.0001) FROM daily_trajectory;

2.2 分页加载轨迹

SELECT path FROM daily_trajectory
WHERE date = '2025-07-31'
ORDER BY worker_id
LIMIT 100 OFFSET 0; -- 分批请求

2.3 空间范围过滤

-- 只查询地图当前视口内的轨迹
SELECT * FROM daily_trajectory
WHERE path && ST_MakeEnvelope(minX, minY, maxX, maxY); -- 地图边界框
3.还可以使用redis数据结构增加查询效率
数据类型用途优势示例内存占用
List存储压缩轨迹点顺序访问快,支持批量操作50点轨迹≈300字节
GEO实时位置查询原生支持半径搜索每点16字节
Sorted Set热点轨迹空间索引范围查询高效 (ZRANGEBYSCORE)每成员64字节
HyperLogLog访问量统计百万计数仅需12KB固定12KB
Hash轨迹元数据存储快速存取字段每字段100字节
4.总结
  • 立即添加空间索引 + 按日分区表
  • 夜间批处理生成聚合轨迹表
  • 前端启用Canvas/WebGL渲染并设置视口过滤
  • 长期规划流处理架构替代批处理

三、java中常见的垃圾回收算法

  • 标记-清除算法(Mark-Sweep)
    • 工作原理:先遍历堆中的对象,标记出所有的存活对象,接着清除未标记的对象。
    • 优点:实现简单,能够处理堆中的所有对象。
    • 缺点:标记和清除的过程会产生内存碎片,影响后续内存分配的效率。
  • 标记-整理算法(Mark-Compact) :
    • 工作原理:首先标记出所有存活的对象,然后将存活的对象整理到一边, 后清除未标记的对象。
    • 优点:避免了内存碎片问题。
    • 缺点:整理阶段需要移动对象,导致额外的开销。
  • 复制算法(Copying) :
    • 工作原理:将内存分成两部分,每次只使用其中一半,垃圾回收时将存活的对象从一半复制到另一半,清除原区域的所有对象(朴素的复制算法是这样的,实际使用会分为两个survivor和一个eden区)。
    • 优点:无处理内存碎片,分配效率高。
    • 缺点:需要双倍的内存空间,浪费了一 半的空间。

四、如何合理的规划新生代、老年代的生命周期

为提高垃圾回收效率,根据对象的生命周期特点来进行优化。

  • 对象的生命周期特点
    • 大多数对象存活时间短:大部分对象会很快变成垃圾,不再被使用,这些短生命周期的对象会被分配到新生代。
    • 少部分对象存活时间长:一些长期存活的对象不会很快被回收,分配在新生代的对象经过多次垃圾回收仍然存活,将晋升到老年代。
      所以按照存活时间分区管理更加高效,也因为不同分区的生命周期不同,所以可以采用不同的清除算法来优化处理。
  • 不同的回收算法:
    • 新生代的回收:新生代通常采用复制算法,因为新生代中大部分对象生命周期短,大部分会在一次GC中被回收,复制算法只需要在内存中保留少量存活对象,并将它们复制到Survivor空间,回收剩余区域。这种算法效率很高,适合新生代频繁创建和回收的特点。
    • 老年代的回收:老年代中对象存活时间长,回收频率低,使用标记-整理算法 或 标记-清除算法,更适合老年代对象的特性。
      且分区后可以减少GC暂停的时间(你想想每次处理一个对的数据,还是将堆分区处理来的快)。
      总而言之,分区是为了更高效的管理不同生命周期的对象。
  • 堆的分代机制
    java堆内存根据对象生命周期被划分为三部分:
    • 新生代(Young Generation): 存放新创建的对象
    • 老年代(Old Generation): 存放存活时间较长的对象,通常是从新生代晋升过来的对象
    • 永久代(Metaspace)(JDK 8以前为永久代,JDK8之后为元空间):存放类的元数据信息,包括类的静态变量、方法等
  • 新生代结构
    新生代进一步划分为三个区域:
    • Eden区:所有新创建的对象首先被分配到Eden区
    • Survivor区:Eden区中的存活对象会被复制到Survivor区(一般分为两个区域,S0和S1),经过多次GC存活的对象会逐渐晋升到老年代。
      新生代中采用复制算法,每次垃圾回收时,将Eden和Survicor中的存活对象复制到另一个Survivor空间,效率高且避免内存碎片化。
  • 老年代的作用
    老年代用于存放生命周期较长的对象,通常是新新生代晋升过来的,老年代使用的回收算法不同于新生代,常用标记-清除算法或标记-整理算法,适合回收长生命周期的对象。

五、单例模式实现方式?

1.单例模式常见有:饿汉式、懒汉式(线程安全版需要加双重检查锁定)、静态内部类、枚举单例(Java特有)等几个实现方式。

  • 饿汉式:实例在类加载时就创建,线程安全,但是如果实例初始化较多或没有被使用会浪费资源。
  • 懒汉式:实例在首次访问的时候创建,节约资源,但需要确保线程安全。
  • 双重检查锁定:在懒汉式的基础上优化,直接加锁效率太低了,双重检查锁只在第一次检查实例为null时加锁,提高性能。
  • 静态内部类:利用类加载机制实现懒加载和线程安全,推荐使用。
  • 枚举单例(java特有):通过枚举实现单例,简单且防治反射和序列化攻击

2.如何保证线程安全?
推荐静态内部类或双重检查锁定,配合volatile
3.为什么需要双重检查锁定?
因为懒汉式单例在多线程环境下,可能出现多个线程同时初始化实例的问题。

public static synchronized Singleton getInstance() {
    if (instance == null) {
        instance = new Singleton();
    }
    return instance;
}

不过每次调用getInstance方法都需要加锁,实际上只需要在第一次创建实例时加锁。在高并发环境下,频繁获取单例对象的锁操作会显著降低性能。
而双重检查锁定通过减少加锁的范围,避免每次获取实例都加锁的问题,提升性能。

public class Singleton {
    private static volatile Singleton instance; // 使用 volatile 防止指令重排

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) { // 第一次检查:避免不必要的同步
            synchronized (Singleton.class) {
                if (instance == null) { // 第二次检查:确保实例唯一
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

4.为什么双重检查锁定需要volatile关键字
在java中,volatile修饰符用于防指令重排序,从而确保双重检查锁定的正确性。
instance = new SingleTon()是一个非原子操作,分为三步:

  • 1.初始化内存空间
  • 2.初始化对象
  • 3.将对象的引用赋值给instance

在没有volatile的情况下,编译器和CPU可能对这些步骤进行重排序(比如执行顺序变成1 - 3 - 2)。此时,另一个线程可能会在instance被赋值后,但是对象尚未完成初始化时访问它,从而导致错误。
所以将instance声明为volatile,可以禁止指令重排序,确保对象的初始化过程对所有线程可见。

private static volatile Singleton instance;

保证instance的写操作对其他线程立即可见,并禁止重排序优化,确保双重检查锁定的准确性。
总结:

  • 双重检查锁定通过缩小加锁范围,仅在必要时同步代码块,既保证线程安全,又提升性能。
  • volatile关键字是实现双重检查锁定的关键,防止指令重排序导致未初始化对象被访问。

饿汉式(线程安全,类加载时初始化)

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;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

双重检查(线程安全,推荐)

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

静态内部类(线程安全,推荐)

public class Singleton {
    private Singleton() {}

    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}

枚举单例

public enum Singleton {
    INSTANCE;
    
    public void bizMethod() {
        // 一些业务逻辑方法
    }
}

//使用
Singleton singleton = Singleton.INSTANCE;
singleton.bizMethod();

六、列举常用的Spring MVC注解

  • 核心请求映射注解
注解功能描述示例
@RequestMapping通用请求映射支持所有HTTP方法@RequestMapping(“/users”)
@GetMappingGET 请求映射@GetMapping(“/user/{id}”)
@PostMappingPOST 请求映射@PostMapping(“/create”)
@PutMappingPUT 请求映射@PutMapping(“/update/{id}”)
@DeleteMappingDELETE 请求映射@DeleteMapping(“/delete/{id}”)
@PatchMappingPATCH 请求映射@PatchMapping(“/partial-update”)
  • 请求参数处理
  1. URL 参数绑定
注解功能描述
@PathVariable路径变量绑定,从URI模板中提取值 @GetMapping(“/user/{id}”) public User get(@PathVariable Long id)
@RequestParam查询参数绑定,获取URL?后的参数 @RequestParam(“name”) String username
@RequestParam(带默认值)带默认值的参数 @RequestParam(defaultValue = “0”) int page
  1. Header/Cookie 处理
注解功能描述
@RequestHeader获取HTTP头信息 @RequestHeader(“User-Agent”) String agent
@CookieValue获取Cookie值 @CookieValue(“JSESSIONID”) String sessionId
  1. 请求体处理
注解功能描述
@RequestBody解析请求体将JSON/XML自动转为对象 @PostMapping public User create(@RequestBody User user)
@RequestPart处理文件上传 @RequestPart(“file”) MultipartFile file
  • 响应处理注解
注解功能描述
@ResponseBody直接返回数据不经过视图解析器 @ResponseBody public User getUser()
@RestController组合注解= @Controller + @ResponseBody @RestController public class UserController
@ResponseStatus自定义HTTP响应状态码 @ResponseStatus(HttpStatus.CREATED)
@ResponseEntity完整控制响应 包含状态码/头信息/响应体 return new ResponseEntity<>(user, HttpStatus.OK)
  • 模型与视图处理
注解功能描述
@ModelAttribute模型数据绑定
1. 方法级:每次请求前执行
2. 参数级:绑定表单数据 @ModelAttribute(“user”) public User preload()
@SessionAttributes将模型数据存储到Session @SessionAttributes(“cart”)
@SessionAttribute从Session中获取数据 @SessionAttribute(“cart”) ShoppingCart cart
  • 异常处理
注解功能描述
@ControllerAdvice全局异常处理定义全局异常处理器 @ControllerAdvice public class GlobalExceptionHandler
@ExceptionHandler处理特定异常 @ExceptionHandler(UserNotFoundException.class)
@RestControllerAdvice= @ControllerAdvice + @ResponseBody @RestControllerAdvice
  • 内容协商与格式
注解功能描述
@CrossOrigin跨域支持配置CORS策略 @CrossOrigin(origins = “*”, maxAge = 3600)
@Valid / @Validated参数校验配合JSR-303校验注解 public User create(@Valid @RequestBody User user)
@DateTimeFormat 日期格式转换 @DateTimeFormat(pattern = “yyyy-MM-dd”) Date birthDate
  • 高级特性
注解功能描述
@InitBinder自定义数据绑定注册属性编辑器 @InitBinder public void initBinder(WebDataBinder binder)
@MatrixVariable处理矩阵变量 /users;department=IT
@MatrixVariableString department
@RequestScope请求作用域的Bean @Component @RequestScope public class UserSession

七、平时开发过程中遇到的异常

  • Spring核心异常
  1. NoSuchBeanDefinitionException
    原因:依赖注入时找不到Bean
    场景:
@Autowired 
private NonExistentService service;

      解决:
      检查Bean是否被@Component/@Service标注
      确保包扫描路径包含该Bean
      检查是否存在多个同类型Bean导致冲突

  1. BeanCreationException
    原因:Bean初始化失败
    典型子类:
    UnsatisfiedDependencyException:依赖不满足
    BeanInstantiationException:构造函数执行失败
    解决:检查构造函数和依赖注入
  • Spring MVC异常
  1. HttpMessageNotReadableException
    原因:请求体解析失败(JSON格式错误/类型不匹配)
    示例:
{"age": "abc"}  // 期望int但收到字符串

      解决:
      前端校验数据格式
      添加@ExceptionHandler处理异常

  1. MethodArgumentNotValidException
    原因:@Valid参数校验失败
    示例:public ResponseEntity<?> createUser(@Valid @RequestBody User user)
    解决:
@ExceptionHandler
public ErrorResponse handleValidationException(MethodArgumentNotValidException ex) {
    return ex.getBindingResult().getAllErrors().stream()
            .map(error -> new ErrorResponse(error.getDefaultMessage()))
            .collect(Collectors.toList());
}
  1. MissingServletRequestParameterException
    原因:缺少必需的请求参数
    示例:@GetMapping public User get(@RequestParam String id) {...}
    调用:GET /user (缺少id参数)
  • 数据库相关异常
  1. DataAccessException (Spring抽象异常)
    子类:
    DuplicateKeyException:唯一键冲突
    DataIntegrityViolationException:数据完整性违规
    DeadlockLoserDataAccessException:数据库死锁
    JpaSystemException (JPA)
    常见原因:
    延迟加载(Lazy Loading)时Session已关闭
    实体状态管理错误
  • 空指针异常
  1. NullPointerException
    高频场景:
// 1. 对象方法调用
user.getName().toUpperCase(); // user或name为null
// 2. 集合操作
list.get(0).doSomething(); // list为空
// 3. 自动拆箱
int id = Integer.parseInt(idStr); // idStr为null
防御方案:
// 使用Optional
Optional.ofNullable(user)
        .map(User::getName)
        .map(String::toUpperCase)
        .orElse("UNKNOWN");

// 使用Objects.requireNonNull
public User updateUser(User user) {
    Objects.requireNonNull(user, "User不能为空");
    // ...
}

五、IO与资源异常

  1. FileNotFoundException
new FileInputStream("non_existent.txt");
IOException

网络超时、文件读写中断等

  • 并发异常
  1. ConcurrentModificationException
    场景:遍历集合时修改集合
List<String> list = new ArrayList<>(List.of("A", "B"));
for (String s : list) {
    list.remove(s); // 抛出异常
}

解决:使用迭代器的remove()方法

  1. RejectedExecutionException
    原因:线程池拒绝新任务(队列满或已关闭)
  • 类型转换异常
    ClassCastException
Object obj = "Hello";
Integer num = (Integer) obj; // 抛出异常

//NumberFormatException

int num = Integer.parseInt("123abc"); // 数字格式错误
  • 安全相关异常
  1. AccessDeniedException
    Spring Security权限不足

  2. AuthenticationException
    认证失败(用户名/密码错误)

八、Maven的Release版本和Snapshot版本有什么区别?我使用Release1.0版本,修改代码,进行打包,push到仓库中,不修改版本号,别人进行拉取得到最新的代码吗?

1.核心区别:发布机制与版本稳定性
  • Release版本(稳定版)

    • 不可变性:同一版本号(如 1.0)的Release包发布到Maven仓库后,不允许覆盖。若代码修改后需重新发布,必须升级版本号(如 1.0 → 1.1)。

    • 缓存策略:项目依赖Release版本时,Maven仅下载一次并缓存到本地。即使远程仓库有同名版本的新内容(理论上不应存在),本地也不会自动更新。

  • Snapshot版本(快照版)

    • 可覆盖性:同一快照版本(如 1.0-SNAPSHOT)可多次发布到仓库,新包会覆盖旧包(或按时间戳保留多个副本)。
    • 动态更新:依赖Snapshot版本的项目,每次构建时会主动检查远程仓库是否有更新,并自动下载最新版本。
2.Release版本无法不升级版本号更新

场景:你将Release 1.0 的代码修改后,不升级版本号直接打包推送至仓库。
结果:
仓库拒绝覆盖:Maven仓库(如Nexus)默认禁止覆盖Release包,推送操作会失败。
他人无法获取更新:即使强制覆盖成功(需仓库特殊配置),他人依赖 1.0 时因其本地已有缓存,不会主动拉取新内容。
✅ 结论:Release版本必须升级版本号才能更新代码并让他人获取到最新内容。

3.总结:关键差异速查表
特性Release版本Snapshot版本
版本号示例1.0.01.0.0-SNAPSHOT
是否允许覆盖❌ 禁止覆盖✅ 允许覆盖
本地缓存策略不自动更新每次构建检查远程更新
适用阶段生产环境开发/测试阶段
版本升级要求必须修改版本号无需修改版本号
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值