https://www.1point3acres.com/bbs/interview/software-engineer-509217.html
头条一面:
○ 自我介绍
○ 算法题
一个数列,选两个数,使得后面的数与前面的数差最大,要求10分钟写完
后序遍历,存储最大的后数;
当前遍历到的数为前数:
如果后数大,就算差值,比较之前的res并更新;
如果前数大,后数=前数,继续往前遍历。
○ 问问项目
项目当中印象最深刻的点
session和cookie的区别?
Cookie存储在客户端阅读器中,对客户端是可见的,客户端的一些程序可能会窥探、复制以至修正Cookie中的内容。 而Session存储在服务器上,对客户端是透明的,不存在敏感信息泄露的风险。 假如选用Cookie,比较好的方法是,敏感的信息如账号密码等尽量不要写到Cookie中。
1. 由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session.典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的Session,用用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几本书。这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件都有。集群的时候也要考虑Session的转移,在大型的网站,一般会有专门的Session服务器集群,用来保存用户会话,这个时候 Session 信息都是放在内存的,使用一些缓存服务比如Memcached之类的来放 Session。
2. 思考一下服务端如何识别特定的客户?这个时候Cookie就登场了。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。实际上大多数的应用都是用 Cookie 来实现Session跟踪的,第一次创建Session的时候,服务端会在HTTP协议中告诉客户端,需要在 Cookie 里面记录一个Session ID,以后每次请求把这个会话ID发送到服务器,我就知道你是谁了。有人问,如果客户端的浏览器禁用了 Cookie 怎么办?一般这种情况下,会使用一种叫做URL重写的技术来进行会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。
3. Cookie其实还可以用在一些方便用户的场景下,设想你某次登陆过一个网站,下次登录的时候不想再次输入账号了,怎么办?这个信息可以写到Cookie里面,访问网站的时候,网站页面的脚本可以读取这个信息,就自动帮你把用户名给填了,能够方便一下用户。这也是Cookie名称的由来,给用户的一点甜头。
所以,总结一下:
Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;
Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。
讲一下Http状态码http方法(get,post,put,patch,delete)
进程和线程
线程用于小任务,而进程用于更多的'重量级'的任务- 应用基本执行。 一个线程和进程之间的另一个区别是,在同一进程中的线程共享相同的地址空间,而不同的进程没有。 因此线程可以读写同样的数据结构和变量,便于线程之间的通信。
定义方面:进程是程序在某个数据集合上的一次运行活动;线程是进程中的一个执行路径。
linux下怎么实现ctrl+c退出程序的?(信号)
信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称作软中断。内核给一个进程发送软中断信号的方法,是在进程所在的进程表项的信号域设置对应于该信号的位。
这里要补充的是,如果信号发送给一个正在睡眠的进程,那么要看 该进程进入睡眠的优先级,如果进程睡眠在可被中断的优先级上,则唤醒进程;否则仅设置进程表中信号域相应的位,而不唤醒进程。这一点比较重要,因为进程检查是否收到信号的时机是:一个进程在即将从内核态返回到用户态时;或者,在一个进程要进入或离开一个适当的低调度优先级睡眠状态时。
内核处理一个进程收到的信号的时机是在一个进程从内核态返回用户态时。所以,当一个进程在内核态下运行时,软中断信号并不立即起作用,要等到将返回用户态时才处理。进程只有处理完信号才会返回用户态,进程在用户态下不会有未处理完的信号。
数据库E-R模型了解吗,谈谈
基本 E-R图中的元素包括实体集、联系集、属性。椭圆框表示属性,矩形框表示实体集,菱形框表示联系。
实体分为强实体和弱实体。强实体的实例的存在不依赖于任何其他实体类型的实例;有自己独立的主键,唯一性地标识它的每个实例。弱实体的实例的存在依赖于其它实体类型的实例;其主键包括它所依赖的实体类型的主键。
主要是两点:
第一点:依赖,弱实体应该依赖于强实体;
第二点:主键,弱实体的主键应该是组合主键(其他实体的主键组成的)。
数据库实体间有三种对应关系:一对一,一对多,多对多。多对多需要在实体之间增添一个关系表来实现,一对多也可以通过新增关系表实现,更多地采用的是在一对多的两个实体间,在“多”的实体表中新增一个字段,该字段是“一”实体表的主键,并通过外键进行连接。一对一则一般任选一个实体,增添一个字段作为外键,链接到另一个实体的主键。
数据库索引是什么?
索引是用来存储数据表中指定列值的一种数据结构,可以有效提高检索速度。
数据库包含三种索引:普通索引、唯一索引(值唯一,任意两行的索引列不能够重复)、主键索引(唯一标识表中的每行,不能空)和聚集索引(组合索引,多列共同构成索引,在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同)。
索引的结构通常是B+Tree。索引可以加快查询速度,但是会减慢插入、删除、修改等操作,因为需要更新索引的结构。
知不知道redis,为什么redis比sql快
Redis是一个开源的内存中的数据结构存储系统,它可以用作:数据库、缓存和消息中间件。支持多种数据类型,比如String、List、Set、Hash、ZSet(Sorted set)等。Redis是单线程,可以持久化(定期操作)。
速度快的原因:
数据储存在内存里面,读写数据的时候都不会受到硬盘 I/O 速度的限制
数据结构简单,对数据的操作也简单
单线程操作,避免了多线程导致的上下文切换和竞争条件。
多路 I/O 复用模型。这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。添加了事件机制。利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作
Redis可以启动多个实例,组成master-slave形式,实现主从复制和读写分离。master负责写操作,更新数据,slave负责耗时的读操作,当有修改操作时,master修改数据后,开始增量复制到slave机器上。每次slave和master连接都会启动一次全量复制。
Redis可以组建集群。通过算法将数据均量存放到不同的物理机上,索引的时候先由 proxy 判断数据所存放的物理机,然后再去访问物理机得到数据。此处需要指出:不能够让集群把热key全部打在 redis 上面。
知道程序的局限性吗?有什么用?
时间局限性:最近被访问的单元,很可能在不久的将来还要被访问
空间局部性:最近被访问的单元,很可能它附近的单元也即将被访问
好好利用缓存
头条二面:
项目中遇到什么困难?怎么解决的?
怎么判断用户的登录状态(维持在线)?
当用户登录的时候,请求发送到服务端,如果登录成功,服务端生成一个token,并绑定到该用户身上,同时将token返还给用户,之后用户的请求都带上这个token。
token可以根据需求设置过期时间。
如果自动登录或者记住密码,则用户登录的时候,把用户名和密码记录在cookie中,发送给服务端,服务端进行保存。当用户下次访问页面的时候,返还cookie。浏览器读取cookie内容并自动填写。
HTTP怎么保证安全?(HTTPS)
http是明文传输,传输的数据很可能被中间节点获取,从而导致数据传输不安全
https是加密传输,可以保证数据的传输安全
讲一蛤HTTPS的原理
http是应用层协议,它会将要传输的数据以明文的方式给传输层,这样显然不安全。https则是在应用层与传输层之间又加了一层,该层遵守SSL/TLS协议,用于数据加密。
加密分为两种:
对称加密:速度快,但是加密和解密的钥匙是相同的;
非对称加密:算法更加复杂,速度慢,加密和解密钥匙不相同。
加密过程:
首先服务器将公钥给浏览器
浏览器拿到公钥之后,生成一个“会话密钥”(这个会话密钥用于对称加密)
然后浏览器用公钥加密这个“会话密钥”,发送给服务器
最后,在数据传输的过程中,就用这个会话密钥来加密数据。
上述的过程是在3次握手中完成,采用明文发送,握手完成以后,客户端和服务端就约定好了“会话密钥”,以后的数据传输,就采用这个会话密钥加密。
利用公钥和私钥以及数字签名,我们可以保证信息传输过程中的私密性和完整性。数字签名:用于判断发送的加密信息是否被破坏过,如果破坏过则丢弃,反之则利用密钥解密。数字证书:第三方权威机构,用于认证的服务器并颁发数字证书,数字证书内包含服务端的公钥。
非对称加密和对称加密是什么意思,有哪些方式
对称加密只有一个秘钥,作为私钥。常见的对称加密算法:DES,AES,3DES等等。
非对称加密有两把密钥:一把作为公钥,另一把作为私钥。公钥加密的信息,只有私钥才能解密。私钥加密的信息,只有公钥才能解密。常见的非对称加密算法:RSA,ECC
现实的浏览器可以两种共用。
进程通信有哪些方式?
进程间的通信主要是指多个进程间的数据交互。
同步主要指维护多个进程或者线程之间数据的准确性和一致性。
进程通信方式:管道(Pipe)、信号(signal)、信号量(semophere)、消息队列(message queue)、共享内存(shared memory)、套接字(socket)。
同步方式:将线程串行化(wait notify方法来睡眠和唤醒线程)、互斥锁(加锁 mutex)
线程安全是啥?为啥要保证线程安全?
线程安全: 指多个线程在执行同一段代码的时候采用加锁机制,使每次的执行结果和单线程执行的结果都是一样的,不存在执行程序时出现意外结果。
线程不安全: 是指不提供加锁机制保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据
保证安全的措施:设置方法的原子性;增添互斥锁;
JAVA操作:使用线程安全的类;使用synchronized同步代码块,或者用Lock锁
命名管道是什么?有什么用?
匿名管道:可以用于有血缘关系之间的进程间的通信(pipe)
命名管道:可以用于任意进程间的通信(fifo)。它是一种文件类型。
与管道的区别:提供了一个路径名与之关联,以FIFO文件的形式存储于文件系统中,能够实现任何两个进程之间通信。而匿名管道对于文件系统是不可见的,它仅限于在父子进程之间的通信。
FIFO是一个设备文件,在文件系统中以文件名的形式存在,因此即使进程与创建FIFO的进程不存在血缘关系也依然可以通信,前提是可以访问该路径。
Linux中,“一切皆文件”,所以管道就是使得两个进程能够同时访问一个文件。
匿名管道不存在与磁盘上,是存在于内存上的特殊的文件,因此创建后直接能够使用,pipe(fd)之后,fd[0]是读文件描述符,fd[1]是写文件描述符,然后fork创建子进程,两者选择性关闭其中一个文件描述符。
#include<stdio.h>
#include<unistd.h>
int main(){
int fd[2]; // 两个文件描述符
pid_t pid; char buff[20];
if(pipe(fd) < 0) // 创建管道
printf("Create Pipe Error!\n");
if((pid = fork()) < 0) // 创建子进程
printf("Fork Error!\n");
else if(pid > 0) // 父进程 {
close(fd[0]); // 关闭读端
write(fd[1], "hello world\n", 12);
} else {
close(fd[1]); // 关闭写端
read(fd[0], buff, 20);
printf("%s", buff);
}
return 0;
}
命名管道在磁盘上创建一个文件,如下两个方法,使用之前需要使用open()打开。
命名管道调用open()打开有可能会阻塞,情况如下:
读写方式(O_RDWR)打开 —— 一定不会阻塞;
只读方式(O_RDONLY)打开 —— 调用open()的函数会被阻塞直到有数据可读;
只写方式(O_WRONLY)打开 —— 被阻塞,直到有以读方式打开该管道。
原型
#include <sys/stat.h>
int mknod(const char* path, mode_t mod, dev_t dev);
int mkfifo(const char* path, mode_t mod);
针对即将上线的项目,设计一个缓存系统,有哪些需要考虑的(缓存清理的优先级,带宽…)
算法题部分:
1. 水算法题: 对一个序列求和最大的连续子序列,直接前缀和走起啊
2. 二叉树层次遍历(queue) 怎么在相邻层次之间换行? (每个节点加一个depth即可)还有其他方法吗?(类似滚动数组,设置两个队列)
头条三面:
自我介绍。
由于简历的项目是用Spring做的,于是开始问Spring相关:
Spring Beans是怎么实现的?
讲一下Java的动态代理。
Spring 的AOP如何实现。
接着问了一个数据库设计题:假设用户量爆棚,怎么设计微博系统的数据库?
https://www.zhihu.com/question/19715683
微博的读请求量要远大于写请求量,所以有必要对数据库的读写请求进行分离,写请求访问Master,读请求访问Slave,可以采用“一主多从”的架构。
微博的存储除了大量使用MySQL和Memcached以外,还有一种存储也被广泛使用,那就是Redis。并且基于微博自身的业务特点,微博对原生的Redis进行了改造,因此诞生了两类主要的Redis存储组件:CounterService和Phantom。
CounterService的主要应用场景就是计数器,比如微博的转发、评论、赞的计数。同时对数据进行了冷热数据分离,热数据放到内存里,冷数据放到磁盘上,并使用LRU,如果冷数据重新变热,就重新放到内存中。
Phantom利用Bloomfilter来记录某个用户是否看过或者点赞过某条微博。通过把内存分成N个Table,Phantom的每个Table是一个完整的BloomFilter,每个BloomFilter存储的某个ID范围段的Key,所有Table形成一个列表并按照Key范围有序递增。当所有Table都存满的时候,就把最小的Table数据清除,存储最新的Key,这样的话最小的Table就滚动成为最大的Table了。
接着是算法题部分。
两个数列长度都为n,要求找到满足条件的数对(l,r)使得max(a_l,a_r)<min(b_l,b_r),求一共有多少对满足条件的数对。
三面难度陡增啊。
三面前面答的不好,不过后面的算法题思路来的很快,写的也很快,就很幸运的过了。
总结和几条建议:
前面的基础题部分,好好背书即可。
对于自己做过的项目和应用的框架,一定要事先了如指掌,不仅是项目实现的过程,还有其背后的底层实现。
面试的时候切记不要一言不发,不会就随便猜,猜的只要有点沾边面试官就会提示你。