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

2021-09-29 15:53:11 浏览数 (1)

持续更新,每天进步一点点。。。

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

~~~6.小米一面~~~

代码语言:javascript复制
1.让你设计一个框架,主要模块有哪些?怎么设计路由更高效?
     答:
     (1)框架流程
     入口文件->定义变量->引入函数库->自动加载类->启动框架
     ->路由解析->加载控制器->返回结果
     (2)

 2.二叉树的非递归前序遍历

 3.mysql的undo日志原理,中继日志是干嘛的
     参考mysql拾遗

 4.nginx和php的关系,一个请求进来怎么到php的
 答:通过fastcgi协议,请求到nginx,通过fastcgi转发到9000端口,
 php-fpm监听9000端口,然后php程序处理

 5.反转链表怎么反转的?

 6.mysql的myisam的索引结构是什么样子的
     MyISAM引擎使用B Tree作为索引结构,索引文件叶节点的data域存放的是
     数据记录的地址,指向数据文件中对应的值,每个节点只有该索引列的值。
     myisam的主键索引和二级索引的结构没区别

~~~7.度小满一面~~~

代码语言:javascript复制
1.有序数组里面查询某个值出现的次数
    二分获取索引位置,双指针从索引位置左右遍历

   微信公众号:码农编程进阶笔记,关注可获得更多的视频教程及面试技巧。

2.php的static什么情况下会用,好处是什么
    答:静态的东西都是给类用的(包括类常量),非静态的都是给对象用的
    (1)静态方法可以直接被类访问,不需要实例化
    (2)函数执行完静态属性的值会一直都在

3.mysql的mvcc实现原理
    undo 间隙锁

4.redis的rehash过程,中间有读写会分别怎么处理?断电呢?
    答:
    ht[0],是存放数据的table
    ht[1],只有正在进行扩容时才会使用,它也是存放数据的table,长度为ht[0]的两倍
    进行读操作:会先去ht[0]中找,找不到再去ht[1]中找。
    进行写操作:直接写在ht[1]中。
    rehashidx 初始是-1,开始rehash则为0,每次rehash都 1,
    rehash结束则修改为-1

~~~8.b站一面~~~

代码语言:javascript复制
0.介绍项目里面的亮点
1.go的引用类型有哪些
    答:
    值类型分别有:int系列、float系列、bool、string、数组和结构体
    引用类型有:指针、slice切片、管道channel、接口interface、map、函数等
    值类型的特点是:变量直接存储值,内存通常在栈中分配
    引用类型的特点是:变量存储的是一个地址,这个地址对应的空间里才是真正存储的值,
    内存通常在堆中分配
2.go的select的default作用
    当 select 中的其他条件分支都没有准备好的时候,`default` 分支会被执行。
    为了非阻塞的发送或者接收,可使用 default 分支:

3.go的defer,里面有多个函数,执行顺序是什么
    答:后面的函数先执行
    defer特性:
        1. 关键字 defer 用于注册延迟调用。
        2. 这些调用直到 return 前才被执。因此,可以用来做资源清理。
        3. 多个defer语句,按先进后出的方式执行。
        4. defer语句中的变量,在defer声明时就决定了。
    defer用途:
        1. 关闭文件句柄
        2. 锁资源释放
        3. 数据库连接释放

4.两个Goroutine,怎么控制先后顺序?
    答:参考:https://www.cnblogs.com/qingfj/p/14881692.html
    通过两个channel来控制,
    方法一:一个channel A有值,执行打印奇数。
        然后给另一个channel B赋值
    方法二: 读取channel B,打印偶数
        给channel A赋值
    注意:方法中需要是for循环的状态。其次是结束的话,也通过chan来阻塞。
        当要结束的时候,给channel C赋值,则main主协程会读取到C

5.channel被关闭还能读出值吗,多次读的时候会返回什么?
    对一个关闭的channel写入呢,返回值是什么类型?
    答:
    (1)channel被关闭后是可读的,直到数据读完为止。
        如果继续读数据,得到的是零值(对于int,就是0)。
     (2)写已经关闭的 chan 会 panic 

6.go的并发模型
    参考:https://segmentfault.com/a/1190000018150987
    (1)多线程共享内存 ,java/c  等语言实现的就是这个
    (2)CSP并发模型,go语言特有的,通过goroutine和channel来实现的
    Go语言的线程模型:MPG
    M指的是Machine,一个M直接关联了一个内核线程。由操作系统管理。
    P指的是”processor”,代表了M所需的上下文环境,也是处理用户级代码逻辑的处理器。
        它负责衔接M和G的调度上下文,将等待执行的G与M对接。
    G指的是Goroutine,其实本质上也是一种轻量级的线程。包括了调用栈,重要的调度
        信息,例如channel等。
    P和M数量一般会保持一致,跟cpu的核数有关。
    goroutinue会在一个队列里面,每次执行就会pop一个出来,当阻塞时,
    会调用其他的协程来做切换。
    优势:
        1)开销小,比常规线程小
        2)调度性能好,go可以控制goroutinue的调度
    缺点:协程调度机制无法实现公平调度。 

 7.go的缓冲channel 和单个channel有什么区别   
    无缓冲: 当向ch1中存值后需要其他协程取值,否则一直阻塞
    有缓冲: 不会阻塞,因为缓冲大小是1,只有当放第二个值的时候,
    第一个还没被人拿走,才会阻塞。

~~~9.得物A部门二面~~~

代码语言:javascript复制
---php
1.php怎么实现常驻进程的,如何配置,如何监控
    为啥要常驻?常驻有什么好处
    答:通过pcntl 扩展和 posix扩展实现。参考:https://www.jianshu.com/p/161d9981112a
    创建步骤:
        1.创建子进程,终止父进程 (脱离父进程终端)
        2.在子进程中创建新会话(posix_setsid()方法)
        3.改变工作目录 (chdir('/') )
        4.重设文件创建掩码 (umask(0))
        5.关闭文件描述符   (主要是关闭父进程打开的文件等)
      优点就是可以在后台一直处理任务。常用的管理工具是:Supervisor 


2.php内存泄露的排查与处理
    原因:(1)大内存变量未释放
        (2)循环引用自己
        (3)php-fpm在频繁删除缓存的时候会编译报错
        排查:看代码,看内存峰值,x-debug查看程序性能损耗


--golang
1.golang的缓冲channel,在切换业务的时候怎么处理?
    答:不懂
2.golang的context的withcancel用过吗,什么场景
    (1) 关闭goroutinue
    (2)父级的context被关闭之后,他的子context也会被关闭
    1. WithCancel()函数接受一个 Context 并返回其子Context和取消函数cancel
    2. 新创建协程中传入子Context做参数,且需监控子Context的Done通道,
        若收到消息,则退出
    3. 需要新协程结束时,在外面调用 cancel 函数,即会往子Context的Done通道
        发送消息
    4. 注意:当 父Context的 Done() 关闭的时候,子 ctx 的 Done() 也会被关闭

3.goroutinue的变量作用域问题,
    在循环中调用goroutinue修改变量,传递的变量会改变吗?如何优化
    答:循环中调用goroutinue,并在协程中打印value:很可能value指向最后一个元素。
    解决方案:(1)value作为参数传递给goroutinue
            (2) 在循环中新创建变量

4.golang如何调度goroutinue的: 答:看概念
5.golang的switch和php的switch的区别
    (1)go中加上了默认break,匹配到对应case,在执行完相应代码后就会退出整个
         switch 代码块
    (2)go中用fallthrough关键字继续执行后续分支的代码

--- 中间件
1.kafuka10个分区,一个消费者,golang会起几个协程
    答:可以是单个线程,也可以是多个线程。多线程的话不是线程安全的,
        需要注意点
2.kafka的offset是存在哪里的?   
    答:Kafka版本[0.10.1.1],已默认将消费的 offset 迁入到了 
        Kafka 一个名为 __consumer_offsets 的Topic中。
    原理:利用 Kafka 自身的 Topic,以消费的Group,Topic,以及Partition做为组合
     Key。所有的消费offset都提交写入到上述的Topic中。因为这部分消息是非常重要
     ,以至于是不能容忍丢数据的,所以消息的 acking 级别设置为了 -1,
     生产者等到所有的 ISR 都收到消息后才会得到 ack(数据安全性极好,当然,
     其速度会有所影响)。所以 Kafka 又在内存中维护了一个关于 Group,Topic 和
      Partition 的三元组来维护最新的 offset 信息,消费者获取最新的offset的时候
      会直接从内存中获取。
   (2)kafka的position 
    在 consumer 端,大家都知道可以控制 offset,所以可以控制消费,其实 offset 
    只有在重启的时候才会用到。在机器正常运行时我们用的是 position,
    我们实时消费的位置也是 position 而不是 offset。

3.rabbitmq的ack和kafka的ack区别?
    rabbitmq:处理完数据才发送ack,mq就可以放心删除数据了。
        当消费者异常退出没有发送ack,此消息会发送给下一个消费者,保证不丢失。
    kafka的ack机制就是指生产者的ack了

4.mysql分库分表,每个表的id都从1开始吗,为什么要设置
    一般会设置全局的分布式id,主要方法为:
        (1)redis维护全局id底层,有宕机风险
        (2)简单号段模式,比如long类型,从1亿开始递增
        (3)雪花算法的发送器,百度的百度uid-generator,美团的ecp-uid

5.kafka如何保证消息都分发到指定的分区上去
    (1)设置相同的key,kafka是hash(key)%numPartitions ,相同的key
        可以保证发送到同一个分区
    (2)生产端设置connection=1,该参数指定了生产者在收到服务器响应之前可以
        发送多少个消息。
    (3)分区的话,一个分区最好,保证写入顺序 
    (4)为实现Producer的幂等性,Kafka引入了Producer ID(即PID)和Sequence Number
        每个生产者Commit一条消息时将其对应序号递增

--- 架构层面
1.整体业务的架构大概聊一下,服务之间的通信方式,信息流处理

2.k8s的请求怎么转发到内部实际的ip的?服务之间的通信呢?
    答:
    (1)Service就是一个把所有Pod统一成一个组,然后对外提供固定一个IP,
    (2)我们访问service ip,k8s中的kube-proxy会自动负载均衡后端的8个pod,
    这一套服务集群内部访问,只需要一个service ip 和端口号就可以
    (3)外网访问:在每个Node上打开一个随机端口并且每个Node的端口都是一样的,
    通过<NodeIP>:NodePort的方式Kubernetes集群外部的程序可以访问Service。
        访问到service之后,自然也就能找到对应的pod提供服务了
    (4).ClusterIP:提供一个集群内部的虚拟IP(与Pod不在同一网段),
        以供集群内部的pod之间通信使用。
    (5)生产环境下的外网访问:
    Ingress 能把集群内 Service 配置成外网能够访问的 URL,流量负载均衡,
        终止SSL,提供基于域名访问的虚拟主机等等。
        1)Nginx 反向代理负载均衡器
        2)Ingress Controller
        Ingress Controller 可以理解为控制器,它通过不断的跟 Kubernetes API
         交互,实时获取后端 Service、Pod 等的变化,比如新增、删除等,
         然后结合 Ingress 定义的规则生成配置,然后动态更新上边的 Nginx 
         负载均衡器,并刷新使配置生效,来达到服务自动发现的作用。
        3)Ingress
        Ingress 则是定义规则,通过它定义某个域名的请求过来之后转发到集群中指定的
         Service。它可以通过 Yaml 文件定义,可以给一个或多个 Service 定义一个
         或多个 Ingress 规则。

3.服务限流怎么做的?服务熔断怎么做的?
    限流:(1)代码里的队列计数
    redis-cell是一个用rust语言编写的基于令牌桶算法的的限流模块,
    提供原子性的限流功能,并允许突发流量,可以很方便的应用于分布式环境中
        (2)网关限流
        nginx自带的ngx_http_limit_req_module模块是对请求进行限流,
        即限制某一时间段内用户的请求速率;且使用的是漏桶算法

   熔断:熔断就是用php扩展滑动窗口计数
       滑动窗口算法限流最适合的需求场景,就是X秒内,最多允许Y个请求

~~~10.度小满二面~~~

代码语言:javascript复制
1.一大堆元素中,求最大的n个数(分治,大顶堆)
    1)分治 大顶堆排序
    2)小顶堆,然后遍历,每次跟小顶堆的元素做对比,小的废弃。
        大于的话,就把这个大的元素入堆即可。最终结果就是小顶堆。

2.查找第k大元素

2.golang的withgroup
    Add():每次激活想要被等待完成的goroutine之前,先调用Add(),用来设置或添加
    要等待完成的goroutine数量
例如Add(2)或者两次调用Add(1)都会设置等待计数器的值为2,表示要等待2个goroutine
完成
    Done():每次需要等待的goroutine在真正完成之前,应该调用该方法来人为
        表示goroutine完成了,该方法会对等待计数器减1
    Wait():在等待计数器减为0之前,Wait()会一直阻塞当前的goroutine

3.mysql的事务如何优化提升速度
    答:通过减小锁粒度和减少锁的持有时间进行调优
    (1)结合业务场景,使用低级别事务隔离
    (2)sql优化避免行锁升级表锁
    (3)更新等行锁操作放到事务后面,尽量减少持有锁的时间。
        比如我们先创建订单,执行逻辑,最后扣除库存。

4.红黑树和二叉树的区别,性能为什么比二叉树好?        
红黑树是一种平衡二叉查找树。它是为了解决普通二叉查找树在数据更新的过程中,
复杂度退化的问题而产生的。红黑树的高度近似 log2n,所以它是近似平衡,
插入、删除、查找操作的时间复杂度都是 O(logn)。

AVL 树是一种高度平衡的二叉树,所以查找的效率非常高,但是,有利就有弊,
AVL 树为了维持这种高度的平衡,就要付出更多的代价。每次插入、删除都要做调整,
就比较复杂、耗时
AVL树的查询性能更稳定,如果更新频繁的话,红黑树更好。
(1)红黑树的查询性能略微逊色于AVL树,因为他比avl树会稍微不平衡最多一层,
    也就是说红黑树的查询性能只比相同内容的avl树最多多一次比较,
 (2)红黑树在插入和删除上完爆avl树,avl树每次插入删除会进行大量的平衡度计算,
     而红黑树为了维持红黑性质所做的红黑变换和旋转的开销,相较于avl树为了维持
     平衡的开销要小得多


5.什么情况下用rabbitmq,什么情况下用kafka呢
    答:
    (1)吞吐量高的时候用kafka
    (2)需要低延迟的情况下用rabbitmq
    (3)部分延时队列用rabbitmq

~~~11.伴鱼一面~~~

代码语言:javascript复制
1.链表有next指针和random指针,深拷贝 (ok)
2.矩阵里面有1和0,1代表岛屿,求出所有岛屿的数量 (深度搜索)
3.二叉树的中序遍历,迭代方式
    1)根节点入栈,左节点入栈
    2)一直到左节点为null,出栈,打印值
    3)右节点入栈

4.php怎么连接rabbitmq和kafka的
    (1)rabbitmq
        1)php下载amqp扩展,里面带的也有rabbitmq扩展文件
        2)php通过amqp连接到mq上面,然后就可以发送调用了
    (2)kafka
        1)安装librdkafka 扩展
        2)连接broker,初始化生产者即可
微信公众号:码农编程进阶笔记,关注可获得更多的视频教程及面试技巧。

~~~12.知乎一面~~~

代码语言:javascript复制
1.M个有序数组的合并
有若干有序子数组, 合并为一个有序数组。假设M个子数组, 子数组的平均长度是 N

2.LRU Cache
支持普通put 和 get 功能, 长度限制 1k。 不考虑缓存时间失效策略。
    1)数组存储每个节点,key=>节点查询的时候先判断key在不在数组
    2)注意数组要定义长度的,超过长度则删除尾部值
    3)put的时候,注意数组满没满,没满就生成新节点,
    然后插入到链表头部

3.go的new和make的区别
(1)内置函数new按指定类型长度分配零值内存,返回指针,并不关心类型内部构造和
        初始化方式。(int、float、bool和string这些类型都属于值类型)
(2)内置函数make对引用类型进行创建,编译器会将make转换为目标类型专用的创建函数,
    以确保完成全部内存分配和相关属性初始化。
    (引用类型:slice、map、channel)
 (3)两个函数都有内存分配

~~~13.小米二面~~~

代码语言:javascript复制
 1.14亿个数的排序,求出第1亿个数是多少
 2.假如要你编写一个框架,DB类怎么实现
        conn()
        query()
 3.用sql求出每门课的平均分和最大分。
select b.课程名,avg(a.分数) as 平均成绩,max(a.分数) as 最高成绩,
min(a.分数) as 最低成绩
from 成绩表 a,课程表 b
where a.课程号=b.课程号
group by b.课程名

0 人点赞