2021年最新大厂php+go面试题集(1)

2021-09-29 15:52:49 浏览数 (1)

首先面试都是从小公司到大公司的过程,小公司主要为了练手,熟悉面试节奏,后面才去面大公司。尽量不要一开始就奔着大公司去,容易出现准备不足的情况。。。另外,算法是真的难!遇到的面试题也都记了下来,主要是php go的部分面试题。部分问题附带答案,希望对大家找工作能有帮助。你要做的就是每天进步一点点。。。

微信公众号:码农编程进阶笔记 关注可获得更多的视频教程及面试技巧。问题或建议,请公众号留言!

~~~~~1.不知名小公司A~~~~~

代码语言:javascript复制
1.k8s的服务注册
    答:参照k8s笔记
2.rabbitmq的消息确认机制,项目里面怎么确认的
    消息确认:
    1)生产者到消息队列,这个是利用消息队列

    的confirm机制和持久化机制,持久话之后就给生产者发送一个ack确认
        生产者只能设置为事务,或者confirm,不能共用
    2)消息队列,内部有唯一的msg_id,会先根据该id判断消息是否重复发送,mq再决定是
    否接收该消息
    3)消费端,消费成功则发送ack给消费队列,消费队列才会删除该消息
        $q->nack($message->getDeliveryTag()); // deliveryTag 可以用来回传告诉 rabbitmq 这个消息处理成功 清除此消息
    避免重复消费:
    1)业务需要有一个唯一的id,避免消息队列重复下发消息。一般可以跟db的唯一字段对应起来,或者用redis来实现过滤
    消息分发策略:
        1)轮训分发模式。 每个消费者收到的消息数量是一样的
        2) 公平分发,会考虑到消费者的当前消费能力等
3.rabbitmq实时性怎么实现?
    答:维护一个常驻进程,实时读取队列消费即可,一般使用的维护工具是:
3.redis和mysql的一致性,项目里面怎么用的
    查询:
    数据分为静态数据和动态数据。
    静态数据:直接读redis,不存在则返回默认值
    动态数据:直接读redis,不存在则返回默认值
    更新:
    1)旧数据缓存的映射(删除key),更新缓存映射关系(Set)
    2)mq异步更新db
    问题:redis断电,不去读db,直接返回默认值吗?
    答:是的,因为有集群的高可用,最多出问题几秒就重新拉起来一个。
        而且数据是最终一致性的。
   3)创建数据,开启事务,先写入到db,后更新到redis,事务提交。
   4) 每个数据都有默认值处理,防止缓存查询失败,返回无数据的情况
   4)redis结构:hash结构存储, hmget ,hgetall
   5)初始化呢,缓存中无数据怎么办? 写的有脚本,遍历数据写入到redis
   6)mq里面的数据在哪消费的,在udc.job

4.高并发秒杀场景设计,redis怎么设计秒杀的
    参照redis部分,已经设计了一个秒杀系统
5.redis大key存储,value是怎么存储的
    1)拆分为多个key-value,用multi事务去组合查询。分解单次操作压力
    2)使用hash存储,然后每个field代表一个属性。查询的时候查询部分属性,
    存储的话也可以按照属性存储。本质上还是拆分
    3)存储的时候,对key做取模拆分,分配到不同的key上面
6.redis集群同步数据
    (1) 【所有的redis节点彼此互联(PING-PONG机制)】,内部使用二进制协议优化
    传输速度和带宽。
    (2) 节点的fail是通过集群中【超过半数的节点检测失效】时才生效。
    (3) 客户端与redis节点直连,不需要中间代理层。客户端不需要连接集群所有节点,
    【客户端连接集群中任何一个可用节点即可】。
    (4)Redis集群预分好16384个桶,当需要在Redis集群中放置一个key-value 时,
    计算key属于哪个桶,然后存放value
    (5)集群正常工作至少需要3个主节点,一共就需要6个节点,其中3个为主节点,
    3个为从节点
    ---查询
    (1)计算key所在槽,在本节点上,就直接返回数据
    (2)不在本节点上,则执行move,指引client转向负责对应槽的节点,
    并客户端需要再次发送想要执行的和key相关的命令
7.对mysql架构的理解
    答:客户端和服务端组成。
    客户端进程向服务器进程发送MySQL语句,服务器进程处理后再向客户端进程发送处理结果
    客户端:对应配置为:[client],[mysql],[mysqladmin]
    服务端:对应配置为:[server],[mysqld],[mysqld_safe]
    引擎部分:mysql中具体与文件打交道的子系统,是官方提供的文件访问层的
    一个抽象接口来定制一种文件访问机制
    执行过程:
        (1)客户端连接服务端,mysql-uxxx -pxxx
        (2)服务端进行查询缓存,不过5.7不建议使用,8.0废弃
        (3)服务端语法解析,判断请求的语法是否正确,然后从文本中将要查询的表等
        (4)查询优化:生成一个执行计划,这个执行计划表明了应该使用哪些索引进行查询,表之间的连接顺序是啥样的
        (5)存储引擎:MySQL server完成了查询优化后,只需按照生成的执行计划调用
        底层存储引擎提供的API,获取到数据后返回给客户端就好了。


8.为什么项目里面是用curl来调度服务的,怎么不用rpc,差距在哪?
    答:https://blog.csdn.net/AlbenXie/article/details/105230018
    (1)http的报文header头占用空间太多了,rpc一般会优化这块
    (2)rpc是基于http2.0的,减少rtt,长连接方面有优势
    (3)rpc传输的序列化反序列化可以是protobuf
    (4)rpc框架包含了重试机制,路由策略,负载均衡策略,高可用策略,
    流量控制策略等等能用在消息处理上的功能


9.php的桶结构
    (1)bucket桶结构,实际的数据存储在这里,用链地址法防止冲突。(redis也是)
        数据是链表连接的,foreach就是根据赋值顺序,找到下一个元素的指针,依次遍历
    (2)一个hashtable默认分配8个bucket,如果存储的元素大于8个会自动扩容,
    扩容后的大小为2的倍数。一般是先看看删除的元素是否到达阈值,到达的话则重建
    索引。没有到达阈值则扩容,*2
    (3)php的forach比for快,就是因为forach直接拿首个bucket的指针开始遍历,省去了
    计算key的hash值的过程,同样的,next(),prev()等方法也是直接在hashtable上就能取到值
    (4)php7之后,是先通过计算key得到value的位置,然后把key存到中间表,中间表
    主要存储key和value的映射关系。扩容的时候,中间表也要重新计算
    (5)php删除数组的中的元素,并不是立刻删除的,只是给标识为IS_UNDEF,扩容的时候
    才会真正的删除掉
    (6)查找时先在散列表中映射到nIndex,得到value在Bucket数组的位置idx,
    再从Bucket数组中取出元素。

~~~~~2.不知名小公司B~~~~

代码语言:javascript复制
1.include和require的区别
答:
1)报错
    include 引入文件的时候,如果碰到错误,会给出提示,并继续运行下边的代码。
    require 引入文件的时候,如果碰到错误,会给出提示,并停止运行下边的代码。
2)文件引用方式
include() 执行时需要引用的文件每次都要进行读取和评估,
require() 执行时需要引用的文件只处理一次
3)include_once 函数和include类似,只不过只会引入一次


2.composer insall和update的区别
    答:install读取lock文件,没有的话,则读取json文件,并生成lock
        update会读取json,拉取最新依赖,把新的版本写入lock
        也就是说,当本地没有lock文件的时候,install和update是一样的

3.cookie和session
    答:服务端生成cookie返回给客户端,客户端请求带着cookie,
    服务端获取cookie和session_id,
    然后读取session文件,就可以对比客户端的cookie了。
    session是依附于cookie的,需要cookie来存储session_id。当禁用cookie的时候,
    通过url重写或者表单隐藏域来提交session_id
4.sql注入,xss,csrf
    答:sql注入,用户输入sql命令或者sql注释,拼接sql的时候,会查出所有的
    用户信息。
    防范:是过滤用户输入,使用预处理来拼接sql
    xss跨站脚本:网页中注入恶性脚本。持久型是存入到数据库,读出的时候弹出恶意代码,
    反射型是通过电子邮件等,引导用户点击恶意链接。
    防范:用户输入过滤,cookie加密
    csrf:跨站请求伪造。拿到A的cookie,访问恶意网站b,b就可以拿着a的cookie去访问a网站。
    防范:token机制,验证referer

5.git pull和git fetch的区别
    答:都是更新远程代码到本地
    (1)git pull相当于暴力合并,直接拉取代码,并合并,相当于git fetch   git merge
    (2)git fetch(下载)拉取代码后,一般需要手动合并下代码

6.线程是什么,线程的上下文切换
    答: 上下文切换:上下文切换就是从当前执行任务切换到另一个任务执行的过程。但是,
    为了确保下次能从正确的位置继续执行,在切换之前,会保存上一个任务的状态。
    进程切换:
    (1)切换页目录以使用新的地址空间
    (2)切换内核栈(函数)和硬件(寄存器)上下文
    寄存器:cpu内部的元件,可以保存数据,保存地址,指令等


7.trait的好处
    伪多继承。php中一个类能继承多个接口,但只能继承一个父类。
    使用trait,可以实现继承多个父类,避免复用代码

8.负载均衡原理
    Lvs的nat: 
        客户端 --> Load Balancer --> RS --> Load Balancer --> 客户端
        1)LB可以修改客户端发来的ip头,tcp头,定位带rs服务器群
        2)服务器响应后,会发送给LB网关,LB再修改ip和tcp报文,发送给客户端
    LVS的DR:
        客户端 --> Load Balancer --> RS --> 客户端
        1)Rs公用一个ip,LB对外服务,拿到请求后,分配给rs
        2)rs直接返回数据包给客户端    

9.mysql的长连接和短连接,都有什么特点,框架里有使用吗?
答:长连接指在一个连接上可以连续发送多个数据包,在连接保持期间,
    如果没有数据包发送,需要双方发链路检测包。
    mysql的长连接如果长期闲置,mysql会8小时后(默认时间)主动断开该连接。

10.线程池的大概设计
     (1)要设置最大连接和最大连接空闲数。小于最空闲数则使用完入池。大于最大空闲数则释放
    (2)需要多一个队列,来表示等待队列。当请求没有 数据库连接空闲,则进入队列。设置有默认的超时时间,超时报错
    (3)当一个链接使用完,要判断是否有等待队列需求,有的话直接返回给等待中的需求,没有的话就入池
    (4)均衡和保活。均衡可采用队列先进先出的方式保持 。保活的话,类似于发送心跳,保持连接活性

11.php的数组扩容
    我们知道,数组存储需要连续的内存空间,那么扩容的时候呢,是虚拟内存的方式,
    还是直接申请一大块内存呢?
    答:一个hashtable默认分配8个bucket,如果存储的元素大于8个会自动扩容,
    扩容后的大小为2的倍数。

~~~~3.马蜂窝一面~~~~

代码语言:javascript复制
1.python的切片了解吗
    答:切片操作基本表达式:object[start_index:end_index:step]
    (1)冒号':'可以省略,step默认为1.
    (2)step的正负代表切片的方向

2.okr和kpi的区别
    (1)OKR 强调全员思考;KPI 强调管理层思考。
    (2)OKR 强调自我驱动;KPI 强调外在驱动。
    (3)KPI 只能让驴使劲走,而 OKR 用于保证驴头朝正确的方向。


3.es数据超过一亿,有没有做过什么优化
    答:首先es数据在磁盘上,每次查询也是去查询缓存,不存在缓存
    则去磁盘查找,刷新到缓存。缓存一般占机器内存的50%
    (1)热数据单独建索引,类似于mysql的分表。
        可以hashd这样,减小索引大小。
    (2)查询部分不要使用复杂的join,parent-child这种
        其次是filter查询效率比query高,而且会缓存数据,方便下次查询。
    (3)分页不要太大,es每次分页都会向所有节点查询数据,然后
    返回给node1,node1最终返回数据,所以分页小点好。


4.mysql插入数据,断电重启之后,数据会丢失吗,为什么
    答:靠的是redo log,事务每次执行会先写入到缓冲区,通过两段提交方式,
        保证恢复已经commit的数据。
        checkpoint:记录被刷新到磁盘的redo log的id,mysql重启之后会从上一次的
        checkpoint开始恢复,加快恢复的速度。
        (1)事务执行的几个阶段
            ① InnoDB)prepare redo log
            ② Server)write binlog
            ③ InnoDB)commit redo log
        (2)从上个checkpoint开始恢复,如果redo log有两个状态,则直接提交。
        如果redo 只有prepare,则拿些事务id去查询binlog,binlog有写入则提交,
        binlog无写入则回滚该事务。


5.tcp的三次握手是特有的吗,udp会有吗,了解udp吗?
    (1)tcp和udp的区别
        1)tcp可靠,udp不可靠
        2)tcp需要先建立连接,udp不需要
        3)tcp是一对一,udp可以1对多
        4)tcp效率低,udp效率高
        5)TCP 有滑动窗口可以用来控制流量,而 UDP 则不具备流量控制的能力
        6)TCP 是面向字节流的传输层协议,而 UDP 是面向报文的传输层协议;
        7)TCP 的应用场景是对消息准确性和顺序要求较高的场景,
        而 UDP 则是应用于对通信效率较高、准确性要求相对较低的场景。
    (2)面向字节流和面向报文的区别
        面向字节:TCP把应用程序看成是一连串的无结构的字节流。TCP有一个缓冲,
        当应用程序传送的数据块太长,TCP就可以把它划分短一些再传送。
        如果应用程序一次只发送一个字节,TCP也可以等待积累有足够多的字节后
        再构成报文段发送出去
        面向报文:送方的UDP对应用层交下来的报文,不合并,不拆分,
        只是在其上面加上首部(最小8字节)后就交给了下面的网络层。
    (3)tcp粘包
        答:发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,
        后一包数据的头紧接着前一包数据的尾。
        半包:数据包比较大,tcp每次发送只能发送一半

        注意:udp不存在粘包,不会合并小包。
        造成粘包原因:
            1)发送方合并多个小分组,在一个确认到来时一起发送
            2)接收方接收数据到缓存,程序去缓存中读取。当程序读取速度<接收速度,
            就可能粘包。
        解决方案:
            1)发送方使用TCP_NODELAY选项来关闭Nagle算法
            2)发送的时候把长度也发送过去。程序收到之后,根据长度确认
                包的大小,然后进行分割。
     (4)tcp其实没有包的概念
         TCP是字节流协议,确实没有包的概念,包是应用层的概念,只是概念叫做
         粘包。
     (5)ip包分片
         1)最大传输单元:数据链路层对数据帧的长度都有一个限制,也就是链路层所能
         承受的最大数据长度,这个值称为最大传输单元,即MTU。
         通常是1500字节。
         2)在IP包头中,以16位来描述IP包的长度。一个IP包,最长可能是65535字节
         3)当ip包大于MTU,则要进行分片,分为多个小包传输。如果设置
         不可分片,则数据包被丢弃,出现报错。
         4)TCP的选项字段中,有一个最大报文段长度(MSS),一般是1024字节
         5)ip头长度,tcp头长度都是固定20字节。
         6)IP包头中,用了三个标志来描述一个分片包,分别是:
             分片标志(0/1),分片偏移标志(分片在原包的位置),不允许分片标志



6. mysql和redis如何保证数据一致性
    从应用场景分析。读多写少,和读少写多,可以了解下缓存策略。
    缓存策略:
    读多写少:
        (1)缓存为主,不存在则返回默认值
        (2)更新的时候更新缓存,队列异步更新db
        (3)数据预热,启动系统之前先用脚本去跑缓存

    读少写多:
        (1)每次读取,没有缓存就写入缓存
        (2)更新的时候,更新数据,删除缓存。
        (3)写多读少的话,会减小缓存的更新消耗。


7.php7.0对于引用计数的优化有哪些?
    答:
    1)当对整型,浮点型,静态字符串赋值时,引用计数是0
        动态字符串就是用函数生成的字符串,这种的会有引用计数。
        因为php7的引用计数value 中而不是 zval_struct,当数据类型简单的时候,
        value可以直接存下。
    2)引用&之后,refcount 为2
    3)不可变数组,就是直接赋值固定内容的数组,初始计数是2.
    动态数组初始计数是1.
    4)循环引用会造成内存泄露。比如:
        // 数组循环引用
        $arrA['arrA'] = &$arrA;

~~~~4.马蜂窝二面~~~~

代码语言:javascript复制
1.  rabbitmq是分布式的吗,大概架构是怎么样的?
    答:是分布式的。
    主备集群模式,通过备用实现高可用。
    镜像模式,一个节点的数据会同步到3个其他节点上,保证
    数据不丢失。

2.kafka,会丢数据吗,丢数据在哪一步,怎么处理?
    答:会丢的,主要从生产者,服务器,消费者几个方向来处理。
    (1)Broker:broker存储topic的数据。如果某topic有N个partition,
    集群有N个broker,那么每个broker存储该topic的一个partition。
    kafka采用了批量刷盘的做法,数据存在缓冲区。
    只能通过调整刷盘机制的参数缓解该情况。比如,减少刷盘间隔,
    减少刷盘数据量大小。时间越短,性能越差,可靠性越好(尽可能可靠)。
    这是一个选择题
    (2)生产端:设置及ack为-1或者all.
        ack=0:只负责发送,效率最高。
        ack=1:保证leader能收到
        ack=-1:保证leader和ISR列表都能收到,注意isr列表不能设置的太小
        生产端也是异步批量发送数据到broker的,要保证数据不丢失,可以设置
        同步发送,扩大Buffer的容量配置
    (3)消费端
    消费端手动提交offset
    (4)每个partition都有leader和floower.
    生产者发布消息时根据消息是否有键,采用不同的分区策略。消息没有键时,
    通过轮询方式进行客户端负载均衡;消息有键时,根据分区语义(例如hash)
    确保相同键的消息总是发送到同一分区
    (5)Rebalance
    Rebalance 本质上是一种协议,规定了一个 Consumer Group 下的所有 consumer 
    如何达成一致,来分配订阅 Topic 的每个分区
    触发方式:
        组成员个数发生变化。例如有新的 consumer 实例加入该消费组或者离开组。
        订阅的 Topic 个数发生变化。
        订阅 Topic 的分区数发生变化。

3.kafka发现消息积压了怎么办?增加消费者有用吗?
    答:一个分区最多被一个消费者消费,消费者多了之后没用的。
    我们可以在消费者中只做不耗时的操作,耗时的操作打入到二级队列,
    二级队列多做几个分区,这样消费能力跟得上
4.redis多个master怎么平均分配数据进去,会不会出现有的负载很高的情况
    答:不管是codis还是redis官方集群,都是hash算法计算key,找到对应的槽,
    最后找到节点,也就是master了。codis是1024个槽
5.redis的bitmap存储的key是什么?为什么不用redis提供的命令来求交集并集?
    答:(1)key存储的是用户id
    (2)redis提供的命令非常耗费cpu性能,自己程序做位操作好一些。

6. redis的分布式锁,过期时间如何续约?
    答:https://segmentfault.com/a/1190000022436625
    (1)redis的setnx和set都是针对单机redis的。如果是主从或者集群redis,
    当matser宕机,锁还没到slave.slave成为新master的时候,锁会失效。

    (2)redis的redlock是分布式集群锁,总体思想是尝试锁住所有节点,当有
    一半以上节点被锁住就代表加锁成功  .
    (3)zookeeper的分布式锁
        1)一个ZooKeeper分布式锁,首先需要创建一个父节点,尽量是持久节点
        (PERSISTENT类型),然后每个要获得锁的线程,都在这个节点下创建个
        临时顺序节点。由于ZK节点,是按照创建的次序,依次递增的。
        2)判断逻辑就是序号最小的先加锁,其他的阻塞。前一个锁
        释放之后,会通知后面的节点。
        3)可重入性,同一个线程可以重复加锁。
   (4)zookeeper和redis的优劣势
       (1)基于ZooKeeper的分布式锁,适用于高可靠(高可用)而并发量不是太大
           的场景;
        (2)基于Redis的分布式锁,适用于并发量很大、性能要求很高的、而可靠性
            问题可以通过其他方案去弥补的场景

~~~~~5.得物A部门一面~~~~

代码语言:javascript复制
1.lru算法的大概实现,go怎么实现lru算法
      答:步骤如下:
    (1)当访问的数据命中缓存,遍历得到这个数据对应的结点,并将其从原来的
    位置删除,然后再插入到链表的头部;(修改链表指针O(1))
    (2) 如果此数据没有在缓存链表中,分为两种情况:
    (3) 如果此时缓存未满,则将此结点直接插入到链表的头部;
    (4)如果此时缓存已满,则遍历至链表尾结点将其删除,将新的数据结点
    插入链表的头部。
    php采用:数组 单链表的方式实现
    golang采用:map 结构体链表的方式实现


 2. mysql的主从不一致怎么解决
     答:
     (1)如何避免主从不一致:
        1、主库binlog采用ROW格式
        2、主从实例数据库版本保持一致
        3、主库做好账户权限把控,主库不可以停止写binlog
        4、从库开启只读 read_only=ON,不允许人为写入
        5、定期进行主从一致性检验
     (2)解决方案
     1、将从库重新实现
     2.使用percona-toolkit工具辅助(最佳方案)
     3、手动重建不一致的表
     4.另起脚本做一致性校验,不一致的话报警

 3.go的协程为什么比线程更轻量级
     答:线程切换需要切换上下文,寄存器,堆栈等
     (1)go协程也叫用户态线程,协程之间的切换发生在用户态。
     在用户态没有时钟中断,系统调用等机制,因此效率高。
     (2)就协程是一段代码,一个函数入口,以及在堆上为其分配的一个堆栈。
     占用内存小,一般是2kb,线程需要8M

 4.kafka怎么防止重复消费?kafka的消费ack跟rabbitmq有什么区别
     答:(1)在断电或者重平衡的时候,有可能消费者还没提交offset,导致
     重复消费问题。一般是业务唯一id,数据库唯一键或者用redis存储消费过的id,
     做一次判断过滤。
     (2)一个是提交ack之后删除数据
         kafka是提交offset之后不删除数据,数据可以重复消费

 5.go怎么实现的锁
     答:(1)读写锁sync.RWMutex 的 RLock())和写锁 Lock()
        (2)互斥锁sync.Mutex
        (3)atomic 包操作保证原子性 


 6.你认为项目最有亮点的地方说一下

 7.mysql分库的场景,如何连表查询?
     (1)相同mysql下的不同库join查询。
     可以带上库名,比如a.demo 和b.demo
     (2)不同mysql下的查询
     可以通过mysql的federated引擎,创建的表只是在本地有表定义文件,
     数据文件则存在于远程数据库中

0 人点赞