天天看點

後端開發師面試常見題目

什麼是面向對象,談談你對面向對象的了解。

後端開發師面試常見題目

三大特性

封裝:明确辨別出允許外部使用的所有成員函數和資料項

内部細節對外部調用透明,外部調用無需修改或者關心内部實作

javabean 的屬性私有,提供get/set對外通路。

orm架構

操作資料庫,我們不需要關心連結是如何建立,sql是如何執行,隻需要引入mybatis,調用方法即可

繼承:繼承基類的方法,并做出自己的改變或擴充,求同存異。

多态:基于對象所屬類的不同,外部對同一個方法的調用,實際執行的邏輯不同

條件:繼承、方法重寫、父類引用指向子類對象

無法調用子類特有的功能(隻能是重寫的)

JDK、JRE、JVM三者差別和聯系

JDK:

Java Develpment Kit java 開發工具

JRE:

Java Runtime Environment java運作時環境

JVM:

java virtual Machine java 虛拟機

JDK directory 包含 JRE目錄以及java工具目錄(統稱),JRE目錄包含bin(JVM)和lib(類庫)目錄

== 和 equals

==對比的是棧中的值,基本資料類型是變量值,引用類型是堆中記憶體對象的位址(引用類型在棧中存的是堆中對應的位址,而真實的值存在于堆中)

equals:object中預設也是采用 == 比較,通常會重寫

後端開發師面試常見題目

String 中進行了重寫,對于引用類型,會将位址裡面的值進行一一比較:

後端開發師面試常見題目

String類中,被複寫的equals()方法其實是比較兩個字元串的内容

後端開發師面試常見題目

簡述final作用

最終的

修飾類:表示類不可被繼承

修飾方法:表示方法不可被子類覆寫,但可以重載

修飾變量:表示變量一旦被指派就不可更改它的值

修飾成員變量

如果final修飾的是類變量,隻能在靜态初始化塊中指定初始值或者聲明該類變量時指定初始值。

如果final修飾的是成員變量,可以在非靜态初始化塊、聲明該變量或者構造器中執行初始值。

修飾局部變量

系統不會為局部變量進行初始化,局部變量必須由程式員顯示初始化。是以使用final修飾局部變量時,即可以在定義時指定預設值(後面的代碼不能對變量再次指派),也可以不指定預設值,而在後面的代碼中對final變量指派(僅一次)

後端開發師面試常見題目

修飾基本類型資料和引用類型資料

如果基本資料類型的變量,則其數值一旦在初始化之後便不能更改

如果是引用類型的變量,則在對其初始化之後便不能再讓其指向另一個對象。但是引用的值是可變的。

後端開發師面試常見題目

為什麼局部内部類和匿名内部類隻能通路局部final變量?

編譯之後生成兩個class檔案,Test.class Test1.class

匿名内部類

後端開發師面試常見題目

局部内部類

後端開發師面試常見題目

attention: 内部類和外部類是出于同一個級别的,内部類不會因為定義在這個方法中就會随着方法的執行完比就被銷毀。

problem: 當外部類的方法結束時,局部變量就會被銷毀了,但是内部類對象可能還在(隻有沒有人再引用它時,才會死亡)。如此就出現了一個沖突:内部類對象通路了一個不存在的變量(比如,1中,Thread中通路了a b,但是a和b已經被銷毀了)。為了解決這個問題,就将局部變量複制了一份作為内部類的成員變量,這樣當局部變量死亡後,内部類依舊可以通路它,實際通路的是局部變量的“copy”。這樣就好像延長了局部變量的生命周期。

将局部變量複制為内部類的成員變量時,必須保證這兩個變量是一樣的,也就是如果我們在内部類中修改了成員變量,方法中的局部變量也得跟着改變,怎麼解決問題呢?

就将局部變量設定為final,對它初始化後,我就不讓你再去修改這個變量,就保證了内部類的成員變量和方法的局部變量的一緻性。這實際上也是一種妥協,使得局部變量和内部類建立的拷貝保持一緻。

String、StringBuffer、StringBuilder差別及使用場景。

String是final修飾的,不可變,每次操作都會産生新的String對象

StringBuffer是線程安全的,StringBuilder是線程不安全的

StringBuffer方法都是synchronized修飾的

performance:StringBuilder>StringBuffer>String

(Synchronized:多線程,共享變量,結果不受影響 == 線程安全)

場景:經常需要改變字元串内容時使用後面兩個

優先使用StringBuilder,多線程使用共享變量時使用StringBuffer

重載和重寫的差別

重載:發生在同一個類中,方法名必須相同,參數類型不同、個數不同、順序不同,方法傳回值和通路修飾符可以不同,發生在編譯時。

重寫:發生在父子類中,方法名、參數清單必須相同,傳回值範圍小于等于父類,抛出的異常範圍小于等于父類,通路修飾符大于等于父類;如果父類方法修飾為private則子類就不能重寫該方法。

後端開發師面試常見題目

接口和抽象類的差別

抽象類可以存在普通函數,接口隻能存在public abstract 方法

抽象類中的成員變量可以各種類型的,而接口中的成員變量隻能是public static final類型的(預設)。

抽象類隻能繼承一個,接口可以實作多個。

接口的設計目的,是對類的行為的一種“有”限制,因為接口不能規定類不可以有什麼行為,也就是提供一種強制要求不同的類具有相同行為的機制。它隻限制了行為的有無,但不對如何實作進行限制。

抽象類的設計目的,是代碼複用。當不同的類某些相同的行為(A),且其中一部分行為的實作方式一緻時(B),可以讓這些類都派生于一個抽象類。這個抽象類實作了B,避免讓所有的子類自己來實作B,這樣就達到了代碼複用的目的。而A減B的部分,抽象類隻是定義為抽象方法,留給各個子類自己實作。正是因為A-B在這裡沒有實作,是以抽象類不允許執行個體化對象。

抽象類是對類本質的抽象,表達的是is a的關系,比如:BMW is a car。抽象類包含并實作子類的通用特性,将子類存在差異化的特性進行抽象,交由子類去實作。

而接口本質是對行為的抽象,表達的是like a的關系。比如:Bird like a Aircraft(飛行器)。接口的核心是定義行為,即實作類可以做什麼,至于實作類主體是誰、是如何實作,接口并不關心。

使用場景:當你關注一個事物本質的時候,用抽象類;當你關注一個操作的時候,用接口。

抽象類的功能要遠超過接口,但是,定義抽象類的代價高。因為進階語言來說每個類隻能繼承一個類。在這個類中,你必須繼承或者編寫出其所有子類的所有共性。雖然接口在功能上會弱化很多,但是它隻是針對一個動作的描述。而且你可以在一個類中同時實作多個接口。在設計階段會降低難度。(接口,可以預先定義;而抽象類,隻能在子類定義完成後,再進行抽取)

List和Set的差別

List: 有序,按照對象進入的順序儲存對象,可重複,允許多個Null元素對象,可以使用Iterator取出所有元素,再逐一周遊,還可以使用get(int index)擷取指定下标的元素

Set: 無序,不可重複,最多允許有一個Null元素對象,取元素時隻能使用Iterator接口取得所有元素,再逐一周遊各個元素。

hashCode與equals

後端開發師面試常見題目

hashCode()定義在JDK的Object.java中,Java中任何類都包含hashCode()函數,散列碼(int整數)、确定該對象在哈希表中的索引位置。散清單中存儲的是鍵值對。

以“HashSet如何檢查重複”為例子進行說明:

對象加入HashSet時,HashSet會先計算該對象的hashCode值來判斷對象加入的位置,看該位置是否有值,如果沒有,HashSet會認為該對象沒有重複出現。如果發現有值,這時會調用equals()方法來檢查兩個對象是否真的相同。如果兩者相同,HashSet就不會讓其加入操作成功。如果不同的話,就會重寫散列到其它位置。這樣就大大減少了equals的次數,相應就大大提高了執行速度。

如果兩個對象相等,則hashCode一定也是相同

如果兩個對象相等,則兩個對象分别調用equals方法都傳回true

兩個對象有相同的hashCode值,它們也不一定是相等的

是以,equals方法被覆寫過,則hashCode方法也必須被覆寫過

hashCode()的預設行為是對堆上的對象産生特殊值。如果沒有重寫hashCode(),則該class的兩個對象無論如何都不會相等(即使這兩個對象指向相同的資料)

ArrayList和LinkedList差別

如果indexOf要查找索引的元素,不存在于LinkedList中,也同樣會周遊整個清單。是以,大部分情況下,使用ArrayList。

雖然,LinkedLis在插入和删除操作時,涉及的時間複雜度低,但是,如果插入新節點的次數很多,則會建立大量的内部類node對象,嚴重影響性能。此時,使用ArrayList初始化足夠容量(不需要進行copy操作) 加 尾插法的性能更好。

HashMap和HashTable的差別?底層實作是什麼?

差別:

HashMap方法沒有synchronize修飾,線程非安全,HashTable線程安全

HashTable允許key和value為空,而HashTable不允許

底層實作:數組+連結清單實作

jdk8開始連結清單高度到8,數組長度超過64,連結清單轉變為紅黑樹,元素以内部類Node節點存在

計算key的hash值,然後用hash值對數組長度取模,對應到數組下标

如果沒有産生hash沖突(下标位置沒有元素),則直接建立Node存入數組

如果産生hash沖突,先對key值進行equal比較,相同則取代該元素。不同,則判斷連結清單高度插傳入連結表,連結清單高度達到8,并且數組長度到64則轉變為紅黑樹,紅黑樹元素個數低于6則轉回連結清單

key為null,存在下标0的位置

擴容機制,HashMap中預設的hash數組大小是16個,如果HashMap裡存儲的元素大于門檻值(長度乘以擴容因子0.75)時,就會重新new 一個數組,進行遷移,并且長度擴大一倍。

ConcurrentHashMap原理,jdk7和jdk8版本的差別

JDK7:

資料結構:ReentranLock+segment+HashEntry,一個Segment包含一個HashEntry數組,每個HashEntry元素又是一個連結清單結構。(HashEntry和HashMap一緻)

元素查詢:二次hash,第一次Hash定位Segment,第二次Hash定位到元素所在的連結清單的頭部(HashEntry數組中的位置)

分段鎖:segment分段鎖 segment繼承了ReentranLock,鎖定操作的Segment,其它的Segment不受影響,并發度為segment個數,可以通過構造函數指定,數組擴容機制不會影響其它的segment

get方法無需加鎖,volatile保證,且是對數組和Node節點加volatile(用volatile修飾的變量,線程在每次使用變量的時候,都會讀取變量修改後的最的值)

JDK8:

資料結構:synchronize+CAS(樂觀鎖)+Node+紅黑樹,Node的val和next都用volatile修飾,保證可見性

查找、替換、指派操作都使用CAS,其它操作使用synchronize查漏補缺,譬如擴容

鎖:鎖連結清單的head節點,不影響其它元素的讀寫,鎖粒度更細,效率更高,擴容時,阻塞所有的讀寫操作,并發擴容

讀操作無鎖:

Node的val和next使用volatile修飾,讀寫線程對該變量互相可見

數組用volatile修飾,保證擴容時被讀線程感覺

如何實作一個IOC容器

配置檔案 配置 包掃描路徑

遞歸包 掃描擷取.class檔案

反射、确定需要交給IOC管理的類

對需要注入的類進行依賴注入

配置檔案中指定需要掃描的包路徑

定義一些注解,分别表示通路控制層、業務控制層、資料持久層、依賴注入注解、擷取配置檔案注解

從配置檔案中擷取需要掃描的包路徑,擷取目前路徑下的檔案資訊以及檔案夾資訊,我們将目前路徑下的所有以.class結尾的檔案添加到一個Set集合中進行存儲

周遊這個Set集合,擷取在類上有指定注解的類,并将其交給IOC容器,定義一個安全的Map用來存儲這些對象

周遊這個IOC容器,擷取到每一個類的執行個體,判斷裡面是否依賴其它的類的執行個體,然後進行遞歸注入

什麼是位元組碼?采用位元組碼的好處是什麼?

Java類加載器

JDK自帶有三個類加載器:

BootStrapClassLoader是ExtClassLoader的父類加載器(不是繼承,而是通過變量 parent 引用),預設負責加載 %JAVA_HOME%/lib 下的 jar 包 和 class 檔案。

ExtClassLoader 是 AppClassLoader 的父類加載器(同上), 負責加載 %JAVA_HOME%/lib/ext檔案夾下的 jar 包 和 class 類。

AppClassLoader 是 自定義加載類的父類,負責加載 classpath 下的類檔案。系統加載類(SystemLoader ,輔助AppClassLoader 加載系統檔案和classpath檔案,即自己寫的代碼和一下引用的jar包),線程上下文加載器(貫穿三個系統加載器,都能于之直接聯系)

繼承ClassLoader實作自定義加載器。

雙親委派模型

後端開發師面試常見題目

向上委派查找緩存是否已經加載了該類,如果到了頂層類加載器的緩存也沒有找到,就從頂層類加載器向下根據其加載路徑查找,直到發起加載的類加載器為止(不一定是AppClassLoader,也可以是自定義的類加載器發起)

雙親委派模型的好處:

主要是為了安全性,避免使用者自己編寫的類動态替換了Java的一些核心類,比如String。

同時也避免了類的重複加載,因為JVM區分不同類,不僅僅是根據類名,相同的class檔案被不同的ClassLoader加載就是不同的兩個類。

Java 中的異常體系

Java中的所有異常都來自頂級父類Throwable

Throwable下有兩個子類Exception和Error

Error是程式無法處理的錯誤,一旦出現這個錯誤,則程式将被迫停止運作(OOM OutOfMemoryError 異常)

Exception不會導緻程式停止,它又分為兩個部分 RuntimeException 運作時異常 和 CheckedException 檢查異常。

RuntimeException 常常發生在程式運作過程中,會導緻程式目前線程執行失敗(代碼的邏輯錯誤,除零、數組越界、空指針異常)。CheckedException 常常發生在程式編譯過程中,會導緻程式編譯不通過(文法問題,idea能夠自動提醒)。

GC如何判斷對象可以被回收 !!!

引用計數法(python采用):每個對象有一個引用計數屬性,新增一個引用計數加1,引用釋放時計數減1,計數為0時可以回收

可達性分析法(Java采用):從GC Roots 開始向下搜尋,搜尋所走過的路徑稱為引用鍊。當一個對象到GC Roots 沒有任何引用鍊相連時,則證明該對象是不可用的,那麼虛拟機就判斷是可回收對象。

引用計數法,可能會出現A 引用了 B, B又引用了A,這時候就算他們都不在使用了,但因為互相引用計數器 = 1 永遠無法被回收。

GC Roots的對象有:

虛拟機棧(棧幀中的本地變量表)中引用的對象

方法區中類靜态屬性引用的對象

方法區中常量引用的對象

本地方法棧中JNI(即一般說的Native方法)引用的對象

可達性算法中的不可達對象并不是立即死亡的,對象擁有一次自我拯救的機會。對象被系統宣告死亡至少要經曆兩次标記過程:第一次經過可達性分析發現沒有與GC Roots相連接配接的引用鍊,第二次是由虛拟機自動建立的Finalizer隊列中判斷是否需要執行finalize()方法。

當對象變成不可達時,GC會判斷該對象是否覆寫了finalize方法,若未覆寫,則直接将其回收。否則,若對象未執行過finalize()方法,将其放入F-Queue隊列,由一低優先級線程執行該隊列中對象的finalize方法。執行finalize方法完畢後,GC會再次判斷該對象是否可達,若不可達,則進行回收,否則,對象“複活”

每個對象隻能出發一次finalize()方法

由于finalize()方法運作代價高昂,不确定性大,無法保證各個對象的調用順序,不推薦大家使用。

線程的生命周期,線程有哪些狀态

線程通常有5種狀态,建立、就緒、運作、阻塞和死亡

阻塞的情況又分為三種:

等待阻塞:運作的線程執行wait方法,該線程會釋放占用的所有資源,JVM會把該線程放入“等待池”中。進入這個狀态後,是不能自動喚醒的,必須依靠其它線程調用notify或者notifyAll方法才能被喚醒,wait是object類的方法。

同步阻塞:運作的線程在擷取對象的同步鎖時,若該同步鎖被别的線程占用,則JVM會把該線程放入“鎖池”中。

其它阻塞:運作的線程執行sleep和join方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀态。當sleep狀态逾時,join等待線程終止或者逾時、或者I/O處理完畢時,線程重新轉入就緒狀态。sleep是Thread類的方法

建立狀态(New):建立了一個線程對象

就緒狀态(Runnable):線程對象建立後,其它線程調用了該對象的start方法。該狀态的線程位于可運作線程池中,變得可運作,等待擷取CPU的使用權

運作狀态(Running):就緒狀态的線程擷取了CPU,執行程式代碼

阻塞狀态(Blocked):阻塞狀态是線程因為某種原因放棄CPU使用權,暫時停止運作。直到線程進入就緒狀态,才有機會轉到運作狀态。

死亡狀态(Dead):線程執行完了或者因異常退出了run方法,該線程結束生命周期。

sleep()、wait()、join()、yield()的差別

鎖池

所有需要競争同步鎖的線程都會放在鎖池中,比如目前對象的鎖已經被其中一個線程得到,則其它線程需要在這個鎖池進行等待,目前面的線程釋放同步鎖後,鎖池中的線程再去競争同步鎖,當某個線程得到後會進入就緒隊列進行等待CPU資源配置設定。

等待池

當我們調用wait()方法後,線程會放到等待池當中,等待池的線程是不會去競争同步鎖。隻有調用了notify()或者notifyAll()後,等待池的線程才會開始去競争鎖,notify()是随機從等待池選出一個線程放到鎖池中,而notifyAll()是将等待池的所有線程放到鎖池當中。

sleep是Tread類的靜态本地方法,wait則是Object類的本地方法。

sleep方法不會釋放lock,但是wait會釋放,而且會加入到等待隊列中。

後端開發師面試常見題目

sleep方法不依賴同步器synchronized,但是wait需要依賴synchronized關鍵字

sleep不需要被喚醒(休眠之後退出阻塞),但是wait需要(不指定時間需要被别人中斷)

sleep一般是用于目前線程休眠,或者輪詢暫停操作,wait則用于多線程之間的通信(A線程wait,需要其它線程調用notify喚醒)

sleep會讓出CPU執行時間且強制上下文切換,而wait則不一定,wait後可能還是有機會重新競争到鎖,繼續執行。

yield()執行後線程直接進入就緒狀态,馬上釋放cpu的執行權,但是依然保留了cpu的執行資格,是以有可能cpu下次進行線程排程還是會讓這個線程擷取到執行權繼續執行。

join()執行後線程進入阻塞狀态,例如線上程B中調用線程A的join(),那麼線程B會進入到阻塞隊列,直到線程A結束或中斷線程。

後端開發師面試常見題目

線上程main方法中,調用線程t1的join方法,main方法進入阻塞,是以首先是t1線程執行,列印22,然後是列印11

對線程安全的了解

Thread、Runnable的差別

Thread和Runnable的實質是繼承關系,沒有可比性(Runnable是接口,而Thread是實作Runnable的類)。無論使用Runnable和Thread,都會new Thread,然後執行run方法。用法上,如果有複雜的線程操作需求,那就選擇繼承Thread(因為Thread實作了Runnable接口,且自身也有方法擴充),如果隻是簡單的執行一個任務,那就實作runnable。

Question:

後端開發師面試常見題目
後端開發師面試常見題目

Reason:MyThread建立了兩個執行個體,而ticket屬于局部變量,是以每次都會從5開始,自然賣出兩倍,屬于用法錯誤

Solution:将ticket定義為static類變量,且用synchronized修飾。

說說對守護線程的了解

守護線程:為所有非守護線程(使用者線程)提供服務的線程;任何一個守護線程都是整個JVM中所有非守護線程的保姆;

守護線程類似于整個程序的一個默默無聞的小喽喽;它的生死無關重要,它卻依賴整個程序而運作;如果其它線程都結束了,沒有要執行的了,程式就結束了,不會去理會守護線程,直接就把它中斷。

**attention: ** 由于守護線程的終止是自身無法控制的,是以千萬不要把IO、File等重要操作邏輯配置設定給它;是以它不靠譜;

守護線程的作用?

舉例,GC垃圾回收線程:就是一個經典的守護線程,當我們的程式中不再有任何運作的Thread,程式就不會再産生垃圾,垃圾回收器也就無事可做,是以當垃圾回收線程是JVM僅剩的線程時,垃圾回收線程會自動離開。它始終在低級别的狀态中運作,用于實時監控和管理系統中的可回收資源。

應用場景:

來為其它線程提供服務支援的情況

在任何情況下,程式結束時,這個線程必須正常且立刻關閉,就可以作為守護線程來使用;反之,如果一個正在執行某個操作的線程必須要正确地關閉掉否則就會出去不好地後果的話,那麼這個線程就不能作為守護線程,而是使用者線程。通過都是些關鍵的事務,比如說,資料庫錄入或者更新,這些操作都是不能中斷的。

thread.setDeamon(True)必須在thread.start()之前設定,否則會跑出一個IllegalThreadStateException異常,你不能把正在運作的正常線程設定為守護線程

在Deamon線程中産生的新線程也是Deamon的。

守護線程不能通路固有資源,比如讀寫操作或者計算邏輯。因為它會在任何時候甚至在一個操作的中間發生中斷。

Java自帶的多線程架構,比如ExecuteService,會将守護線程轉換為使用者線程,是以如果使用背景線程就不能使用Java的線程池。

ThreadLocal的原理和使用場景

後端開發師面試常見題目
後端開發師面試常見題目

解讀:

場景1,當在controller、service、dao層,傳遞資料時,需要每個方法都定義相應的形參,可以将要傳遞的資料存到ThreadLocal中,再通過get方法取出來。

其它場景,是由于ThreadLocal的獨立性,每個線程都有各自的私有的ThreadLocalMap容器,無需同步機制就能保證多個線程通路容器的互斥性

後端開發師面試常見題目

ThreadLocal記憶體洩漏原因,如何避免

記憶體洩漏為程式在申請記憶體後,無法釋放已經申請的記憶體空間,一次記憶體洩漏危機可以忽略,但記憶體洩漏堆積後果很嚴重,無論多少記憶體,遲早會被占光

不再會被使用的對象或者變量占用的記憶體不能回收,就是記憶體洩漏。

強引用:使用最普遍的引用(new),一個對象具有強引用,不會被垃圾回收器回收。當記憶體不足,java虛拟機甯願抛出OutOfMemoryError錯誤,使程式異常終止,也不回收這種對象。

如果想取消強引用和某個對象之間的關聯,可以顯式地将引用指派為null,這樣可以使JVM在合适的時間就會回收該對象

弱引用:JVM進行垃圾回收時,無論記憶體是否充足,都會回收被弱引用關聯的對象。在java中,用java.lang.ref.WeakReference類來表示。可以在緩存中使用弱引用。

ThreadLocal的實作原理,每一個Thread維護一個ThreadLocalMap,key為使用弱引用的ThreadLocal執行個體,value為線程變量的副本

後端開發師面試常見題目

實線=強引用、虛線=弱引用

headLocalMap使用ThreadLocal的弱引用作為key,如果一個ThreadLocal不存在外部強引用時,key(ThreadLocal)勢必會被GC回收,這樣導緻ThreadLocalMap中key為null,而value還存在着強引用,隻有thread線程退出以後,value的強引用鍊條才會斷掉,如果目前線程遲遲不結束的話,這些key為null的Entry的value就會一直存在一條強引用鍊(紅色鍊條)

key使用強引用

當threadLocalMap的key為強引用,回收ThreadLocal時,因為ThreadLocalMap還持有ThreadLocal的強引用,如果沒有手動删除,ThreadLocal不會被回收,導緻Entry記憶體洩漏

key使用弱引用

當ThreadLocalMap使用key為弱引用,回收ThreadLocal時,由于ThreadLocalMap持有ThreadLocal的弱引用,即使沒有手動删除,ThreadLocal也會被回收。當key為null時,在下一次ThreadLocalMap調用set(),get(),remove()方法時會被清除value值。

是以,ThreadLocal記憶體洩漏的根源是:由于ThreadLocalMap的生命周期和Thread一樣長,如果沒有手動删除對應key就會導緻記憶體洩漏,而不是因為弱引用

ThreadLocal正确的使用方法

每次使用完ThreadLocal都調用它的remove()方法清除資料

将ThreadLocal變量定義成private static,這樣就一直存在ThreadLocal的強引用,也就保證任何時候都能通過ThreadLocal的弱引用通路到Entry的value值,進而清除掉。

并發、并行、串行的差別

串行在時間上不能重疊,前一個任務沒搞定,下一個任務隻能等着。

并行在時間上是重疊的,兩個任務在同一時刻互不幹擾的同時執行(多核CPU)。

并發允許兩個任務彼此幹擾。同一時間點,隻有一個任務執行,交替執行(單核CPU)。

并發的三大特性

原子性是指在一個操作中CPU不可以中途暫停然後再排程,即不被中斷操作,要麼全部執行完成,要麼全部不執行。比如轉賬,從賬戶A向賬戶B轉1000元,那麼必然包括兩個操作:從賬戶A減去1000元,往賬戶B加上1000元。2個操作必須全部完成。

将count從主存讀到工作記憶體中的副本中(每個線程都有自己的工作記憶體,把count copy 到該線程的工作記憶體中)

在工作記憶體中進行 +1 的運算

将結果寫入工作記憶體,即指派給工作記憶體的count

将工作記憶體的值刷回主存(什麼時候刷入由作業系統決定、不确定的)

程式中的原子性指的是最小的操作單元,自增操作,它本身其實并不是原子性操作,分了3步的,包括讀取變量的原始值、進行加1操作、結果寫入工作記憶體。是以在多線程的情況下,有可能一個線程還沒自增完成,才執行到第二步,另一個線程就已經讀取到了主存的值,導緻結果錯誤(正常情況,線程二讀取到的應該是1,錯誤情況,線程二讀取到的還是0)。如果我們能夠保證自增操作是一個原子性的操作,那麼就能保證其它線程讀取到的一定是自增後的資料。

關鍵字:synchronized 保證原子性

attention:在count++中,原子性是指前三步的原子性

可見性是當多個線程通路同一個變量時,一個線程修改了這個變量的值,其它線程能夠立即看得到修改的值。

若兩個線程在不同的CPU,線程1正在循環執行,那麼線程2改變了stop的值還沒重新整理到主存,線程1中的stop依舊為false在循環執行。是以,線程2對變量的修改,線程1沒看到這就是可見性問題。

如果線程2改變stop的值,線程1一定會停止嗎?不一定。當線程2更改了stop變量的值之後,但是還沒來得及寫入到主存當中,線程2轉去做其它事情了,那麼線程1由于不知道線程2對stop變量的更改,是以會一直循環下去。

關鍵字:volatile、synchronized、final 保證可見性

attention: 在count++中,可見性保證的是第四步

有序性 是指虛拟機在進行代碼編譯時,對于那些改變代碼執行順序之後不會對最終結果造成影響的代碼,虛拟機不一定會按照我們寫的代碼的順序來執行,有可能将他們重排序。實際上,對于有些代碼進行重排序之後,雖然對變量的值沒有造成影響,但有可能會出現線程安全問題。

write方法裡的1和2做了重排序,線程1先對flag指派為true,随後轉執行到線程2,ret直接計算出結果,再到線程1,這時候a才指派為2,很明顯發生錯誤(正确:a=4;錯誤:a=0)

關鍵字:volatile、synchronized

volatile本身就包含了禁止指令重排的語義,而synchronized關鍵字是由“一個變量同一個時刻隻允許一條線程對其操作”

為什麼用線程池?解釋下線程池的參數?

降低資源消耗:提高線程使用率,降低建立和銷毀線程的消耗

提高響應速度:任務來了,直接有線程可用可執行,而不是先建立線程、再執行

提高線程的可管理性:線程是稀缺資源,使用線程池可以統一配置設定調優監控

首先通過ThreadFactory 生成出 corePoolSize大小的核心線程數,如果核心線程已經全部在執行任務,再有新的任務進來,就會把這些新的待執行任務放到workQueue中,直到這個隊列被放滿,此時,還再持續進入新任務,就會建立最多maxinumPoolSize-corePoolSize個線程用于處理隊列中的任務。如果達到最大線程數maxinumPoolSize,線程池沒有能力繼續處理新送出的任務時,就會Handler(第二種政策)任務拒絕。如果高峰期一過,maxinumPoolSize-corePoolSize大小的線程閑置時間超過keepAliveTime,這些空閑線程就被銷毀。

簡述線程池處理流程

後端開發師面試常見題目
後端開發師面試常見題目

線程池中阻塞隊列的作用?為什麼是先添加任務到隊列而不是先建立最大線程池?

一般的隊列隻能保證作為一個有限長度的緩沖區,如果超出了緩沖長度,就無法保留目前的任務了,而阻塞隊列通過阻塞可以保留住目前想要繼續入隊的任務。

阻塞隊列可以保證任務隊列中沒有任務時阻塞擷取任務的線程,使得線程進入wait狀态,釋放CPU資源

阻塞隊列自帶阻塞和喚醒的功能,不需要額外的處理,無任務執行時,線程池利用阻塞隊列的take方法挂起,進而維持核心線程的存活,不至于一直占用CPU資源

在建立新線程的時候,是要擷取全局鎖的,這個時候其它的就得阻塞,影響了整體效率

後端開發師面試常見題目

線程池中線程複用原理

線程池将線程和任務進行解耦,線程是線程,任務是任務,擺脫了之前通過Thread建立線程時的一個線程必須對應一個任務的限制

線上程池中,同一個線程可以從阻塞隊列中不斷擷取任務來執行,其核心原理在于線程池對Thread進行了封裝,并不是每次執行任務都會調用Thread.start()來建立新線程,而是讓每個線程去執行一個“循環任務”,在這個“循環任務”中不停檢查是否有任務被執行,如果有則直接執行,也就是調用任務中的run方法,将run方法當成一個普通的方法執行,通過這種方式隻使用固定的線程就将所有任務的run方法串聯起來。

spring是什麼?

輕量級的開源的J2EE架構。它是一個容器架構,用來封裝javabean(java對象),中間層架構(萬能膠)可以起到一個連接配接作用,比如說把Structs和hibernate粘合在一起運作,可以讓我們的企業開發更快、更簡潔

spring是一個輕量級的控制反轉(IOC)和面向切面(AOP)的容器架構

從大小與開銷兩方面而言Spring都是輕量級的

通過控制反轉(IoC)的技術達到松耦合的目的

提供了面向切面程式設計的豐富支援,允許通過分離應用的業務邏輯與系統級服務(日志服務)進行内聚性的開發

包含并管理應用對象(Bean)的配置和生命周期,這個意義上是一個容器

将簡單的元件配置、組合成為複雜的應用,這個意義上是一個架構

談談AOP的了解

後端開發師面試常見題目

每個controller不僅要實作核心的功能(業務邏輯功能),也會負責

一些日志、安全等功能,是以可能每個對象裡面都有這些日志、安全的代碼(橫切關注點),導緻重複備援,是以AOP會把這些交叉業務邏輯(安全、日志、事務等)封裝成一個切面,然後注入到目标對象(具體業務邏輯)中去。

談談你對IOC的了解

後端開發師面試常見題目
後端開發師面試常見題目

個人了解:Set集合中儲存着包路徑下的各個class檔案,然後根據XML裡面配置的bean節點、類的@repository、@service、@service、@controller、@component注解,通過反射建立對象放到map裡面(IOC容器)。以前類之間的關系,譬如A類引用B類,即在A類中去主導 new B類對象,而控制反轉,即我們将類之間的依賴關系交給了IOC容器,當A類需要引用B類時,通過autowired 、resourced等注解進行辨別,IOC容器自動建立對象建立雙發的聯系。

BeanFactory和ApplicationContext有什麼差別?

ApplicationContext是BeanFactory的子接口

是以,ApplicationContext提供了更完整的功能:

繼承MessageSource,是以支援國際化

統一的資源檔案通路方式

提供在監聽器中注冊bean的事件

同時加載多個配置檔案

載入多個(有繼承關系)上下文,使得每一個上下文都專注于一個特定的層次,比如應用的web。

後端開發師面試常見題目

Spring Bean的生命周期

掃描并解析類得到BeanDefinition

如果Bean有多個構造方法,則要推斷構造方法

确定好構造方法後,進行執行個體化得到一個對象

對對象中的加了@Autowired注解的屬性進行填充(依賴注入)

回調Aware方法(自定義一些操作),比如BeanNameAware,BeanFactoryAware

調用BeanPostProcessor的初始化前的方法

調用初始化方法

調用BeanPostProcessor的初始化後的方法,在這裡會進行AOP

如果目前建立的Bean是單例的則會把bean放入單例池

使用bean

Spring容器關閉時調用DisposableBean中destory()方法

Spring架構中的單例Bean是線程安全的麼?不安全

後端開發師面試常見題目

總結:

Spring中的Bean預設是單例模式,是以線程不安全

如果Bean有狀态(執行個體變量、類變量),儲存資料,需要開發人員自己保證線程安全,方法:“singleton”->“protopyte”、ThreadLocal、synchronized、lock、CAS

如果Bean無狀态(方法),不儲存資料,多線程調用裡面的方法,會在記憶體中複制變量到自己線程的工作記憶體中,是以是安全的。

Spring架構中都用到了哪些設計模型

簡單工廠:由一個工廠類根據傳入的參數,動态決定應該建立哪一個産品類。

工廠方法:

單例模式:保證一個類僅有一個執行個體,并提供一個通路它的全局通路點

擴充卡模式:

裝飾器模式:動态地給一個對象添加一些額外的職責。就增加功能來說,Decorator模式相比生成子類更為靈活。

動态代理:

Spring事務的實作方式和原理以及隔離級别

後端開發師面試常見題目

解釋:

程式設計式,即調用類似于 rollback、commit這些api方法;神明式,使用@Transaction注解。

@Transaction注解,就是開啟事務,這個方法中的所有sql都會在一個事務中執行,統一成功或失敗。

方法添加@Transaction注解後,spring會基于該方法的類生成一個代理對象作為bean,放入容器中,如果使用這個帶有注解的方法,代理邏輯會先把事務的自動送出設定為false,然後再去執行原本的業務邏輯方法,如果業務邏輯方法沒有異常(這個異常可以配置的,@Transaction中的rollback屬性進行配置,預設會對RuntimeException和Error進行復原),那麼代理邏輯就會把事務進行送出,反之,事務復原。

spring事務隔離級别就是資料庫的隔離級别:外加一個預設級别

read uncommitted (未送出讀)

read committed(送出讀、不可重複讀)

repeatable read (可重複讀)

serializable(可串行化)

spring事務傳播機制

多個事務方法互相調用時,事務如何在這些方法間傳播

後端開發師面試常見題目

A方法調用B方法

REQUIRED(Spring預設的事務傳播類型):如果A目前沒有事務,則B自己建立一個事務,如果A目前存在事務,則B加入到A的事務中去。

SUPPORTS:A目前存在事務,B則加入到A的事務中,如果A目前沒有事務,B就以非事務方法運作

MANDATORY:A目前存在事務,B則加入到A的事務中,如果A目前沒有事務,則抛出異常

REQUIRED_NEW:如果A目前沒有事務,則B建立一個自己的事務,互不相幹;如果目前A存在事務,則挂起A的事務,B建立自己的事務。無論如何,B建立自己的事務

NOT_SUPPORTED:不管A的事務的有無,B都以非事務狀态運作;如果A存在事務,則挂起該事務。

NEVER:B不使用事務,如果A目前存在事務,則抛出異常

NESTED:如果A目前存在事務,則B在嵌套事務中執行(A的事務為父事務,B的事務為子事務);如果A目前不存在事務,則和REQUIRED的操作一樣,B建立一個事務。

spring事務什麼時候會失效?

Spring事務的原理是AOP,進行了切面增強,那麼失效的根本原因是這個AOP不起作用!常見情況如下:

發生自調用,類裡面使用this調用本類的方法(this通常省略),此時這個this對象不是代理類,而是UserService對象本身。

解決方法,讓那個this變成UserService的代理類即可

方法不是public的

資料庫不支援事務

代理對象沒有被spring管理

異常被吃掉,無法抛出,事務不會復原(或者抛出的異常沒有被定義,預設為 RuntimeException)

什麼是bean的自動裝配,有哪些方式

在spring中,對象無需自己查找或建立與其關聯的其它對象,由容器負責把需要互相協作的對象引用賦予各個對象,使用autowire來配置自動裝載模式。

開啟自動裝配,隻需要在xml配置檔案中定義“autowire”屬性

autowire屬性有五種裝配的方式:

no-預設情況下,預設的方式是不進行自動裝配的,通過手工設定ref來進行裝配bean。

byName-根據bean的屬性名稱進行自動裝配

byType-根據bean的類型進行自動裝配

constructor-類似byType,不過是應用于構造器的參數。如果一個bean與構造器參數的類型相同,則進行自動裝配,否則導緻異常

autodetect-如果有預設的構造器,則通過constructor方式進行自動裝配,否則使用byType方式進行自動裝配(spring 3.0+棄用)

@Autowired自動裝配bean,可以在字段,setter方法、構造函數上使用。

Spring Boot、Spring MVC 和 Spring 有什麼差別

Spring是一個IOC容器,用來管理Bean,使用依賴注入實作控制反轉,可以很友善的整合各種架構,提供AOP機制彌補OOP的代碼重複問題,更友善将不同類不同方法中的共同處理抽取成切面、自動注入給方法執行,比如日志、異常等

Spring MVC 是spring對web架構的一個解決方法,提供了一個總的前端控制器dispatchServlet,用來接受請求,然後定義了一套路由政策(url到handle的映射)及适配執行handle,将handle結果使用視圖解析技術生成視圖展現給前端。

Springboot是Spring提供的一個快速開發工具包,讓程式員更友善、更快速的開發spring+springmvc應用,簡化了配置(約定了預設配置),整合了一系列的解決方案(starter機制)、redis、mongodb、es,可以開箱即用。

SpringMVC的工作流程

後端開發師面試常見題目

使用者發送請求到前端控制器 DispatchServlet

DispatchServlet收到請求調用HandlerMapping處理器映射器。

處理器映射器找到具體的處理器(可以根據xml配置、注解進行查找),生成處理器以及處理器攔截器(如果有則生成)一并傳回給DispatchServlet。

DispatchServlet調用HandlerAdapter處理器擴充卡。

HandlerAdapter經過适配調用具體的處理器(Controller)

Controller執行完成後傳回 ModelAndView

HandlerAdapter将controller執行結果ModelAndView傳回給 DispatchServlet。

DispatchServlet将ModelAndView傳給ViewResolver.

ViewResolver解析後傳回具體View。

DispatchServlet根據View進行渲染視圖(即将模型資料填充至視圖中)

DispatchServlet響應客戶

Spring MVC的主要元件

Handler:處理器。它直接應對着MVC中的C也就是Controller層,它的具體表現形式有很多,可以是類,也可以是方法。在Controller層中@RequestMapping标注的所有方法可以看成是一個Handler,隻要可以實際處理請求就可以是Handler.(重點掌握前兩個)

後端開發師面試常見題目
後端開發師面試常見題目

Spring Boot 自動配置原理

@Import + @Configuration + Spring spi

自動配置類由各個starter提供,使用@Configuration + @Bean定義配置類,放到META-INF/Spring.factories下

使用Spring spi掃描META-INF/spring.factories下的配置類

使用@Import導入自動配置類

後端開發師面試常見題目

EnableAutoConfiguration中是以<key, value>進行存儲,value中,都是類的全路徑,SpringFactoriesLoader.loadFactoryNames将讀取這些類的全路徑,并通過反射生成bean對象存到容器中。

如果這些bean對象的類被@Configuration修飾,那麼類裡面的被@Bean修飾的變量,也會變成bean對象。

如何了解Spring Boot 中的Starter

使用Spring + SpringMVC,如果需要引入mybatis等架構,需要到xml中定義mybatis需要的bean

starter就是定義一個starter的jar包,寫一個@Configuration配置類,将這些bean定義在裡面,然後在starter包的META-INF/spring.factories中寫入該配置類,springboot會按照約定來加載該配置類

開發人員隻需要将相應的starter包引入到pom檔案中,進行相應的屬性配置(使用預設配置時,不需要配置),就可以直接進行代碼開發,使用對應的功能了,比如mybatis-spring-boot-starter,spring-boot-starter-redis

什麼是嵌入式伺服器?為什麼使用嵌入式伺服器

節省了下載下傳安裝tomcat,應用不需要再打包成war包,然後放入webapp目錄中運作了

隻需要一個安裝了java的虛拟機,就可以直接在上面部署應用程式了

springboot内置了tomcat.jar,而main方法時會去啟動tomcat,并利用tomcat的spi機制加載springmvc

mybatis的優缺點

後端開發師面試常見題目

優點5:即表的字段映射到entity的屬性

缺點2:由于基于SQL語句程式設計,如果xml檔案中使用了一些mysql支援的文法,再切換到oracle時,就需要進行修改。

MyBatis 與 Hibernate對比

國内:面向表結構設計,不使用面向對象

國外:面向對象開發

後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目

Mybatis 在處理#{}時,會将sql中的#{}替換為?号,調用PreparedStatement來指派;

Mybatis 在處理${}時,就是将其替換成變量的值,調用Statement來指派;

${} 的變量替換是在DBMS外,變量替換後,其對應的變量不會加上單引号;

使用#{}可以有效的防止SQL注入,提高系統安全性

簡述Mybatis 的插件運作原理,如何編寫一個插件

Mybatis 隻支援針對 ParameterHandler、ResultSetHandler、StatementHandler、Executor這四種接口的插件,Mybatis使用JDK的動态代理,為需要攔截的接口生成代理對象以實作接口方法攔截功能,每當執行這4種接口對象的方法時,就會進入攔截方法,具體就是InvocationHandler的invoke()方法,攔截那些你指定需要攔截的方法。

Executor:代表執行器,由它排程StatementHandler、ParameterHandler、ResultSetHandler等來執行對應的SQL,其中StatementHandler是最重要的。

StatementHandler:作用是使用資料庫的Statement(PreparedStatement)執行操作,它是四大對象的核心,起到承上啟下的作用,許多重要的插件都是通過攔截它來實作的。

ParameterHandler:是用來處理SQL參數的。

ResultSetHandler:是進行資料集(ResultSet)的封裝傳回處理的,它非常的複雜,好在不常用。

編寫插件:實作Mybatis的 Interceptor 接口并覆寫intercept()方法,然後在給插件編寫注釋,指定要攔截哪一個接口的哪些方法即可,在配置檔案中配置編寫的插件

後端開發師面試常見題目

@Signature 是起到一個方法重載的作用,因為method方法重載,是以需要指定args

@Component 将對象加載進IOC容器中。

invocation.proceed() 之前編寫代碼,即具體業務邏輯執行前進行處理,同理之後編寫代碼,即具體邏輯執行後進行處理。

索引基本原理

索引用來快速地尋找那些具有特定值的記錄。如果沒有索引,一般來說執行查詢時周遊整張表。

索引的原理:就是把無序的資料變成有序的查詢。

把建立了索引的列的内容進行排序

對排序結果生成倒排表

在排序表内容上拼上資料位址鍊

在查詢的時候,先拿到倒排表内容,再取出資料位址鍊,進而拿到具體資料

mysql聚簇和非聚簇索引的差別

都是B+樹的資料結構

聚簇索引:将資料存儲與索引放到了一塊,并且按照一定的順序組織的,找到了索引也就找到了資料,資料的實體存放順序與索引順序是一緻的,即:隻要索引是相鄰的,那麼對應的資料一定也是相鄰地存放在磁盤上。

非聚簇索引:葉子節點不存儲資料、存儲的是資料行位址,也就是說根據索引查找到資料行的位置再取磁盤查找資料,這個有點類似于一本書的目錄,比如我要找第三章第一節,那麼先在目錄裡面找,找到對應的頁碼再去找文章。

後端開發師面試常見題目
後端開發師面試常見題目

mysql索引的資料結構、各自優劣

索引的資料結構和具體存儲引擎的實作有關,再MySQL中使用較多的索引有Hash索引,B+索引等,InnoDB存儲引擎的預設索引實作為:B+樹索引。對于哈希索引來說,底層的資料結構為哈希表,是以在絕大多數需求為單條記錄查詢(where id = '')的時候,可以選擇哈希索引,查詢性能最快;其餘大部分場景,建議選擇BTree索引。

B+樹:

B+樹是一個平衡的多叉樹,從根節點到每個葉子節點的高度內插補點不超過1,而且同層級的節點間有指針互相(雙向)連結。在B+樹上的正常檢索,從根節點到葉子節點的搜尋效率基本相當,不會出現大幅度波動,而且基于索引的順序掃描時,也可以利用雙向指針快速左右移動,效率非常高。是以,B+樹索引被廣泛應用于資料庫、檔案系統等場景。

後端開發師面試常見題目

哈希索引:

哈希索引就是采用一定的雜湊演算法,把鍵值換算成新的哈希值,檢索時不需要類似B+樹那樣從根節點到葉子節點逐級查找,隻需要一次雜湊演算法即可立刻定位到相應的位置,速度非常快。

後端開發師面試常見題目

索引的設計原則

查詢更快、占用空間更小

後端開發師面試常見題目

第9點,區分度,就是細粒度過于粗糙,導緻查詢處理的數量過多。

mysql鎖的類型有哪些

基于鎖的屬性分類:共享鎖、排他鎖

基于鎖的粒度分類:行級鎖(INNODB)、表級鎖(INNODB、MYiSAM)、頁級鎖(DBD引擎)、記錄鎖、間隙鎖、臨建鎖。

基于鎖的狀态分類:意向共享鎖、意向排他鎖

後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目

mysql執行計劃怎麼看 !!!!

https://www.bilibili.com/video/BV1cf4y1G7dC?p=60

事務的基本特性和隔離級别 !!!

ACID:

Atomicity:原子性,指的是一個事務中的操作要麼全部成功,要麼全部失敗

Consistency:一緻性,指的是資料庫總是從一個一緻性的狀态轉換到另外一個一緻性的狀态。比如A轉賬給B1000塊錢,假設A隻有90塊,支付之前我們資料庫裡的資料都是符合限制的,但是如果事務執行成功了,我們的資料庫資料就破壞限制了,是以此事務不能成功,這裡我們說事務提供了一緻性的保證。

Isolation:隔離性,指的是一個事務的修改在最終送出前,對其它事務是不可見的。

duration:持久性,指的是一旦事務送出,所做的修改就會永久儲存到資料庫中。

隔離性的4個級别:

後端開發師面試常見題目
後端開發師面試常見題目

關心過業務系統裡面的sql耗時嗎?統計過慢查詢嗎?對慢查詢都怎麼優化過。

在業務系統中,除了使用主鍵進行的查詢,其他的都會在測試庫上測試其耗時,慢查詢的統計主要由運維在做,會定期将業務中的慢查詢回報給我們。

慢查詢的優化首先要搞明白慢的原因?是查詢條件沒有命中索引?是load了不需要的資料列?還是資料量太大?

是以優化主要針對這三個方面。

首先分析語句,看看是否load了額外的資料,可能是查詢了多餘的行并且抛棄掉了,可能是加載了許多結果中并不需要的列,對語句進行分析以及重寫

分析語句的執行計劃,然後獲得其使用索引的情況,之後修改語句或者修改索引,使得語句盡可能的命中索引。

如果對語句的優化已經無法進行,可以考慮表中的資料量是否太大,如果是的話可以進行橫向或者縱向的分表

ACID靠什麼保證的

A原子性由undo log日志保證,它記錄了需要復原的日志資訊,事務復原時撤銷已經執行的sql

C一緻性有其它三大特性保證,程式代碼保證業務上的一緻性

I隔離性由MVCC來保證

D持久性由 記憶體+redo log 來保證,mysql修改資料同時在記憶體和redo log記錄這次操作,當機的時候可以從redo log恢複

redo log的刷盤會在系統空閑時進行

什麼是MVCC !!!!

後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目

mysql主從同步原理

後端開發師面試常見題目

全同步複制

主庫寫入binlog後強制同步日志到從庫,所有的從庫都執行完成後才傳回給用戶端,但是很顯然這個方式的話性能會受到嚴重影響。因為主庫需要一直等待。

半同步複制

和全同步不同的是,半同步複制的邏輯是這樣,從庫寫入日志成功後傳回ACK确認給從庫,主庫收到至少一個從庫的确認就認為寫操作完成。

簡述MyIsAM和InnoDB的差別

MyISAM:

不支援事務,但是每次查詢都是原子的

支援表級鎖,即每次操作是對整個表加鎖

存儲表的總行數;(select count(*) from table)很快

一個MYIISAM表有三個檔案:索引檔案、表結構檔案、資料檔案

采用非聚集索引,索引檔案的資料域存儲指向資料檔案的指針。輔索引與主索引基本一緻,但是輔索引不用保證唯一性。

InnoDB:

支援ACID的事務,支援事務的四種隔離級别

支援行級鎖以外鍵限制;是以可以支援寫并發

不存儲總行數

一個InnoDB引擎存儲在一個檔案空間中(共享表空間,表的大小不受作業系統控制,一個表可能分布在多個檔案裡),也有可能為多個(設定獨立表空間,表大小受作業系統檔案大小限制,一般為2G),受作業系統檔案大小的限制;

主鍵索引采用聚集索引(索引的資料域存儲資料檔案本身),輔索引的資料域存儲主鍵的值;是以從輔助索引查找資料,需要先通過輔助索引找到主鍵值,再通路主鍵索引;最好使用自增主鍵,資料能夠有序的插入在之後,防止插入資料時,為維持B+樹結構,檔案的大調整。

mySQL中索引類型以及對資料庫性能的影響

普通索引:允許被索引的資料列包含重複的值,即對應着多行

唯一索引:可以保證資料記錄的唯一性,對應一行資料,但實際存儲和普通索引一緻,B+樹中隻存儲主鍵id

主鍵:是一種特殊的唯一索引,在一張表中隻能定義一個主鍵索引,主鍵用于唯一辨別一條記錄,使用關鍵字PRIMARY KEY 來建立(聚集索引,B+書中存儲實際資料)

聯合索引:索引可以覆寫多個資料列,比如像INDEX(column A,column B)索引

全文索引:通過建立反向索引,可以極大的提升檢索效率,解決判斷字段是否包含的問題(使用like效率會很低),是目前搜尋引擎使用的一種關鍵技術。可以通過AlTER TABLE table_name ADD FULLTEXT(column);建立全文索引

優勢:

索引可以極大的提升資料的查詢速度。

通過使用索引,可以在查詢過程中,使用優化隐藏器,提高系統的性能。

缺點:

會降低插入、删除、更新表的速度,因為在執行這些寫操作時,還要操作索引檔案。

索引需要占實體空間,除了資料表占資料空間之外,每一個索引還要占一定的實體空間,如果要建立聚集索引,那麼需要的空間就會更大,如果非聚集索引很多,一旦聚集索引改變,那麼所有非聚集索引都跟着改變,是以非聚集索引指向聚集索引。

RDB 和 AOF機制

RDB:redis DateBase

在指定的時間間隔内将記憶體中的資料集快照寫入磁盤,實際操作過程是fork一個子程序,先将資料集寫入臨時檔案,寫入成功後,再替換之前的檔案,用二進制壓縮存儲。

優點:

這個Redis資料庫将隻包含一個檔案dump.rdb,友善持久化

容災性好,友善備份

性能最大化,fork子程序來完成寫操作,讓主程序繼續處理指令,所有IO最大化。使用單獨子程序來進行持久化,主程序不會進行任何IO操作,保證了redis的高性能

相對于資料集大時,比AOF的啟動效率更高。

資料安全性低。RDB是間隔一段時間進行持久化,如果持久化之間redis發生故障,會發生資料丢失。是以這種方式更适合資料要求不嚴謹的的時候

由于RDB是通過fork子程序來協助完成資料持久化工作的,是以,如果資料集較大時,可能會導緻整個伺服器停止服務幾百毫秒,甚至1秒鐘。

AOF:Append only File

以日志的形式記錄伺服器所處理的每一個寫、删除操作,以文本的方式記錄,可以打開檔案看到詳細的操作記錄

優點

資料安全,Redis提供了3種與AOF檔案的同步政策,即每秒同步、每修改同步和不同步。事實上,每秒同步也是異步完成的,其效率也是非常高的,所差的是一旦系統出現當機現象,那麼這一秒鐘之内修改的資料将會丢失。而每修改同步,我們可以将其視為同步持久化,即每次發送的資料變化都會被立即記錄到磁盤中。

通過append模型寫檔案,即使中途伺服器當機也不會破環已經存在的内容,可以通過redis-check-aof工具解決資料一緻性問題。

AOF機制的rewrite模式。定期對AOF檔案進行重寫,以達到壓縮的目的

AOF檔案比RDB檔案大,且恢複速度慢

資料集大的時候,比rdb啟動效率低

運作效率沒有RDB高

總結

AOF檔案比RDB更新頻率高,優先使用AOF還原資料

AOF比RDB更安全也更大

RDB性能比AOF好

如果兩個都配了,優先加載AOF

Redis的過期鍵的删除政策

Redis是key-value資料庫,我們可以設定Redis緩存的key的過期時間。Redis的過期政策是指當Redis中緩存的key過期了,Redis如何處理。

惰性過期:隻有當通路一個key時,才會去判斷該key是否已經過期,過期則清除。該政策可以最大化地節省CPU資源,卻對内容非常不友好。極端情況可能出現大量的過期key沒有再次被通路,進而不會被清除,占用記憶體。

定期過期:每隔一定的時間,會掃描一定數量的資料庫的expires字典中一定數量的key,并清除其中已經過期的key。該政策是前兩者(CPU and 記憶體)的一個折衷方案。通過調整定時掃描的時間間隔和每次掃描的限定耗時,可以在不同情況下使得CPU和記憶體資源達到最優的平衡效果。

(expires字典會儲存所有設定了過期時間的key和過期資料,其中,key是指向鍵空間中的某個鍵的指針,value是該鍵的毫秒精度的unix時間戳表示的過期時間。鍵空間是指該Redis叢集中儲存的所有鍵)

Redis中同時使用了惰性過期和定期過期兩種過期政策。

Redis線程模型,單線程為什麼快

後端開發師面試常見題目

檔案事件處理器:多個Socket、IO多路複用程式、檔案事件分派器以及事件處理器

流程:IO多路複用程式會監聽多個Socket,會将Socket放入一個隊列中排隊,每次從隊列中取出一個Socket給事件分派器,事件分派器把Socket給對應的事件處理器。

緩存雪崩、緩存穿透、緩存擊穿

緩存雪崩是指緩存同一時間大面積失效,是以,後面的請求都會落到資料庫上,造成資料庫短時間内承受大量的請求而崩掉。

解決方案:

緩存資料的過期時間設定随機,防止同一時間大量資料過期現象發生。

給每一個緩存資料增加相應的緩存标記,記錄緩存是否失效,如果緩存标記失效,則更新資料緩存。

緩存預熱,即在啟動服務之前,先将熱點資料加載進緩存中去

互斥鎖,避免大量的請求對資料庫的同一個鍵進行操作,每次隻能一個請求進行操作,其它請求排隊。

緩存穿透是指緩存和資料庫中都沒有的資料,導緻所有的請求都落在資料庫上,造成資料庫短時間内承受大量的請求而崩掉。

接口增加校驗,如使用者鑒權校驗,id做基礎校驗,id<0的直接攔截(業務層進行規避)

從緩存取不到資料,在資料庫中也沒有取到,這時可以将key-value對改寫為key-null,緩存有效時間設定短點。這樣可以防止攻擊使用者反複用同一個id暴力攻擊。

采用布隆過濾器,将所有可能存在的資料哈希到一個足夠大的bitmap中,一個一定不存在的資料被這個bitmap攔截掉,進而避免了對底層存儲系統的查詢壓力

緩存擊穿是指緩存中沒有但資料庫中有的資料(一般是緩存時間到期),這時由于并發使用者特别多,同時讀緩存沒有讀到資料,又同時去資料庫去取資料,引起資料庫壓力瞬間增大,造成過大壓力。和緩存雪崩不同的是,緩存擊穿指并發查同一條資料,緩存雪崩是不同資料都過期了,很多資料查不到進而查資料庫。

解決方案

設定熱點資料永不過期

加互斥鎖

簡述Redis事務實作

後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目

redis叢集方案 !!!

後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目

redis主從複制的核心原理

後端開發師面試常見題目
後端開發師面試常見題目

CAP理論、BASE理論

Consistency:用戶端,即并發通路時,目前請求能夠擷取其它請求更新過的資料,即互相可見;服務端,即分布式的多台伺服器,其中一台伺服器資料發生更改,其餘的伺服器也要進行資料同步。

Availability:即服務一直可用,并且響應時間正常。

Partition Tolerance:即分布式系統中,發生網絡分區故障,節點之間無法通行,依舊能夠向外提供一緻性和可用性的服務。對于使用者而言沒有體驗上的影響。

CP和AP:之是以無法同時滿足強一緻性和可用性,是因為當發生網絡分區故障時,勢必會出現伺服器資料不同步,是以有兩種方案:1.等故障恢複,然後資料同步,再提供向外的服務,這樣就滿足了一緻性,但犧牲了可用性;2.即便故障未恢複,依舊向外提供服務,這樣隻能滿足可用性。

後端開發師面試常見題目

負載均衡算法、類型

後端開發師面試常見題目
後端開發師面試常見題目

Nginx主要在應用層進行負載均衡,LVS在傳輸層進行負載均衡。

分布式架構下,Session共享有什麼方案

cookie-session:單伺服器的Session,進行登入時,第一次使用者需要送出賬戶密碼,然後Session會在伺服器端存儲該使用者的登陸态,傳回一個該使用者的登陸态id給cookie進行存儲,下次使用者再進行通路,就會自動攜帶該使用者的登陸态id與伺服器Session中的進行對比,如果成功,就會自動登陸。

問題:分布式架構下,由于是由多台伺服器組成一個分布式系統,是以使用者的登陸态可能隻是存儲某台伺服器中,如果下次該使用者的請求配置設定到其它的伺服器上,就需要重新登陸。

後端開發師面試常見題目

采用無狀态服務,抛棄session,可以使用JWT或者token機制

因為cookie存在用戶端,是以會有風險

會引發其它同步延遲以及同步失敗等問題

這樣導緻以後該使用者的請求,隻會在指定的伺服器上進行通路,違背了負載均衡的意義。

使用redis存儲,因為所有的伺服器都可以從redis中存取值。

簡述你對RPC、RMI的了解

RPC:在本地調用遠端函數,遠端過程調用,可以跨語言實作 httpClient

RMI:遠端方法調用,java中用于實作RPC的一種機制,RPC的java版本,是J2EE的網絡機制調用,跨JVM調用對象的方法,面向對象的思維方式

後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目

分布式id生成方案

後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目

分布式鎖解決方案

需要這個鎖獨立于每一個服務之外,而不在服務裡面

資料庫:利用主鍵沖突控制一次隻有一個線程才能擷取鎖,非阻塞、不可重入、單點、失效時間。

Zookeeper分布式鎖:

驚群效應:即一個線程擷取鎖再執行,另外多個線程在阻塞等待,當這個線程執行完成後,釋放出鎖,如果喚醒全部的阻塞線程,就稱為驚群效應。

Redis分布式鎖:setNx,單線程處理網絡請求,不需要考慮并發安全性。

所有服務節點設定相同的key,傳回為0,則鎖擷取失敗

删除鎖:判斷線程的唯一辨別,再删除

可重入性及鎖續期沒有實作,通過redisson解決(類似AQS的實習,看門狗監聽機制)

redlock:以上的機制都隻操作單節點,即使Redis通過sentinel保證高可用,如果這個master節點由于某些原理發生了主從切換,那麼就會出現鎖丢失的情況(redis)同步設定可能資料丢失

。redlock從多個節點申請鎖,當一半以上節點擷取成功,鎖才能算擷取成功,redission有相應的實作。

分布式事務解決方案

XA規範:分布式事務規範,定義了分布式事務模型

四個角色:事務管理器(協調者TM)、資料總管(參與者RM)、應用程式AP、通信資料總管CRM

全局事務:一個橫跨多個資料庫的事務,要麼全部送出、要麼全部復原

JTA事務是java對XA規範的實作,對應JDBC的單庫事務

後端開發師面試常見題目

第一階段(prepare):每個參與者執行本地事務但不送出,進入ready狀态,并通知協調者已經準備就緒

第二階段(commit):當協調者确認每個參與者都ready後,通知參與者進行commit操作;如果有參與者fail,則發送rollback指令,各參與者進行復原

問題:

單點故障:一旦事務管理器出現故障,整個系統不可用(參與者都會阻塞)

資料不一緻:在階段二,如果事務管理器發送commit消息,由于網絡發送異常,那麼隻有部分參與者接受到commit消息,也就是說隻有部分參與者送出了事務,使得系統資料不一緻。

響應時間長:參與者和協調者資源都被鎖住,送出或復原之後才能釋放

不确定性:當事務管理器發送commit之後,并且此時隻有一個參與者收到了commit,那麼當參與者與事務管理器同時當機之後,重新選舉的事務管理器無法确定該條消息是否送出成功。

三階段協定:主要針對兩階段的優化,解決了2PC單點故障問題,但是性能問題和不一緻問題依舊沒有根本解決

後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目

TCC:是通過純業務邏輯代碼實作分布式事務解決;2PC,3PC則是通過資料庫的commit,rollback來解決分布式事務。

如何實作接口的幂等性

場景:使用者點選注冊按鈕,送出資訊,由于網絡等因素,背景沒反應,是以使用者送出了多次,如何保證背景在接受相同資料時,隻會對資料庫操作一次。

唯一id。每次操作,都根據操作和内容生成唯一的id,在執行之前先判斷id是否存在,如果不存在則執行後續操作,并且儲存到資料庫或者redis等(還是會通路資料,對資料造成壓力)

服務端提供發送token的接口,業務調用接口前先擷取token,然後調用業務接口請求時,把token攜帶過去,伺服器判斷token是否已經存在redis中,未存在表示第一次請求,可以繼續執行業務,執行業務完成後,最後需要把redis中的token删除。

建去重表。将業務中有唯一辨別的自動儲存到去重表,如果表中存在,則表示已經處理過了

版本控制。增加版本号,當版本号符合時,才能更新資料

狀态控制。例如訂單有狀态 已支付、未支付、支付中、支付失敗,當處于未支付的時候才能允許修改支付中。

簡述ZAP協定

後端開發師面試常見題目

zk的資料模型和節點類型

後端開發師面試常見題目
後端開發師面試常見題目

簡述zk的命名服務、配置管理、叢集管理

後端開發師面試常見題目

Zookeeper watch機制

後端開發師面試常見題目

zk和eureka的差別

zk:CP設計(強一緻性),目标是一個分布式的協調系統,用于進行資源的統一管理。

當節點crash後,需要進行leader的選舉,在這個期間,zk服務是不可用的。

eureka:AP設計(高可用),目标是一個服務注冊發現系統,專門用于微服務的服務發現注冊。

Eureka各個節點都是平等的,幾個節點挂掉不會影響正常節點的工作,剩餘的節點依然可以提供注冊和查詢服務。而Eureka的用戶端在向某個Eureka注冊時如果發現連接配接失敗,會自動切換至其它節點,隻要一台Eureka節點還在,就能保證注冊服務可用(保證可用性),隻不過查詢的資訊可能不是最新的(不保證強一緻性)

同時當eureka的服務端發現85%以上的服務都沒有心跳的話,他就會認為自己的網絡出了問題,就不會從服務清單中删除這些失去心跳的服務,同時eureka的用戶端也會緩存服務資訊。eureka對于服務注冊發現來說是非常好的選擇。

Spring Cloud和Dubbo的差別

底層協定:springcloud基于http協定,dubbo基于tcp協定,決定了dubbo的性能相對會比較好

注冊中心:Spring Cloud使用的eureka,dubbo推薦使用zookeeper

模型定義:dubbo将一個接口定義為一個服務,springCloud則是将一個應用定義一個服務

springCloud是一個生态,而Dubbo是SpringCloud生态中關于服務調用一種解決方案(服務治理)

什麼是Hystrix?簡述實作機制

分布式容錯架構

阻止故障的連鎖反應,實作熔斷

如果某個請求的服務優先級不高,且占用資源較多,就會快速失敗,實作優雅降級

提供實時的監控和警告

資源隔離:線程隔離、信号量隔離

線程隔離:Hystrix會給每一個command配置設定一個單獨的線程池,這樣在進行單個服務調用的時候,就可以在獨立的線程池裡面進行,而不會對其它線程池造成影響

信号量隔離:用戶端向依賴服務發起請求時,首先要擷取一個信号量才能真正發起調用,由于信号量的數量有限,當并發請求量超過信号量個數時,後續的請求都會直接拒絕,進入fallback(降級)流程。信号量隔離主要是通過控制并發請求量,防止請求線程大面積阻塞,進而達到限流和防止雪崩的目的。

熔斷和降級:調用服務失敗後快速失敗

熔斷是為了防止異常不擴散,保證系統的穩定性

降級:編寫好調用失敗的補救邏輯,然後對服務直接停止運作,這樣這些接口就無法正常調用,但又不至于直接報錯,隻是服務水準下降。

後端開發師面試常見題目

springcloud核心元件以及作用 !!!

後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目

解釋:Ribbon做負載均衡,即某個eureka client 從 eureka server 擷取目标服務的位址時,而該目标服務由多個服務機器組成,通路其中一台目标服務機器都能完成請求,是以 Ribbon控制這些目标服務的負載均衡,根據每個機器的資源情況,将eureka client的請求轉發到目标服務機器上去。

後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目

Zuul也是作為一個 eureka client ,目前端通路背景時,Zuul會根據該請求從eureka server 上找到目标服務的位址,然後進行通路。

Dubbo的整體架構設計及分層

後端開發師面試常見題目
後端開發師面試常見題目

簡述RabbitMQ的架構設計

後端開發師面試常見題目
後端開發師面試常見題目
後端開發師面試常見題目

RabbitMQ如何確定消息發送?消息接受?

後端開發師面試常見題目
後端開發師面試常見題目

RabbitMQ事務消息

通過對信道的設定實作,發送端

channel.txSelect(); 通知伺服器開啟事務模式;服務端會傳回Tx.Select-Ok

channel.basicPublic;發送消息,可以是多條,可以是消息送出ack

channel.txCommit();送出事務

channel.txRollback();復原事務

解釋:在開啟事務的情況下,生産者發送消息給服務端,服務端不會直接把它存到queue中,而是放在臨時隊列中,當生産者送出事務後,服務端才會把臨時隊列的消息存到Queue中。由于使用事務,會經常通路服務端,是以性能不如confirm模式

消費者使用事務:

auto=false,手動送出ack,以事務送出或復原為準

auto=true,不支援事務,也就是說你即使在收到消息之後再復原事務也是于事無補,隊列已經把消息移除了

如果在事務中任意一個環節出現問題,就會抛出IoException異常,使用者可以攔截異常進行事務復原,或決定要不要重複消息

事務消息會降低rabbitmq的性能

RabbitMQ死信隊列、延時隊列

後端開發師面試常見題目

解釋:死信隊列和死信交換機,即等同于普通的Queue和exchange;延時隊列,即用一個Queue+死信隊列實作,當Queue的消息的存活時間超出了TTL,就會轉到死信隊列中,是以消費者隻需要消費死信隊列的消息即可

簡述kafka架構設計

後端開發師面試常見題目
後端開發師面試常見題目

Kafka消息丢失的場景及解決方案 !!!

消息發送

後端開發師面試常見題目

解釋:ack=0,即ack累加最大到0,意味着服務端不需要傳回ack,是以無ack可用;ack=1,即ack累加最大到1,意味着隻能lead寫入成功傳回ack,但是這個時候leader crash,fallower沒來得及同步資料,資料就會丢失;ISR存的都是資料同步好的節點,OSR存的是資料落後于leader的節點,是以如果讓OSR也參加選舉,會造成資料丢失

消費

解釋:先把offset送出後,再處理,如果在處理offset位置的資料發送異常,沒有消費成功,以後也不會消費到這個消息了

broker的刷盤

減小刷盤間隔

解釋:刷盤的間隙發生資料丢失

Kafka是pull?push?優劣勢分析

pull模式:

根據consumer的消費能力,consumer進行資料拉取,可以控制速率

可以批量拉取、也可以單條拉取

可以設定不同的送出方式,實作不同的傳輸語義

缺點:如果kafka沒有資料,consumer會一直拉取空循環,消耗資源

優點:通過參數設定,consumer拉取資料為空或者服務端的資料沒有達到一定數量時進行阻塞

push模式:不會導緻consumer循環等待,由服務端主動推送資料

缺點:速率固定,忽略了consumer的消費能力,可能導緻拒絕服務或者網絡擁塞等情況。

Kafka中的zk的作用(新版本的kafka中,zk的作用越來越小)

後端開發師面試常見題目

Kafka中(讀寫)高性能的原因

KafKa不基于記憶體,而是移動硬碟,是以消息堆積能力更強

順序寫:利用磁盤的順序通路速度可以接近記憶體,kafka的消息都是append操作,partition是有序的,節省了磁盤的尋道時間,同時通過批量操作(累計到一定量再一次性寫入),節省寫入次數,partition實體上分為多個segment存儲,友善删除。

傳統:

讀取磁盤檔案資料到核心緩沖區

将核心緩沖區的資料copy到使用者緩沖區

将使用者緩沖區的資料copy到socket的發送緩沖區

将socket發送緩沖區中的資料發送到網卡、進行傳輸

零拷貝:

直接将核心緩沖區的資料發送到網卡傳輸

使用的是作業系統的指令支援

kafka不太依賴jvm,主要使用作業系統的pageCache,如果生産消費速率相當,則直接使用pageCache交換資料,不需要經過磁盤IO

從平凡到超凡,隻有一萬個小時的距離!