一、前言
最近打算從二線去一線,借著遠程面試盛行的機會,果斷遠程面了一圈,遇到的面試題也都記了下來,主要是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**
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/293131.html
標籤:其他
上一篇:Kafka之Fetch offset xxx is out of range for partition xxx,resetting offset情況總結
