后端面试真题整理

面试问题整理

本人主要记录2024年秋招、春招过程中的疑难八股真题,参考来源:牛客网、知乎等。

八股

  1. 深拷贝与浅拷贝
  • 浅拷贝: 浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。
  • 深拷贝: 深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。

https://blog.51cto.com/u_16099325/7762799

链接中有深拷贝与浅拷贝的实现代码:

  • map实现深拷贝与浅拷贝:

    ####################浅拷贝#####################
    Map<String,String> map = new HashMap<>();
    Map<String,String> map1 = map;
    map.put("a","A");
    System.out.println(map);
    System.out.println(map1);
    // map1复制的是对象map的引用,map1和map指向的是同一个对象。这里我们可以通过System.identityHashCode(Object obj)来返回对象内存地址转化后的hashcode,之所以用不直接用map.hashCode(),是因为HashMap的实现类重写了hashCode方法,返回的值不子再是对象内存地址转化的hashcode了。
    
    ####################深拷贝#####################
    Map<String,String> map = new HashMap<>();
    Map<String,String> map1 = new HashMap<>();
    map.put("a","A");
    map1.putAll(map);
    System.out.println(map);
    System.out.println(map1);
    System.out.println(System.identityHashCode(map));
    System.out.println(System.identityHashCode(map1));
    //这里map1通过new初始化,并将map通过putAll()方法将map的key和value复制到map1,所以这里输出时虽然map和map1的内容都是{a=A},但map和map1的内存地址对应的hashCode却不相同,二者在内存中不是同一对象。putAll通过循环map的entrySet调用map1的put()方法将key和value赋值给map1。经过putAll,map1只是对map的key和value分别进行了对象浅拷贝。如下,通过输出结果发现map和map1中的key和value的内存地址都是一致的。
    
    

在这里插入图片描述

  1. HTTP 与 HTTPS 的区别?HTTPS的加密过程?
HTTPHTTPS
端口80443
安全性无加密,安全性较差有加密机制,安全性较高
资源消耗较少由于加密处理,资源消耗更多
是否需要证书不需要需要
协议运行在TCP协议之上运行在SSL协议之上,SSL运行在TCP协议之上
  1. 有哪些非对称加密算法,哪些对称加密算法?

    • 对称加密即加密与解密所使用的密钥是同一个,常见的有(AES/DES)
    • 非对称加密是使用公钥加密,私钥解密,如RSA算法。
  2. HTTP 请求头有哪些内容?

    HTTP 报文是在客户端和服务器之间通过 HTTP 协议进行通信时传输的数据格式。HTTP 报文包括请求报文和响应报文两种类型。HTTP 请求报文是由客户端(如浏览器)发送给服务器的,而 HTTP 响应报文是由服务器发送给客户端的。

    请求头部和响应头部是 HTTP 报文的一部分,它们包含了一系列键值对,用于描述和控制报文的传输。这些键值对提供了关于客户端、服务器、请求或响应的元数据,以及有关如何处理报文主体的指示。HTTP 报文的基本结构如下:

    请求报文:

    请求行:包含 HTTP 方法(如 GET、POST、PUT、DELETE 等)、请求的资源的 URI 和 HTTP 协议的版本。
    请求头部:包含一系列键值对,用于描述客户端和请求的信息,例如 User-Agent、Accept、Content-Type、Authorization 等。
    空行:用于分隔请求头部和请求主体。
    请求主体(可选):包含请求相关的数据,例如在 POST 或 PUT 请求中发送的表单数据或 JSON 数据。
    响应报文:

    状态行:包含 HTTP 协议的版本、状态码(如 200、404、500 等)和状态消息(如 “OK”、“Not Found”、“Internal Server Error” 等)。
    响应头部:包含一系列键值对,用于描述服务器和响应的信息,例如 Server、Content-Type、Content-Length、Cache-Control 等。
    空行:用于分隔响应头部和响应主体。
    响应主体:包含响应相关的数据,例如 HTML、CSS、JavaScript、JSON 数据或图像等。

  3. TCP 和 UDP 的区别;TCP 三握手;TCP 四挥手;

  4. 怎么构建可靠传输的 UDP?

​ UDP是一种面向无连接的传输协议,不提供可靠性保证。然而,你可以在UDP的基础上实现一些机制来增加可靠性。以下是一些常用的方法:

​ 应用层确认机制:在应用层上,可以实现自定义的确认机制。发送方在发送数据后等待接收方的确认消息,如果在一定时间内未收到确认,则重新发送数据。这样可以确保数据的可靠传输。
​ 数据校验和重传:在UDP数据包中添加校验和字段,接收方在接收数据时计算校验和并与发送方的校验和进行比较。如果不匹配,则要求发送方重新发送数据。
​ 序列号和确认号:类似于TCP协议的序列号和确认号机制,发送方给每个数据包分配一个唯一的序列号,接收方收到数据后发送确认消息,并在其中包含确认号。发送方根据确认号判断哪些数据包已经被成功接收,可以进行相应的重传。
​ 超时重传:发送方可以设置一个超时计时器,如果在指定时间内未收到确认消息,则认为数据丢失,触发重传操作。
​ 流量控制和拥塞控制:通过控制发送数据的速率和接收数据的处理速度,可以避免网络拥塞和数据丢失。这可以通过动态调整发送速率、使用滑动窗口等方法来实现。

  1. Java中常用的List有哪些?
  • ArrayList:基于数组实现。每次增删都会形成新的数组,但数组有索引。优点:查询快;缺点:增加、删除慢、线程不安全。
  • LinkedList:基于链表(双向)实现。每个元素存储本身内存地址的同时,还会存储下一个元素的地址。优点:增加、删除快;缺点:查询慢、线程不安全。
  • Vector:基于数组实现。Vector和ArrayList用法上几乎相同,但Vector比较古老,一般不用。优点:线程安全;缺点:效率低。采用同步方法(synchronized)实现线程安全。
  • CopyOnWriteArrayList:是线程安全的List实现,通过对底层数组进行复制来实现线程安全。读操作不会阻塞,而写操作会创建一个新的数组进行修改,确保写操作不影响读操作。
  1. COW机制:
  • Copy on Write (COW) 是一种在并发编程中常用的技术,它可以在不使用锁的情况下,实现对共享数据的并发访问。在 Java 中,Copy on Write 通常用于对 List、Map 等集合进行并发访问。
  • 基本思想:当需要修改某个共享数据时,先将原始数据复制一份,并在副本上进行修改,修改完成后再将副本的引用赋值给原始数据的引用。当一个程序退出时,它所修改的部分会被保留下来,其他程序仍然可以访问它们。这样做的好处是,当多个线程同时读取共享数据时,它们各自持有原始数据的引用,不会发生互相干扰的情况;而在需要修改共享数据时,只有一个线程在修改,其他线程在读取,因此也不需要加锁。
  • 优点:COW技术的主要优点是节省内存和提高效率。由于只有一个副本需要被修改,所以它比传统的复制算法更节省内存。此外,由于其他程序可以继续访问原始的共享内存,所以COW技术也提高了程序的效率。
  • 缺点:COW技术的主要缺点是可能会导致数据不一致的问题。如果一个程序修改了共享内存,但另一个程序没有正确地复制该内存区域,那么后者可能会读取到错误的数据。为了解决这个问题,通常需要使用一些同步机制来确保所有程序都正确地处理共享内存。
  1. 反射是什么?如何获取?有什么使用场景?
  • 定义:反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
  • 获取方式:
    • Class.forName(“类的路径”);当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。
    • 类名.class。这种方法只适合在编译前就知道操作的 Class。
    • 对象名.getClass()。
  • 应用:JDBC 的数据库的连接Spring 框架的使用,最经典的就是xml的配置模式
  1. kafka高吞吐原因?
  • 页缓存技术:Kafka是基于操作系统的页缓存来实现写入的。操作系统本身有一层缓存,叫做page cache,是在内存里的缓存,我们也可以称之为 os cache,意思就是操作系统自己管理的缓存。Kafka在写入磁盘文件的时候,可以直接写入到这个os cache里,也就是仅仅写入到内存中,接下来由操作系统自己决定什么时候把os cache里的数据真的刷入磁盘文件中。这样可以很大提升写性能。
  • 磁盘顺序写:Kafka写数据的时候,Kafka的消息是不断追加到文件末尾的,而不是在文件的随机位置写入数据,这个特性使Kafka可以充分利用磁盘的顺序读写性能。顺序读写不需要磁盘磁头的寻道时间,避免了随机磁盘寻址的浪费,只需很少的扇区旋转时间,所以速度远快于随机读写。
  • 零拷贝:在消费数据的时候,实际上要从kafka的磁盘文件里读取某条数据然后发送给下游的消费者。Kafka在读数据的时候为了避免多余的数据拷贝,使用了零拷贝技术。也就是说直接让os cache里的数据发送到网卡后然后传输给下游的消费者,跳过中间从os cache拷贝到kafka 进程缓存和再拷贝到socket缓存中的两次缓存,同时也减少了上下文切换。
  1. 零拷贝?
  • 定义:零拷贝是指计算机执行IO操作时,CPU不需要将数据从一个存储区域复制到另一个存储区域,从而可以减少上下文切换以及CPU的拷贝时间。它是一种I/O操作优化技术。

  • 传统的IO流程:在这里插入图片描述

  • mmap流程:在这里插入图片描述
    mmap是将读缓冲区的地址和用户缓冲区的地址进行映射,内核缓冲区和应用缓冲区共享,所以节省了一次CPU拷贝‘’并且用户进程内存是虚拟的,只是映射到内核的读缓冲区,可以节省一半的内存空间。

  • sendfile实现的零拷贝:在这里插入图片描述

https://mp.weixin.qq.com/s/KbMTFu68A5ivJAy4tr5vdg

  1. mysql索引,int(11)和int(10)区别
  • 因为int(10)或int(11)显示宽度都已经能涵盖了int整个取值范围了,所以int(10),int(11)只是一个显示宽度的问题,其所能存储的范围是一样的。如果字段没设定zerofill的话,没有任何差别!
  1. 操作系统中虚拟内存的好处?
  • 定义:虚拟内存(Virtual Memory) 是计算机系统内存管理非常重要的一个技术,本质上来说它只是逻辑存在的,是一个假想出来的内存空间,主要作用是作为进程访问主存(物理内存)的桥梁并简化内存管理。
  • 隔离进程:物理内存通过虚拟地址空间访问,虚拟地址空间与进程一一对应。每个进程都认为自己拥有了整个物理内存,进程之间彼此隔离,一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。
  • 提升物理内存利用率:有了虚拟地址空间后,操作系统只需要将进程当前正在使用的部分数据或指令加载入物理内存。
  • 简化内存管理:进程都有一个一致且私有的虚拟地址空间,程序员不用和真正的物理内存打交道,而是借助虚拟地址空间访问物理内存,从而简化了内存管理。
  • 多个进程共享物理内存:进程在运行过程中,会加载许多操作系统的动态库。这些库对于每个进程而言都是公用的,它们在内存中实际只会加载一份,这部分称为共享内存。
  • 提高内存使用安全性:控制进程对物理内存的访问,隔离不同进程的访问权限,提高系统的安全性。
  • 提供更大的可使用内存空间:可以让程序拥有超过系统物理内存大小的可用内存空间。这是因为当物理内存不够用时,可以利用磁盘充当,将物理内存页(通常大小为 4 KB)保存到磁盘文件(会影响读写速度),数据或代码页会根据需要在物理内存与磁盘之间移动。
  1. kafka producer发送数据,ack为0,1,-1分别是什么意思?
  • 1(默认) 数据发送到Kafka后,经过leader成功接收消息的的确认,就算是发送成功了。在这种情况下,如果leader宕机了,则会丢失数据。
  • 0 生产者将数据发送出去就不管了,不去等待任何返回。这种情况下数据传输效率最高,但是数据可靠性确是最低的。
  • -1producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。当ISR中所有Replica都向Leader发送ACK时,leader才commit,这时候producer才能认为一个请求中的消息都commit了。
  1. Kafka消费者的ack没回复给Kafka服务器造成消息不完整,怎么解决?
  • 消费端丢消息最主要体现在消费端offset的自动提交,如果开启了自动提交,万一消费到数据还没处理完,此时你consumer直接宕机了,未处理完的数据 丢失了,下次也消费不到了,因为offset已经提交完毕,下次会从offset出开始消费新消息。

  • 解决办法是采用消费端的手动提交

  1. 如果Kafka的主节点挂掉了,会怎样?
  • Kafka副本当前分为领导者副本和追随者副本。只有Leader副本才能对外提供读写服务,响应Clients端的请求。Follower副本只是采用拉(PULL)的方式,被动地同步Leader副本中的数据,并且在Leader副本所在的Broker宕机后,随时准备应聘Leader副本。
  1. 索引的数据结构(B+树)
  2. 说一下索引失效的场景:
  • like前缀命中,如 xx% ,而%xx%,%xx 无法命中索引
  • or 应该无法命中索引
  • 在where语句中 使用!= 或< >这种操作符,无法命中索引
  • where语句中,做null值判断,无法命中
  • 组合索引的话,如(a,b,c)上建立组合索引,则a|(a,b)|(a,b,c)能命中索引(最左匹配原则), 比如你select * from table where b=‘xx’ , 是无法命中索引的
  • where语句中,对查询条件做±*/,无法命中索引。
  1. 怎样查看SQL执行策略(explain):

在这里插入图片描述

  • id 选择标识符,id 越大优先级越高,越先被执行;
  • select_type — 表示查询的类型;
  • table — 输出结果集的表;
  • partitions — 匹配的分区;
  • type — 查询执行的类型,描述了查询是如何执行的。所有值的顺序从最优到最差排序为:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
  • possible_keys — 表示查询时,可能使用的索引;
  • key — 表示实际使用的索引;
  • key_len — 索引字段的长度;
  • ref — 列与索引的比较;
  • rows — 大致估算出找到所需的记录或所需读取的行数,数值越小越好
  • filtered — 按表条件过滤的行百分比;
  • Extra — 执行情况的描述和说明。外部的索引排序、覆盖索引、创建临时表
  1. Redis数据类型有哪些,说一下底层实现?

​ Redis 共有 5 种基本数据类型:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。

​ 这 5 种数据类型是直接提供给用户使用的,是数据的保存形式,其底层实现主要依赖这 8 种数据结构:简单动态字符串(SDS)、LinkedList(双向链表)、Dict(哈希表/字典)、SkipList(跳跃表)、Intset(整数集合)、ZipList(压缩列表)、QuickList(快速列表)。

参考链接:

跳跃表是一种随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳跃表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。跳跃表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

redis跳跃表结点怎么实现的:在这里插入图片描述

  1. Redis的缓存策略有哪些?
  2. MYSQL的事务以及特性?
  3. MySQL的隔离级别?
  4. 数据库的索引类别?
  • 普通索引(NORMAL)
  • 唯一索引(UNIQUE)
  • 主键索引 (PRIMARY)
  • 组合索引
  • 全文索引(FULLTEXT)
  1. redis设置过期时间?
    EXPIRE <KEY> <TTL> : 将键的生存时间设为 ttl 秒
    PEXPIRE <KEY> <TTL> :将键的生存时间设为 ttl 毫秒
    EXPIREAT <KEY> <timestamp> :将键的过期时间设为 timestamp 所指定的秒数时间戳
    PEXPIREAT <KEY> <timestamp>: 将键的过期时间设为 timestamp 所指定的毫秒数时间戳.
    PERSIST 命令用于移除给定 key 的过期时间,使得 key 永不过期。
  1. redis大量数据过期怎么办(缓存雪崩)并讲解决方案

​ 1、事前:

  • 均匀过期:设置不同的过期时间,让缓存失效的时间尽量均匀,避免相同的过期时间导致缓存雪崩,造成大量数据库的访问。如把每个Key的失效时间都加个随机值,setRedis(Key,value,time + Math.random() * 10000);,保证数据不会在同一时间大面积失效。
  • 分级缓存:第一级缓存失效的基础上,访问二级缓存,每一级缓存的失效时间都不同。
  • 热点数据缓存永远不过期。永不过期实际包含两层意思:
    • 物理不过期,针对热点key不设置过期时间
    • 逻辑过期,把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建
  • 保证Redis缓存的高可用,防止Redis宕机导致缓存雪崩的问题。可以使用 主从+ 哨兵,Redis集群来避免 Redis 全盘崩溃的情况。

​ 2、事中:

  • 互斥锁:在缓存失效后,通过互斥锁或者队列来控制读数据写缓存的线程数量,比如某个key只允许一个线程查询数据和写缓存,其他线程等待。这种方式会阻塞其他的线程,此时系统的吞吐量会下降
  • 使用熔断机制,限流降级。当流量达到一定的阈值,直接返回“系统拥挤”之类的提示,防止过多的请求打在数据库上将数据库击垮,至少能保证一部分用户是可以正常使用,其他用户多刷新几次也能得到结果。

​ 3、事后:

​ 开启Redis持久化机制,尽快恢复缓存数据,一旦重启,就能从磁盘上自动加载数据恢复内存中的数据。

  1. HTTP1和HTTP2的区别:
  • 新的二进制格式:HTTP1.1的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
  • 多路复用,即连接共享,即每一个request都是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。
  • 头部压缩,HTTP1.1的头部(header)带有大量信息,而且每次都要重复发送;HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
  • 服务端推送:服务器除了对最初请求的响应外,服务器还可以额外的向客户端推送资源,而无需客户端明确的请求。
  1. 操作系统的内存管理? 缺页中断?TLB?

https://mp.weixin.qq.com/s/miq1GTn9xn9oKY-fYBiYDQ

  • 进程切换时需要刷新TLB并获取新的地址空间,然后切换硬件上下文和内核栈,线程切换时只需要切换硬件上下文和内核栈。(TLB: 转译后备缓冲区)
  1. 进程的通信方式:

在这里插入图片描述

  1. Linux文件系统:
  • / -根目录,整个文件系统层次结构的根目录,所有内容都位于此目录下。
  • /bin -存放基本的可执行的程序(二进制文件),包括最基本的命令,如ls和cp。
  • /boot -包含内核引导加载程序文件。
  • /etc -核心系统配置目录,应该只保存配置文件。
  • /home -用户的主目录,保存你的文档,文件,设置等。
  • /lib、/lib32、/lib64、/libx32 -主要目的是存放特定的库,这些库是在/bin和/sbin目录里的工具所需要的库,/lib中的库可以是32位或64位
  • /lost+found -这个目录一般情况下是空的,当系统非法关机后,如果你丢失了一些文件,在这里能找回来,通常很少用到此目录
  • /root -root用户的主目录。
  • /tmp -临时文件的存储
  1. 断开连接中CLOSE-WAIT状态在哪一方?场景:在服务器端有大量连接出现了CLOSE-WAIT状态,可能是什么原因?
  • 服务端接收到断开连接的请求以后,服务端同意断开连接并发送确认应答ACK。发送完ACK以后,Server端变为CLOSE_WAIT状态,CLOSE_WAIT状态表明四次挥手没有完全走完,或者说连接还没有完全断开。服务端状态:CLOSE_WAIT(连接还没有完全断开,等待服务端主动调用close函数断开连接)
  • 但是如果服务器端不执行 socket 的 close() 操作,那么就没法进入 LAST_ACK, 导致大量连接处于 CLOSE_WAIT 状态。所以,如果服务器出现了大量CLOSE_WAIT状态,一般是程序 Bug,或者关闭 socket 不及时。
  1. 一条Update语句执行过程:

具体更新一条记录的流程如下:

在这里插入图片描述

  1. 观察者和发布订阅的区别:观察者模式中观察者和目标直接进行交互,而发布订阅模式中统一由调度中心进行处理,订阅者和发布者互不干扰。这样一方面实现了解耦,还有就是可以实现更细粒度的一些控制。比如发布者发布了很多消息,但是不想所有的订阅者都接收到,就可以在调度中心做一些处理,类似于权限控制之类的。还可以做一些节流操作。
  2. cookie,session,localstorage区别:
  • Cookie:cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下,存储的大小很小只有4K左右。(key:可以在浏览器和服务器端来回传递,存储容量小,只有大约4K左右)
  • sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持,localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。(key:本身就是一个回话过程,关闭浏览器后消失,session为一个回话,当页面不同即使是同一页面打开两次,也被视为同一次回话)
  • localStorage:localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。(key:同源窗口都会共享,并且不会失效,不管窗口或者浏览器关闭与否都会始终生效)
  1. 常见状态码
  2. 浏览器跨域原因及处理办法
  • 原因:同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能不能使用。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
  • 解决:CORS 跨域资源分享
  1. ConcurrentHashMap原理
  2. synchronized做的优化手段有哪些:锁膨胀
  3. 线程池的核心参数
  4. 协程与线程的区别:

​ 协程是一种轻量级的用户态线程,它们允许在单个线程内实现多个协程的并发执行。协程在执行过程中可以主动挂起和恢复,这使得编写高效的异步代码变得更加容易。协程通常用于处理I/O密集型任务,能够提高程序的响应性能。

​ 协程的特点包括:

​ 用户态线程:协程不依赖于操作系统的线程管理,由程序员手动控制。

​ 轻量级:协程切换的开销非常小,适用于高并发的场景。

​ 高度可控性:程序员可以精确控制协程的执行流程。

  1. TCP 和 http 的长连接 keepAlive 是一个东西吗?

如何开启长连接要开启HTTP的长连接,需要在HTTP协议层面进行设置,并且不需要显式地开启TCP层的Keep-Alive。在HTTP/1.1中,默认情况下,连接是持久化的,也就是说,客户端和服务器之间的连接会保持打开状态,以便在同一个连接上发送多个请求和响应。这样可以减少连接的建立和关闭的开销,提高性能。要开启HTTP的长连接,可以通过以下方式:在HTTP请求头中添加"Connection: keep-alive"字段。这告诉服务器要保持连接打开。在HTTP响应头中添加"Connection: keep-alive"字段。这告诉客户端要保持连接打开。在服务器端,可以通过配置服务器软件(如Apache、Nginx等)来设置长连接的超时时间,以控制连接的保持时间。需要注意的是,虽然HTTP的长连接可以提高性能,但长时间保持连接也会占用服务器资源。因此,在实际应用中,需要根据具体的业务需求和服务器的负载情况来合理设置长连接的超时时间。至于TCP层的Keep-Alive,它是一种TCP协议的机制,用于检测连接是否仍然有效。在HTTP的长连接中,默认情况下,浏览器和服务器会自动发送TCP层的Keep-Alive探测包,以保持连接的有效性。因此,不需要显式地开启TCP层的Keep-Alive。

  1. 一致性哈希算法:保证当机器增加或者减少时,节点之间的数据迁移只限于两个节点之间,不会造成全局的网络问题。哈希环
  2. 为什么有timewait状态?
  • 可靠终止TCP连接。如果最后一个ACK报文因为网络原因被丢弃,此时server因为没有收到ACK而超时重传FIN报文,处于TIME_WAIT状态的client可以继续对FIN报文做回复,向server发送ACK报文。
  1. 中间人攻击:中间人攻击对通信又是一大打击,不过对于数据传输的安全性,还是有办法可以得到解决——引入证书。证书可以保障服务器与客户机通信不受任何外界因素影响,极大的保障网络通信的安全性和双方的身份和信息安全。

在这里插入图片描述

  1. 查看机器上所有占用的端口:netstat -tuln

  2. #{}防止 SQL 注入原理:
    MyBatis的#{}之所以能够预防SQL注入是因为底层使用了PreparedStatement类的setString()方法来设置参数,此方法会获取传递进来的参数的每个字符,然后进行循环对比,如果发现有敏感字符(如:单引号、双引号等),则会在前面加上一个’/'代表转义此符号,让其变为一个普通的字符串,不参与SQL语句的生成,达到防止SQL注入的效果。

  3. 线程同步?

    1. 互斥锁(Mutex):采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。比如 Java 中的 synchronized 关键词和各种 Lock 都是这种机制。
    2. 读写锁(Read-Write Lock):允许多个线程同时读取共享资源,但只有一个线程可以对共享资源进行写操作。
    3. 信号量(Semaphore):它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。
    4. 屏障(Barrier):屏障是一种同步原语,用于等待多个线程到达某个点再一起继续执行。当一个线程到达屏障时,它会停止执行并等待其他线程到达屏障,直到所有线程都到达屏障后,它们才会一起继续执行。比如 Java 中的 CyclicBarrier 是这种机制。
    5. 事件(Event) :Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作。
  4. 无网线能ping通127.0.0.1吗?能,拔掉计算机网线仍然可以ping通127.0.0.1的原因是因为127.0.0.1是一个特殊的IP地址,被称为环回地址,是计算机本身的一个虚拟接口。当我们使用ping命令ping 127.0.0.1时,计算机会将数据包发送给自己的网络接口,因此不需要依赖于物理网络连接。

  5. HashMap和TreeMap的区别

  • HashMap是通过hash值进行快速查找的;HashMap中的元素是没有顺序的;TreeMap中所有的元素都是有某一固定顺序的,如果需要得到一个有序的结果,就应该使用TreeMap。

  • HashMap和TreeMap都是线程不安全的;

  • HashMap继承AbstractMap类;覆盖了hashcode() 和equals() 方法,以确保两个相等的映射返回相同的哈希值;TreeMap继承SortedMap类;他保持键的有序顺序;

  • HashMap:基于hash表实现的;使用HashMap要求添加的键类明确定义了hashcode()和equals() (可以重写该方法);为了优化HashMap的空间使用,可以调优初始容量和负载因子;TreeMap:基于红黑树实现的;TreeMap就没有调优选项,因为红黑树总是处于平衡的状态;

  • HashMap:适用于Map插入,删除,定位元素;TreeMap:适用于按自然顺序或自定义顺序遍历键(key)

  1. 红黑树的性质:一颗自平衡的二叉搜索树

​ 性质1:每个节点要么是黑色,要么是红色。
​ 性质2:根节点是黑色。
​ 性质3:每个叶子节点(NIL)是黑色。
​ 性质4:每个红色结点的两个子结点一定都是黑色。
​ 性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。

  1. TCP连接服务端主动关闭会出现什么情况?

​ 如果是服务端主动发起关闭,此时四次挥手的顺序会颠倒。那么此时客户端再向服务端发送数据时,根据TCP协议的规定,认为它是一个异常终止连接,客户端将会收到一个RST复位响应(而不是ACK响应)如果客户端再次向服务端发送数据,系统将会发送一个SIGPIPE信号给客户端进程,告诉客户端进程该连接已关闭,不要再写了。系统给SIGPIPE信号的默认处理是直接终止收到该信号的进程,所以此时客户端进程会被极不情愿地终止。

  1. SSL/TLS 协议位于网络 OSI 七层模型的会话层,用来加密通信
  2. TCP粘包和拆包以及怎么处理:
  • 原因:TCP 作为传输层协议并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的划分,所以在业务上认为是一个完整的包,可能会被 TCP 拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就会出现粘包拆包的问题。

    例如,TCP缓冲区是1024个字节大小,如果应用一次请求发送的数据量比较小,没达到缓冲区大小,TCP则会将多个请求合并为同一个请求进行发送,站在业务上来看这就是「粘包」;

    如果应用一次请求发送的数据量比较大,超过了缓冲区大小,TCP就会将其拆分为多次发送,这就是「拆包」,也就是将一个大的包拆分为多个小包进行发送。

  • 解决:TCP 是面向流的,会发生粘包和拆包,那作为应用程序,如何从这源源不断涌来的数据流中拆分出或者合并出有意义的信息呢?通常会有以下一些常用的方法:

    (1)发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。

    (2)发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。

    (3)可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。

  1. oauth协议
  2. icmp-ping

算法

  1. 无序数组中获取第k个最大数:

​ 方法1:排序法
​ 对数组进行排序,如果是正序,则取索引为 [length-k] 的值;如果是倒叙,则取索引为 [k-1] 的值。

​ 方法2:选择排序法
​ 将数组分为已排序和未排序两部分,每次从未排序中选择最大的元素,放到已排序部分的最末位置,只需进行k次操作,便可得到第k个大的元素,即索引为 [k-1] 的值。

​ 方法3:分治法(借助快排思想)
​ 从数组中随机选择一个数,将大于等于该数的放到该数的左侧,小于该数的放到该数的右侧。假设该数在调整之后的数组中的索引为pivot。如果k-1 == pivot,则第k个最大元素的值为nums[pivot];如果k-1 > pivot,则根据上述方法,在 [pivot+1,length-1] 范围内查找第k个最大值;如果k-1 < pivot,则根据上述方法在 [0, pivot] 范围内查找第k个最大值。

​ 方法4:最大最小堆法

​ a、最小堆
​ 建立一个只有k个元素的最小堆,假设取了 [0, k-1] 的元素构建了最小堆,然后遍历 [k, length-1] 的元素,如果该原素大于堆顶元素,则弹出堆顶元素,将该原素加入最小堆。遍历完所有元素之后,最小堆中存储的k个元素是数组中最大的前k个,因为堆顶元素是最小的,那么堆顶元素也就是第k个最大的元素。

​ b、最大堆
​ 建立一个只有 length-k+1 个元素的最大堆,假设取了 [0, length-k] 的元素建了最大堆,然后遍历 [length-k, length-1] 的元素,如果该元素大于堆顶元素,则弹出堆顶原元素,加入该元素。遍历完所有元素之后,最大堆种存储的 length-k+1 个元素是数组中最小的前 length-k+1个,因为堆顶元素最大,那么堆顶元素也就是第k个最大的元素。

  1. 排序算法的时间复杂度:

https://blog.csdn.net/qq_54693844/article/details/136658836

  1. 如何查询一段英文中出现最多的字母
  2. 输入一个数组,输出 ai 的下一个大于的ai的位置(单调栈)

场景题

  1. 假设有100万的键,不重复,平均大小10B,问在redis服务器上存下这些数据大概需要多大空间

  2. 5L和3L的杯子怎么量出4L的水:加满3L,全部倒到5L,再加满3L,用3L把5L装满,这样3L中就剩下1L,把5L的水倒掉,把3L剩下的1L倒到5L中,然后再加满3L,倒到5L中,就拥有4L的水了.

  3. 服务器上的优化?

  4. 使用limit查询慢的原因?
    MySQL通过 limit 实现分页查询。limit 接收一个或两个整数型参数。如果是两个参数,第一个指定返回记录行的偏移量,第二个指定返回记录行的最大数目。初始记录行的偏移量是 0。对于小的偏移量,直接用 limit 查询没有什么问题。随着数据量的增大,越往后分页,limit 语句的偏移量越大,速度也会明显变慢。

limit 90000,10的意思扫描满足条件的90010行,扔掉前面的90000行,返回最后的10行,问题就在这里,如果是limit 100000,100,需要扫描100100行,在一个高并发的应用里,每次查询需要扫描超过10W行,性能肯定大打折扣。文中还提到limit n性能是没问题的,因为只扫描n行。

  1. 优化limit查询?
  • 使用主键索引(使用子查询或者连接查询查询id)优化,比如加上where id>1000 limit X,X,从而加速查询

  • 使用 between and 语句分页效率快N倍

  • 分表存储

  1. 大量数据写入文件:nio高效写文件,先写入20个小文件,最后合并,每个小文件开一个线程。

  2. 如何统计网页日活:

  • 方式1:通过 Redis 的 Set 集合来实现。
  • 方式2:利用 Hash 类型实现
  • 方式3:利用 bitmap 实现:1千万用户只要 1M多的空间,但是 1亿的用户需要12M的大小即可记录。
  • 方式4:利用 HyperLogLog 实现,12 KB 内存,就可以计算接近 2^64 个不同元素的基数。因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身
  1. 如果设计搜索,不借助ES、MySQL ,设计一个根据名词 进行图书搜素,你会如何设计:

    • 分词器 + 倒排索引
  2. 你会如何设计倒排索引,如何进行 document_id 的取并集操作

  3. 十万、百万数据量如何进行实时排序https://cloud.tencent.com/developer/article/1456823?ivk_sa=1024320u

  4. 100w个无序数字,如何分为相等的两部分,各50w,左边的全比比右边的小,64位数字(二分找到划分点)

  5. 搜索框下方推荐搜索的实现,从百万级词条数据找到topK的含有关键字的记录,多个词条的id列表,求它们的并集并返回topK的id

  6. 一百万个单词,如何判断一个单词在不在里面,有没有更快的方法?二分快(O(logn))还是字典树(O(k)(k表示字符串的长度))快,还有什么方法吗?

  7. 百万字符串查高频词。分治哈希+堆 or 字典树 https://zhuanlan.zhihu.com/p/496869606?utm_id=0

  8. 两个线程各执行100次i++,得到的可能值

最大值 200,最小值 2

  1. 线程1:在 CPU0 执行,从内存取值 0 到寄存器 RegX。此时内存中为 memory = 0.
  2. 线程2:在 CPU1 执行,从内存取值 0 到寄存器 RegY,执行 99 次自加操作,这其中可能回写到内存。第 99 次计算完毕,写回到内存,此时内存中为 memory = 99。
  3. 线程1:开始第 1 次自加,并写回内存。此时内存中为 memory = 1.
  4. 线程2:从内存取值(此时为 memory = 1)到寄存器 RegY,完成第 100 次自加,结果为 RegY = 2,在还没回写到内存之前,线程 1 已经完成了以下第 5 步。
  5. 线程1:从内存取值(此时为 memory = 1)到寄存器 RegX,完成剩余 99 次自加,此时 RegX = 100,然后写回内存,此时 memory = 100。
  6. 线程2:将 RegY = 2 写回内存,最终 memory = 2.
  1. 假如已经爬取好了大量文章,但这些文章可能出现重复或者类似(比如分别在a平台和b平台发布的同一篇文章,因为不同的审核机制,所以有所修改,但主体内容一致),如何做到去重,只统计不同文章的数量 ?
  2. 雪花算法怎么做的高并发下是唯一的且递增的 ?

雪花算法有以下几个优点:

  • 高并发分布式环境下生成不重复 id,每秒可生成百万个不重复 id。

  • 基于时间戳,以及同一时间戳下序列号自增,基本保证 id 有序递增。

  • 不依赖第三方库或者中间件。

  • 算法简单,在内存中进行,效率高。

    雪花算法有如下缺点:

  • 依赖服务器时间,服务器时钟回拨时可能会生成重复 id。算法中可通过记录最后一个生成 id 时的时间戳来解决,每次生成 id 之前比较当前服务器时钟是否被回拨,避免生成重复 id。

  • 需要配置机器ID和服务器ID

  • 14
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值