前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2021年最新大厂php+go面试题集(1)

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

作者头像
码农编程进阶笔记
发布2021-09-29 15:52:49
4820
发布2021-09-29 15:52:49
举报

首先面试都是从小公司到大公司的过程,小公司主要为了练手,熟悉面试节奏,后面才去面大公司。尽量不要一开始就奔着大公司去,容易出现准备不足的情况。。。另外,算法是真的难!遇到的面试题也都记了下来,主要是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的分表。
        可以hash%64这样,减小索引大小。
    (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引擎,创建的表只是在本地有表定义文件,
     数据文件则存在于远程数据库中
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-09-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农编程进阶笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档