2021中大厂php+go面试题(1)

一、前言

      最近打算从二线去一线,借着远程面试盛行的机会,果断远程面了一圈,遇到的面试题也都记了下来,主要是php+go的部分面试题。部分问题附带答案,希望对大家找工作能有帮助。

      首先面试都是从小公司到大公司的过程,小公司主要为了练手,熟悉面试节奏,后面才去面大公司。尽量不要一开始就奔着大公司去,容易出现准备不足的情况。。。另外,算法是真的难!

      本文章主要是部分小公司,马蜂窝,得物,b站,小米,度小满,知乎等。剩余部分面试题放到另一篇文章:
2021中大厂php+go面试题(2)

友情提示: 大部分的面试题都是重复的,因此会越写越少。。

二、正式面试题

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

6.小米一面

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.度小满一面

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站一面

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部门二面

---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.度小满二面

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.伴鱼一面

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.知乎一面

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.小米二面

 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.课程名

**介于篇幅过长,剩下部分放到另一篇文章。希望对大家能有帮助。

2021中大厂php+go面试题(2)

end**

在Python期末考试,一道常见的大题可能会要求学生综合运用所学的编程知识,解决实际问题。例如,考试可能会出现一个涉及文件操作、函数定义、条件判断和循环处理的大题。下面是一个可能的题目示例: ``` 题目:学生信息管理系统 描述: 你被要求设计一个简单的学生信息管理系统,该系统能够记录学生的姓名和成绩,并提供查询和更新信息的功能。请使用Python编程实现以下功能: 1. 创建一个学生信息字典,其学生姓名作为键,成绩作为值。 2. 实现一个函数add_student,它接受两个参数:学生姓名和成绩,然后将学生信息添加到字典。 3. 实现一个函数update_student_score,它接受学生姓名和新的成绩作为参数,如果学生存在,则更新其成绩。 4. 实现一个函数get_student_score,它接受学生姓名作为参数,返回该学生的成绩。如果学生不存在,返回“学生不存在”。 5. 实现一个函数print_all_students,打印出所有学生的姓名和成绩。 6. 实现一个简单的文本文件操作,将所有学生信息保存到一个名为students.txt的文件,并从该文件读取学生信息。 要求: - 使用字典来存储学生信息。 - 每个函数都需要有适当的错误处理,确保程序的健壮性。 - 在主程序使用上述函数完成学生信息的添加、更新、查询和文件保存功能。 请提供完整的Python代码实现以上功能,并在代码包含必要的注释。 附加题: - 在主程序添加一个功能,允许用户输入命令来执行不同的操作,例如添加新学生、更新成绩等。 - 增加异常处理,例如处理文件读写错误或输入错误等。 ``` 请注意,这只是一个示例题目,实际的考试题目可能会有所不同,具体取决于教学大纲和教师的授课内容。学生需要根据题目的要求编写代码,并确保代码能够正确运行和处理各种情况。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铁柱同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值