一、前言
廢話不多說,本篇是面試題系列的第二篇,文章面試題包括:快手,位元組,得物,滴滴,百度等,第一篇面試題為:
2021中大廠php+go面試題(1)
友情提示: 大部分的面試題都是重復的,因此會越寫越少,,
二、正文
1、快手一面
1.CDn作業原理
答:CDN通過廣泛的網路節點分布,提供快速、穩定、安全、可編程的全球內容分發
加速服務,支持將網站、音視頻、下載等內容分發至接近用戶的節點,使用戶可就近
取得所需內容,提高用戶訪問的回應速度和成功率,
https://blog.csdn.net/aliyunbaike/article/details/84952966
2.go的init用過嗎,主要使用場景是哪些?
答:每一個源檔案都可以包含一個 init 函式,該函式會在 main 函式執行前,
被 Go 運行框架呼叫,也就是說 init 會在 main 函式前被呼叫,
3.快速排序
4.go的map怎么洗掉元素
delete(map, 鍵)
如果要清空map元素,直接make一個新map就可以
5.go的syncmap
1)map在并發編程中,讀是執行緒安全的,寫不是
2)sync.map是執行緒安全的,不需要初始化,宣告即可
3)Store 表示存盤,Load 表示獲取,Delete 表示洗掉
4)
2、邊鋒一面
1.redis的連接跟mysql的連接有什么區別?為什么redis可以承受
更高的訪問量?
io多路復用
2.dns決議的具體流程
3.nginx訪問php的方式,優缺點,怎么訪問的
4.php獲取請求到生成opcode的程序 ,opcode是干什么的,跟機器碼有什么區別
答:
本質上一個opcode由兩個引數(op1,op2)、回傳值和處理函陣列成,它的官方解釋就
是PHP腳本編譯后的中間語言,類似于java中的bytecode或者是.net中的MSL,
5.php定義類外的 靜態變數,還有類里面的靜態變數有什么區別?你說到類里面的靜態變數是不會立刻釋放的,那么有100個請求去請求這個類,靜態變數的值累加,100次之后你認為這個值是多少,為什么
6.php定義類外的 靜態變數,還有類里面的靜態變數有什么區別?你說到類里面的靜態變數是不會立刻釋放的,那么有100個請求去請求這個類,靜態變數的值累加,100次之后你認為這個值是多少,為什么
答:都是在程式一啟動時就分配了記憶體空間,生命周期一樣,但是作用域和可見性卻
不一樣
1)類內部靜態變數必須通過類名或者物件名去訪問,該變數在整個程式中都可見,
2)類外的靜態變數只能在定義檔案中使用,只在定義檔案中可見,無法在非定義檔案中使用,
7.nginx發送請求到php,這個請求是什么格式的,里面都包含了哪些內容
用戶訪問域名->域名進行DNS決議->請求到對應IP服務器和埠->
nginx監聽到對應埠的請求->nginx對url進行location匹配->
執行匹配location下的規則->nginx轉發請求給php->
php-fpm的master行程監聽到nginx請求->master行程將請求分配給閑置的worker行程
->worker行程執行請求->worker行程回傳執行結果給nginx->nginx回傳結果給用戶
nginx連接php的方式:
1)tcp方式: ip+9000埠
2)unix_socket:(要求nginx和php必須在同一臺服務器上)
fastcgi_pass unix:/tmp/php-fpm.socket
8.redis做佇列,如果消費者掛掉了,這個資料丟失怎么辦
Redis有個命令叫做LPUSHRPOP(以及阻塞版本和不同方向的版本),即從一個佇列
彈出的同時將這個訊息送入另一個佇列,同時回傳給客戶端,原子操作,
9.rebbitmq會積壓到記憶體爆掉,kafka為什么不會
答:當RabbitMQ收到訊息時,如果是持久化訊息,則會儲存在記憶體中,同時也會寫入
磁盤;如果是非持久化訊息,則只會存在記憶體中
3、位元組一面
1.bitmap設定的長度是多大,用到了哪些hash函式
bitmap底層使用的還是字串結構,最多是512M
,也就是2的32次方 位
2.漸進式hash,對漸進式的理解
1)從dict.ht[0].table[0]的bucket進行rehash,
2)處理完一個bucket后,將ht[0].table[dict.rehashidx] 置為 NULL,
3)將dict.rehashidx加1,處理下一個bucket
查詢: 比較當前key的大小和 rehashidx的大小,來選擇
去h0還是h1中進行查詢,
3.kafka的磁區和消費者的分配原則
(1)磁區數: Tt / max(Tp, Tc)
(2) key怎么分配到磁區的?
1)有key的時候,做一次hash,根據磁區數取模
2)key為null,則隨機找一個磁區寫入
(3)磁區和消費者的策略
1)range :partitions的個數除于消費者執行緒的總數來決定每個消費者執行緒消費
幾個磁區,如果除不盡,那么前面幾個消費者執行緒將會多消費一個磁區
2)將所有主題的磁區組成 list串列,然后對 list串列按照 hashCode
進行排序
4.統計相同用戶的訪問次數,sort排序
cat logs/baidu.access.log | awk '{print $(NF-1)}' | sort | uniq -c | sort -k 1 -n -r|head -10
5.有n個任務,每個任務有開始和結束時間,如何安排任務的順序,
使得完成的任務個數是最多的
4、b站B部門一面
1.php實作鎖
1)靜態變數(同一個請求的話,會不釋放,多個請求的話會初始化)
2)apcu
將PHP代碼編譯之后所產生的bytecode暫存在共享記憶體內供重復使用,以提升應用的運行效率,(Opcode Cache)
提供用戶資料快取功能,需要顯示的呼叫,和redis/memcache類似,(User Data Cache)
2.訂單表的分庫,如何查詢?資料量比較大的話呢
淘寶的做法是拆分買家庫和賣家庫,也就是兩個庫:買家庫、賣家庫,
買家庫,按照用戶的id來分庫分表,賣家庫,按照賣家的id來分庫分表,
寫入的時候,先寫入買家庫,然后通過訊息佇列異步寫入到賣家庫,
3.http304產生的原因,我們可以用它來實作什么場景
4.redis的bitmqp的缺點
資料比較松散的情況下不好用
5.go的讀寫鎖
6.redis的list做佇列,當資料比較多的時候怎么處理?
(1)拆分法
可以遍歷list,拆分到多個list中去,多個list可以按照順序,比如1,-1000,
10001-2000 這樣取名字
(2)如果有范圍查詢的需求,可以考慮轉換為zset來處理范圍查詢,按照時間戳
來進行排序即可
7.外部請求介面很慢,該怎么排查?服務器資源不足怎么辦
(1)服務器擴容
(2)需要預估一個qps,擴容*120%即可
1.記憶體使用過高,頻繁gc導致cpu占滿
2.記憶體使用不高,出現了類似死回圈場景
通過第三方監控平臺查看堆疊資訊
8.lru的加鎖,在操作鏈表的時候需要加讀寫鎖的
5、貨拉拉一面
1.php-fpm的超時配置,超時之后會顯示什么
(1)Nginx 504 Gateway Time-out的含義是沒有請求到可以執行的PHP-CGI,
(2)Nginx 502 Bad Gateway的含義是請求的PHP-CGI已經執行,但是由于
讀取資源的程式沒有執行完畢而導致PHP-CGI行程終止,
(3) 502錯誤是php-fpm控制的,超時會終止cgi
(4) 504是nginx報出的,代表nginx連接fastcgi超時
2.codis的缺點是什么
(1)master掛掉了,只能靠運維人員去維護
(2)codis不保證資料一致性,不支持主從復制
(3)非官方出品,后續升級沒保障
(4)codis不支持事務
3.mysql的死鎖產生,還有如何防止
(1)按同一順序訪問物件,
(2)保持事務簡短并在一個批處理中,
(3)使用低隔離級別,
(4)避免長事務,將事務拆解
(5)設定鎖超時等待innodb_lock_wait_timeout
4.mysql分表的查詢問題,怎么不遍歷所有表做一個查詢
(1)借用redis或者資料表,存盤映射關系,映射查詢欄位和分表的關系
(2)找到分表之后,直接執行查詢,組合資料即可
5.php5.6和7.0的區別,寫代碼用到什么新特性了
(1)PHP7.0之前出現的致命錯誤,都改成了拋出例外
(2)增加了空結合運算子(??),效果相當于三元運算子
(3) PHP7.0新增了函式的回傳型別宣告
(4)define 可以定義常量陣列
為什么php7比較快:
1、存盤變數的結構體變小,盡量使結構體里成員共用記憶體空間,減少參考,
這樣記憶體占用降低,變數的操作速度得到提升,
2、字串結構體的改變,字串資訊和資料本身原來是分成兩個獨立記憶體塊存放,
php7盡量將它們存入同一塊記憶體,提升了cpu快取命中率,
3、陣列結構的改變,陣列元素和hash映射表在php5中會存入多個記憶體塊,
php7盡量將它們分配在同一塊記憶體里,降低了記憶體占用、提升了cpu快取命中率,
4、改進了函式的呼叫機制,通過對引數傳遞環節的優化,減少一些指令操作,
提高了執行效率,
6.laravel,yii,ci的區別,laravel的特點?控制反轉?依賴注入?
(1)laravel社區最活躍,支持的擴展也多
(2)yii的組態檔比較麻煩,不過支持多種環境配置
(3)laravel的路由比較強大,但是基于組件式,稍微臃腫點
(4)yii的view和model層不太好用
控制反轉:和依賴注入配合使用,只不過是用容器去系結依賴,
這樣相當于控制權給到了容器,
依賴注入:不用在程式里實體化類,可以通過引數的形式注入進去
ci:CI中的超級物件就是當前控制器物件,它提供了很多屬性.($this)
7.composer的自動加載機制
(1)autoload機制 可以使得 PHP 程式有可能在使用類時才自動包含類檔案,
而不是一開始就將所有的類檔案include進來,這種機制也稱為
Lazy loading (惰性加載),
(2)單個autoload也不方便,難以維護,所以就出現了composer:
composer 會找到符合 PR4 規范的第三方庫的源
將其加載到 vendor 目錄下
初始化頂級域名的映射并寫入到指定的檔案里
寫好一個 autoload 函式,并且注冊到 spl_autoload_register()里
8.redis的持久化,會持久化過期的key嗎
(1)aof是以記錄命令方式,所以如果還沒過期,那么不會有變更的命令,
如果過期,會在aof日志插入一條del命令,
(2)rdb的話是記憶體快照方式,如果持久化時,key已經過期,那么不會持久化,
如果在過期之前就已經持久化了,那么在恢復資料時,會判斷key是否過期,
如果過期不會匯入,
9.redis和memcache的區別
(1)存盤資料安全--memcache掛掉后,資料沒了;redis可以定期保存到磁盤(持久化);
(2)災難恢復--memcache掛掉后,資料不可恢復; redis資料丟失后可以通過aof恢復;
(3)redis資料結構更豐富
(4)Memcached單個key-value大小有限,一個value最大只支持1MB,而Redis最大支持512MB
6、滴滴一面
1.go的協程比執行緒輕到哪里了?
(1)協程在用戶態進行背景關系切換,耗時是執行緒的30/1,
執行緒需要:1,000 ~ 1,500 納秒
(2)協程初始2kb,執行緒好幾m
(3)執行緒切換:cpu背景關系(暫存器),私有的堆疊,執行緒狀態等
協程千幻:cpu背景關系(暫存器)
缺點:cpu無法實作搶占式呼叫協程
2.演算法題:堆排序
升序----使用大頂堆
降序----使用小頂堆
(1)為什么升序要用大頂堆呢
大頂堆的特點:每個結點的值都大于或等于其左右孩子結點的值,我們把大頂堆構建
完畢后根節點的值一定是最大的,然后把根節點和最后一個元素(也可以說最后一個節
點)交換位置,那么末尾元素此時就是最大元素了
3.go的runtime
(1)在把用戶寫的程式翻譯成可執行檔案的程序中,把 runtime 代碼塞進了
可執行檔案
1)初始化全域變數,
2)呼叫每個模塊的init函式
3)初始化 GC,以及初始化 Go scheduler
4) 啟用一個協程,呼叫用戶寫的 main 函式,
7、滴滴二面
1.php的autoload加載機制
//根據類名找到檔案
$path = str_replace('_', '/', $class_name);
//直接引入
require_once $path . '.php';
2.php的陣列擴容
(1)有個引數,當洗掉的數量比較多時,先進行rehash計算,去掉洗掉的部分
(2)洗掉比較少,空間不足,則擴容+rehash,申請2倍的空間,然后rehash計算
3.redis的zset結構
(1)資料量小于128或者存盤的key小于64則為ziplist
特點是記憶體連續,占用空間小
(2)正常情況下是跳躍表,存在多級索引,第一層是雙向鏈表結構
由許多層結構組成,
每一層都是一個有序的鏈表,最底層 (Level 1) 的鏈表包含所有元素,
如果一個元素出現在 Level i 的鏈表中,則它在 Level i 之下的鏈表也都會出現
(3)為什么用跳躍表不用紅黑樹
(1)跳躍表底層是雙向鏈表,適合做范圍查詢
(2)跳躍表的修改,洗掉只需要更改相鄰節點的指標,不需要重建樹
(3)跳躍表占用的記憶體比紅黑樹少一些
(3)zet結構
1)由字典和跳躍表構成,字典讓我們查詢單個元素的時間復雜度是o(1),
跳躍表主要是按照分值對元素排序.范圍查詢的時候,時間復雜度是o(loginN)
4.一個請求到php程式的程序
(1)三次握手之后,請求建立連接,進入全連接佇列,accept()函式可以取到
(2)Nginx會按照FastCGI協議的訊息格式發送資料,
worker行程再按照協議多次read()資料并決議
(3)詞法語法分析,生成語言片段
(4)zend引擎根據opcode呼叫機器指令
(5)執行用戶通過register_shutdown_function()注冊的關閉函式
(6)釋放資源,清理符號表,銷毀超全域變數,重置max_execution_time 等等
5.一個請求到go程式的程序
golang作為常駐行程, 請求第三方服務或者資源(http, mysql, redis等)完畢后,
需要手動關閉連接, 否則連接會一直存在;
(1)為什么請求之后要defer關閉
https://www.cnblogs.com/lovezbs/p/13197587.html
如果請求不手動關閉的話,每個請求都會創建兩個goroutine,y
分別去往這個連接寫入請求(writeLoop函式)和讀取回應(readLoop函式),
且請求結束不會主動釋放,會導致goroutine不斷增加,導致記憶體泄露
(2)go的鏈接復用問題
在go的原始碼中特意指出,需要讀取rep.body才能復用鏈接,有時候
我們只讀取header做判斷,code不是200就回傳錯誤,這樣的話該鏈接
就不會復用,導致出現大量的tcp : rst.
--- go作為客戶端
1.創建http.Client物件client
2.創建http.Request物件req
3.發送請求client.do(req)
4.關閉resp.Body.Close()
---- 原始碼部分
(1)client.do
主要是引數校驗,設定默認值,呼叫client.send
(2)client.send
cookie的裝載,獲取Transport物件,呼叫http.send
(3)http.send
校驗請求引數,超時取消(setRequestCancel)和請求事務: rt.RoundTrip(req)
(4)client.setRequestCancel
創建一個協程利用select chan機制阻塞等待取消請求
(5)Transport.RoundTrip
引數校驗,獲取快取的或新建的連接
(6)Transport.getConn
連接池有空閑則取出空閑連接
連接池無空閑則創建新連接
達到最大數量則阻塞,等待空閑連接
同時開啟了兩個goroutine,分別 讀取response 和 寫request
--- go作為服務端是如何處理的
http.HandleFunc("/hello", SayHello)
(0) 首先呼叫Http.HandleFunc
往DefaultServeMux的map[string]muxEntry中增加對應的handler和路由規則
(1)http.ListenAndServe(":9090", nil) //設定監聽的埠
(2)ListenAndServe內部使用net包呼叫了 net.Listen("tcp", addr) 來監聽埠
--- 接收客戶端請求
(1)啟動for回圈,使 Listener 不斷地接收來自客戶端的請求,accept
(2)給每個請求實體化一個conn,serve.NewConn
(3)呼叫go c.serve啟動協程,用戶的每一次請求都是在一個新的 goroutine中服務,互相不影響
--- 處理請求
(1)分析請求,取出請求體resp,req
(2)根據ServeMux 路由規則管理器map,判斷請求分發給哪些handle
(3)我們注冊的函式會轉化為handleFunc型別,然后通過
handler.serveHttp(resp,req),從而實作請求處理
6.gin框架的介面會開啟協程嗎
答:會的,參照上面的請求流程
客戶端:每次請求開啟兩個協程,負責讀寫
服務端:每次開啟一個協程處理請求
7.外部怎么訪問k8s的pod
(1)nodeip: service設定為nodeip型別,集群外就可以使用K8s任意一個節點
的IP加上30000埠訪問該服務了,kube-proxy會自動將流量以輪詢的方式轉發給
該service的每一個pod,
(2)LoadBalancer:公有云提供的負載均衡器,
任意節點的IP加30051埠訪問服務 10.97.121.42:30051
使用EXTERNAL-IP來訪問,這是云供應商提供的負載均衡IP
(3)ingress
K8s管理的負載均衡容器,它的鏡像包含一個nginx或HAProxy負載均衡器和
一個控制器守護行程
外部訪問URL,訪問該服務,入口是80埠,然后Ingress controller直接將流量
轉發給后端Pod,不需再經過kube-proxy的轉發,比LoadBalance方式更高效
8.k8s的服務發現
(1)通過service實作的
(2)etcd是干什么的
https://zhuanlan.zhihu.com/p/96721097
etcd 是一個分布式的、可靠的 key-value 存盤系統,它用于存盤分布式系統
中的關鍵資料,
使用Raft 一致性演算法來實作分布式一致性
服務注冊:
(1)提交服務配置,創建service物件,創建endpoint物件
(2)DNS監控service變化,注冊服務
服務發現:
(1)kube-proxy 監控ep變化,通過IPVS修改路由規則,去往service流量轉向pod
(2)服務通過DNS和service name 尋找 cluster ip
(3)流量轉發給 cluster ip,隨后被路由規則轉給 對應POD
(4)kube-proxy 監控 pod,一旦發現 pod 服務變化,將會把新的 ip 地址更新
到 service,kube-proxy 更新的存盤在 etcd 里的映射關系(ep)
9.dns決議步驟
1)作業系統會先檢查自己本地的hosts檔案是否有這個網址映射關系
2)如果hosts里沒有這個域名的映射,則查找本地DNS決議器快取
3)查找本地DNS服務器,也就是配置中的首選dns服務器
4)轉發模式:把請求轉發到上一層dns服務器,不斷轉發
5)非轉發: 先發送到13臺根服務器,根服務器回傳頂級域名服務器的ip,
客戶端訪問該ip,然后由頂級域名服務器進行下一級的查詢
6)為什么根域名只有13臺?
答:因為采用udp協議減小開銷,dns規定512位元組的傳輸上限,
一次回傳最多回傳13個根域名記錄
10.redis集群的分布式是什么樣子的
不同的服務在不同的節點上,這些服務組合起來才是完整的功能,
這就是分布式,
8、b站B部門二面
1.k8s的服務發現,服務注冊,服務呼叫是為了什么
服務注冊:
(1)提交服務配置,創建service物件,創建endpoint物件
(2)DNS監控service變化,注冊服務
服務呼叫:
(1)有兩種方式,一種是RPC方式,另一種是事件驅動(Event-driven)方式,也就是發訊息方式
服務發現:
(1)kube-proxy 監控ep變化,通過IPVS修改路由規則,去往service流量轉向pod
(2)服務通過DNS和service name 尋找 cluster ip
(3)流量轉發給 cluster ip,隨后被路由規則轉給 對應POD
(4)kube-proxy 監控 pod,一旦發現 pod 服務變化,將會把新的 ip 地址更新
到 service,kube-proxy 更新的存盤在 etcd 里的映射關系(ep)
2.k8s我們發布服務的時候,怎么進行一個平滑啟動
答:https://yuerblog.cc/2019/12/11/k8s-%E5%A6%82%E4%BD%95%E5%B9%B3%E6%BB%91%E5%8F%91%E5%B8%83%E5%BA%94%E7%94%A8%EF%BC%9F/
上線:
1)POD上線需要配置健康檢查
2)健康檢查通過,service才會將POD加入endpoints串列,
流量進入可以正常回應
下線:
1)POD下線時會在etcd中先標記POD狀態為terminating退出中,
其他相關聯動資源會監聽到變化并采取后續動作,
2)先關閉監聽,處理完已有請求,退出行程
3.redis集群擴容的一個程序
(1)添加節點
(2)分配hash槽,可以全部重新分配,
也可以指定節點分出一部分槽出來
(3)槽遷移
每個節點計算自己的槽,比如原來節點負責5000個槽,現在負責4000個,
則把多余的1000遷移到新節點,主要是遷移槽上的key,
4.mysql同時讀寫一行會資料,會觸發鎖嗎
對于普通SELECT陳述句,InnoDB不會加任何鎖,所以讀寫同時進行沒有問題,
讀為快照讀,寫為當前讀
5.go的map的底層結構
(1)結構:散串列+bucket
(2)底層一個陣列arr
index = hash(key)
arr[index] = struct{xxxx}
(3)每個bucket中可以存盤8個kv鍵值對,
(4)hash值的低八位和bucket陣列長度取余,定位到在陣列中的那個下標,
hash值的高八位存盤在bucket中的tophash中,用來快速判斷key是否存在,
6.redis和mysql的強一致性如何實作
(1)兩段式提交,引入一個協調者,
事務A和事務B都分為準備和提交階段,狀態同步給協調者,任一步驟出問題則回滾
(2)raft演算法實作
7.kafka在擴容的時候,怎么在不影響現有業務的情況下擴容
(1)kafka新增節點,對其他節點來說是無感知的
(2)在新節點上創建topic,或者遷移topic磁區即可
8.redis的熱點key如何處理
(1)收集熱點key,比如自己寫日志或者用redis提供的命令
(2)加載到記憶體,直接在記憶體中讀取
(3)熱點key分散,加個亂數進行分散,分散到多個redis機器,
讀的時候隨機從有備份的redis上讀取即可
9.redis集群的腦裂如何避免
min-slave解決,當腦裂之后,會有一個磁區的slave升級為master,
此時根據組態檔,當slave少于配置個數則停止寫入,
這樣保證只有一個master提供寫入,等網路恢復也不會有問題
10.es的架構,index的內部實作
https://blog.csdn.net/u013380694/article/details/101760607
它可以將索引劃分為多個分片,可以部署到集群中的任何一個節點,
且每個分片都有副本,實作高可用
(1)它允許水平切分內容卷,
(2)它允許通過分片來分布和執行操作來應對日益增長的執行量,
(3)一個索引就像資料庫,而type就相當于每一張表,
而mapping就相當于表的結構定義,定義了什么欄位型別等
往index的一個type里添加一行資料就叫做一個document
每一個document有多個field
查詢:
(1)客戶端發送請求到協調節點
(2)協調節點從分片上查詢資料,由協協調節點進行聚合
(3)合并資料,回傳給客戶端
9、回響科技一面
1.kafka多個磁區怎么保證訊息順序
(1)首先發送訊息可以通過指定key+單磁區實作
(2)多個消費者消費的時候,可以自己對key取模,放入到佇列中,
開多執行緒去消費這些佇列,佇列內是有序的
2.mysql在沒有隔離級別的情況下,多執行緒修改一行資料可以嗎
(1)隔離級別是為了解決事務的并發問題,比如臟讀,不可重復讀,幻讀問題等
(2)當沒有隔離級別的時候,多執行緒修改一行資料,就會
出現:原始資料是0.執行緒1想+1,執行緒2也想+1,那么同時執行,結果是2,
但是對于執行緒1來說,我只是想+1而已
3.冪等性和執行緒安全?兩個執行緒修改一個變數,為什么不行
(1)確保在多執行緒訪問的時候,我們的程式還能按照我們預期的行為去執行,
那么就是執行緒安全,
(2)兩個執行緒修改一個變數是可以的,但結果可能不是我們想要的
4.redis為什么要有單執行緒,除了鎖還有其他原因嗎
(1)鎖開銷
(2)不存在多行程或者多執行緒導致的切換而消耗CPU
(3)無法發揮多核CPU性能,不過可以通過在單機開多個Redis實體來完善,
同時給redis實體系結cpu核即可發揮多核的優勢
5.rpc和http訪問的區別在哪
相同點:底層通訊都是基于socket,都可以實作遠程呼叫,都可以實作服務呼叫服務
不同點:
(1)速度來看,RPC要比http更快,雖然底層都是TCP,但是http協議的資訊往往比較臃腫
(2難度來看,RPC實作較為復雜,http相對比較簡單
(3)如果對效率要求更高用rpc,靈活性通用性要求高用http
(4)rpc是長連接,http是短連接,效率更高
(5)rpc可以壓縮訊息,實作更極致的流量優化
6.mysql直接修改庫存有什么問題?
(1)沒什么問題,主要是怕mysql承受不住太大的流量掛掉
(2)常規方法是庫存設定無符合,不能是負數,使用事務,
代價是速度比較慢
(3)我們可以考慮使用樂觀鎖,查詢出version,修改的時候根據version來修改
7.go中的鎖是怎么實作的
互斥鎖:通過狀態status和信號量來實作的,
協程1加鎖的話,lock=1
協程2加鎖的話,waiter=1,代表等下
鎖釋放:
協程A主要是通過釋放信號量來通知協程b,此時協程B可以加鎖
自旋:加鎖失敗會持續請求加鎖,不會立刻阻塞,是通過cpu的空轉實作的,30個時鐘周期
8. redis的rdb和aof程序大概說一下
10、全民快樂一面
1.go常用的包有哪些,說說http和io包的函式
2.php的trait函式,trait參考的方法和原父類方法哪個優先級比較高
父級使用trait關鍵字,當前類 通過use使用父類
(1)代碼復用,相當于copy了一份代碼
(2)類成員優先級為:當前類>Trait>父類
3.mysql主從不一致的原因,在配置一樣,不考慮網路因素的情況下
(1)主從兩臺機器的負載不一致,執行緒忙不過來
(2)max_allowed_packet ,主庫設定的大,當有大sql的話,從庫無法執行
(3)自增鍵不一致 ,自增步長不一致導致
(4)同步引數未設定
=1
4.go的channel怎么保證執行緒安全的
(1)channel內部維護了一個互斥鎖,來保證執行緒安全
5.100W用戶刷視頻,怎么保證用戶刷的視頻不不重復
6.php安裝擴展的步驟,編譯的命令是哪個
1. wget extension.tar.gz下載相應的擴展包并解壓,
2. cd extension/切換到擴展extension的目錄中
3. /php/bin/phpize 運行php安裝目錄下的phpize檔案,
這時候會在extension目錄下生成相應的configure檔案,
4. /configure --with-php-config=/php/bin/php-config 運行配置,
如果你的服務器上只是裝了一個版本的php則不需要添加--with-php-config ,后面的引數只是為了告訴phpize要建立基于哪個版本的擴展,
5. make && make install 編譯模塊
7.唯一索引和主鍵索引的區別
(1)一個表只能有一個主鍵索引,可以有多個唯一索引;
(2)主鍵索引一定是唯一索引, 唯一索引不是主鍵索引;
(3)主鍵可以與外鍵 構成 參照完整性約束, 防止資料不一致,
11、貨拉拉二面
1.kafka保證訊息順序性寫入
生產者發送訊息的send有四個引數
(磁區號、時間戳、key、headers),我們可以指定key,
來保證訊息都發送到同一個磁區
2.php的while..true常駐行程會造成什么影響
3.快取擊穿和快取穿透
(1)快取擊穿,是redis額熱點key過期
1)不給熱點key設定過期時間
2)互斥鎖,發現無快取,加鎖去更新快取
(2)快取穿透是redis+mysql都頂不住了
1)引數校驗,防止不存在的key
2)布隆過濾器
3)快取空值或者默認值
12、b站B部門三面
1.介面網路超時如何排查
(1)代碼層面
1)下游sql等查詢是否超時
2)資料庫連接是否滿了,代碼中是否出現死回圈等
占用大量的cpu和記憶體
(2)網路層面
1)運營商網路問題
2.kafka的offset和mysql的索引的區別
kafka索引:
(1)偏移量索引檔案用來建立訊息偏移量(offset)到物理地址之間的映射關系,
方便快速定位訊息所在的物理檔案位置;
(2)時間戳索引檔案則根據指定的時間戳(timestamp)來查找對應的偏移量資訊,
查找步驟:
(1)根據offset找到日志分段的索引檔案(.index檔案)
(2)讀取偏移量索引索引檔案,使用二分找到最大索引項
(3)讀取日志分段檔案并且從日志分段檔案中順序查找(.log檔案)
relativeOffset對應的訊息
區別:
(1)kafka是系數索引,mysql是b+樹索引
(2)kafka維護索引使用了跳躍表結構,索引維護結構不會隨便變動,有新索引
檔案才更新,mysql的索引樹更新比較頻繁
(3)應用場景不同,kafka是主要是順序寫入,順序讀出,很少有檢索的操作,
3.x=1 and y>1 order by z如何建索引
13.好未來一面
1.服務間通信的實作
微服務必須使用行程間通信機制來互動,微服務架構
異步訊息機制和同步請求/回應機制這兩類 IPC 機制可用
2.服務探針的實作
存活探針:為了查看容器是否正在運行,如果回傳false則重啟pod
就緒探針:查看容器是否準備好接受HTTP請求,通過則把流量發到pod上
存活探針和就緒探針被稱作健康檢查,
3.行程,執行緒間的通信方式
行程--------
1)管道( pipe ):一般是父子行程通信
2)信號量:主要作為行程間以及同一行程內不同執行緒之間的同步手段
也是一種鎖機制
3)共享記憶體:最快的ipc通信
4)套接字:可用于不同的行程通信,
5)訊息佇列:由訊息的鏈表,存放在內核中并由訊息佇列識別符號標識
執行緒----------------
執行緒間的通信目的主要是用于執行緒同步,所以執行緒沒有像行程通信中的用于
資料交換的通信機制,
1)鎖機制:包括互斥鎖、條件變數、讀寫鎖
2)信號量機制(Semaphore):包括無名執行緒信號量和命名執行緒信號量
3)信號機制(Signal):類似行程間的信號處理
14、映客一面
1.手寫lru
2.mysql的acid分別是怎么實作的
Atomicity)原子性: 事務是最小的執行單位,不允許分割,原子性確保動作
要么全部完成,要么完全不起作用;
(1)通過undo日志,事務回滾時能夠撤銷所有已經成功執行的sql陳述句
(Consistency)一致性: 執行事務前后,資料保持一致;
(1)一致性是事務追求的最終目標,前問所訴的原子性、持久性和隔離性,
其實都是為了保證資料庫狀態的一致性,
(Isolation)隔離性: 并發訪問資料庫時,一個事務不被其他事務所干擾,
(1)四種隔離級別實作的
(Durability)持久性: 一個事務被提交之后,對資料庫中資料的改變是持久的,
即使資料庫發生故障,
(1)Innnodb有很多 log,持久性靠的是 redo log,
(2)如果出現緩沖丟失,可以從redo logo日志中恢復
3.go的syncmap怎么實作并發安全的
(1)步驟
a、過 read 和 dirty 兩個欄位將讀寫分離,讀的資料存在只讀欄位 read 上,
將最新寫入的資料則存在 dirty 欄位上
b、讀取時會先查詢 read,不存在再查詢 dirty,寫入時則只寫入 dirty
c、讀取 read 并不需要加鎖,而讀或寫 dirty 都需要加鎖
d、另外有 misses 欄位來統計 read 被穿透的次數(被穿透指需要讀 dirty
的情況),超過一定次數則將 dirty 資料同步到 read 上
e、對于洗掉資料則直接通過標記來延遲洗掉
(2)原理
sync.map實作就是依靠兩張map對讀操作和寫操作分離,后續根據需要在把
dirty map合入 read map中,
4.tcp的timewait怎么產生的,如何防范
作用:
(1)保證服務器能收到最后一次ack
(2)同時2msl能保證舊報文消失,防止舊報文出現在新的連接中
防范:
(1)服務器設定套接字:so_reuseaddr
(2)短連接改成長連接
(3)linux內核引數:net.ipv4.tcp_tw_reuse
5.介紹專案的時候,最好是把技術堆疊各方
面都詳細的說一下
6.插入100W資料,大概耗時多少,如何優化?批量插入的時候會影響
其他操作嗎,如何優化?
7.redis的分布式鎖在高并發情況下會出現什么問題
(1)鎖續約問題,可以用redisson的看門狗機制
(2)鎖超時時間一定要設定
(3)根據value上鎖,防止釋放鎖混亂
(4)分布式場景下,master加鎖之后掛掉,slave會成為新的master
此時A客戶端認為自己上鎖了,B客戶端也能獲取到鎖,會造成鎖混亂
使用redlock會好一些
8.https到底是對稱加密還是非對稱加密?
答:是非對稱加密(公私鑰) +對稱加密(傳輸內容對稱加密)
1.客戶端請求服務端,獲取公鑰,
2.服務端生成公私鑰,自己保存私鑰(SK),將公鑰(PK)發給客戶端,
3.客戶端生成隨機字串key,通過公鑰(PK)加密后發送給服務端,
4.服務端拿到加密后的內容后,用自己的私鑰(SK)進行解密,得到key,
后續的程序都是通過密鑰(key)來進行對稱加密來傳輸,
15、小豬民宿
1.php的worker執行緒假死,如何重連的
(1)首先,php-fpm假死一般是執行緒繁忙或者請求數過多,超時等原因,
主要是修改組態檔,增加請求數量限制,超時時間等,
(2)kill掉worker之后,master行程會自動創建一個work出來
2.設計php框架的問題
3. mysql的查詢優化器作業原理
(1)主要是判斷cost,cost包括掃描行數等
(2)參照mysql拾遺
4.單機多少配置才能頂住1000qps
參考:https://blog.csdn.net/weixin_34346099/article/details/88679411
假如機器是4核8G:
(1)同時處理的請求做多4個
(2) 假設一個行程30M,那么4048/30 = 135(留一些給mysql)
(3) 假設一個請求200ms,那么1000qps要求
T = (1000 / n ) * t
總耗時:T
一次處理請求:n
每次請求時間:t 200ms
(4) 根據計算可以得知,我們一次要處理200個請求才行
首先我們的worker行程是足夠的,其次是4核也夠用
(5 )其他需要注意的點
1)mysql連接數
2) 服務器句柄限制, ulimit -n查看
3)tcp的timewait影響,允許埠復用
5.壓測需要注意的引數
1)qps
2) 請求處理時間
3)
16.滴滴二面
1.一致性hash
(1)對2的32次方取模,構造0-2的32次方哈希環
(2)對服務器的IP或主機名作為關鍵字進行哈希
(3)計算key的hash,計算出在哪個空間,順時針往下尋找節點即可
(4)為了防止資料分布不均勻,構造虛擬節點
(5)節點宕機,只會影響不一部分資料,其他節點還能正常使用
2.兩個檔案a和b里面都是id,求出不同的id
(1)分治,hash(id)%1000,分成1000個小檔案,a0...a99
(2)此時a99中的id肯定也在b99中,檔案就轉化為了對小檔案的求去重
(3) 讀取a0,構造hash表,再便遍歷b0,如果a0中不存在則為不重復
的id,放入新集合中即可
4.myisam的應用場景
特點:高性能讀取,存盤的有行數,無事務
場景:(1)需要頻繁count的場景
(2)讀多寫少的場景
17.百度1-5面
大部分八股文跟上面雷同,只記錄不同的
1.對于團隊管理的理解
2.專案的架構設計,為什么這么設計
3.字串中,中括號,大括號,小括號, 判斷是否匹配(演算法)
4.亂序陣列,構建二叉樹(演算法)
三、后記
以上就是博主歷時2個月的面試記錄了,提醒大家一定要刷演算法題,博主基本都是倒在演算法題上面,其次面試看運氣,不要在意,面就完了,
end
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/293127.html
標籤:其他
