⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 捡田螺的小男孩 「捡田螺的小男孩」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

前言

之前有位读者去字节面试,面的是国际支付部门,他凭记忆,回忆被问到的一些面试真题。于是,我整理了比较全的答案,希望对大家找工作有帮助呀,加油~

1. 聊聊工作中,你是如何设计数据库表的

  • 命名规范
  • 选择合适的字段类型
  • 主键设计合理
  • 选择合适的字段长度
  • 优先考虑逻辑删除,而不是物理删除
  • 每个表必备的几个字段(如create_timeupdate_time等)
  • 一张表的字段不宜过多
  • 尽可能使用not null定义字段
  • 设计表时,评估哪些字段需要加索引
  • 不需要严格遵守3NF,通过业务字段冗余来减少表关联
  • 避免使用MySQL保留字
  • 不搞外键关联,一般都在代码维护
  • 一般都选择INNODB存储引擎
  • 选择合适统一的字符集。
  • 如果你的数据库字段是枚举类型的,需要在comment注释清楚
  • 时间类型选择恰当
  • 不建议使用Stored procedure(包括存储过程,触发器) 。
  • 1:N关系的设计
  • 大字段如何设计
  • 考虑是否需要分库分表
  • 索引的合理设计

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)复杂度的节点查找,还可以通过顺序性操作批量处理节点。
  • 跳跃表实现由zskiplistzskiplistNode两个结构组成,其中zskiplist用于保存跳跃表信息(如表头节点、表尾节点、长度),而zskiplistNode则用于表示跳跃表节点。
  • 跳跃表就是在链表的基础上,增加多级索引提升查找效率。

9. 你平时是如何优化慢SQL的

数据库慢查询主要有这些原因

  • 如果是SQL没加索引,那就加恰当的索引
  • 如果 SQL 索引不生效,那就关注索引失效的十种经典场景(如不满足最左匹配原则等)
  • 关注limit深分页问题(标签记录法和延迟关联法
  • 单表数据量太大(那就分库分表
  • join 或者子查询过多(尽量不要有超过3个以上的表连接,而且关联的字段需要加索引
  • in元素过多 (in元素查询数量做限制
  • 数据库在刷脏页
  • order by 走文件排序
  • 拿不到锁
  • delete + in子查询不走索引!

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;
}
}

最后

本文介绍了字节国际支付十连问。如果对你有帮助,麻烦给个三连(点赞、在看、转发)。

文章目录
  1. 1. 前言
  2. 2. 1. 聊聊工作中,你是如何设计数据库表的
  3. 3. 2.什么是三范式?你做过违反三范式的设计嘛
  4. 4. 3. TCP的四次挥手?三次挥手行不行
  5. 5. 4. 进程线程的区别,打开迅雷是开了个进程嘛。
  6. 6. 5. 进程是如何通讯的?
  7. 7. 6.什么是零拷贝?零拷贝实现的几种方式?哪些中间件应用了零拷贝技术?
  8. 8. 7. 你如何设计分布式锁?有哪些坑?
  9. 9. 8. Redis跳表
  10. 10. 9. 你平时是如何优化慢SQL的
  11. 11. 10.十亿个数字里里面找最小的10个
  12. 12. 最后