字节国际支付十连问
前言
大家好,我是田螺。
之前有位读者去字节面试,面的是国际支付部门,他凭记忆,回忆被问到的一些面试真题。于是,我整理了比较全的答案,希望对大家找工作有帮助呀,加油~
1. 聊聊工作中,你是如何设计数据库表的
命名规范 选择合适的字段类型 主键设计合理 选择合适的字段长度 优先考虑逻辑删除,而不是物理删除 每个表必备的几个字段(如 create_time
和update_time
等)一张表的字段不宜过多 尽可能使用 not null
定义字段设计表时,评估哪些字段需要加索引 不需要严格遵守 3NF
,通过业务字段冗余来减少表关联避免使用 MySQL
保留字不搞外键关联,一般都在代码维护 一般都选择 INNODB
存储引擎选择合适统一的字符集。 如果你的数据库字段是枚举类型的,需要在 comment
注释清楚时间类型选择恰当 不建议使用 Stored procedure
(包括存储过程,触发器) 。1:N
关系的设计大字段如何设计 考虑是否需要分库分表 索引的合理设计
我之前写过,做表的设计时,需要考虑哪些点,大家可以看下哈:21个MySQL表设计的经验准则
2.什么是三范式?你做过违反三范式的设计嘛
第一范式:对属性的原子性,要求属性具有原子性,不可再分解; 第二范式:对记录的唯一性,要求记录有唯一标识,即实体的唯一性,即不存在部分依赖; 第三方式:对字段的冗余性,要求任何字段不能由其他字段派生出来,它要求字段没有冗余,即不存在传递依赖。
我们设计表及其字段之间的关系, 应尽量满足第三范式。但是有时候,可以适当冗余,来提高效率
3. TCP的四次挥手?三次挥手行不行
第一次挥手( FIN=1,seq=u
),发送完毕后,客户端进入FIN_WAIT_1
状态第二次挥手( ACK=1,ack=u+1,seq =v
),发送完毕后,服务器端进入CLOSE_WAIT
状态,客户端接收到这个确认包之后,进入FIN_WAIT_2
状态第三次挥手( FIN=1,ACK=1,seq=w,ack=u+1
),发送完毕后,服务器端进入LAST_ACK
状态,等待来自客户端的最后一个ACK
。第四次挥手( ACK=1,seq=u+1,ack=w+1
),客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入TIME_WAIT
状态,等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime
)之后,没有收到服务器端的ACK
,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入CLOSED
状态。服务器端接收到这个确认包之后,关闭连接,进入CLOSED
状态。
TCP为什么需要四次挥手?三次行不行呢?
举个生活的例子吧,假设小明和小红打电话聊天,通话差不多要结束时:
小红说,“我没啥要说的了”。小明回答,“我知道了”。但是小明可能还有要说的话,小红不能要求小明跟着自己的节奏结束通话,于是小明可能又叽叽歪歪说了一通,最后小明说“我说完了”,小红回答“知道了”,这样通话才算结束。
4. 进程线程的区别,打开迅雷是开了个进程嘛。
进程是运行中的应用程序,线程是进程的内部的一个执行序列 进程是资源分配的最小单位,线程是CPU调度的最小单位。 一个进程可以有多个线程。线程又叫做轻量级进程,多个线程共享进程的资源 进程间切换代价大,线程间切换代价小 进程拥有资源多,线程拥有资源少地址 进程是存在地址空间的,而线程本身无地址空间,线程的地址空间是包含在进程中的举个例子:
你打开QQ,开了一个进程;打开了迅雷,也开了一个进程。
在QQ的这个进程里,传输文字开一个线程、传输语音开了一个线程、弹出对话框又开了一个线程。
所以运行某个软件,相当于开了一个进程。在这个软件运行的过程里(在这个进程里),多个工作支撑的完成QQ的运行,那么这“多个工作”分别有一个线程。
所以一个进程管着多个线程。
通俗的讲:“进程是爹妈,管着众多的线程儿子”...
5. 进程是如何通讯的?
进程间的通信方式有这几种:
管道 消息队列 共享内存 信号量 信号
每个进程的用户地址空间都是相互独立、不能互相访问的。而内核空间则是每个进程都共享的,因此进程之间要通信必须通过内核。
管道:它的本质是内核里面的一串缓存。它传输数据是单向的,这种通信方式效率低,不适合进程间频繁地交换数据。比如我们写 linux
命令时,ps -ef | grep java
这个「|」
竖线就是一个匿名管道。消息队列:它是保存在内核中的消息链表。消息的发送方和接收方要约定好消息体的数据类型。有了消息队列,两个进程之间的通信就像平时发邮件一样,你来一封我一封。但是它也有不足,通信不及时,二是附件也有大小限制。 共享内存:就是拿出一块虚拟地址空间来,映射到相同的物理内存中,节省了用户态与内核态之间切换的开销。 信号量:它其实是一个整型的计数器,主要用于实现进程间的互斥与同步,而不是用于缓存进程间通信的数据。为了防止多进程竞争共享资源,而造成的数据错乱。 信号:是进程间通信机制中唯一的异步通信机制,因为可以在任何时候发送信号给某一进程 Socket:如果想跨网络与不同主机上的进程之间通信,需要socket。
大家可以参考下这篇文章哈:张三同学没答好「进程间通信」,被面试官挂了
6.什么是零拷贝?零拷贝实现的几种方式?哪些中间件应用了零拷贝技术?
零拷贝是指计算机执行IO操作时,CPU不需要将数据从一个存储区域复制到另一个存储区域,从而可以减少上下文切换以及CPU的拷贝时间。它是一种I/O操作优化技术。
零拷贝实现的方式主要有这三种:
mmap+write
sendfile
带有 DMA
收集拷贝功能的sendfile
Kafka为什么快等,也跟零拷贝技术有关。
我之前写过一篇零拷贝技术的文章,收到了很多读者好评,大家可以看下哈,看一遍就理解:零拷贝详解
7. 你如何设计分布式锁?有哪些坑?
大家可以看下我之前的这几篇文章哈:
8. Redis跳表
跳跃表是有序集合 zset
的底层实现之一跳跃表支持平均 O(logN)
,最坏O(N)
复杂度的节点查找,还可以通过顺序性操作批量处理节点。跳跃表实现由 zskiplist
和zskiplistNode
两个结构组成,其中zskiplist
用于保存跳跃表信息(如表头节点、表尾节点、长度),而zskiplistNode
则用于表示跳跃表节点。跳跃表就是在链表的基础上,增加多级索引提升查找效率。
9. 你平时是如何优化慢SQL的
数据库慢查询主要有这些原因
如果是SQL没加索引,那就加恰当的索引 如果 SQL 索引不生效,那就关注索引失效的十种经典场景(如不满足最左匹配原则等) 关注limit深分页问题(标签记录法和延迟关联法) 单表数据量太大(那就分库分表) join 或者子查询过多(尽量不要有超过3个以上的表连接,而且关联的字段需要加索引) in元素过多 (in元素查询数量做限制) 数据库在刷脏页 order by 走文件排序 拿不到锁 delete + in子查询不走索引!
详细讲解,大家可以看下我之前这篇文章哈:盘点MySQL慢查询的12个原因
10.十亿个数字里里面找最小的10个
这是一道经典的TopK问题,可以使用分治法+快速排序原理解决。直接上代码
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if(arr==null||arr.length==0){
return null;
}
// k大于arr数组,直接返回arr
if(k >= arr.length) return arr;
int low=0,high=arr.length-1;
quick(arr, low, high, k);
//将前K个最大的元素返回
return Arrays.copyOf(arr,k);
}
void quick(int[] arr, int low, int high, int k) {
if (low < high) {
int pivot = partition(arr, low, high);
//pivot刚好等于k-1的话,那0~k-1就是要求的top k
if (pivot == k - 1) {
return;
}
//pivot还是大于k-1的话,还需要high指针左移,因此high=pivot - 1
if (pivot > k - 1) {
quick(arr, low, pivot - 1, k);
}else {
//pivot<=k - 1的话,需要low指针右移,因此low=pivot + 1
quick(arr, pivot + 1, high, k);
}
}
}
private int partition(int[] arr,int low,int high){
//取arr[low]作为枢纽元素pivot
int pivot=arr[low];
while(low<high){
//右边找到比pivot小的
while(low<high&&arr[high]>=pivot){
high--;
}
//交换
arr[low]=arr[high];
//左边找到比pivot大的
while(low<high&&arr[low]<=pivot){
low++;
}
//交换
arr[high]=arr[low];
}
//枢纽元素归位
arr[low]=pivot;
return low;
}
}
最后
本文介绍了字节国际支付十连问。如果对你有帮助,麻烦给田螺哥一个三连(点赞、在看、转发)。如果你觉得答案哪些不全对的话,可以加我好友,一起讨论哈。大家一起加油!