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


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

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

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

    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~~~~

    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.马蜂窝一面~~~~

    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.马蜂窝二面~~~~

    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部门一面~~~~

    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引擎,创建的表只是在本地有表定义文件,
         数据文件则存在于远程数据库中

    往期精选

    853680d90d130747084fbe3cc882d2d0.gif

    php程序员面试题(偏中级面试题)

    PHP 中高级程序员面试模拟题

    2021年最新PHP 面试、笔试题汇总(一)

    2021年最新PHP 面试、笔试题汇总  (二)

    2021年最新PHP 面试、笔试题汇总(三)

    da190ac7df5e7999a44f178623243330.png

    赞赏码

    非学,无以致疑;非问,无以广识

  • 相关阅读:
    POJ1112 Team Them Up!
    WebSocket相关介绍
    长轮询 & 短轮询
    图片上传 & 预览
    sessionStorage & localStorage & cookie & session
    进程 & 线程
    https介绍与讲解
    http请求
    TCP/IP的连接与断开
    浏览器缓存
  • 原文地址:https://www.cnblogs.com/lxwphp/p/15452456.html
Copyright © 2020-2023  润新知