面經問題學習

你都不知道我多菜發表於2020-12-14

面經內容整理和學習

1.語言相關

1.1 Python

1.1.1 Python的類方法(例項方法、靜態方法、類方法)

這三種方法在Python中都有自己的特徵。

在類定義的時候,類內方法的例項方法,通常不需要使用裝飾器,並且傳參的時候第一項是self關鍵字。self指的是例項,在呼叫該類方法的時候,相當於呼叫了self.func()

而對於類方法的話,通常需要@classmethod裝飾器來修飾,並且函式傳入的引數必須有類本身,常用cls

對於靜態方法的話,需要使用@staticmethod裝飾器來進行裝飾。並且可以不傳入引數。

例項方法靜態方法類方法
裝飾器@staticmethod@classmethod
引數selfcls
行為將例項作為引數進行傳遞可以直接通過類或者使用例項呼叫不與類或例項繫結
場景解決和物件繫結問題管理類中的資料,對資料進行重構驗證資料等

1.1.2 程式與執行緒

多程式

Python使用多程式在Windows端常用模組為multiprocessing,該模組提供了Process類來代表一個程式物件。即建立一個Process例項,然後使用start()方法啟動程式。並且常常使用join()方法,該方法保證主程式會等待子程式結束後再執行下一步,常常用於程式間的同步。

同樣,若要啟動大量子程式,可以使用程式池的方式批量建立子程式,同樣,該方法也是用join()方法來等待子程式結束,但是區別是在使用join()之前必須要呼叫close(),在呼叫close()後就無法在新增新的程式了。值得注意的是,Pool()的大小取決於CPU的核數。

Python中程式間的通訊方法常用QueuePipes方式來實現,其中Queue常用的方法有putget。在使用多執行緒的時候,往往牽扯到的使用。多執行緒中所有的變數都由所有的執行緒共享,任何一個變數都可以被任何一個執行緒修改。因此,執行緒之間的共享資料最大的危險在於多個執行緒同時修改一個變數。為了保證共享資料的安全性,就需要給當前變數操作上一把鎖,即當某個程式要執行某些操作時,必須時獲得鎖的狀態。並且此時,別的執行緒不能同時執行這個操作,除非鎖釋放了。Python中通常使用threading.Lock()來實現。將其例項化後,可以使用lock.acquire()來獲取鎖。而且,鎖在使用完畢後一定要釋放,因此常用try....finally結構。

由於Python中每個執行緒都會持有自己的區域性變數,因此需要傳參的時候,程式碼往往非常冗餘。Python提供了ThreadLocal方法,在例項化後得到的物件可以看成全域性變數,但是當執行緒例項使用它時,它對每個執行緒都是區域性變數。

多執行緒

Python中的多執行緒主要是靠_threadthreading模組提供,通常使用threading模組,使用時,將一個函式傳入並建立Thread例項,然後使用start()開始執行。

1.1.3 多程式+協程的使用以及為何使用

首先,介紹什麼是協程:

協程是一種使用者級的輕量級執行緒。協程擁有自己的暫存器上下文和棧。協程排程切換的時候,將暫存器上下文和棧儲存到其他地方,在切回來的時候,恢復先前儲存的暫存器上下文和棧。因此,協程能保留上一次呼叫時的狀態。

用協程的好處在於避開程式、執行緒的系統呼叫設計問題,避開搶佔式排程執行順序無法確定的問題。協程是由使用者自己來編寫排程邏輯的,對CPU來說,它是單執行緒的,所以CPU不用去考慮怎麼排程、切換上下文,甚至不需要多執行緒的鎖機制,省去了CPU的開銷。

協程和多執行緒比較起來有如下優勢:

  • 協程執行效率極高,因為子程式切換不是執行緒切換,而是程式自身控制,沒有執行緒切換的開銷,尤其是線上程數量比較多的時候,協程的效能優勢就越明顯;
  • 不需要多執行緒的鎖機制,因為協程執行的時候相當於單執行緒,不存在同時寫變數的衝突,控制共享資源不需要鎖,只需要判斷狀態就可。

由於協程是單執行緒,因此要使用多個CPU需要使用到多程式+協程的方式。協程是使用生成器來實現得,在使用時C端向S端通過send()方法傳遞訊息。但是在進行send()操作前,需要將協程進行預激處理,即需要通過next(gen)或者gen.send(None)來啟用協程。不過在通常情況下可以直接使用裝飾器來進行自動的預激處理,常用的有coroutine()方法。值得注意的是,如果使用yield from來實現的協程,是與這些裝飾器衝突的,因為其會自動預激。

協程還有一項任務是終止協程異常處理

協程中未處理的異常會向上冒泡,傳給next或者send方法的呼叫方。因此終止協程可以使用某個哨符值,讓協程退出。在Python中提供了在客戶程式碼可以直接呼叫的方法,generator.throw以及generator.close

使用多程式+協程的原因在於,既可以充分利用多核,又可以充分發揮協程的高效率。

1.1.4 程式通訊與執行緒通訊

程式執行緒
管道
訊息佇列訊號量
共享記憶體訊號
訊號
訊號量

2.資料庫相關

2.1 Mysql

2.1.1 索引

2.1.1.1 索引的實現方式
2.1.1.2 聚簇索引與非聚簇索引

什麼是聚簇索引

表資料按照索引的順序來儲存,也就是說索引項的順序與表中記錄的物理順序一致。對於聚集索引,葉子結點就是儲存了真實的資料行的節點,不再有單獨的資料頁。在一張表上最多隻能建立一個聚簇索引。

什麼是非聚簇索引

表資料儲存與索引順序無關,對於非聚簇索引,葉結點包含索引欄位值及指向資料頁資料行的邏輯指標。

總的來說,聚簇索引是一種稀疏索引,資料頁上一級的索引頁儲存的是頁指標,而不是行指標。而非聚簇索引是密集索引,在資料頁的上一級索引頁,它為每一個資料行儲存一條索引記錄。

2.1.2 事務

事務其實就是由多條資料庫操作語句構成的。主要用於處理操作量大、複雜度高的資料。

事務主要擁有四個特性,即ACID:

  • 原子性:操作不可拆分,要麼全成功,要麼全部失敗進行回滾。
  • 一致性:事務開始前和結束後,資料庫的完整性沒有遭到破壞。總是從一個一致性狀態轉換到另一個一致性狀態。
  • 隔離性:一個事務所做的修改在最終提交前,對其他事務都是不可見的。
  • 持久型:一旦事務提交,則其所做的修改會永久到儲存到資料庫中。

其中由併發事務帶來一系列的問題:

  • 更新丟失:當兩個或多個事務選擇同一行,然後基於最初選定的值更新行的時候,由於每個事務都不知道其他事務的存在,就會發生丟失更新問題。即最後更新會覆蓋由其他事務進行的更新。
  • 髒讀:一個事務正在對一條記錄進行修改,再還沒有提交這次修改之前,另一個事務也來讀取這一條資料,若沒合適的控制機制,第二個事務就會讀取這些不一致狀態下的資料。
  • 不可重複讀:一個事務在讀取某些資料後的某個時間,再次讀取該資料,此時讀出的資料可能已經發生了變化,或者某些記錄已經被刪除了。
  • 幻讀:一個事務按之前相同的查詢條件重新讀取之前檢索過的資料,卻發現其他事務插入了滿足其查詢條件的新資料。

根據上述問題,SQL標準定義了4類隔離級別:

  • Read Uncommitted(讀取未提交內容)

    該級別會引發髒讀問題,即讀取到未提交的資料。該級別中,所有事物都可以看到其他未提交事務的執行結果。

  • Read Committed(讀取提交內容)

    該級別滿足了隔離的簡單定義,即一個事務只能看見已經提交事務所作的改變。這種隔離方式引發的問題是不可重讀。

  • Repeatable Read(可重讀)

    Mysql的預設事務隔離級別,它確保同一事務的多個例項在併發讀取資料的時候,會看到同樣的資料行。這個隔離級別會出現的問題是幻讀。通過MVCC和間隙鎖解決幻讀問題。

  • Serializable(可序列化)

MVCC:多版本併發控制

MVCC是用於Read Committed與Repeatable Read下的控制機制。實際上是儲存了資料在某個時間節點的快照。可以理解為對每一個表,都隱藏了兩列,這兩列的內容分別為create_versiondelete_version。在事務操作過程中,每一條操作語句會對current_version進行更新。MVCC的原理就是查詢建立版本小於等於當前事務版本,刪除版本為空或者大於當前事務版本的項。

2.1.3 Mysql的常用資料型別

名稱型別說明
INT整型4位元組整數型別
BIGINT長整型8位元組整數型別
REAL浮點型4位元組浮點數
DOUBLE浮點型8位元組浮點數
CHAR(N)定長字串指定長度字串
VARCHAR(N)變長字串儲存可變長度字元
BOOLEAN布林型別True或False
DATE日期型別儲存時間,如2020-12-12
TIME時間型別儲存時間,如16:31:22
DATETIME日期和時間型別上面兩項的組合

2.1.4 drop、delete與truncate的區別

名稱作用
drop直接刪掉表
delete刪掉的是表中的資料,事務會記錄
truncate刪除表中資料,可以在後面新增where語句。不會記錄,刪除行無法恢復。該關鍵字只能對某個表進行操作。

2.2 資料庫設計的三正規化

  • 要求有主鍵,並且要求每一個欄位原子性不可再分;

第一正規化要求資料庫的每一行必須唯一,也就是每個表必須有一個主鍵。這是資料庫設計的最基本要求。

  • 要求所有非主鍵欄位完全依賴主鍵,不能產生部分依賴;

若違背該正規化,會造成資料出現大量的冗餘。此時可以改變設計為“多對多”。

  • 所有非主鍵欄位和主鍵欄位之間不能產生傳遞依賴;

【例子參考】三正規化例子

3.計算機網路相關

3.1 Session 和 Cookie

Cookie

Cookie是一段由Server送給使用者瀏覽器的一小塊文件。瀏覽器儲存他,並且在瀏覽器下次傳送請求的時候將他送回原來的服務端。

Cookie是用來區分兩個請求是否來自同一個瀏覽器,以此去保持使用者的登陸狀態的方案。

其具備兩個特性,並且向domain的服務傳送請求的時候,Cookie也會一併在請求中傳送:

  • 只針對原本的網段起作用。
  • 有生命週期,到了所設定的生命週期後會失效。

Session

Session負責記錄在使用端上的使用者訊息,會在一個使用者完成身份認證後,存下所需要的使用者資料,接著產生一組對應的ID存入Cookie後,傳回客戶端。一句話就是Session是賬戶登陸過後,Sever端所發的識別證。

3.2 HTTP狀態碼

大方向上區分:

分類描述
1**資訊,伺服器收到請求,需要請求者繼續執行操作
2**成功,操作被成功接收並處理
3**重定向,需要進一步的操作來完成請求
4**客戶端錯誤,請求包含語法錯誤或者無法完成
5**服務端錯誤,伺服器在處理請求過程中發生錯誤

具體的常問的:

名稱內容
200請求成功,一般用於get和post
301請求的資源被永久移動到的新的url
302臨時移動
400客戶端請求語法錯誤
保留,將來使用
伺服器理解請求,但是拒絕執行
伺服器無法根據客戶端的請求找到資源
500伺服器內部錯誤,無法完成請求
502作為閘道器或者代理工作的伺服器嘗試請求執行時,從遠端伺服器接收到一個無效響應

4.設計模式

4.1 單例模式

餓漢式

#include <iostream>

class singleton{
private:
    static singleton* p;
    singleton;
public:
    static singleton * getInstance(){
        return p;
    }
}

singleton* singleton::p = new singleton();

int main(){
    singleton* p = singleton::getInstance();
    singleton* p2 = singleton::getInstance();
    std::cout << p <<std::endl;
    std::cout << p2 << std::endl;
    return 0;
}

餓漢式是執行緒安全的實現方法,因為p在進入main函式之前就由單執行緒方式例項化了。

懶漢式

class singleton{
private:
	static singleton* p;
    singleton();
public:
    static singleton* getInstance(){
        if (p == NULL){
            p = new singleton();
            std::cout << "onece" << std::endl;
        }
        else std::cout << "not once" <<std::endl;
        return p;
    }
}

懶漢式存線上程安全問題,必須加鎖。

雜項

1. 什麼是計算密集型、什麼是IO密集型

計算密集型任務:

計算密集型任務的特點是要進行大量的計算,消耗CPU資源,比如計算圓周率、對視訊進行高清解碼等等,全靠CPU的運算能力。這種計算密集型任務雖然也可以用多工完成,但是任務越多,花在任務切換的時間就越多,CPU執行任務的效率就越低,所以,要最高效地利用CPU,計算密集型任務同時進行的數量應當等於CPU的核心數。

計算密集型任務由於主要消耗CPU資源,因此,程式碼執行效率至關重要。Python這樣的指令碼語言執行效率很低,完全不適合計算密集型任務。對於計算密集型任務,最好用C語言編寫。

IO密集型任務:

涉及到網路、磁碟IO的任務都是IO密集型任務,這類任務的特點是CPU消耗很少,任務的大部分時間都在等待IO操作完成(因為IO的速度遠遠低於CPU和記憶體的速度)。對於IO密集型任務,任務越多,CPU效率越高,但也有一個限度。常見的大部分任務都是IO密集型任務,比如Web應用。

IO密集型任務執行期間,99%的時間都花在IO上,花在CPU上的時間很少,因此,用執行速度極快的C語言替換用Python這樣執行速度極低的指令碼語言,完全無法提升執行效率。對於IO密集型任務,最合適的語言就是開發效率最高(程式碼量最少)的語言,指令碼語言是首選,C語言最差。

2.什麼是執行緒安全

執行緒安全可以簡單理解為一個方法或者一個例項可以在多執行緒環境中使用而不會出現錯誤。

3. kill -9 與 kill -15的差別

在使用kill -9 or kill -15的時候,實際上是執行Linux中的中止訊號。他們的差別主要在於:

kill -9kill -15
立即殺死程式,並且該訊號不可被中斷、阻塞正常退出程式,該訊號可以回撥或者阻塞

相關文章