Fork me on GitHub

面试问题总结

前言

  • 相信大家在面试过程中,都会经历面试官一些问题答复,或多或少会有一些答不出来或者答的不好。其实面试过程就好比一次技术的交流,或者说是一次自我的技术检查,其实面试管对面试者的技术面试是看我们在工作中解决问题的思路与能,所以面试者需要认真对待每一次面试,我们都需要在面试中成长。所以我们需要做到如下几点:
    • 面试过程中遇到不会的问题,需要铭刻记住,以便回来之后对技术的补充和复习
    • 尽量对不会的问题做到表里如一,由浅到深
    • 多动手实战,学会画流程图或者思维导图来加深自己对技能的掌握
    • 勤做技术笔记,形成一种良好的习惯,阅读官方文档或者源码分析

面试问题

  • 以下是笔者在面试中遇到的一些问题,下面将以分类形式具体说明

    redis

  • 我有个业务场景,需要往redis list 数据结构插入上百万数据量或者更多,如何提高插入速度和减轻网络磁盘IO消耗?

    • 笔者当时并未使用过 redis list,所以当时只想到了,可使用缓存批量插入
    • 经过查看redis提供以下两种方式:
      • 利用redis pipline 管道技术
      • 把需要插入数据分块批量插入
  • 实战

    • 举个例子需要添加1000万数据用于图标展示分析,现在需要把1000万的数字插入redis数据库

      • 普通的插入redis set集合方法

        1
        2
        3
        4
        5
        r = redis.Redis(host="127.0.0.1", port=6379)
        for i in range(1, 3 * 10 ** 8):
        r.sadd('xxxxx', i)

        数据量少还行,一次插入这么多数据就会出现问题
      • 利用redis pipline 管道技术

        1
        2
        3
        4
        5
        6
        7
        r = redis.Redis(host="127.0.0.1", port=6379)
        pipeline = r.pipeline()
        for i in range(1, 3 * 10 ** 8):
        pipeline.sadd('xxxxx', i)
        pipeline.execute()

        这种操作相当于写了一堆的命令一次性执行完,一旦某个命令出问题那么这次插入数据就会失败。这种方式的好处。节省了本机与redis服务器链接的 IO 延时,一般来说节省了很多时间
      • 把需要插入的数据分块批量插入

        1
        2
        3
        4
        5
        for i in range(30):
        ls = list(range(i*1000000,(i+1)*1000000))
        r.sadd('xxxxx', *ls)

        这样的方式是一次插入多个数据,不会出现使用pipline如果某个命令出问题就全部插入失败的现象,速度非常快,比使用pipline 的方式快了几倍

MQ消息队列

  • mq消息队列如何实现幂等性的?

    • 在消费者业务处理或者业务设计中,如果消息已经被消费,可更改实体状态(根据msgId区分),如果更新过,则不进行重复处理业务
  • mq如何保证有序性?

    • 场景分析:比如异步执行业务,发送2条消息,且处理业务是,先增加,修改,删除。但在并发情况下(消费者不止一个)那么如何保证业务的执行顺序呢?
    • 解决方案:
      • 如果是同步肯定不存在,异步且高并发,可针对业务执行顺序,分别使用不同的消息队列。在业务逻辑处理时,可对顺序进行控制
      • 可在消息体中加入版本号 & 状态机 & msgid & parent_msgid,通过 parent_msgid 判断消息的顺序(需要全局存储,记录消息的执行状态)
    • 缺点:因为要求业务顺序,我们在业务处理中,需要额外的io或者内存资源来对顺序业务的处理,导致一部分性能降低,这也是MQ不提高顺序性的理由之一
    • 总结:有没有一种可能,我们在设计业务的时候是否可以避免有序执行业务,从而提高程序执行性能

zookeeper

  • zookeeper Leader选举原理?

spring mvc

  • 过滤器与拦截器的区别

    • 过滤器:

    • 过滤器是javaEE标准,采用函数回调的方式进行。请求进入容器之后,还未进入servlet之前进行预处理,并且在请求结束返回给前端这之间进行处理

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      //实现过滤功能
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
      System.out.println("before...");
      chain.doFilter(request, response);
      System.out.println("after...");
      }

      //用于完成Filter的初始化
      void init(FilterConfig config):

      //用于Filter销毁前,完成某些资源的回收
      void destory():
    • 工作原理,在web.xml配置拦截客户请求,此时你可以对请求或者响应做统一设置编码,一些逻辑判断,如用户是否已登录,是否有权限访问页面等,Filter是随web容器启动的,只会初始化一次。

    • 拦截器:

    • spring mvc 拦截器是基于java反射机制实现,会动态拦截指定的service方法,可对请求的共性抽取出来独立,可以共享一个行为,一旦行为发生改变,不必修改很多类,只需要更改这个行为就可以
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      public interface HandlerInterceptor {
      //该方案将在请求处理前进行调用,spring mvc的interceptor是链式调用的,可以同时存在多个Interceptor,每个Interceptor调用会依据它的声明顺序依次执行
      boolean preHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;

      //当preHandle返回true此方案才会被调用,该方法就是在当前请求进行处理之后,也就是controller方法调用之后执行,会在视图返回渲染之前调用,也就是说这个方法对controller处理之后的ModelAndView对象进行操作
      void postHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4) throws Exception;

      //该方法也是需要当前对应的preHandle方法返回true才执行,此方法将在整个请求结束之后,也就是在DispatcherServlet渲染对应的视图之后执行。该方法主要作用是用于进行资源清理工作
      void afterCompletion(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4) throws Exception;
      }
  • 总结
    • Filter只在Servlet前后起作用且无法使用spring容器资源,而拦截器可以深入到方法前后,异常抛出前后等
    • 拦截器是一个Spring的组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如 Service对象、数据源、事务管理等,通过IoC注入到拦截器即可

mysql

  • 索引的使用