百度面试
-
自我介绍(比较短,一直不知道怎么自我介绍)
-
问项目
-
Redis为什么快(基于内存,IO多路复用,单线程,使用C语言并有很多优化)
1. 纯内存操作,肯定快
数据存储在内存中,读取的时候不需要进行磁盘的 IO
2. 单线程,无锁竞争损耗
单线程保证了系统没有线程的上下文切换
使用单线程,可以避免不必要的上下文切换和竞争条件,没有多进程或多线程引起的切换和 CPU 的消耗,不必考虑各种锁的问题,没有锁释放或锁定操作,不会因死锁而降低性能;
3. C 语言实现,更接近底层操作
Redis 是用 C 语言开发完成的
4. 多路 I/O 复用模型,非阻塞 IO
采用多路 I/O 复用技术可以让单个线程高效的处理多个网络连接请求(尽量减少网络 IO 的时间消耗)
非阻塞 IO 内部实现采用 epoll,采用了 epoll+自己实现的简单的事件框架。epoll 中的读、写、关闭、连接都转化成了事件,然后利用 epoll 的多路复用特性,绝不在 io 上浪费一点时间。
5. 数据结构简单,底层又做了优化
数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的;
6. 源码精湛、简短
扩展:
在 Redis 中,常用的 5 种数据结构和应用场景
- String: 缓存、计数器、分布式锁等。
- List: 链表、队列、微博关注人时间轴列表等。
- Hash: 用户信息、Hash 表等。
- Set: 去重、赞、踩、共同好友等。
- Zset: 访问量排行榜、点击量排行榜等。
多路 I/O 复用模型
多路 I/O 复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。
这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。
采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响 Redis 性能的瓶颈,主要由以上几点造就了 Redis 具有很高的吞吐量。
-
Redis数据结构对于内存占用的优化(sdshdr5, sdshdr8, sdshdr16, sdshdr32, sdshdr64, 字符串越短,使用越少的内存存储额外信息;list 和 hash 元素少的时候使用 ziplist 编码)
-
利用zipList来替代大量的key-value
-
海量用户系统经常会碰到这样的需求,要求根据用户的某个唯一标识迅速查到该用户id
-
特点是数据量很大、千万或亿级别,key是比较长的字符串,如32位的md5或者uuid这种。
如果不加以处理,直接以key-value形式进行存储,我们可以简单测试一下,往redis里插入1千万条数据,1550000000 - 1559999999,形式就是key(md5(1550000000))→ value(1550000000)这种。
然后在Redis内用命令info memory看一下内存占用
可以看到,这1千万条数据,占用了redis共计1.17G的内存。当数据量变成1个亿时,实测大约占用8个G。
同样的一批数据,我们换一种存储方式,先来看结果:在我们利用zipList后,内存占用为123M,大约减少了85%的空间占用
zipList最大的特点就是,它根本不是hash结构,而是一个比较长的字符串,将key-value都按顺序依次摆放到一个长长的字符串里来存储。如果要找某个key的话,就直接遍历整个长字符串就好了。
所以很明显,zipList要比hashTable占用少的多的空间。但是会耗费更多的cpu来进行查询
4.总结
如果key的长短不一,譬如有些40位,有些10位,因为对齐问题,那么将产生巨大的内存碎片,占用空间情况更为严重。所以,保持key的长度统一(譬如统一采用int型,定长8个字节),也会对内存占用有帮助。
string型的md5,占用了32个字节。而通过hash算法后,将32降到了8个字节的长整形,这显著降低了key的空间占用。
zipList比hashTable明显减少了内存占用,它的存储非常紧凑,对查询效率影响也很小。所以应善于利用zipList,避免在hash结构里,存放超过512个field-value元素。
如果value是字符串、对象等,应尽量采用byte[]来存储,同样可以大幅降低内存占用。譬如可以选用google的Snappy压缩算法,将字符串转为byte[],非常高效,压缩率也很高。
为减少redis对字符串的预分配和扩容(每次翻倍),造成内存碎片,不应该使用append,setrange等。而是直接用set,替换原来的。
-
-
Mysql 索引类型(聚簇索引和非聚簇索引,hash索引)
- 1.普通索引
2.唯一索引
3.主键索引
4.组合索引
5.全文索引
- 1.普通索引
-
数据库读写分离的优缺点(写会加锁,降低数据库并发量,读写分离提高并发量;缺点是读服务器会存在延迟)
-
1.增加冗余(通信工程里的名字,对重要的部件多一种保护机制)
2.增加了机器的处理能力
3.对于读操作为主的应用,使用读写分离是最好的场景,因为可以确保写的服务器压力更小,而读又可以接受点时间上的延迟
4.读写分离适用与读远大于写的场景,如果只有一台服务器,当select很多时,update和delete会被这些select访问中的数据堵塞,等待select结束,并发性能不高
-
-
rabbitmq 介绍(说了topic,direct,fanout,header模式),重复消费问题(没答上来,说项目中没考虑到)
-
springboot 最大的特点(自动配置),启动注解和配置文件(记不清了)
- @SpringBootApplication:用于标注该类为一个主程序类或者主配置类,我们点进去查看该注解
- application.properties:Spring Boot的配置文件
-
JVM 内存模型和各自作用(程序计数器,JVM虚拟机栈,本地方法栈,堆,方法区),哪些是线程私有的(程序计数器,JVM虚拟机栈,本地方法栈)
-
String 能被继承吗(不能,被final修饰),和StringBuilder,StringBuffer区别(这两个可变,StringBuffer线程安全)
-
重写和重载区别(重载:方法名字相同,参数不同;重写发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写),重写的修饰符和抛出异常大小(重写方法的修饰符更大,异常更小)
-
查看进程的指令(ps),查看cpu使用情况(top),改变文件权限(chmod)
-
TCP 可靠性(面向连接,数据包校验,重新排序,超时重发,流量控制)
算法题:
-
判断一颗树是否为avl树(递归做)
-
如果左子树是AVL树,右子树是AVL树,他们的高度差小于等于1,那就是AVL树;否则不是.
public class IsAVLTree { public static boolean isAVL(TreeNode root) { if (root==null) { return true; } int diff=Math.abs(getHeigt(root.lchild)-getHeigt(root.rchild)); if (isAVL(root.lchild)&&isAVL(root.rchild)&&diff<=1) { return true; } else { return false; } } public static int getHeigt(TreeNode root) { if (root==null) { return 0; } return Math.max(getHeigt(root.lchild), getHeigt(root.rchild))+1; } } class TreeNode{ int data; TreeNode lchild; TreeNode rchild; }
-
-
判断一个数是否回文(转换为字符数据,使用双指针法或者StringBuilder的reverse()方法)
public static void main(String[] args) {
int[] arr={1,2,3,4,5};
printRandM(arr,3);//1 5 4
System.out.println(isPalindrome(32456));//false
System.out.println(isPalindrome(-12021));//true
}//判断一个数是否是回文数 public static boolean isPalindrome(int n){ if(n==Integer.MIN_VALUE){ //32位负数最小值,转不成正数,也明显不是回文数 return false; } n=Math.abs(n); int help=1; while(n/help>=10){//防止help溢出 help*=10;//使Help和n位数一样 } while(n!=0){ if((n/help)!=n%10){ return false;//n的最高位不等于最低位,非回文 } n=(n%help)/10;//n除去最高位和最低位 help/=100;//使help变得跟n位数一样 } return true; }
最后反问
总结:rabbitmq,springboot 只是使用,没有详细了解原理,相关问题没答上来
薄弱点:
- Springboot还需要继续学习,了解原理
- Rabbitmq常见问题的解决方案要熟悉,挺常问的
- 自我介绍 一直很短,一分钟以内,想问下大家都是怎么自我介绍,要介绍的多细
第二天收到电话约二面
二面 8.12
- 接口和抽象类区别(接口是动作的抽象,抽象类是一种所属关系…)
- volatile作用(可见性,禁止指令重排),什么是可见性问题
- 常见容器(List,Set,Hashmap),Hashmap原理(数组 + 链表、红黑树),resize() 过程,多线程会产生什么问题(循环链表),怎么产生循环链表(没答出来)
- 怎么解决HashMap多线程问题(加锁),说了ConcurrentHashMap的put过程
- 说说线程(CPU调度分派基本单位…)
- 进程如何同步(管道,命令管道,信号,信号量,共享内存,套接字),信号量怎么通信(P操作,V操作)
- 线程独有的对象(ThreadLocal,原理没说清)
- 类加载机制(双亲委派模型),有什么加载器,分别加载什么,怎么破坏双亲委派机制(重写loadClass方法),破坏双亲委派机制自己实现加载器加载java.lang.String可以吗(不可以,不懂怎么保证的)
- 标记-清理过程
代码:
最后反问环节
总的来说,面试官是个小姐姐,面试过程中会引导你,面试体验很棒
问题讨论:
- HashMap多线程下**循环链表**产生的原理,有大佬详细说一下嘛
- 破坏双亲委派机制后,怎么保证java.lang.String不能加载(java.lang.ClassLoaderd的preDefineClass 方***检查类名,发现以java作为一级包名,则抛出安全异常:禁止使用的包名)