转载

快手面经篇一,据说看了面试通过率提升50%

面试官很亲切,说Excel表用过吧,Excel表中的编号一般是这样的,A....Z AA...AZ BA...BZ,分别对应数字0...25 26....51 52...77,类比做数字映射,给出一个字符串,求映射的结果。

分析这个题目其实很基础,可以理解为是以26为基准的进制转换,一个for循环,除了末尾的字符直接加到结果上之外,其他的字符位-'A'+1的结果乘以26*(该字符位置与末尾的差值)。做这种题目一定要先思考,自己手动实现一下。 如果想看具体代码答案,可以扫码关注【程序员之道】,后台回复“快手列转换”。

快手面经篇一,据说看了面试通过率提升50%

第二个算法,就稍微有一点偏了,如何实现redis的分布式锁。

如果没有接触过高并发,或者没有使用过redis作为分布式锁,那这这个算法肯定是写不出来的,而且像这种算法,一般来说可能也就是让讲讲思路。具体实现确实有点难。 关于分布式锁,其实是有几个坑的:

  1. 加锁,必须设置过期时间(防止释放锁失败,有过期时间,锁可以自动释放)。且加锁和设置过期时间必须为原子操作。否则,如果加锁成功,但设置过期时间时客户端崩溃,那设置过期时间就失败了。
  2. 加锁和释放锁必须是同一个客户端。用唯一id来标志。
  3. 释放锁时,判断锁是否属于自己及释放锁必须是原子操作。

思考了这些,你能写成正确的加锁,解锁方式吗?具体的坑及正确的加解锁方式,关注【程序员之道】,后台回复“redis分布式锁”。

基础

  • mysql索引怎么建立,查询语句select * from T where a="a" and b="b" and c="c",与select * from T where a="a" and c="c" and b="b"执行有什么区别吗?建议索引遵循什么原则? 要点:
(1)尽量减少like,但不是绝对不可用,”xxxx%” 是可以用到索引的
    (2)表的主键、外键必须有索引
    (3   谁的区分度更高(同值的最少),谁建索引,区分度的公式是count(distinct(字段))/count(*)
    (4)单表数据太少,不适合建索引
    (5)where,order by ,group by 等过滤时,后面的字段最好加上索引
    (6)如果既有单字段索引,又有这几个字段上的联合索引,一般可以删除联合索引;
    (7)联合索引的建立需要进行仔细分析;尽量考虑用单字段索引代替:
    (8)联合索引: mysql 从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分。例如索引是key index(a,b,c). 可以支持 a|a,b|a,b,c 3种组合进行查找,但不支持 b,c 进行查找.当最左侧字段是常量引用时,索引就十分有效。
    (9)前缀索引: 有时候需要索引很长的字符列,这会让索引变得大且慢。通常可以索引开始的部分字符,这样可以大大节约索引空间,从而提高索引效率。其缺点是不能用于ORDER BY和GROUP BY操作,也不能用于覆盖索引 Covering index(即当索引本身包含查询所需全部数据时,不再访问数据文件本身)。
   (10)NULL会导致索引形同虚设
复制代码

=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式。

  • redis里有哪些数据结构,都用过什么?redis里Sorted Set怎么用,需要传什么参数? 要点: redis的数据结构:String,Hash、List、Set、Sorted Set,用过哪些就说哪些就行了,没用过的,估计你根据名词也能大概猜出是什么。 面试官问Sorted Set大概是你听到你说了Sorted Set,所以问一下你命令,看你是不是真的知道啊,不知道的话,这下没法蒙混过关了吧。有序集合,设置每个key的时候需要传入一个score参数。具体命令zadd key score value。还有一些其他的命令google学习一下吧!

  • java volatile干什么用的。public int incrment() { count++},两个线程同时访问是否有问题,count如果用volatile修饰呢? 要点: volatile主要是保证多线程访问时的可见性。我们知道计算机为了提高访问内存的速度,引入了工作内存和主内存的概率,多线程访问数据时,访问的是工作内存的数据,各个线程之间的工作内存是分别隔离的。这就可能导致同一个变量,由于工作内存的存在,在不同线程“看到的值”是不一样的。但volatile关键字,强制了各线程读取变量时必须从主内存读取,同时对变量的修改也直接刷新到主内存,这样就保证了同一变量修改的同时可以立刻被其他线程“看到”。这里面使用了“内存屏障”的技术。 对于count++,操作系统执行时,并不是一个原子操作,分为三步:1)将count变量load到内存。2)执行count+1。3)将结果存入内存。非原子性操作,任何一个步骤执行的时候,都可能被其他线程打断,所以多线程执行时会有问题。 使用volatile修饰也是不可以的,因为始终不是原子操作,也只是保证可见性而已,原子性的问题无法解决。

  • jvm里内存分配什么样的,分别用来干什么? 要点: JVM内存分配几乎是每个java开发人员的面试必考点,单纯这部分的内容都够写几个篇章的了。这里只是简单的介绍一下。 JVM内存分为年轻代和老年代,其中年轻代又分为S0、S1、Eden区,JVM采用分代垃圾回收算法,因为这样才能更充分的利用年轻代和老年代的对象特点,最大化的提高垃圾回收效率。

  • 类对象定义后分配在年轻代。

  • 大对象或大数组直接分配在老年代。

    常见的垃圾回收算法有复制算法、标记清除、标记整理,然后又引出不同的垃圾回收器,垃圾回收器的迭代是不断发现问题并优化的过程,新生代收集器使用的收集器:Serial、PraNew、Parallel Scavenge;老年代收集器使用的收集器:Serial Old、Parallel Old、CMS。然后结合自己的理解再说一下!

  • jvm的栈是做什么,为什么有堆又有栈,只使用堆可以吗? 要点: JVM的栈是线程私有的,一些基本变量都是存储在栈中的,Java栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池(运行时常量池的概念在方法区部分会谈到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些额外的附加信息。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。 为什么有了堆之后还要有栈?栈的存在可以说是为了解决递归调用的问题。如果只有堆内存,那就不会有递归调用了。

  • 分布式自增id怎么实现,如果用redis实现,怎么保证与数据库的一致性? 分布式自增id一般使用MySQL的自增id、redis的incr函数,还有比较经典的雪花算法。 MySQL自增id受数据库访问速度的限制,在分布式使用时qps不大。 使用redis产生自增id,就要防止redis崩溃的可能性,一般在MySQL或hbase中记录当前最大的value值。或者如果你设计的是一个聊天室,那肯定是有持久化存储当前聊天室的最大seqId,如果redis集群出现崩溃,从持久化存储的地方取出最大seqId然后自增即可。

  • ArrayList,LinkedList有什么区别,分别什么时候使用? ArrayList的底层实现是数组,数组的扩容是不断通过复制来完成的,所以存储的数据容量不断发生变化时,ArrayList的性能是比较差的。使用ArrayList时一般都是预知数据的最大容量。如果能直接使用数组,那使用数组当然是最好的了。 LinkedList的底层实现是链表,发生数据扩容时,性能较好,但同容量情况下占用的空间比ArrayList要大。对于数据频繁扩容的情况,推荐使用LinkedList。

面试的内容还有很多,限于篇幅问题,在下一篇介绍。 程序员的小伙伴们,觉得自己孤单么,那就加入公众号[程序员之道],一起交流沟通,走出我们的程序员之道!

快手面经篇一,据说看了面试通过率提升50%
原文  https://juejin.im/post/5dc9379b51882543b903b5c3
正文到此结束
Loading...