字节跳动
一、算法题
一面:
1. lc 里最长上升子序列的变形题
2. 实现输入英文单词联想的功能
二面:
1.矩阵旋转,要求空间复杂度 O(1)
2.无序的数组的中位数。要求时间复杂度尽可能的小
二、计算机网络
- tcp 怎么保证数据包有序
主机每次发送数据时,TCP 就给每个数据包分配一个序列号并且在一个特定的时间内等待接收主机对分配的这个序列号进行确认。
如果发送主机在一个特定时间内没有收到接收主机的确认,则发送主机会重传此数据包。
接收主机利用序列号对接收的数据进行确认,以便检测对方发送的数据是否有丢失或者乱序等。
接收主机一旦收到已经顺序化的数据,它就将这些数据按正确的顺序重组成数据流并传递到高层进行处理。
- tcp 和 udp 的异同
TCP 是面向流的可靠数据传输连接
UDP 是面向数据包的不可靠无连接
- tcp 怎么保证可靠性
差错检验机制,反馈机制,重传机制,引入序号,滑动窗口协议,选择重传
- tcp 中拥塞避免和流量控制机制
拥塞避免和流量控制这两种机制很像,但是流量控制是由接收方的接受能力也就是接收窗口所决定的,如果接收窗口够大,以动态调整发送窗口的大小调整发送速度
拥塞避免主要由网络情况所限制,网络情况良好,则加大发送速率,网络状态差(冗余 ACK 和丢包)则降低发送速率(慢启动,拥塞控制,快恢复,快重传) RENO,BBR
- tcp 四次挥手的详细解释
tcp 四次挥手其实可以分为两个阶段
第一:
客户端至服务器的半双工连接关闭
客户端向服务器发送 FIN 信号,进入 FIN_WAIT1 的状态,等待服务器的 ACK 信号
收到服务器的 ACK 后,进入 FIN_WAIT2
第二:
服务器至客户端的半双工连接关闭
客户端收到服务器发来的 FIN 后,发送 ACK,并进入 TIME_WAIT,等待 2msl,若无异常,则客户端认为连接成功关闭
服务器收到客户端发来的 ACK 后,关闭连接
- 四次挥手之后为什么还要等待 2msl
MSL 是报文最大生存时间
1 是因为有可能客户端发往服务器的 ACK 丢失,服务器并不知道客户端已经确认关闭,这时候客户端的关闭会导致服务器端无法正常关闭
2 是为了保证连接中的报文都已经传递。假如短时间关闭又重新实现一个 TCP 还连到了同个端口上,旧连接中尚未消失的数据就会被认为是新连接的数据。
浏览器从输入网址到显示出网页的全过程
输入网址或者 ip。
如果输入的是网址,首先要查找域名的 ip 地址
第一步会在浏览器缓存中查找,如果没有,转至查询系统缓存,如果还是没有,发送请求给路由器,路由器首先会在自身的缓存中查找,如果还是没有,向 ips 发出请求,查询 ips 中的 dns 缓存,如果还是没有递归向上查询直至根服务器。浏览器与 ip 机器之间建立 TCP 连接(三次握手)(HTTP)或者在 TCP 上进一步建立 SSL/TLS 连接 (HTTPS)
接下来就是发送 HTTP 报文啥的了
GET,POST,DELETE,PUT。滑动窗口机制的原理和理解
GBN 协议,回退 N 步协议,这是对停等协议的改进,因为停等协议的传输效率非常低下。每次可发送的数据为 N,基数为 base,小于 base 的数据已经发送并且确认,base 是最小的已发送未确认的报文序号。在接收端同样也有一个接收窗口,(解释)GBN采用的是累计确认方式,这时候说一下选择重传机制。再说一下 TCP 中既不是 GBN 也不是 SR ,而是 GBN 和 SR 的综合体。
N 的大小必须报文序列编号的一半,否则接收端对报文的确认可能发生混淆
Https 原理和实现
cookie 和 session 的区别是什么
由于 HTTP 协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户
cookie 存在本地的上的
session 是存在服务器上的
通俗讲,Cookie 是访问某些网站以后在本地存储的一些网站相关的信息,下次再访问的时候减少一些步骤。另外一个更准确的说法是:Cookies 是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器,是一种在客户端保持状态的方案。
Session 是存在服务器的一种用来存放用户数据的类HashTable结构。
二者都用来保持用户的状态,cookie可更改,对服务器来说并不安全,服务器常见做法有这两种:
1.把 session 加密后放入浏览器的 cookie 中,浏览器重连后将加密的 session 发给服务器
2.cookie 中存储着 session的id,浏览器重连时只需要发送 session_id' 即可
三、操作系统
- 进程和线程的区别
进程就是保存上下文切换的程序执行时间总和 = CPU 加载上下文 + CPU 执行 + CPU 保存上下文
- 线程是什么呢?
进程的颗粒度太大,每次都要有上下的调入,保存,调出。如果我们把进程比喻为一个运行在电脑上的软件,那么一个软件的执行不可能是一条逻辑执行的,必定有多个分支和多个程序段,就好比要实现程序A,实际分成 a,b,c 等多个块组合而成。那么这里具体的执行就可能变成:
程序 A 得到 CPU =》CPU 加载上下文,开始执行程序 A 的 a 小段,然后执行 A 的 b 小段,然后再执行 A 的 c 小段,最后 CPU 保存 A 的上下文。
这里 a,b,c 的执行是共享了 A 的上下文, CPU 在执行的时候没有进行上下文切换的。这里的 a,b,c 就是线程,也就是说线程是共享了进程的上下文环境,的更为细小的 CPU 时间段。
进程切换与线程切换
Linux 中五种 IO 模型
- 阻塞 I/O(blocking I/O)
- 非阻塞 I/O (nonblocking I/O)
- I/O 复用 (select 和poll) (I/O multiplexing)
- 信号驱动 I/O (signal driven I/O (SIGIO))
- 异步 I/O (asynchronous I/O (the POSIX aio_functions))
前四种都是同步,只有最后一种才是异步 IO。
同步 IO 和异步 IO 的区别就在于:数据拷贝的时候进程是否阻塞!
阻塞 IO 和非阻塞 IO 的区别就在于:应用程序的调用是否立即返回!
如何实现一个同步非阻塞的请求
实现进程同步的机制有什么
信号量的实现机制
共享锁和排他锁
实现一个读写锁
设计一个无锁队列
协程的原理
四、数据库
- 索引是什么
索引 (Index) 是帮助 MySQL 高效获取数据的数据结构。
索引能极大的减少存储引擎需要扫描的数据量
索引可以把随机 IO 变成顺序 IO
索引可以帮助我们在进行 分组、 排序等操作时,避免使用临时表
- 为什么要用 B+ 树(B+ 树的优缺点)
B+ 树是 B- 树的变种 (PLUS 版)多路绝对平衡查找树,好的时间复杂度
B+ 树扫库、表能力更强
B+ 树的磁盘读写能力更强
B+ 树的排序能力更强
B+ 树的查询效率更加稳定(仁者见仁、智者见智,有可能 B-Tree 第一次就命中了,直接返回,而 B+Tree 需要找到叶节点,所以查找效率不一定比 B-Tree 更高)
支持顺序排序,叶节点之间存在链接
- B+索引和哈希索引的区别?
B-tree 索引
索引是按照顺序存储的,所以,如果按照 B-tree 索引,可以直接返回,带顺序的数据,但这个数据只是该索引列含有的信息。因此是顺序 I/O
适用于:
精确匹配
范围匹配
最左匹配
Hash 索引
索引列值的哈希值+数据行指针:因此找到后还需要根据指针去找数据,造成随机I/O
适合:
精确匹配
不适合:
模糊匹配
范围匹配
不能排序
1、hash 索引仅满足 “=”、“IN” 和 “<=>” 查询,不能使用范围查询因为 hash 索引比较的是经常 hash 运算之后的hash值,因此只能进行等值的过滤,不能基于范围的查找,因为经过hash算法处理后的 hash 值的大小关系,并不能保证与处理前的 hash 大小关系对应。
2、hash 索引无法被用来进行数据的排序操作由于 hash 索引中存放的都是经过hash 计算之后的值,而 hash 值的大小关系不一定与 hash 计算之前的值一样,所以数据库无法利用 hash 索引中的值进行排序操作。
3、对于组合索引,Hash 索引在计算 Hash 值的时候是组合索引键合并后再一起计算 Hash 值,而不是单独计算 Hash 值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash 索引也无法被利用。
4、Hash 索引遇到大量Hash值相等的情况后性能并不一定就会比 B-Tree 索引高。对于选择性比较低的索引键,如果创建 Hash 索引,那么将会存在大量记录指针信息存于同一个 Hash 值相关联。这样要定位某一条记录时就会非常麻烦,会浪费多次表数据的访问,而造成整体性能低下。
总结:哈希适用在小范围的精确查找,在列数据很大,又不需要排序,不需要模糊查询,范围查询时有用
- B+ 树中叶子节点间的指针有什么用?
使得访问更加简单,b 树的话需要不断在父节点和叶子节点之间来回移动
- 聚簇和非聚簇索引的区别?
聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据,聚簇索引的叶子节点就是数据节点,由于聚簇索引是将数据跟索引结构放到一块,因此一个表仅有一个聚簇索引,聚簇索引具有唯一性
非聚簇索引:非聚簇索引的叶子节点仍然是索引节点,只不过有指向对应数据块的指针。将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行
- 非聚簇索引的查询都要回表吗?
是的
- B+ 树和 AVL 树 B 树二叉搜索树有什么区别?
这几种各有交集但又不尽相同。
什么是二叉搜索树?
它或者是一棵空树,或者是具有下列性质的二叉树:
1.左子节点的值必定小于父节点的值
2.右子节点的值必定大于父节点的值
首先 AVL 树是自平衡的二叉搜索树AVL在该定义的基础上加入了平衡条件,即:某节点的左右子树的高度差小于等于1
另一种非严格自平衡的二叉搜索树是红黑树,二者使用场景略微有些不同。
AVL 追求整体的绝对平衡,适合于少量插入,大量查找的应用场景(因为维护全局平衡,插入一个往往需要 O(log n))
红黑树适用于一部分插入,一部分查询的场景(变色,左旋右旋场景相对少些)
B+ 树是对 B 树的拓展
一棵 m 阶 B 树 (balanced tree of order m) 是一棵平衡的m路搜索树。它或者是空树,或者是满足下列性质的树:
1、根结点至少有两个子女;
2、每个非根节点所包含的关键字个数 j 满足:┌m/2┐ - 1 <= j <= m - 1;
3、除根结点以外的所有结点(不包括叶子结点)的度数正好是关键字总数加 1,故内部子树个数 k 满足:┌m/2┐ <= k <= m ;
4、所有的叶子结点都位于同一层。
B+ 树对 B 树的改进就是:只有叶节点存数据,并且维护了两个指针,一个指向根节点另一个指向顺序叶节点的首位。
B+ 树还在叶子节点中加入了链接指针
五、杂项
这一部分和项目比较相关。基本上项目中有什么或者面试官想到什么问什么,问了很多但是不通用。就只写一点。
- GIL 是什么
全局状态锁
- 为什么会有?
出现多线程编程之间数据和状态一致性问题,为了解决这个数据不能同步的问题
有什么作用?
怎么规避它对于并行的影响?
六、语言相关
- Python 的内存管理机制
(1)垃圾回收
(2)引用计数
(3)内存池机制
- 讲一下 Python GC 的原理和详细解释(分代,标记回收,内存划分)
Python 语言默认采用的垃圾收集机制是『引用计数法 Reference Counting』,『引用计数法』的原理是:每个对象维护一个 ob_ref 字段,用来记录该对象当前被引用的次数,每当新的引用指向该对象时,它的引用计数 ob_ref 加 1,每当该对象的引用失效时计数 ob_ref 减 1,一旦对象的引用计数为 0,该对象立即被回收,对象占用的内存空间将被释放。
为了解决对象的循环引用问题,Python 引入了标记-清除和分代回收两种 GC 机制。
标记就是使用有向图的方式,不可达的清除掉(主要用来清理容器对象)
分代分为三代:年轻,中年,老年。年轻对象满,触发 GC,可回收对象回收掉,不可回收移到中年中去,以此类推。结合标记使用
引用计数增加
1.对象被创建: x=4
2.另外的别人被创建: y=x
3.被作为参数传递给函数: foo(x)
4.作为容器对象的一个元素: a=[1,x,'33']
引用计数减少
1.一个本地引用离开了它的作用域。比如上面的 foo(x) 函数结束时,x指向的对象引用减 1。
2.对象的别名被显式的销毁: del x ;或者 del y
3.对象的一个别名被赋值给其他对象: x=789
4.对象从一个窗口对象中移除: myList.remove(x)
5.窗口对象本身被销毁: del myList,或者窗口对象本身离开了作用域。
垃圾回收
1、当内存中有不再使用的部分时,垃圾收集器就会把他们清理掉。它会去检查那些引用计数为 0 的对象,然后清除其在内存的空间。当然除了引用计数为0的会被清除,还有一种情况也会被垃圾收集器清掉:当两个对象相互引用时,他们本身其他的引用已经为 0 了。
2、垃圾回收机制还有一个循环垃圾回收器, 确保释放循环引用对象 ( a 引用 b, b 引用 a, 导致其引用计数永远不为 0)。
Python 中 static_method 、 class_mathod 、和普通 method 有什么区别
迭代器和生成器有什么区别?
先说结论,生成器式 一种特殊的迭代器:其在使用时生成。
首先明确两个概念:
Iterable :所有实现了 iter__ 的对象均可称作 Iterablec。Iterator:是指同时实现了 iter 和 _next 的对象,迭代器也属于 Iterable。
小结
凡是可作用于 for 循环的对象都是 Iterable 类型;
凡是可作用于 next() 函数的对象都是 Iterator 类型,它们表示一个惰性计算的序列
- 生成器怎么使用?
两种方式:
a =(i for i in range(100))
def a(n):
i = 0
while(i < n):
yield i
i += 1
字节跳动-广告
一面
自我介绍
聊聊项目 (聊(chui)了很长时间)
进程间通信手段
golang python 错误处理比较
三道算法题 跳方块 (贪心, 上升子序列 dp变种, 背包问题变种, 比较简单直接秒了)
二面
一道用到翻转链表竖式相加的题(md, 本垃圾状态不好写了好久, 总觉得写的有问题,差点没写出来)
那个编程语言学的比较好, (python):
python 内存管理 (没清楚问的那层,本垃圾答得 高级对象内部池 中层arean, pool, block, 面试官说不对,,当时懵逼了然后他提示 jmalloc ,问了解不, 我说不太了解原理)
垃圾回收怎么做的(本垃圾简单讲了讲引用计数和分代回收, 讲了讲和 shared_ptr 区别, 感觉答得不太清楚, 米娜使馆不满意...)
并发手段, gil, 我答了 进程, 线程, 有栈协程gevent, 无栈协程 asyncio, 然后问我 python 的线程是真的线程吗, 我说是, 因为虚拟机里 Python的线程和系统线程一一对应, 然后本垃圾好像误解他的意思了, 他的意思好像是能不能同时执行...
sql python 的区别, 懵逼了, 不知道该回答啥...
操作系统:
ctrl+c ctrl+v 区别, 本垃圾只知道发的信号不一样, 具体也不清楚, 被教育了
怎么弄一个后台进程( 本垃圾以为是守护进程, 说 fork balbal, 然后面试官说是命令行怎么搞, 我回答了 nohup ,然后又问怎么进入, 我给忘了...没怎么用过)
又问了问进程间通信的手段, 老问题不谈了
然后看了看我会 redis, 然后问了 redis 特别大的一个 key 会有啥影响, 我想了想, 回答了过期删除比较慢, 不过觉得删除 是放在 背景线程里, 影响应该不大, 也不知道回答啥了, 回来了想想可能 rehash 比较慢...
网络编程:
老问题 epoll, poll 区别
阻塞套接字非阻塞套接字 io复用的关系
问我懂不懂 rpc, 我说不懂, 但是可以随便问问, 他就问, 大量数据下, rpc 应该用阻塞套接字还是非阻塞套接字, 懵逼了...
大数据量下, 非阻塞套接字性能不如阻塞套接字, 为啥? (没想出来, 凉了, 面试官让我回去好好学学....)
项目: 看见本垃圾写了个垃圾网络库, 就问我, 这个压测过没, 我说没(目的是为了学习网络库写法, 确实没压测过)...然后又问了别的类似的方案, 我说 muduo, libevent,然后又问了一些, 想不起来了, 基本这个话题答凉了
总结
我以为我复习的还可以, 结果还是凉了, 有些东西真的很难准备. 我以为我 redis 复习的很好了, 内部机制都还了解, 结果问了一个问题还是不会....我感觉网络编程学的还行,结果问了一个问题还是不会, 准备了很久的 c++ mysql 也没考, 动态规划做出来结果链表翻转给卡了....真的很心累....感觉校招真的很艰难...