面经中的各种问题汇总
语言基础,操作系统,计算机网络,数据库,设计模式,算法
读书的建议:
(1)读每一本书都建议一定要读取书的源码,自己敲一遍,相信我,读没读过源码,是否自己能够写出代码差别还是很大的。
(2)强烈建议处于校招或者准备校招的各位,要有一个读书计划,上面不仅要有读每本书的计划时间(10天或者两周),并且记载读完每本书的感受或者问题。如果可能,可以记录读书过程中发现的问题,并且在很多博客上去查找答案。如果一番读书过程,受益匪浅。
读书:
c++primer,深度探索c++对象模型,STL源码剖析,effective c++,more effective c++
深入理解计算机基础,现代操作系统,UNIX环境高级编程,MIT6.828课程
计算机网络,图解TCP/IP(非必读),TCP/IP详解(卷一),UNIX网络编程(卷一),图解HTTP
算法:剑指offer,程序员代码面试指南,leetcode(在不能确保自己其他基础水平达到的情况下,不建议贪多只花时间刷算法)
数据库:数据库概论,mysql必知必会,高性能mysql(建议读前面索引和性能部分就好)——打好基础就好,没时间的话不用深
设计模式:大话设计模式,——重点研究单例模式,工厂模式等常见设计模式
linux:鸟哥lunux家常菜,深入理解linux内核
-----------------------------------------------------------
以C++开放举例,粗略说说笔试一般会考哪些内容:
(1) 语言基础,C++中的定义,指针,模板,多态,重载等(尤其是多态,给定一个程序,基类子类有不同实现,考虑输出结果,这一类的题目面试题也极为高频)
(2) TCP/IP协议及其编程:计算机网络基础,socket编程,其中子网划分,七层协议(例如交换机属于数据链路层一类题目),五层协议,协议名称及其作用,常用端口号,https1.0 1.1特性与区别,三次握手四次挥手(可能出现的服务器攻击),加密(对称,非对称),IO复用等
(3) 操作系统:进程通信方式(linux/windows,每种方式的特点),线程通信(linux/windows),进程与线程区别,页面置换策略(考察置换几次),进/线程状态转换图,进程调度方式,死锁(必要条件,哲学家就餐,银行家,读者写者问题),
(4) 数据库:索引作用,应该/不应该创建索引的列,索引底层实现,sql语法,常见引擎特点,事务特点,隔离级别(引擎默认级别),隔离级别的问题,锁等
(5) Linux方面:IO模型,linux常见命令(grep,awk,sed),文件系统,
-----------------------------------------------------------
面试高频题:
1语言基础(C++)
(1) 指针和引用的区别
指针是变量,存储的是地址;引用只是别名,不能为空,必须初始化,并且初始化后不能改变,不能指向其它;
指针可以有const,可以为空,可以改变指向,因为它是一个变量,如果sizeof得到的结果也不同,自增操作结果也不同,函数参数传递也不同(一个是拷贝,一个是引用传递)
如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄漏;(为什么??)
(2) 堆和栈的区别
数据结构中,内存结构中区别在于
栈:编译器自动分配释放,连续,高地址向低地址,空间限制,分配速度快,函数调用中先入下一条语句位置,从右到左的参数,局部变量
堆:不连续空间,速度慢,有碎片,向高地址扩展,比较灵活,大
(3) new和delete是如何实现的,new 与 malloc的异同处
new先调用malloc,后调用构造函数,delete先调用析构后调用free;
malloc/free是函数,而new/delete是关键字;
new在申请内存时会自动计算所需字节数,而malloc则需我们自己输入申请内存空间的字节数;malloc需要自己绑定对象类型
new的bad_alloc异常
(4)volatile
程序在进行编译的时候,编译器会进行一系列的优化.比如,某个变量被修饰为 const的,编译器会在寄存器中保存这 个变量的值,但是有时候,我们去了这个变量的地址,然后强行改变这个变量在内存中的值,这就造成了结果的不匹配,而volatile声明 的变量就会告诉编译器,这个变量随时会改变,需要每次都从内从中读取,就是不需要优化,从而避免了这个问题,其实,volatile应用更多的场景是多线程对共享资源的访问的时候,避免编译器的优化, 而造成多线程之间的通信不匹配!;
explicit:禁止类的构造函数进行隐式的类型转换
(4) C和C++的区别
c++:面向对象,class类,支持函数重载,有引用,多态,输入输出流
C++全部变量的默认链接属性是外链接,而C是内连接;(???)
(5) C++、Java的联系与区别,包括语言特性、垃圾回收、应用场景等(java的垃圾回收机制)
java没有指针,运行在JVM上,移植性强,在JVM里面运行得到结果,垃圾回收gc算法,丰富的插件
c++在底层和中间件上更有优势,灵活,支持多继承,运算符重载,自动强制类型转换
(6) Struct和class的区别
默认的数据访问控制,class还用于定义模板参数,默认继承方式:class私有,struct公有;大括号初始化不同;c中的struct不能包含函数,而c++中的可以
(7) define 和const的区别(编译阶段、安全性、内存占用等)
define预编译时简单的替换,没有类型检查,不能调试,但是功能也很强大,可以用来防止头文件重复引用;#ifndef #define #endif
头文件尽量只有声明,不要有定义。这么做不仅仅可以减弱文件间的编译依存关系,减少编译带来的时间性能消耗,更重要的是可以防止重复定义现象的发生,防止程序崩溃。因为,如果用了条件编译的话,也可能存在重复定义,因为头文件中可能包含了已经定义的变量,这些变量被多个.c文件包含,编译阶段都是各单元编译的,因此该变量在各单元是独立的,都有地址,所以在最后main.c中可能出现二义性。解决方法是分离式编译
const带类型,是常量,编译运行的时候起作用,存储在数据段空间,不能重定义。
(8) 在C++中const和static的用法(定义,用途)
const必须赋值,class中的const成员可以在构造函数使用初始化列表赋值;static可以默认值,在类外初始化;
类中的const函数是为了不改类的成员,static函数是为了全局访问,本文件内,不能访问非静态的成员和函数,因为没有this的概念
(11) 计算下面几个类的大小:
class A {};: sizeof(A) = 1; 空类大小为1B,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址
class A { virtual Fun(){} };: sizeof(A) = 4(32位机器)/8(64位机器); 虚函数表指针的大小
class A { static int a; };: sizeof(A) = 1;
class A { int a; };: sizeof(A) = 4;
class A { static int a; int b; };: sizeof(A) = 4;
虚函数、成员函数(包括静态与非静态,因为相当是共享的全局函数)和静态数据成员都是不占用类对象的存储空间的
(13) C++的STL介绍(这个系列也很重要,建议侯捷老师的这方面的书籍与视频),其中包括内存管理allocator,函数,实现机理,多线程实现等
allocator是STL中的内存分配器,基于空闲列表,先分配一块固定大小的内存池,然后实施小对象的快速分配和释放,避免内存碎片,局限于STL容器使用
(14) STL源码中的hash表的实现
数组+链表,拉链法;entry【】数组,key value next
(17) vector使用的注意点及其原因,频繁对vector调用push_back()对性能的影响和原因。
重新分配,注意迭代器,范围for的使用
(18) C++中的重载和重写的区别:
重定义发生在继承中,重新定义了一个函数;重写(虚函数多态关键):与重定义差不多,只是重写的是虚函数;
重载是指发生在同一个类中,名称相同,但是参数个数或者类型不同。
(19) C ++内存管理(热门问题)
(20) 介绍面向对象的三大特性,并且举例说明每一个。
封装,继承,多态、抽象--
(21) 多态的实现(和下个问题一起回答)
在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数
虚函数表指针:有父类的,也可以是子类的虚函数表指针,虚函数表的创建和虚表指针的初始化都在构造函数中完成
(22) C++虚函数相关(虚函数表,虚函数指针),虚函数的实现原理(热门,重要)
(24) 析构函数一般写成虚函数的原因:多态需求;一般有其它虚函数的类,其析构函数也常为虚函数
(25) 构造函数为什么一般不定义为虚函数:对象都还没有实例化,怎么去找虚表
(26) 构造函数或者析构函数中调用虚函数会怎样:和普通函数没有区别,不会发生动态特性;危险,因为子类已经析构了,你还访问子类对象
(27) 纯虚函数:要求派生类必须来实现=virtual
(28) 静态绑定和动态绑定的介绍
(29) 引用是否能实现动态绑定,为什么引用可以实现:指针或引用是在运行期根据他们绑定的具体对象确定(因为它们只是指代1个地址或者别名)
(30) 深拷贝和浅拷贝的区别(举例说明深拷贝的安全性):当有动态生成的对象时,默认拷贝函数是浅拷贝,指向同一块内存区域,析构时两次析构,会有内存泄露
Array(){m_iCount=5;m_pArr=new int[m_iCount];} Array(const Array &arr) { m_iCount=arr.m_iCount; m_pArr=arr.m_pArr; } //改为 Array(const Array &arr) { m_iCount=arr.m_iCount; m_pArr=new int[m_iCount]; for(int I=0;i<m_iCount;i++) { m_pArr[i]=arr.m_pArr[i]; } }
(31) 对象复用的了解,零拷贝的了解(??)如果应用程序永远不会对这块数据进行修改,那么就永远不需要将数据拷贝到应用程序的地址空间去。在stl中string的实现类似这种策略。
是为了降低拷贝的开销,比如read和send的开销其实很大,需要4次数据拷贝,4次上下文切换
我们可以采用零拷贝技术。linux下提供类似的系统调用主要有mmap(),sendfile(),splice().
(32) 介绍C++所有的构造函数:默认,普通,拷贝,赋值:默认无参,普通有参数,其实还有this指针,拷贝就是const class &A,赋值构造函数是对赋值符号的重载,如果有动态生成的对象的话,那么它做赋值,原先的动态空间一定要被释放掉,然后再赋上新的值,所以必须得自己重载=,实现delete。
CExample& operator = (const CExample&); //赋值符重载
CExample & CExample::operator = (const CExample& RightSides) { nSize=RightSides.nSize; //复制常规成员 char *temp=new char [nSize]; //复制指针指向的内容 memcpy(temp,RightSides.pBuffer,nSize*sizeof (char )); delete []pBuffer; //删除原指针指向内容 (将删除操作放在后面,避免X=X特殊情况下,内容的丢失) pBuffer=temp; //建立新指向 return *this }
(33) 什么情况下会调用拷贝构造函数(三种情况):一个对象去初始化,值传递类,返回值是类
(34) 结构体内存对齐方式和为什么要进行内存对齐?:因为计算机每次都是读入32位(32),所以为了保证一次读能直接读出成员:大端(高地址存屁股),如何判断用union,一个char,一个int,然后放入一个int 1,取char类型看是等于0还是1
(35) 内存泄露的定义,如何检测与避免?:可能会有:动态分配(自己,第三方的库);程序递归,隐含内存分配(vector),系统调用(比如消息队列的溢出),多线程(不断申请线程);解决方法:调试运行,查看崩溃前的运行信息;Valgrind工具(???);在用户程序和运行库中间加一层用于记录内存分配的情况(???)
(36) 手写实现智能指针类(34-37我没遇见过):
(37) 调试程序的方法:归纳法、演绎法、测试法,回溯法,暴力法。设置断点,进入调试,单步运行,进入函数,查看变量。linux gbk,win windbg
(38) 遇到coredump要怎么调试:程序崩溃会产生core dump,一般有段错误或者abord。如果有设置core文件的话,就gdb test.cpp core,然后会定位到出错的地方,可以用bt(backtrance)或者where来查看当前调用的堆栈,然后用frame n找到第n层堆栈的信息,就可以找到出错的语句了;然后进一步分析,p可以查看变量的值。
(39) 内存检查工具的了解:linux可以使用开源的Valgrind工具包,包含多个工具:Memcheck常用语检测malloc和new这类的问题,callgrind用来检查函数调用,cachegrind缓存使用,helgrind多线程程序中的竞争。除了valgrind还可以用mtrace这些工具
(40) 模板的用法与适用场景:代码可重用,泛型编程,在不知道参数类型下,函数模板和类模板
(41) 成员初始化列表的概念,为什么用成员初始化列表会快一些(性能优势):使用初始化list这样可以直接调用成员的构造函数,不用去赋值产生临时变量,所以更快。如果不用,可能会去调用成员的构造函数和赋值构造函数(多出来的)。
(42) 用过C11吗,知道C11新特性吗?(有面试官建议熟悉C11):lambda 智能指针 移动,auto,范围for,decltype,array,forward_list,tuple,正则表达式库,随机数库,bitset运算
(43) C++的调用惯例(简单一点C++函数调用的压栈过程):下步语句,参数右到左,局部变量
(44) C++的四种强制转换:static,const,dynamic,rein
(45)函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定。
2 计算机网络(TCP/IP)
(1) 建立TCP服务器的各个系统调用:https://blog.csdn.net/hzrandd/article/details/50973185
(2) 继上一题,说明socket网络编程有哪些系统调用?其中close是一次就能直接关闭的吗,半关闭状态是怎么产生的?
由于是双向的,两边都要发FIN,服务器关闭socket,用close会将该socket的计数-1,如果引用还是大于0,那么socket端口状态保持不变,如果为0,会将sender缓冲中的数发出去,然后发送FIN。可能在多进程中出现半关闭,所以应该使用
shutdown不考虑描述符的引用计数,直接关闭描述符,到LAST_ACK状态。也可选择中止一个方向的连接,只中止读或只中止写。
如果有多个进程共享一个套接字,close每被调用一次,计数减1,直到计数为0时,也就是所用进程都调用了close,套接字将被释放。
在多进程中如果一个进程调用了shutdown(sfd, SHUT_RDWR)后,其它的进程将无法进行通信。但,如果一个进程close(sfd)将不会影响到其它进程。
就是说可能会有多个进程共享使用一个socket。其它的系统调用有
socket(AF_INET, SOCK_STREAM, 0) AF表示地址族,初始化套接口地址结构; socket (int namespace, int style, int protocol)
客户端:socket,bind,connect,send,recv,close
服务器:多了listen,accept
(3) 对路由协议的了解与介绍。内部网关协议IGP包括RIP,OSPF,和外部网关协议EGP和BGP.
(4) 路由协议所使用的算法。
D-V距离矢量算法(RIP,IGRP,EIGRP,BGP),L-S链路状态算法(也称SPF最短路径树算法,如ospf,isis)
(5) TCP和UDP的区别:面向连接,可靠字节流服务,基于可靠性技术来保证:流量控制(滑动窗口),拥塞控制(慢启动),差错控制(检错纠错);udp面向数据报,没有重传
(6) TCP和UDP相关的协议与端口号:TCP:FTP-21;TELNET终端仿真-23;SMTP-25;HTTP-80;POP3-110;HTTPS加密的超文本传输服务-443, UDP:DNS-53,DHCP-67
(7) TCP(UDP,IP)等首部的认识(http请求报文构成):IP首部20字节,TCP固定首部也是20字节,端口号是2字节的,IP号是4字节的,UDP是8字节
IP
(8) 网页解析的过程与实现方法
DNS-递归迭代解析
负载均衡:将用户分摊到多个服务器上(Nginx是一款面向性能设计的HTTP服务器,作为中介来分配)
Nginx默认支持 RR轮转法 和 ip_hash(能会话粘连)法 这2种分配算法。还有url_hash法
Nginx负载均衡服务器将网页传递给filters链处理,之后发回给我们的浏览器
(9) 在浏览器中输入URL后执行的全部过程(如www.baidu.com)
(10) 网络层分片的原因与具体实现
MTU(最大传输单元):是链路层的限制1500B,所以IP要分片
MSS(最大分段大小):是TCP的限制,双方可以协商,往往是减去两个头部,40B。链路层是以太网的是话是1460,internet是512B
UDP不分段,靠IP分片
(11) TCP的三次握手与四次挥手的详细介绍(TCP连接建立与断开是热门问题)
(12) TCP握手以及每一次握手客户端和服务器端处于哪个状态(11种状态):5+6
(13) 为什么使用三次握手,两次握手可不可以?:解决请求/答复丢失,超时重传导致的重复连接
(14) TIME_WAIT的意义(为什么要等于2MSL)
最后一个ACK可能丢失,所以接收服务器的FIN,然后重传ACK;如果立即关闭,那么接收FIN会发送RST报文,服务器以为连接异常了
(15) 超时重传机制(不太高频)
(16) TCP怎么保证可靠性(面向字节流,超时重传,应答机制,滑动窗口,拥塞控制,校验等)?
(17) 流量控制的介绍,采用滑动窗口会有什么问题(死锁可能,糊涂窗口综合征)?
TCP滑动窗口技术通过动态改变窗口大小来调节两台主机间数据传输。当都为1就是停止-等待协议
考虑发送方的传输速率,可能每次都发1字节有效荷载的包,可能接收方每次都能从缓存中取1字节,然后声明自己窗口有1字节
Nagle算法:发送方就把第一个数据字节先发送出去,把后面到达的数据字节都缓存起来。当发送方接收对第一个数据字符的确认后,再把发送缓存中的所有数据组装成一个报文段再发送出去。当到达的数据已达到 发送窗口大小的一半或已达到报文段的最大长度时,就立即发送一个报文段。
Clark解决方法是只要有数据到达就发送确认,但宣布的窗口大小为零,直到或者缓存空间已能放入具有最大长度的报文段,或者缓存空间的一半已经空了。另外,还可以采用延迟确认的方法
(18) tcp滑动窗口协议
(19) 拥塞控制和流量控制的区别:流量控制是端到端的控制,拥塞控制是一个全局性的过程,涉及到所有的主机、路由器,以及与降低网络性能有关的所有因素。
(20) TCP拥塞控制,算法名字?(极其重要)
1)慢启动(开始是指数的),2)拥塞避免,3)拥塞发生(慢启动或者快速恢复),4)快速恢复:门限为当前窗口,窗口为一半,不断+1上升
(21) http协议与TCP联系
Http连接是一种短连接,是一种无状态的连接。所谓的无状态,是指浏览器每次向服务器发起请求的时候,不是通过一个连接,而是每次都建立一个新的连接。
(22) http/1.0和http/1.1的区别
长连接和请求的流水线处理;请求消息中多了Host头域(用来标识虚拟主机,为了实现虚拟的web服务器,实现负载均衡);带宽优化(在请求消息中引入range头域,避免发生断连后要请求所有的包;加入新的状态码100continue,如果不确定服务器是否能够接收大的实体时,发一个只带头域的请求,看返回的是100还是401-unauthorized);消息传递(引入Chunkedtransfer-coding,将大的实体分成任意大小的数据块,都附上长度,最后用一个0长度的块来作为消息结束的标志);缓存(加入了一些cache新的特性,缓存对象expire时变为stale对象,cache不直接抛弃它们,而是与原服务器进行重新激活;增加了cache-control头域;cache使用关键字来索引在磁盘中缓存的对象,而不是URL)
(23) http的请求方法有哪些?get和post的区别。
GET/POST、put/delete,head/trece(head只会返回首部,可以知道元信息,例如资源在不在,资源的情况;trace会回显服务器收到的请求,主要用于测试诊断),options(问服务器能支持那些功能和方法,对于特定的资源);connect(HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器)
区别:一个是拿数据,一个是传数据;get请求没有消息体,只能携带不超过2kB数据(通常),将数据放在url地址中;post有消息体,没有数据大小限制,比get更安全
(24) http的状态码
第一个数字代表了响应的状态。1表示消息;2表示成功;3表示重定向;4表示请求错误;5、6表示服务器错误。
100-continue;200-正常;301-(客户请求的文档在其他地方,新的URL在Location头中给出,浏览器应该自动地访问新的URL);404(not found);500-服务器遇到错误,无法完成请求;503-服务不可用,服务器由于维护或者负载过大不能响应
(25) http和https的区别,由http升级为https需要做哪些操作
安全超文本传输协议在http协议基础上增加了使用SSL加密传送信息的协议,ssl:安全套接字层;加密传输+身份认证的功能
https:建立安全通道,确认网站真实性; https的缺点:信任链可能有问题,加密范围有限,要钱,不高效
区别:(1)端口不同,80和443,连接方式不同;(2)https需要请求证书(就是公钥,包含签发者信息,过期时间),使用对称密钥加密通信;
(26) https的具体实现,怎么确保安全性
(27) http中浏览器一个URL的流程,这个过程中浏览器做了什么,URL包括哪三个部分?
协议+域名+端口+虚拟目录+文件名+锚+参数(后面的可以描述为路径?查询)
(28) 一个机器能够使用的端口号上限是多少,为什么?可以改变吗?那如果想要用的端口超过这个限制怎么办?
16位,0有特殊用处,所以是65535.
(29) 对称密码和非对称密码体系
混合加密方式
(30) 数字证书的了解(高频)
PKI实现的功能就是用一个大家都信任的机构CA给用户颁发一个数字证书,证书中包含用户的公钥(这个公钥可以是用户自己生成的提交给CA的也可以是CA生成发给用户的)及相关身份信息。
(31) 客户端为什么信任第三方证书:签名合法
(32) RSA加密算法,MD5原理(MD5不算加密算法)
(33) 单条记录高并发访问的优化(???)
(34) 介绍一下ping的过程,分别用到了哪些协议:https://blog.csdn.net/github_35156632/article/details/52403961
UDP-ICMP(internet 控制报文协议)-ARP(地址解析的协议,mac地址)-OSPF(开放最短路径优先,内部网关协议)
封装UDP-IP包,然后查看子网掩码看是否在同网段,如果在,那么找ARP缓存表,找不到发送ARP请求,最后转换成数据帧,发送——接收后从IP包提取数据交给ICMP协议处理,发送ICMP应答
如果不在同网段则交给路由器处理,解析到对应的路由器mac,发送,路由器再解析到最终的mac发送
故障基本就是DNS配置不正确unkown host name,没连接DNS服务器,或者路由的问题,网卡问题,IP地址不存在
(35) TCP/IP的分片粘包过程
主要是因为MSS(最大报文段长度)很难确定,使最终数据帧的有效荷载比MTU大,进行IP的分片
粘包(UDP不会出现,因为有消息边界): (1)发送方要等缓存区满才发送数据;(2)接收端不及时接收缓冲区的包,造成多个包的接收;
措施:(1)编程可以强制数据立即传送指令push(2)优化接收方的设计,提高接受进程优先级,使接收方及时接收数据
(36) 有没有抓过TCP包,描述一下
tcpdump是对网络上的数据包进行截获的包分析工具,它支持针对网络层、协议、主机、网络或端口的过滤,并提供and、or、not等逻辑语句来去掉无用的信息。
只能抓取流过本机的包
tcpdump -c 10 -nn -i eth0 tcp dst port 22 //这里 src是源 dst是目的的意思
(37) 一个ip配置多个域名,靠什么识别?
DNS负载均衡:一个域名对应多个IP,将负载均衡放到DNS处,其实大型网站会设置该为第一级的负载均衡;
识别:http请求中的host域,或者是端口号识别,不同端口号指向不同的网站处理
(38) 服务器攻击(DDos攻击):(1)利用TCP3次握手的缺陷SYN timeout;(2)流量攻击,对主机的攻击。。。
3数据库(MySQL)
数据库我自身不是特别熟,面试的时候一般也都坦言了,所以面试官都只是问一些基础题目。
(1) 关系型和非关系型数据库的区别(各自优点)
关系(支持ACID,sql语句,易于维护使用方便,但是维护一致性代价高):外键关联来建立表和表之间关系;非关系(key-value,海量存储,没有耦合可扩展,读写性能好):以对象的方式存储(HBASE.MOGODB)
(2) 常用SQL语句(DDL,DML,DCL,TCL):数据定义,操纵,控制和事务控制语言
DML:select,insert,update,delete,merge,call,
(3) 数据库中join的类型与区别(inner join, outer join, cross join, natural join, self join),注意适用场景和sql语句的编写:多表的连接查询
内连接:一个学校表 inner join一个申请表,那么显示所有有人申请的学校;on 后面表示条件,where进一步过滤连接后的条目
外连接:左连接会把所有学校列出来,补全申请的信息,如果没有那么字段为null;全连接会把申请人的信息也补全,如果学校没有出现在表1,表1字段置为null;
交叉连接:笛卡尔积,a×b行,效率很低
自然连接: SELECT * FROM ORDERS O NATURAL LEFT OUTER JOIN CUSTOMERS C; 无需指定连接列,检查同名列,只能select ×
(4) 数据库的索引类型:索引可以加快检索速度,加快表连接,但是增加空间,还有维护开销,对表更新索引要重建,当我们需要读取的数据量占整个数据量的比例较大抑或者说索引的过滤效果并不是太好的时候,使用索引并不一定优于全表扫描。
ALTER TABLE people ADD INDEX lname (lname);
唯一unique索引:可以是多列的唯一;
主键primary key索引
聚集索引:对正文内容按照一定规则排列的目录称为聚集索引,就相当于按照拼音查字典
(5) 聚集索引和非聚集索引的区别(叶节点存储内容)
(6) 唯一性索引和主码索引的区别
(7) 索引的优缺点,什么时候使用索引,什么时候不能使用索引(重点)
(8) 索引的底层实现(B+树,为何不采用红黑树,B树):文件很大是存在磁盘上的,IO的时间是很重要的,减少IO次数。树的高度越大,那么其IO次数可能越多。矮胖更好,而且B+数中只有关键字,一个盘块能容纳的关键字更多,局部性更好,另外支持范围查找;
(9) B树和B+树具体实现:
(10) 索引最左前缀问题:索引index1:(a,b,c),只会走a、a,b、a,b,c 三种类型的查询,因为索引是有序的,先根据a排序,然后根据b排序,然后c
(11) Mysql的优化(高频,索引优化,性能优化)
查询缓存,为搜索字段建索引;select少用*,limit条数;固定的表长更快,使用not null
(12) 数据库引擎介绍,innodb和myisam的特点与区别
InnoDB:有好的事务支持,行级锁定,适用事务的场景,更新频繁,一致性要求高的场景;myisam:表级锁定,读写互相阻塞,不支持事务,适用并发低,数据修改少,一致性要求低场景;
(13) 数据库中事务的ACID(四大特性都要能够举例说明,理解透彻,比如原子性和一致性的关联,隔离性不好会出现的问题)
一个事务与其他事务隔离的程度称为隔离级别. 数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱
原子,一致,隔离,持久
(14) 数据库隔离性设置不同会出现的问题(脏读、不可重复读、丢失修改、幻读):并发事务,要有锁
脏读:一个事务去读了未提交的数据操作结果,可能会回滚。
虚读:t1去读数据后,t2改了这个数据,t1再去读的时候读到不一样了。所以用不可重复读来避免
幻读:事务两次查询,两次查询中间另外事务对表修改了,所以第二次会发现出现了或者缺少了数据,不一样了
(15) 数据库的隔离级别,mysql和Oracle的隔离级别分别是什么
Mysql 默认的事务隔离级别为: REPEATABLE READ ,支持4种;Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE. Oracle 默认的事务隔离级别为: READ COMMITED Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE. Oracle 默认的事务隔离级别为: READ COMMITED
(16) 数据库连接池的作用
连接复用。通过建立一个数据库连接池以及一套连接使用管理策略,使得一个数据库连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。 getConnection 方法获取连接
(17) Mysql的表空间方式,各自特点
一种是共享表空间,及多张表放在一个文件中,还有一种是独立表(性能更好点)空间,每个表都有独立的数据文件(便于删除,移动单表)。
(18) 分布式事务:XA协议采用两阶段提交方式来管理分布式事务
MVCC多版本的并发控制:只在读提交和重复读下面工作;
(19) 数据库的范式:
(20) 数据的锁的种类,加锁的方式:数据库中一般是悲观锁,认为数据随时会被修改。分为共享s锁(存在死锁,所有s锁的事务都想升级成x锁),排他x锁(写锁,禁止再加任何锁),和更新u锁(允许其他事务读,但是不允许再加u和x锁,当要被更新时升级为x锁)。
悲观锁按作用范围分为行锁和表锁
乐观锁:如果没有吞吐量瓶颈还是不用它,可能有风险
(21) 视图的作用与使用方法(如何删除等):
(22) 分库分表,主从复制,读写分离。(我不会,也没碰到过)
(23) 项目中哪里用到了数据库,怎么用的。 我的项目中没有用到数据库,以前的管理系统用到过。但是很多区块链客户端是用到了google的leveldb数据库存储kv数据,这是开源的
4Linux基础
Linux这一块如果不太熟,可以直接说,但是因为开发岗位一般都是在Linux平台下的,所以几个基础问题还是要会的。
(1) Linux的I/O模型介绍以及同步异步阻塞非阻塞的区别(超级重要)
同步:我调用一个功能,该功能没有结束前,我死等结果。异步:不需要知道该功能结果,该功能有结果后通知我(回调通知)
阻塞:没有接收完数据或者没有得到结果之前,我不会返回。非阻塞,就是调用我(函数),我(函数)立即返回,通过select通知调用者
同步IO和异步IO的区别就在于:数据拷贝的时候进程是否阻塞;阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回
5种IO模型:同步(阻塞IO,非阻塞IO(会返回错误,然后IO操作函数会不断测试是否准备好,会占用很多cpu时间),IO复用(select,poll,epoll函数,能实现多个IO操作的阻塞,同时对多个IO函数进行检测),信号驱动IO(不阻塞,考sigio信号)),异步I/O(通过状态、通知和回调来通知调用者的输入输出操作)
(2) 文件系统的理解(EXT4,XFS,BTRFS)
ext4 :日志文件系统,意味着它会对文件在磁盘中的位置以及任何其它对磁盘的更改做记录。纵观它的所有功能,它还不支持透明压缩、重复数据删除或者透明加密。
ext4 性能不错,但是支持的大小不行;btrfs有很多好用的功能(写复制、扩展校验、快照、清洗、自修复数据、冗余删除以及其它保证数据完整性的功能)
(3) 文件处理grep,awk,sed这三个命令必知必会
grep是文本过滤器,对一个或多个正则进行匹配输出对应的行或文本,速度快,返回搜索的状态,0成功,1搜不到,2文件不存在;*是匹配其前面的任意字符
sed是流编译器,针对行,不改变原内容,文件读行到缓冲区然后处理后送往屏幕。
awk:报告生成器。能按照列来处理,功能很全,有自己的语言,处理文本的编程语言工具
(4) IO复用的三种方法(select,poll,epoll高并发多连接时好)深入理解,包括三者区别,内部原理实现?https://blog.csdn.net/davidsguo008/article/details/73556811
(5) Epoll的ET模式和LT模式(ET的非阻塞)
(6) 查询进程占用CPU的命令(注意要了解到used,buf,cache代表意义)
内存不够-磁盘上的虚拟内存swap;内存够,额外内存中分配出空间设置cache和buf,buf用于存修改后的数据,不急着更新到磁盘;used包含的是所有的buf和cache分配总额
查看cpu:top主要看cpu,内存使用情况,及占用资源最多的进程由高到低排序,关注点在于资源占用情况;ps主要是查看进程的,关注点在于查看需要查看的进程,cat(显示内容) proc/statue
(7) linux的其他常见命令(kill,find,cp等等)
kill 杀死进程,pid标识;find 在指定的目录下查找文件,查找的方式可以根据正则式、类型、深度、时间、大小、权限,可以设置删除这些文件;
cp将1个或多个文件复制到文件/目录下;touch 创建新的空文件;chown 改变文件所属的所有者和组
(8) shell脚本用法
(9) 硬连接和软连接的区别:硬链接通过索引接地那来连接,多个文件名字指向同一索引节点,当只删除1个连接不影响索引节点本身,要删除全部连接
软连接(符号连接-s):相当于快捷方式,指明位置信息,当删除文件,连接无效
(10) 文件权限怎么看(rwx):ls -l 、ll 读/写/可执行(针对目录能不能进去cd) chmod 修改权限
(11) 文件的三种时间(mtime, atime,ctime),分别在什么时候会改变:状态时间修改属性,访问时间读的时候,修改时间
(12) Linux监控网络带宽的命令,查看特定进程的占用网络资源情况命令
(13)Linux内核--内核地址空间分布和进程地址空间
直接映射区就是线性地址和物理地址存在线性转换的关系; 动态内存映射区是内核函数vmalloc分配的;
永久内存映射区是可访问高端内存,固定映射区每个地址项服务于特定的用途
上面的地址都是在3G-4G之间的(32位)(就是说高地址的给os用,低的给进程用,linux通常占1G,win要2G)
进程空间:每个进程有一个3G大小地址空间(32位),但是这个是虚地址,每个进程用户空间都是完全独立的,访问内核通过系统调用和中断。实际的物理内存只有当进程真的去访问新获取的虚地址时,才会产生缺页异常,进入分配实际页的流程。通过页表机制。分布是栈、堆、BSS、data、text。栈和堆生长的方向是为了最大化空间利用率。堆区最大可能是接近3G,要考虑另外的那些的大小
每个进程的带宽使用――nethogs
5操作系统
(1) 进程与线程的区别和联系:进程(资源分配、调度单位,容易管理,如果是微内核os中,调度单位是线程),线程有自己的栈段,存放局部/临时变量,开销小
(2) 一个进程可以创建多少线程,和什么有关:线程的堆栈大小,有些默认是1M;然后和进程的用户地址空间相关,比如2G,最后剩下可能只有2027个线程可以
(3) 一个程序从开始运行到结束的完整过程(四个过程):
预处理(.i,头文件编译进来,宏替换),编译(.s,各种分析后汇编语言),汇编(.o),链接(模块间,动态 静态)
(4) 进程通信方法(Linux和windows下),线程通信方法(Linux和windows下)
管道:半双工的流动;消息队列:消息的链表,在内核中;信号量:计数器,常用与锁机制;信号:比较复杂;共享内存:一个进程创建,多个进程可以访问,快;套接字:可以用于不同机器间通信
信号量:pv,设为资源的数目;互斥量:信号量的一种,0/1;条件变量:通常配合互斥量一起使用
(5) 进程调度方法详细介绍
时间片轮转调度算法(RR),先来先服务调度算法(FCFS),优先级调度算法(HPF),多级反馈队列调度算法,高响应比优先调度算法
(6) 页面置换方法详细介绍
FIFIO,LRU,NRU(最近未使用)
(7) 能否实现一个LRU算法
栈:头尾指针,栈底是旧的,用链表链接起来,双向的,所以移走一页的话,最坏的要改6个指针
(8) 死锁的必要条件(怎么检测死锁,解决死锁问题)
(9) 哲学家就餐,银行家,读者写者,生产者消费者(怎么加锁解锁,伪代码)
(10) 海量数据的bitmap使用原理:海量处理一个bitmap,一个布隆过滤器
40亿的数有一个是不包含的,那么40亿对应的比特位数空间是0.5GB,然后每个数会置相应的位为1,最后统计。如果只有10MB的空间的话,就先分块,每块有计数器,找到缺的块,然后用bitmap
(11) 布隆过滤器原理与优点:k个hash产生k个点阵,然后在位图上置为1,比较这些点如果有0,则不包含。优点快,缺点不能删除元素,还有误判率。
(12) 布隆过滤器处理大规模问题时的持久化,包括内存大小受限、磁盘换入换出问题
(13) 同步IO和异步IO
(14) 文件读写使用的系统调用:open,close,creat,read,write
(15) 线程池的了解、优点、调度处理方式和保护任务队列的方式
池的概念:就是为了重用,减少因为创建销毁带来的性能开销;
线程池能控制最大的并发数目,提高资源利用率,便于管理和使用
(16) 怎么回收线程
(17) 僵尸进程问题:子进程结束但是没有等到父进程wait他,那么他将会变为僵尸进程。内核仍然会保留它的一些信息,这些信息会一直保留到系统为她传递给它的父进程为止,如果父进程结束了也over了/。
解决方法就是父进程wait或者waitpid;可以用信号量机制来通知父进程回收,如果父进程很忙的话
(18) 多线程同步(尤其是如果项目中用到了多线程,很大可能会结合讨论)
(19) memcache了解:分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高了网站访问的速度。存储键值对的HashMap。适合对数据库有高并发读写和海量数据处理的场景。
(20) 异常和中断的区别:外部中断,会有中断请求,转去干别的事,然后回到断点;异常是内部的中断,基本上是程序缺陷,很多都是不能预知的
(21) 一般情况下在Linux/windows平台下栈空间的大小
win是编译器决定栈的大小,记录在可执行文件中,默认是1M。linux是os来决定的,在系统环境变量中设置, ulimit -s 命令查看修改,我的是8M
6设计模式和算法
设计模式一般都不会考太多,除非你明确说自己懂。我基本上就不涉及到设计模式的东西,所以只是简要说说。当然,单例模式和简单工厂模式的概念和使用场景还是要知道的。
(1) 介绍熟悉的设计模式(单例,简单工厂模式)
创建型模式:工厂、抽象工厂、单例;结构性模式:桥接、代理;行为型模式:观察者、命令、策略;J2EE模式:MVC
(2) 写单例模式(饿汉模式和懒汉模式),线程安全版本
(3) MVC设计模式
算法这一块太过庞大,几乎都有可能,牛油们最好还是去刷剑指offer(level 1),leetcode(如果能够刷到最高难度,算法对你来说已经不是什么了,或者说面试对你来说简直就是吃饭喝水的难度),左神的书《程序源代码面试指南》(字符串,数组,dp,海量数据问题,搞定它们也就搞定面试的一半)。
下面还是简单的列举一些吧(包括一些数据结构题目,只列举简单的,面试的算法一半不会太难,但是现在一般都是需要比较好的思维,或者曾经接触过这方面的题,建议就是多刷题,做题感觉是刷出来的)
(1) 红黑树RBTREE的了解(平衡树,二叉搜索树),使用场景
avl用于搜索,插入删除次数少场景,用在win的内核中很多;红黑:查找,用在STL中map set,java 中的treemap,linux进程调度公平调度用于管理进程控制块;B/B++用于文件系统、数据库中做索引,trie用于前缀匹配,NLP中常用的数据结构吧
(2) 红黑树在STL上的应用
对于unordered的结构,红黑树虽然慢,但是比hash的方式省内存
(3) 了解并查集吗?(低频)
并查集用于畅通工程算法,亲戚门派问题;pre{}数组用于记录上一级是谁,两个函数find用于查根,join用于当两个人的掌门人不同的时候,将其中一个掌门人归并到另外一个掌门人门下;可以使用路径压缩算法继续优化
(4) 贪心算法和动态规划的区别:贪心就是考虑当前,与后面无关;dp就是考虑所有相关情况,把结果保存起来,然后回溯
(5) 判断一个链表是否有环,如何找到这个环的起点:如果不考虑空间的话,其实只要把链表节点加入到hashset中去就行,如果在set已经包含了元素,那么就是起点,有环。也可以用双指针快慢来做
(6) 实现一个strcpy函数(或者memcpy),如果内存可能重叠呢
(7) 实现一个循环队列:ring buffer
(8) 排序算法(写快排,归并排序,堆排序),算法的时间复杂度,空间复杂度,是否稳定等
(9) 快排存在的问题,如何优化:最差情况下不好,比较点的选择使用随机的方式(可以取左中右各自3个数取中数,然后得到3个中数再取中数,如果这样的话,后面的N可以设为9);三向切分(把相等的数也考虑进来,不让他们再次递归);当划分已经小于N时,使用插入排序,因为快排会递归自己
(10) 反转一个链表:用3个指针遍历一次
(11) Top K问题(可以采取的方法有哪些,各自优点?) 最小堆k;或者最大堆输出k次,直接直选nk
(12) Bitmap的使用,存储和插入方法:Bitmap是Android系统中的图像处理的最重要类之一
(13) 二叉树的先序、中序、后序遍历(非递归实现)
(14) 二叉树的公共祖先(简单地说,剑指offer上的题大都是高频题)
(15) 1-n中有多少个1:将数分为3块,如3141592考虑每一位,比如百位,那么百位为1,则分为3141和5和92,那么就为3142×100个;千位是1,所以是314×1000+593;
考虑到比较的那个位,如果大于1的话。加8则相当于进位了,所以最后可以得出该式子比较:
int countDigitOne(int n) { int ones = 0; for (long long m = 1; m <= n; m *= 10) { int a = n/m, b = n%m; ones += (a + 8) / 10 * m + (a % 10 == 1) * (b + 1); } return ones; }
(16) 字典树的理解以及在统计上的应用:根节点不包含字符,另外的节点只包含1个字符,所以1个节点只包含1个字符和几个指针
(17) 数组的全排列:关键是递归后要重新交换回来数字,leetcode原题permutations
(18) N个骰子出现和为m的概率:dp
(19) 海量数据问题(可参考左神的书)
(20) 一致性哈希:分布式哈希表DHT就是用到这个,避免了用余数的分布式算法,服务器发生变化会导致重新算,阿里一面的那个问题,其实也可以使用这个来解决。可以选择服务器ip或者主机名来进行哈希的映射,选第一个顺时针的服务器,只需要重定位一小部分的节点就好
增加虚拟节点来优化节点太少而产生的不均衡情况,比如将节点1加后缀进行哈希,分成多个虚拟节点
用在缓存数据库,redis,数据库的水平分表这些地方
7智力题
我特别把智力题单独拿出来讲,是因为这一块其实有些公司很看重(例如腾讯),但是我基本上也没刷过这类题目,接触不多,牛油们自行补充吧。
(1) 100层楼,只有2个鸡蛋,想要判断出那一层刚好让鸡蛋碎掉,给出策略(滴滴笔试中两个铁球跟这个是一类题) dp问题
(2) 毒药问题,n拼毒药,要快速找出哪一瓶有毒,需要几只小白鼠
利用校验码机制可以定位,海明校验码,或者说让1000瓶编号三位数,让三只老鼠分别喝掉所有对应位为1的药
(3) 博弈论问题
(4) 先手必胜策略问题:n本书,每次能够拿X-X本,怎么拿必胜
(5) 放n只蚂蚁在一条树枝上,蚂蚁与蚂蚁之间碰到就各自往反方向走,问总距离或者时间。
(6) 瓶子换饮料问题:多少个瓶子能够换1瓶饮料,问最多能喝几瓶