很久就投递了拼多多的拼越计划,但是笔试挂,然后在提前批的时候笔试成功,大概在9月7号的时候开始的一面,12号的时候二面,然后等了将近一个半月,直接HR面试,然后就过了。。。感觉是白菜,果断拒绝。

一面

第一轮面试比较偏重基础

  1. 写出找到至少两门不及格的人的名次

    name score course
    王星星 98 语文
    辰开 26 数学
    • 思路要分为两步,首先查询成绩小于60的列,将其作为一个子表,然后在该子表上进行聚合,然后在该表上查找出现次数超过两次的姓名,此时,即是所要求的答案。
    1
    2
    3
    4
    5
    select name 
    from (
    select * from question where score < 60)
    group by name
    having count(*) > 2
  2. 聚簇索引

    • 要想理解什么是聚簇索引,我们首先要知道聚簇是什么意思。所谓的聚簇就是我们在查询的时候一次查询就可以命中目的值。最直观的,InnoDB的主键索引即为聚簇索引。
    • 聚簇索引和非聚簇索引是建立在B+树的基础上
    • 聚簇索引:key为主键,value为其余列的数据。一个表只能有一个聚簇索引

    • 非聚簇索引:除了聚簇索引外的都叫非聚簇索引、

      1. 对于MyISAM的主键索引来说,它的非聚簇索引是key为主键,value为行号*(不一定)*
      2. 对于MyISAM的二级索引来说,它的非聚簇索引是key为其他列,value为行号*(不一定)*
      3. 对于InnoDB的二级索引来说,它的非聚簇索引是key为其他列,value是主键
    • 非聚簇索引也叫二级索引

    • 非聚集索引与聚集索引的区别在于非聚集索引的叶子节点不存储表中的数据,而是存储该列对应的主键(行号)

    • 对于InnoDB来说,想要查找数据我们还需要根据主键再去聚集索引中进行查找,这个再根据聚集索引查找数据的过程,我们称为回表。第一次索引一般是顺序IO,回表的操作属于随机IO。需要回表的次数越多,即随机IO次数越多,我们就越倾向于使用全表扫描

    • 通常情况下, 主键索引查询只会查一次,而非主键索引(非聚簇索引)需要回表查询多次。当然,如果是覆盖索引的话,查一次即可

    • 注意:MyISAM无论主键索引还是二级索引都是非聚簇索引,而InnoDB的主键索引是聚簇索引,二级索引是非聚簇索引。我们自己建的索引基本都是非聚簇索引

  3. 有四个属性abcd,一个索引index(a,b,c),查询如下,谁的性能好,为什么?

    1
    2
    select * from table where a = * and c = * 
    select * from table where a = * and d = *
    • 这里面试官其实是在间接考察面试者关于组合索引的知识,面试者需要知道组合索引的结构是怎么样的
    • 因为在index(a,b,c)这个组合索引中,它其实可以分为三个索引,分别是a,ab,abc,对于abc来说,它的存储方式是,当a相同时,再存储b,当B相同时,再存储C,这也是最左原则的来源
    • 所以,这两个查询,都只用到了a的一个索引,所以性能几乎是一样的
  4. 优化调用方式 有一个调用链路,从服务A到服务B,其中B被拆分为B1,B2和B3,然后服务B再调到服务C,其中C是数据库服务,请问调用链路中如何优化?

    • 如果B1,B2,B3是无序的,则可以通过多线程的方式调用
    • 如果在B服务中的子服务需要用到前面的服务,则可以通过协程来操作
    • 对于服务C的DB操作中,可以通过异步回调的方式,来提高IO的性能
  5. CAS的性能如何,它有什么缺点,它和悲观锁的比较

    • CAS的全称是campare and swap,即比较交换,它的本质是一个乐观锁,在并发度低的场景中,乐观锁的性能是要优于悲观锁的。但是在并发高的场景中,CAS需要频繁比较和交换,这种情况下CAS的性能是不如悲观锁的。
    • 对于CAS中的campare来说,极有可能出现ABA问题,即把a变为b,再把b变为a,中间改变了两次,但是对于另一个线程来说,还是a,它会认为这个变量并没有改变。
    • 要想解决ABA问题,需要引入乐观锁的另一种解决方案,即版本号机制,这个在MySQL的MVCC中用的很多
    • 同时,CAS需要去循环的比较,加入并发度高的话,无限的循环也是回消耗性能。这个可以参考Java悲观锁的优化
  6. 并发和QPS有什么区别

    • 并发指的线程或者进程交替占用CPU的调度方式
    • 而QPS则是query per second,表示每秒的查询次数
  7. 算法题 :无序数组找到波峰

    • 这个题的暴力解法,就是On遍历一遍,找到最大值

    • 但是面试官显然想要找到时间复杂度小于On的解法,其实这是排序的一个简单变种,这里我们可以尝试分治法,这样时间复杂度就会被降到Ologn:

      1
      2
      3
      4
      5
      6
      7
      int getMax(int[] nums, int left, int right) {
      if(left == right) {
      return nums[left];
      }
      int mid = (left + right) / 2;
      return Math.max(getMax(nums, left, mid), getMax(nums, mid + 1, right));
      }
  8. 自己的优点

    如果连自己的优点都不知道,那还如何说服面试官呢

  9. 最近在看什么书

    这个主要面试官想考察你的持续学习力,我最近在看Netty和Mybatis相关的书哈哈哈

二面

  1. RPC的序列化方式和原理

    • 对于RPC来说,其全称是Remote Produce Call,即远程过程调用,说白了,就是把远程的方法当做本地的方法来调。它的核心是帮助使用者更简单的调用网络中的接口或者方法。RPC只是一种调用方式,它可以用HTTP实现,可以用TCP实现。很多时候面试官会拿RPC和HTTP作比较,其实他俩完全不一样,如果真的要比较,只能说RPC调用起来更方便,同时如果用TCP实现的话,效率也更高,但是RPC的移植性和易用性不是很好。对于HTTP来说,不同语言之间都可以使用HTTP来进行调用
    • 常见的PRC序列化方式有Java自带的序列化,需要继承Serializable,也有xml,json,或者是protobuf等等
    • RPC的序列化方式要考虑安全性,通用性,兼容性,性能和空间
  2. TCP的脏连接

    • TCP的脏连接发生在TCP的握手的时候,假如第一次请求超时,client会重复发送请求,这时,如果只有两次握手的话,就会产生一个脏连接
  3. Java的网络连接

    • Java中有三种IO方式,分别是BIO,NIO和AIO,这些IO不仅可以用于网络连接,也可用于磁盘等其他IO流
    • 对于BIO来说,一个socket连接一个处理线程,这个线程负责相关数据传输操作,每个服务器需要多个这样的处理线程,然而这种情况下,当多个socket向服务器申请建立连接时,受限于操作系统所允许的最大线程数量的限制,服务器不能提供相应数量的处理线程,没有分配到处理线程的连接就会阻塞等待,所以BIO是阻塞
    • NIO是JDK1.4实现的,通过selector.select()阻塞方法获取监听事件成功的channel,对于Linux来说,底层采用epoll实现
    • JDK7实现的,即Async非阻塞,是异步非阻塞的IO。异步非阻塞I/O模型。同步非阻塞需要一个线程不断去询问是否完成*(select)*,而异步非阻塞无需一个线程去轮询所有IO操作的状态改变,在相应的状态改变后,系统会通知对应的线程来处理
  4. 代理模式的缺点

    • 代理模式就是给某一个对象创建一个代理对象,而由这个代理对象控制对原对象的引用,而创建这个代理对象就是可以在调用原对象时增加一些额外的操作,分为动态代理和静态代理。在Spring的AOP中最为常用。
    • 对于Java来说,静态代理是编译期实现,而动态代理是运行期实现。对于Spring来说,有两种实现方式:
      1. JDK的动态代理需要实现一个公共的接口(通过接口找到代理的方法),动态代理生成的反射类Proxy是具体的代理,我们在实现了InvocationHandler之后的invoke中的方法会进入Proxy(继承了Proxy,实现了接口)的方法中
      2. CGLib采用的是用创建一个继承实现类的子类,用asm库动态修改子类的代码来实现的,所以可以用传入的类引用执行代理类
    • 因为客户端和主题之间增加了一层代理,可能会导致性能变慢。同时对于Java来说,使用动态代理,会调用反射,同样会拖慢性能。
  1. 反射的性能为什么差

    • 编译器无法进行任何优化,因为它无法对所执行的操作有任何真正的了解。这可能JIT也适用
    • 必须发现正在调用/创建的所有内容(即,按名称查找类,查找匹配项的方法等)
    • 参数需要通过装箱/拆箱,包装成阵列,Exceptions包裹在InvocationTargetExceptions和重新抛出等方式进行修饰。
    • 需要进行许多检测,如检查是否有无参数的构造函数,检查无参数构造函数的可访问性,检查caller是否有权使用反射,计算(在执行时)需要分配多少空间,调用构造函数代码(因为它事先不会知道构造函数为空)
  2. 为什么会产生幻读

    • 幻读是指再按照某个条件查询的过程中,被其他线程插入和更新了其他行的数据,导致每次查询结果的范围不一样。
    • 幻读会改变原有语义和数据一致性(在binlog中会有不一样的数据)
    • InnoDB会通过间隙锁解决幻读,间隙锁和行锁合称为next-key lock,是一个前开后闭区间
  3. String a = "aaa"; String b = "aaa",内存中有多少个aaa的对象

    1. 这个真的是老面试题了,aaa在常量池中有一份,然后b也引用的常量池中的aaa,所以内存中只有一份。
    2. 这里要区分下面这个题:String a = new String("aaa");,这个会在内存中存在两份aaa,一个在常量池,一个在堆中
  4. Redis的跳表

    • 跳表是一种利用二分查找思想的数据结构,查找时间复杂度可以低到O(lgn)
    • 对于数组来说,我们通过二分查找可以将时间复杂度降为lgn,但是链表不能直接拿到中间值,所以不能直接像数组一样进行二分查找,所以链表就通过分层的跳表来实现二分查找
    • Redis内部数据结构之跳表
  5. 常量不可变的好处

    1. 一个就是减少内存空间,Java中有运行时常量池,字符串常量池等等,它会把标位final的,基本类型的数值(Integer的范围是-127-128)作为常量存起来,提高了创建和摧毁时的性能开销,同时减少了内存消耗
    2. 第二个就是常量的不可变性,使得我们更容易进行并发编程,我们都知道并发编程的3个特点是,原子性,有序性和一致性,而常量,就天生满足了一致性这一要求。
  6. 主键和唯一索引的区别

    主键索引不需要回表,而唯一索引需要回表

后记

其实拼多多的问题感觉还好很基础,同时拼多多的面试感觉也很好,面试官一直在引导,给了我不错的面试体验。同时,一面面试官是阿里的8跳过去的,感觉问的虽然基础但是有深度,能给人一种探索技术的感觉,而不是单纯的八股文。

其实有一个尴尬的事情,拼多多二面的时候,面试官问我RPC的时候,我顺嘴提了一句阿里的RPC框架,当时说的是“High Speed Framework”,然后被面试官直接拆穿:“不就是好舒服(HSF)吗?”,当时尴尬一笑,原来拼多多二面面试官也是阿里的。。。

果然每年向社会输送7000人才不是乱说的,阿里巴巴,yyds