頭條面試總結
阿新 • • 發佈:2019-01-04
頭條面試問題整理
- 自我介紹
- 專案詳細介紹
- 演算法題
- LeetCode上一個題,給定一個二維陣列和目標值,該二維陣列每一行和每一列都是非遞減的,問二維陣列中的等於目標值的座標。
- 實現一個排序樹,能插入,能刪除,能平衡
- 輸入一個數組,要求輸出陣列中每個數字後面第一個比他大的數字,沒有比他大的輸出-1,時間複雜度O(n)。輸入:5,1,9,6,7 輸出:9,9,-1,7,-1
- 二維陣列中,只能向右和向下,找到從左上角到右下角路徑中和最大的值
- 堆排序
- 快排思想
- 兩個棧實現佇列
- 單鏈表排序
- 資料庫
- MySQL的索引原理、資料庫索引型別
- 索引原理
- 本質
通過不斷地縮小想要獲取資料的範圍來 篩選出 最終想要的結果,同時把隨機的事件變成順序的事件。 - 從資料結構角度考慮,為什麼使用B樹而不用AVL樹or紅黑樹?
- 定義:AVL樹是嚴格的二叉排序樹(保持平衡),紅黑樹是非嚴格的二叉排序樹(不用儲存平衡)
- 顯然,AVL樹和紅黑樹都比B樹的高度h高,因為B樹的節點度d更大(B樹高度O(logdN))
- I/O次數越多:一個節點大小佔用OS中的一個邏輯頁,對應了一次I/O,因此樹越高一次查詢次數(即為樹高)越多。
- 無法運用程式區域性性原理:以為樹太高導致邏輯上很近的節點(父子節點)物理上可能很遠。
- 因此,B樹的I/O次數明顯低於AVL樹和紅黑樹,故選擇B樹作為索引結構。
- 從計算機組成原理角度考慮,大多數資料庫系統為什麼使用B+樹(對B+樹結構做了優化,添加了葉子節點的順序指標)而不使用B-樹?
- 利用磁碟預讀原理(根據區域性性原理),DB的設計者將1個節點大小設定為1頁,故1個節點只需1個I/O就能完全載入。
- B樹一次查詢最多查詢次數=h-1(高度為h,根節點常駐記憶體)
- 樹高度h=O(logdN)。實際應用中,出讀d是非常大的數字,通常超過100,因此h非常小。
- B-樹高度>B+樹高度
因為d(B+)>d(B),d的計算公式為:d=節點大小(1頁,4或8kB)/(keysize+datasize+pointsize)。而B+樹的非葉子節點沒有data域,故datasize值為0,故其d更大。 - 優化後的B+樹能幹嘛?
提高區間訪問的效能。
eg:如果要查詢key為從18到49的所有資料記錄,則從第一個葉子節點開始查詢,當找到key=18後,只需順著結點和指標順序遍歷就可以一次性訪問到所有資料結點,極大提到了區間查詢效率。
- 補充常識
目前大部分資料庫系統和檔案系統都採用B-樹或者B+樹作為索引結構。
- 本質
- 索引原理
- 型別
- 普通索引index :加速查詢
- 唯一索引
- 主鍵索引:primary key :加速查詢+約束(不為空且唯一)
- 唯一索引:unique:加速查詢+約束 (唯一)
- 聯合索引
- primary key(id,name):聯合主鍵索引
- unique(id,name):聯合唯一索引
- index(id,name):聯合普通索引
- 全文索引fulltext :用於搜尋很長一篇文章的時候,效果最好。
db表message_queue_history,包括欄位- 建立語法
alter table message_queue_history add fulltext index idx_message (message); - 查詢語法
select * from message_queue_history where match(message) against(‘10086’);
- 建立語法
- 空間索引spatial :瞭解就好,幾乎不用
- B+樹
- 索引欄位要儘量的小
- 因為IO次數取決於b+樹的高度h ,而索引欄位影響了樹的高度
- 如何影響?
假設當前資料表的資料為N,每個磁碟塊的資料項的數量是m,則有h=㏒(m+1)N,當資料量N一定的情況下,m越大,h越小;而 m = 磁碟塊的大小/資料項的大小,磁碟塊的大小也就是一個數據頁的大小,是固定的,如果資料項佔的空間越小,資料項的數量越多,樹的高度越低。
- 索引的最左匹配特性(即從左往右匹配)
- 索引欄位要儘量的小
- MySQL的索引原理、資料庫索引型別
- 網路通訊
- 畫出tcp斷開連線的圖
- 過程
- Client端收到App的關閉訊號後,傳送FIN給Server端。此後Client處於Fin_Wait1狀態。
- Sever端收到來自Client端的Fin訊號後,傳送Ack訊號給Client端,告訴App可以關閉連線了。此後Server進入Close_Wait狀態。
- Client端收到來自Server端的Ack訊號。此後Client進入Fin_Wait2狀態,等待Server傳送Fin訊號。
- Server端的App準備關閉連線時,傳送Fin訊號給Client端。此後Server進入Last_Ack狀態。
- Client端收到來自Server端的Fin訊號後,傳送Ack訊號給Server端,並等待2msl時長再關閉本地核心連線。此後Client進入Time_Wait狀態。
- 圖[畫圖]
- 過程
- tcp四次揮手 和 狀態 以及 為什麼等待2msl,如果伺服器一直沒收到一直髮斷開連線請求的話,它會一直重置這個時間嗎
- 套接字狀態
- Client端依次為:Fin_Wait1、Fin_Wait2、Time_Wait
- Server端依次為:Close_Wait、Last_Ack
- 狀態說明
- Fin_Wait1
- 連線處於半段開狀態(可以接受、應答資料,當不能傳送資料),並將連線的控制權託管給 Kernel,程式就不再進行處理。
- 一般情況下,連線處理 FIN_WAIT1 的狀態只是持續很短的一段時間
- Fin_Wait2
- 在等待對方的 FIN 資料報
- 當 TCP 一直保持這個狀態時,對方就有可能永遠都不斷開連線,導致該連線一直保持著。
- 不屬於任何應用的孤兒連線保持Fin_Wait2狀態的最長時間預設為:60s。超過這個時間,就會被本地直接關閉,不會進入Time_Wait狀態。
- 處於 Fin_Wait2 狀態的TCP連線,威脅要比 Fin_Wait1 的小,佔用的資源也很小,通常不會有什麼問題。
- Time_Wait
- Client端發出Ack響應後,核心會設定一個時間長度為 2MSL 的定時器,當定時器在到時間點後,核心就會將該連線關閉。
- 當連線尚未關閉的時候,又收到了對方傳送過來的 FIN 請求(可能是我們傳送出去的請求對方並未收到),或者收到 ICMP 請求(比如 ACK 資料報,在網路傳輸中出現了錯誤),該連線就會重新發送 ACK 請求,並重置定時器。
- Close_Wait
- 該連線可能有資料需要傳送,或者一些其他事情要做
- 當這類連線過多的時候,就會導致網路效能下降,耗盡連線數,無法建立新的連線。
- Last_Ack
- 等待對方進行傳送 ACK 資料報
- 收到了響應的ACK資料報後,連線進入CLOSED 狀態,並釋放相關資源。
- 如果超時未收到響應,就觸發了TCP的重傳機制。
- Fin_Wait1
- 等待2msl的原因
保證Server端能夠收到Client端傳送的最後一個Ack訊號。而Server端在收到最後一個Ack訊號後才能關閉連線。
- 套接字狀態
- 程序間通訊方式 & 執行緒間通訊方式
- 程序間通訊方式
- 分類
管道、訊息佇列、訊號量、共享記憶體、Socket、Streams等。其中Socket、Streams支援不同主機上的兩個程序通訊。 - 分別介紹
- 管道
- 無名管道(預設)
- 它是半雙工的(即資料只能在一個方向上流動),具有固定的讀端和寫端
- 它只能用於具有親緣關係的程序之間的通訊(也是父子程序或者兄弟程序之間)
- 它可以看成是一種特殊的檔案,對於它的讀寫也可以使用普通的read、write 等函式。但是它不是普通的檔案,並不屬於其他任何檔案系統,並且只存在於記憶體中
- 通常呼叫 pipe 的程序接著呼叫 fork,這樣就建立了父程序與子程序之間的 IPC 通道
- 有名管道
- 名字:FIFO
- FIFO可以在無關的程序之間交換資料
- FIFO有路徑名與之相關聯,它以一種特殊裝置檔案形式存在於檔案系統中
- 在資料讀出時,FIFO管道中同時清除資料,並且“先進先出”
- 無名管道(預設)
- 訊息佇列
- 存放在核心中
- 一個訊息佇列由一個識別符號(即佇列ID)來標識
- 訊息佇列是面向記錄的,其中的訊息具有特定的格式以及特定的優先順序
- 訊息佇列獨立於傳送與接收程序。程序終止時,訊息佇列及其內容並不會被刪除
- 訊息佇列可以實現訊息的隨機查詢,訊息不一定要以先進先出的次序讀取,也可以按訊息的型別讀取
- 訊號量
- 它是一個計數器。訊號量用於實現程序間的互斥與同步,而不是用於儲存程序間通訊資料
- 訊號量用於程序間同步,若要在程序間傳遞資料需要結合共享記憶體
- 訊號量基於作業系統的 PV 操作,程式對訊號量的操作都是原子操作
- 每次對訊號量的 PV 操作不僅限於對訊號量值加 1 或減 1,而且可以加減任意正整數
- 支援訊號量組
- 共享記憶體
- 是最快的一種 IPC,因為程序是直接對記憶體進行存取
- 因為多個程序可以同時操作,所以需要進行同步
- 訊號量+共享記憶體通常結合在一起使用,訊號量用來同步對共享記憶體的訪問
- Socket
- 參考部落格:https://blog.csdn.net/Hearthougan/article/details/51275765
- 通訊流程
- 圖片
- 說明
- 伺服器根據地址型別(ipv4,ipv6)、socket型別、協議建立socket
- 伺服器為socket繫結ip地址和埠號
- 伺服器socket監聽埠號請求,隨時準備接收客戶端發來的連線,這時候伺服器的socket並沒有被開啟
- 客戶端建立socket
- 客戶端開啟socket,根據伺服器ip地址和埠號試圖連線伺服器socket
- 伺服器socket接收到客戶端socket請求,被動開啟,開始接收客戶端請求,直到客戶端返回連線資訊。這時候socket進入阻塞狀態,所謂阻塞即accept()方法一直到客戶端返回連線資訊後才返回,開始接收下一個客戶端諒解請求
- 客戶端連線成功,向伺服器傳送連線狀態資訊
- 伺服器accept方法返回,連線成功
- 客戶端向socket寫入資訊
- 伺服器讀取資訊
- 客戶端關閉
- 伺服器端關閉
- 圖片
- 流程解釋
- 建立一個套接字—socket()
socket()函式用於根據指定的地址族、資料型別、協議來分配一個套接字的描述字及其所用的資源 - 指定本地地址—bind()函式
將本地地址與一套接字捆綁。當socket()建立套接字 後,它便存在於一個名字空間(地址族)中,bind()函式通過給一個未命名的套接字分配一個本地名字,來為套接字建立本地捆綁(主機地址) - 監聽和請求連線—listen()、connect()函式
- 建立套接字連線—accept()函式
呼叫accept()函式來接收請求,這樣連線便建立好了,之後就可以網路I/O操作了,即類同於普通檔案的讀寫I/O操作 - 資料傳輸—send()與recv()
客戶端and伺服器應用程式都用send函式來向TCP連線的另一端傳送資料
ps:send函式僅僅是把buf中的資料copy到sockfd的傳送緩衝區的剩餘空間裡,而不是sockfd的傳送緩衝中的資料傳送到連線的另一端,那是協議乾的活 - 關閉套接字—close()
關閉套接字s,並釋放分配給該套接字的資源
- 建立一個套接字—socket()
- Streams
- 管道
- 分類
- 執行緒間通訊方式
- 同步:如synchronized關鍵字
- 輪詢:如CAS機制
- wait/notify機制
- 管道通訊(共享記憶體)
- 程序間通訊方式
- 網路分層
- 網路層的功能
控制子網的執行。如分組傳輸、路由選擇等。 - 傳輸層協議
tcp、udp - Tcp和Udp區別
- 是否面向連線
- 是否可靠
- 面向位元組流or報文
- 是否執行多對多
- Tcp只能一對一建立連線,傳輸資料。
- Udp允許一對一,一對多,多對一和多對多的互動通訊。
- 首部開銷大小
Tcp為20B,Udp為8B。
- Tcp長連線和短連線
- 長連結
- 定義
指在一個TCP連線上可以連續傳送多個數據包,在TCP連線保持期間,如果沒有資料包傳送,需要雙方發檢測包以維持此連線,一般需要自己做線上維持 - 過程
連線→資料傳輸→保持連線(心跳)→資料傳輸→保持連線(心跳)→……→關閉連線(一個TCP連線多個讀寫通訊) - 優勢
- 提高傳輸速度
- 降低連續建立連線的資源消耗
- 適用物件
操作頻繁(讀寫),點對點的通訊,而且連線數不能太多情況
- 定義
- 短連線
- 定義
短連線是指通訊雙方有資料互動時,就建立一個TCP連線,資料傳送完成後,則斷開此TCP連線。 - 過程
連線→資料傳輸→關閉連線 - 優勢
管理簡單,存在的連線都是有用的連線,不需要額外的控制手段 - 適用物件
WEB網站的http服務一般都用短連結.
ps:http1.0 -----只支援短連線;
http1.1 -----keep alive 帶時間,操作次數限制的長連線
- 定義
- 長連結
- socket的理解,底層,如何做一個server
- 畫出tcp斷開連線的圖
- 作業系統
- 多程序和多執行緒的區別
- 為什麼區分核心態和使用者態
- 使用者態
程序執行自己的程式碼時 - 核心態
發生系統呼叫時,程序執行核心的程式碼 - 原因
- CPU的所有指令中,有一些指令是非常危險的,如果錯用,將導致整個系統崩潰
- CPU將指令分為特權指令和非特權指令.
- 危險的指令,只允許作業系統及其相關模組使用
- 普通的應用程式只能使用那些不會造成災難的指令
- 使用者態
- 1億個數,100個執行緒,每個執行緒隨機從1億個數裡面取一個數做處理,要求保證不會有兩個執行緒對同一個數做處理。我腦子裡都是java裡的各種鎖,他說不要考慮java,想想作業系統裡的互斥鎖
- 程序間的通訊方式,管道通訊如何實現,命名管道呢,共享記憶體呢
- 如何實現一個程序,說說對殭屍程序的理解,孤兒程序的理解,能手寫一個fork程序嗎
- 孤兒程序
一個父程序退出,而它的一個或多個子程序還在執行,那麼那些子程序將成為孤兒程序。孤兒程序將被init程序(程序號為1)所收養,並由init程序對它們完成狀態收集工作。 - 殭屍程序
程序終止後進入僵死狀態(zombie),等待告知父程序自己終止,後才能完全消失.但是如果一個程序已經終止了,但是其父程序還沒有獲取其狀態,那麼子程序就稱之為殭屍程序。 - 殭屍程序的危害
如果父程序不呼叫wait/waitpid,那麼保留退出子程序的那段資訊就不會釋放+程序號會一直被佔用。因此如果有大量殭屍程序的出現將導致OS不能產生新程序。 - 殭屍程序的解決辦法
- 通過訊號機制
子程序退出時向父程序傳送SIGCHILD訊號,父程序處理SIGCHILD訊號 - fork兩次
原理是將子程序成為孤兒程序,從而其的父程序變為init程序,通過init程序可以處理殭屍程序
- 通過訊號機制
- 手動建立fork程序
- 函式原型
1)include // 必須引入標頭檔案,使用fork函式的時候,必須包含這個標頭檔案,否則,系統找不到fork函式
2)pid_t fork(void); //void代表沒有任何形式引數 - fork函式不需要任何引數,對於返回值有三種情況
- 對於父程序,fork函式返回新建子程序的pid
- 對於子程序,fork函式返回 0;
- 如果出錯, fork 函式返回 -1。
- 程式碼示例
- 函式原型
- 孤兒程序
- 設計題
- 設計火車票訂票系統,場景是對於固定的某一天,輸入起始站和終點站,返回給使用者車次資訊和每個席位的剩餘票數