主頁 > 後端開發 > 跟Java面試官對線的一天!唬住就要50K,唬不住就要5K

跟Java面試官對線的一天!唬住就要50K,唬不住就要5K

2021-07-26 07:40:28 後端開發

個人面經

  • 前言
  • JVM篇
  • 計網篇
  • Java基礎篇
  • 多執行緒篇
  • Spring框架篇
  • MyBatis框架篇
  • MySQL篇
  • Redis篇
  • 分布式、微服務篇
  • 小結

前言

不積跬步無以至千里,不積小流無以成江海

終于呀,懷著期待的心走進了公司的大門,迎面而來的就是一個小姐姐,

在這里插入圖片描述
hr:您好,請問你是今天過來面試的嗎?
我:哇,這里的小姐姐都這么漂亮嗎,嗯,你好,我是今天來面試的,
hr:嗯,那你先簡單的做個自我介紹好吧,
我:(以下自我介紹是自己的)

嗯,好的,面試官你好,我叫彭于晏,畢業于XX大學,今天來面試貴公司的Java開發,我從事這個行業已經兩年多了,先后做過XX專案,最近做的一個專案是在上家公司做的一個XXXX平臺,我主要是負責里面的行為模塊,以及XX功能,在處理并發這塊我也有一定的經驗,之前的專案QPS大概是在100W左右,對于技術堆疊這塊,我比較熟練的是Spring Boot以及Spring Cloud,資料庫方面能熟練的使用MySQL和MongoDB以及Redis,并且搭建過公司的服務集群,對集群化配置這方面也比較熟悉,除此之外,我還喜歡平時看看書,看看妹子之類的,以上就是我的自我介紹,

hr:嗯,還不錯,那你這邊的情況我待會兒會給我們的技術經理 ,然后我們的技術經理會對你進行一個簡單的面試,
我:嗯,謝謝了哦,
我:(這兒的hr妹子都這么漂亮嗎,得加油了呀,為了終身幸福)

終于,面試官頂著一個地中海坐到了我的對面,
面試官:你好,那我們就開始面試可以嗎?
我:好的,

JVM篇

面試官:嗯,看你的簡歷上寫了你對JVM比較熟悉,那我就先問幾個關于JVM的可以吧,
我:嗯,可以,沒問題,

面試官:你簡單的說一下JVM的運行時資料區,
我:我對JVM運行時資料區是這么去理解的,一共有兩個部分,一部分是執行緒私有,另一部分是執行緒共有的區域,我們先說說執行緒私有的區域,在這其中一共又分為三部分:分別是程式計數器,本地方法堆疊和Java虛擬機堆疊,執行緒公有的部分其實在版本迭代的程序中,有了一些變化,在JDK1.6的時候,這部分是由堆和方法區組成的,但是在1.8 的時候,取消了方法區,變成了元空間,元空間使用直接記憶體,

面試官:嗯,你理解的還不錯,那你講一下程式計數器主要是干嘛的,
我:程式計數器在JVM中,是一塊比較小的空間,聽名字主要就是計數,但是我們一般習慣性的將他理解成當前執行緒所執行的位元組碼的行號指示器,之所以這塊部分是執行緒私有的呢,是因為每個執行緒都擁有屬于自己的程式計數器,最大的作用就是可以找到上一次執行緒執行的位置,從而繼續執行我們的代碼,各個執行緒之間的計數器是互不影響的,

面試官:嗯,回答的不錯,那你知道在JVM中唯一一塊不會出現OOM的區域是哪塊區域嗎?
我:嗯,就是我剛才說的程式計數器,因為這塊是跟著執行緒的創建而創建,執行緒執行完了,也就銷毀了,

面試官:那你再簡單的講一下Java的虛擬機堆疊這塊區域吧
我:好的,這塊區域和我們的程式計數器是一樣的,也是執行緒私有的部分,而且他的生命周期和執行緒相同,描述的是方法執行的記憶體模型, 在Java記憶體中,我們大概就粗分為堆記憶體和堆疊記憶體,其中的堆疊就是我們說的這個虛擬機堆疊,實際上,虛擬機堆疊是由一個個的堆疊幀組成,而每個堆疊幀中都有區域變數表,運算元堆疊,動態鏈接和方法出口等資訊,我們的區域變數表主要存放了編譯期間的各種資料型別,比如你寫個int i=0;那么他就會放在我們的區域變數表,

面試官:嗯,那我們的方法是怎么被呼叫的呢?
我:其實方法呼叫比較簡單,主要就是將我們的方法壓入堆疊,然后對應的呼叫就是一次方法被彈出堆疊的程序,比如說你呼叫了return或者說thr一下,都會被彈出堆疊,

面試官:那你再講一下動態鏈接是什么?
我:動態鏈接最大的作用就是我們的多型型別了,我們舉個例子,在堆疊中有個Map,但是在堆區有個HashMap,這倆貨的型別肯定是子類和父類,所以動態鏈接最主要的就是將我們的型別自動指向子類去呼叫,

面試官:可以嘛小伙子,看不出來知識量儲備還不錯,那你接著說一下JVM中的垃圾回收,
我:首先,我們在講這塊的時候得去想,垃圾回收是在哪兒回收的,在我們new的所有物件,其實都是放在堆區中進行存盤的,那么當這個new物件沒有參考了咋辦?那自然就會觸發JVM的垃圾回識訓制,為了方便垃圾回收,將堆區又細分成了年輕代和老年代這兩個部分,他們的比例分別是占1/3和2/3,其中,在年輕代中,又細分成了伊甸區(Eden)和幸存區,在幸存區中我們又習慣性的說成是From區和To區,這三部分的比例是8:1:1,當我們的物件在Eden區放不下的時候,這時候就會觸發一次GC,將我們的物件放進From區,然后當From區也滿了之后,就會將部分未清理掉的物件放進去To區,當To區也滿了之后,就會將物件放進From區,這樣來回回圈15次之后,物件如果還不死,就將這個物件放進去老年代,當我們的老年代也滿了之后,就會完成一次Full GC(整堆收集),會造成STW(Stop The Word)的情況,所以我們JVM調優,其實最主要的就是盡量避免Full GC的次數,如果說我們創建的物件很大,那么這個物件就會直接進入老年代,一般來說,如果你想調整From和To區的次數,可以通過-XX:MaxTenuringThreshold來調節,

面試官:嗯,你講的很不錯,不過你剛才說的時候忽略了一個地方,那就是堆區容易產生OOM例外,當你的垃圾回收用了太多的時間,就會出現這個問題,
我:嘿嘿,尷尬尷尬,沒想起來這塊,

面試官:那你接著說一說垃圾收集器吧,
我:在說這塊之前我們先說一個問題,那就是垃圾收集演算法;我們知道,常見的垃圾收集演算法大致分為以下兩種:
參考計數法和可達性分析演算法,
參考計數法主要實作的原理就是,給定一個標志位,當一個物件被參考的時候,就給這個標志位加上1,當沒有物件去參考的時候,就去減1,這樣當這個標志位變成0的時候就代表這個物件沒有參考,可以收集,但是,這種演算法有個弊端,那就是當有一對物件互相參考的時候,這時就沒辦法使用,所以又衍生出來另一種演算法:可達性分析演算法,
可達性分析演算法最主要的原理是有一個GC Roots作為根節點,向下搜索,如果這個根節點搜索的程序中,沒有物件去參考這個根節點,那么就說明這個物件是一塊垃圾,需要去清理掉他,就我們常見的GC Roots,在Java中,有如下幾種:static屬性變數,JNI變數,字串常量池的參考,被同步鎖持有的物件,
垃圾收集演算法一共大致分為以下幾種:
分代收集,標記-清除,標記-整理,標記-復制,就拿jdk1.8來說,是混合使用的垃圾收集機制,在年輕代使用的標記復制,而在老年代使用的是標記整理,
我們說完了垃圾收集演算法,再來聊一聊垃圾收集器,
垃圾收集器正是基于這些垃圾收集演算法而實作的一些具體的收集器,現在的垃圾收集器共有以下幾種:
Serial,ParNew,Parallel Scavenge,CMS,Garbage First(又稱G1),
這其中的一些細節問題,如果有兄弟需要的,請在文章底部留言,我會單獨出一篇文章來詳細解釋,

面試官:那你知道Java中的四種參考型別嗎?
我:在Java中,一共有四種參考型別,分別是強、軟、弱、虛,
在強參考中,被參考的型別不管怎么樣都不會被垃圾收集器回收,
在軟參考中,通過可以理解為可有可無的物件,如果說在Java中,記憶體足夠就把你留著,記憶體不夠了,就把你扔了,你可以這樣去理解,你去打工,人家店里面并不是很想要你,因為人已經招滿了,所以就告訴你,如果哪天我們實在是人太多了,你就自己主動拜拜,
弱參考指的是只要垃圾收集器一運轉,那么弱參考的物件就會被自動回收,
虛參考主要的作用是用來跟蹤物件被回收的狀態,收到一個回收的通知,

面試官:你再說說Java中的類加載機制吧,
我:(心里mmp,這么多了,還要問)嗯,好的,Java中的類加載是一種懶加載的形式,只有當這個類需要被使用的時候才會去加載,這樣做的好處就是避免一次性加載全部,從而占用很大的記憶體,
在類加載的程序中,大致會經歷以下7個階段:加載、驗證、準備、決議、初始化、使用、卸載,
在加載程序中,會將我們的.java檔案轉成class檔案,然后將這個class檔案轉成二進制流,
在驗證階段會去驗證當前的二進制流會不會威脅到虛擬機的安全,并且驗證是不是當前虛擬機所需要的東西,
在準備階段,為類變數進行賦值,并分配記憶體,但是應該注意一點,如果是實體變數,那只是做了一個初始化,而不是去賦值,比如說

public static int value = 123; // 這兒只是給了一個初始化值為0,而不是123
public static final int value = 123; // 這兒作為一個類常量,就是直接賦值為123

決議階段,將常量池的符號替換成直接參考,
初始化階段開始真正執行類中定義的Java程式代碼,在準備階段,類變數已經賦值過一次系統要求的初始化,在初始化階段,根據程式員通過制定的主觀計劃去初始化類變數和其他資源,

我們說完了上面的加載生命周期,再說類的雙親委派和全盤委托,
以前使用的類,在加載階段中,將這個類關聯的所有類加載器全都加載出來,但是這樣做有一個問題,那就是如果找到的類和我們使用的類的加載級別不是同一個,這樣就會出現大問題,舉個例子:我們知道在java.lang包下有個String類,那么如果我們也自定義一個這樣的包和類,在使用的時候去呼叫這個類的main方法(原本自帶的不存在main方法),這個時候就會出現一個例外(名字我忘了),所以為了解決這樣的問題,Java虛擬機采用了雙親委派機制來處理類加載,通常上面的問題也被說成是為了解決沙箱安全機制,
所謂的雙親委派機制大致就是這樣的流程:在jvm中,按照等級一共分為三種類加載器,分別是系統類加載器,拓展類加載器和引導類加載器,在類加載的程序中,首先會去使用系統類加載器,但是系統類加載器會將這個類交給拓展類加載器,拓展類加載器又會將這個類交給引導類加載器去完成,簡單的一句話就是,自己不想做的事情交給爹去做,然后在引導類加載器中發現沒有,就又會往下一級一級的回傳去加載,如果找不到,就報錯,
這樣做的好處是什么呢?就是可以讓Java類隨著它的類加載器一起具有一種帶有優先級層次關系,從而使基礎類得到統一,舉個例子:Object類放在rt.jar中,如果我們自己去寫一個一樣的包結構和類名,那么由于雙親委派的存在,會從引導類加載器自上而下的去加載,由于rt.jar的優先級比ClassPath等級高,所以在整個程序中使用的都是rt.jar中的Object類,

面試官笑著說:小伙子可以嘛,JVM這一塊你還是準備的不錯,那我們接著問下一個部分唄,
我:嘿嘿,謝謝,那我們繼續(繼續幻想著hr小姐姐和那個前臺小姐姐,順便貼一張前臺小姐姐的圖),
在這里插入圖片描述

計網篇

面試官:那我簡單的問問計網的知識,
我:好的,沒問題,

面試官:OSI與TCP/IP各層的功能結構你簡單介紹一下
我:在學習計算機網路的時候,我們一般采用折中的方法,將OSI的七層協議抽成五層,將最上層的應用層和表示層以及會話層抽成一個應用層,
所以自上而下一般都是:應用層、運輸層、網路層、資料鏈路層和物理層,我簡單的介紹一下這些層的作用,
應用層:主要任務是通過應用行程間的互動來完成特定的網路應用,應用層謝以定義的是應用行程間的通信和互動的規則,對于不同的網路應用需要不同的應用層協議,比如域名系統DNS,HTTP協議,電子郵件的SMTP協議,
運輸層:主要作用是負責向兩臺主機行程之間的通信提供通用的資料傳輸服務,應用行程利用該服務傳送應用層報文,
網路層:在計算機網路中進行通信的兩個計算機之間可能會經過很多個資料鏈路,也可能還要經過很多通信子網,網路層的任務就是選擇合適的網間路由和交換節點,確保資料及時傳送,
資料鏈路層:通常簡稱為鏈路層,兩臺主機之間資料傳輸,總是在一段一段的鏈路上傳送的,這就需要使用專門的鏈路層協議,在節點之間傳輸資料時,鏈路層將網路層交下來的IP資料報組成幀,然后在節點上進行傳輸,
物理層:物理層的作用是實作相鄰計算機節點之間位元流的透明傳送,盡可能屏蔽掉具體傳輸介質和物理設備的差異,

面試官:那你能講一下三次握手和四次揮手嗎?
我:在網路傳輸的程序中,為了保證資料傳輸無誤,通常采用三次握手的策略,
客戶端發送帶有SYN標志的資料包 代表第一次握手
服務端發送帶有SYN/ACK標志的資料包 代表第二次握手
客戶端發送帶有AC標志的資料包 代表第三次握手

面試官:那為什么斷開連接又需要四次揮手呢?
我:任何一方都可以在資料傳送結束后發出連接釋放的通知,等待物件確認后進入半關閉狀態,當另一方也沒有資料再發送的時候,則發出連接釋放的通知,對方確認后就完全關閉了TCP連接,舉個例子:你和張三打電話,然后你沒啥說的了,你跟張三說我們掛了吧,然后張三說好,但是張三還沒有說完,于是又說了一會兒,然后張三說,好了沒啥說的了,掛了吧,然后你回張三,好的,

面試官:嗯…回答的比較簡單,這個你回去了之后可以再看看這方面的書,我們問下一個問題,一個URL輸入之后發生了什么?
我:通常來說,一個URL輸入之后,會先給DNS決議,然后找到這個域名,然后去瀏覽器快取或者DNS快取或者路由器快取去查找,這里使用的協議是DNS,找到了之后向web服務器發送一個HTTP請求(比如我們的Tomcat或者Netty),攜帶我們的cookie,服務器處理這個請求,生成一個HTML回應,然后將這個回應發送給瀏覽器,瀏覽器顯示這個HTML,這里使用到的協議有TCP協議,用來與服務器建立連接,IP協議,發送資料,OPSF協議,路由選擇器使用,ARP協議,將IP地址轉為MAC地址,HTTP協議,訪問網頁,

面試官:TCP如何保證傳輸可靠性,
我:首先將應用資料包分割成TCP適合發送的資料塊,然后對這些資料塊進行編號,排序,再把有序資料傳送給應用層,然后開始校驗和,主要目的是檢測資料在傳輸程序中的任何變化,如果有差錯,將丟棄掉這個報文段,然后TCP的接收端會丟棄重復的資料,完事兒之后做一次流量控制,保證只能接識訓沖區能接受的資料,如果來不及處理,則提示對方降低發送的速率,防止丟失,

隨后面試官問了我一個問題,你覺得我們公司的氛圍咋樣,我想也沒想,公司的前臺妹子長得不錯,面試官聽完笑了笑,其實我們公司的UI部門妹子也很不錯,你要不要看看,說完就掏出手機找到了UI妹子朋友圈,翻了幾張生活照給我看,
在這里插入圖片描述
我頓時就有精神了,說,來吧,面試官我們繼續,
面試官:嗯,可以啊,那我再問幾個基礎題,

Java基礎篇

面試官:既然有了位元組流,為啥還要有個字符流呢?
我:在Java中處理資料最終都是轉換成字符流來處理,因為我們的資料每次都需要通過位元組去轉換字符,而且這轉換的程序中還很耗時,如果我們不知大編碼的話轉換還很麻煩,所以Java為了避免這種頻繁的轉換,就直接有一個字符流來供我們操作,

面試官:淺拷貝和深拷貝有什么區別?
我:淺拷貝在基本資料型別中只是拷貝了一份資料值,在參考資料型別中是拷貝了一份記憶體地址(你可以理解為Linux中的軟鏈接)
深拷貝:對基本資料型別進行值傳遞,對參考資料型別,新建一塊記憶體區,將原地址拷貝一份放在這個記憶體區中(你可以理解為檔案復制)

面試官:講一下介面和抽象類的區別
我:在Java層面,介面是使用interface修飾,抽象類是使用abstract class修飾,在設計層面,介面更多是作為一種規范,而抽象類更多的是作為一種模板,在介面中除了static和final變數不能有其他變數,在抽象類中則沒有這個限制,一個類可以實作多個介面,但是只能繼承一個抽象類,

面試官:==和equals()區別
我:這個得分兩種情況, == 如果比較的是基本資料型別,那么就是比較兩者的值是否相同,如果比較的是參考資料型別,那么就會去比較兩者所在的記憶體地址是否相同,比如new String(“a”)和new String(“a”)是不一樣的,
equals比較如果比較的是基本資料型別,那和 == 一樣,如果比較的是參考資料型別,則需要看這個類是否重寫了equals()方法,如果重寫了,那么就比較的是兩者的資料值,如果沒有重寫,那么比較的是兩者的記憶體地址,

面試官:為什么重寫了equals()還必須重寫hashcode()
我:這是因為兩者如果hashCode一樣,但是equals()并不一定一樣,在了解這塊的時候先去了解一下hashCode是如何被計算出來的,hashCode其實就類似于一個int值,采用的雜湊演算法實作,越撈的雜湊演算法越容易計算出相同的hashCode值,所以如果不重寫hashCode()方法的話,兩者比較的值可能就會出現明明不同,但是計算出來的hash值卻是相同的,(仔細想想hashSet是不是這個道理,建議看看原始碼),

面試官:你知道Java創建物件有哪些方式嗎?
我:一共可以通過四種方式創建,分別是 new一個物件 、 通過clazz.getInstance() 、通過constructor.getInstance(),和克隆(其實這兒我不確定序列化會不會創建物件,所以就暫定為四種)

面試官:知道jdk8中的stream流嗎?怎么創建這個stream流
我:假如我們有一個陣列,那么可以通過呼叫Arrays.stream(arrs)來創建,除此之外,如果我們有一個list,那么可以通過list.stream()來創建,得到的物件是一個Stream物件,用來對我們的陣列進行操作,

面試官:剛才聽你說自我介紹的時候,對多執行緒這塊還可以,那我們換個多執行緒的話題接著聊,
我:…(嗯)

多執行緒篇

面試官:你知道哪幾種創建執行緒的方式
我:可以通過繼承Thread類,可以實作Rannable介面,還可以實作Callable介面,可以通過執行緒池來創建,
面試官:Callable介面和Rannable介面,這兩個有啥區別?
我:Callable介面可以回傳值,通過FutureTask來接識訓傳值,Runnable介面沒有回傳值,只能執行執行緒,

面試官:說一說執行緒的生命周期和狀態,
我:在執行緒創建后處于 (new)創建 狀態,呼叫start()方法開始執行,這個時候執行緒處于 (ready)可運行 狀態,可運行的執行緒獲得了CPU時間片后處于 (running)運行 狀態,當執行緒執行wait()方法后,執行緒進入 (wait)等待 狀態,進入等待狀態的執行緒需要依靠其他執行緒的通知才能夠回傳到運行狀態,而 (TIME_WAITING)超時等待 狀態相當于在等待狀態的基礎上增加了超時限制,比如通過sleep()方法或者wait()方法可以將執行緒置于timed waiting狀態,當超時時間到達之后Java執行緒將會回傳到runnable狀態,當執行緒呼叫同步方法時,在沒有獲取到鎖的情況下,執行緒將會進入到 (block)阻塞 狀態,執行緒在執行Runnable的run()方法之后將會進入到 (terminated)終止 狀態,

面試官:為什么我們啟動執行緒時呼叫start()方法而不是run()方法?
我:因為呼叫start()方法才是讓執行緒進入可運行狀態,呼叫run()方法只是會當成一個main()下面執行的一個普通方法,

面試官:你對synchronized關鍵字怎么理解的,
我:synchronized關鍵字解決的是多個執行緒之間訪問資源的同步性,synchronized關鍵字可以保證被他修飾的方法或者代碼塊在任意時刻只能有一個執行緒執行,
在早期的Java版本中,synchronized屬于重量級鎖,效率比較低,但是在Java6以后對synchronized做了優化,所以能看到現在很多的框架原始碼底層都是用的synchronized,
(ps:鎖升級的程序參考《Java并發編程的藝術》,這里就不多贅述了)

面試官:你平時用的哪種鎖用的多,說說業務場景,
我:我個人比較喜歡使用Lock類中的lock()鎖,首先,這個鎖自由控制度比較高,而且能知道是否獲取了這個鎖,而且Lock鎖可以自由設定公平與非公平鎖,業務場景是使用了BlockingQueue來實作一個資料同步和監視的功能(具體不做贅述)

面試官:JMM了解嗎?說一下
我:在jdk1.2之前,Java的記憶體模型總是從主記憶體中讀取變數,而在當前的記憶體模型中,中間加了一個高速快取,執行緒可以把變數保存在本地記憶體,這就會導致從主記憶體中讀取到的值和從本地記憶體中讀取到的值不一樣,所以為了解決這個問題,我們就需要使用volatile來解決,volatile的主要作用就是保證變數的可見性和防止JVM的指令重排,

面試官:synchronized和volatile的區別是什么?
我:首先,兩者是互補,而不是對立,volatile關鍵字是執行緒同步的輕量級實作,所以性能肯定是比synchronized好的,但是volatile只能用于變數,而synchronized可以修飾方法塊和代碼塊,
volatile能保證資料的可見性,但是不能保證資料的原子性,synchronized兩者都能保證,
volatile主要用于解決變數在多個執行緒之間的可見性,而synchronized關鍵字解決的是多個執行緒之間訪問資源的同步性,

面試官:ThreadLocal用過嗎?講一下底層原理實作,
我:我們平時使用ThreadLocal最主要的還是為了解決一個變數在多個操作中不同步的問題,假如說,我用了一個jdbc連接,但是在使用的時候發現這個連接沒有了,這就有可能是其他執行緒在使用的時候使用完畢,就已經關掉了,所以用ThreadLocal就可以將這個連接保存一份副本,然后存盤在自己的私有區域,這樣,每個執行緒拿到的連接就都是自己的那一份,而不會互相打擾,他的底層原理還是使用的一個叫做ThreadLocalMap,我們可以理解為一個執行緒安全的concurrentHashMap,這個map中保存的就是我們每次存放進去的私有的副本,如果查看原始碼可以知道,其實我們的值并不是放在ThreadLocal中,而是放在ThreadLocalMap中,其中的有個方法執行的就是map.put(this,value);

面試官:執行緒池用過嗎?講一下他的構造實作
我:所有執行緒池的父介面都是Executor,但是根據阿里巴巴的開發規范,并不推薦我們使用這個去創建執行緒池,容易導致OOM例外,更加希望我們自己去設定引數,如果你自己去觀察之后會發現,默認提供的四個執行緒池物件都是呼叫了ThreadPoolExecutor,其中共分為七個引數:分別是核心執行緒數,最大執行緒數,佇列容量,存活時間,時間單位以及拒絕策略,(詳細的我就不講了,有需要的小伙伴可以去看一下構造方法)

面試官:你在自己的業務中怎么去使用執行緒池的?
我:在Spring boot中有自帶的執行緒池叫做ThreadPoolTaskExecutor,它內部是維護了一個ThreadPoolExecutor,我們平時使用的時候就去重寫這個類,然后設定好相應的引數進行使用,

面試官:JUC下的四種原子類你知道嗎?簡單的介紹一種
我:分別是基本型別、陣列型別、參考型別和物件的屬性修改型別,就簡單的介紹其中用的比較多的AtomicInteger,我們知道i++并不是一個原子類的操作,正是由于主記憶體和作業記憶體的切換,導致變數i的值在多執行緒的操作下可能會出現資料紊亂的情況,我們除了使用上面說的那種volatile解決外,還可以使用AtomicInteger來保證i的原子性,

面試官:AQS你用過嗎?你的業務場景是什么樣的,
我:AQS全稱叫做抽象佇列同步器(AbstractQueuedSynchronizer),他的內部設計主要是運用了模板設計模式,使用者去繼承這個類,然后實作里面的模板方法,其中Java自帶的核心的三個組件就是CountDownLatch、CyclicBarrier以及Semaphore,我在處理業務的時候遇到一個需求就是將一個很大的excel檔案匯入資料庫中,最開始考慮采用MyBatis的forEach進行批量插入,但是最后執行的效果并不理想,最后使用了CountDownLatch,將資料進行切片,然后采用多執行緒+異步的方式進行處理,

面試官:嗯,你寫個CountDownLatch的代碼,說完就喊了一聲,小馨,拿只筆和一張紙過來,
說完,門打開,進來了一個身穿漢服的小姐姐,長得是真漂亮,我都懷疑自己進了一個不正規的場所,要不是面試官是地中海我都準備辦會員卡了,
我:


public class CountDownLatchDemo {
   public static void main(String[] args) {
      CountDownLatch latch = new CountDownLatch(6);
       ExecutorService threadPool = Executors.newFixedThreadPool(10);
      for (int i = 0; i < 6; i++) {
         threadPool.execute(()->{
               System.out.println(Thread.currentThread().getName()+"Go out");
               latch.countDown();
         });
       }
       try {
           latch.await();// 等待計數器歸零,再向下執行
      } catch (InterruptedException e) {
           e.printStackTrace();
       }
       threadPool.shutdown();
       System.out.println("執行完畢,關門");
   }
}

面試官:嗯,這部分的知識我們暫時就面到這里,我們來說一下Spring框架,你對Spring框架掌握的怎么樣?
我:嗯,出了手寫框架不太可能之外,其他的都沒問題,
面試官:好,那我們繼續,
(ps:順便貼一下小馨的圖)
在這里插入圖片描述

Spring框架篇

面試官:看你做過的專案中大多都是Spring做的,那你說說Spring中的幾個重要的模塊
我:在Spring中,一共有以下幾個模塊:core、Aspect、aop、jdbc、jms、orm、web、test,其中,我們最常用的就是core中的ioc功能,aop實作切面,jms實作訊息服務,web實作網路請求,test實作測驗功能

面試官:剛才你提到了ioc,那你能說說ioc是什么嗎?(談談對ioc的理解)
我:ioc(Inverse of Contol)名叫控制反轉,是一種設計思想,就是原本將在程式中手動創建物件的控制權交給Spring框架來管理,ioc并不是spring中特有的,ioc容器是spring用來實作ioc的載體,他的本質實際上就是一個ConcurrentHashMap,里面存放的key就是bean的名字,value就是一個個物件,
將物件之間的相互依賴關系交給ioc容器來管理,并由ioc容器完成物件的注入,這樣可以很大程度上簡化應用的開發,把應用從復雜的依賴關系中解放出來,ioc容器就像一個工廠一樣,當我們需要創建一個物件的時候,只需要配置好組態檔即可,完全不需要考慮物件是怎么被創建出來的,

面試官:你是怎么去理解aop的
我:aop,俗稱面向切面編程,能夠將哪些與業務無關,但是又能為業務模塊共同呼叫的邏輯或責任(事務、權限、日志)封裝起來,便于減少系統的重復代碼,降低模塊之間的耦合度,并有利于拓展性和可維護性,
Spring AOP本質是基于動態代理,如果要代理的物件,實作了某個介面,那么Spring AOP會使用JDK Proxy去創建物件,而對于沒有實作介面的物件,就無法使用JDK Proxy去創建,這個時候就可以使用Cglib來生成一個被代理物件的子類來作為代理,

面試官:Spring中使用了哪些設計模式?
我:工廠設計模式、代理、單例模式、包裝器、觀察者模式、配接器模式,

面試官:Spring的事務管理方式有幾種?
我:編程式事務和申明式事務,不過一般來說不推薦使用編程式事務,那樣對代碼的拓展性不強,所以一般都是使用申明式事務,而申明式事務又分兩種,分別是基于XML配置和基于注解,

面試官:Spring中的事務隔離級別有哪些你知道嗎?
我:一共有五種隔離級別,default、uncommitted、committed、repeated_read、serialzable,其中default主要使用的就是資料源默認的隔離級別,uncommitted允許讀取尚未提交的資料變更,可能導致臟讀、幻讀和不可重復讀,committed允許讀取并發事務已經提交的資料,但是可能會有幻讀或者不可重復讀的發生,repeated_read可能會導致幻讀,serialzable這個隔離級別可以阻止以上的全部問題,但是會嚴重影響程式性能,一般來說也不會用這個,像MySQL默認的就是repeatable,

ps:其實小伙伴關注的比較多的spring回圈依賴在我認知中很少遇到問這個的,如果有需要,后期我會補上,

面試官:嗯,聊了這么久了,我們換個話題聊一聊,
我:哦,愿聞其詳,
面試官:你平時喜歡釣魚嗎?
我:喜歡啊,就XX那個湖,我每次有時間就往那兒去,
面試官:哦,你也去那里嗎?那下次我們有機會一起,加個微信
我:好啊,沒問題(內心獨白:嗯,感覺這次穩了)
面試官:嗯,那我們繼續換個問題問問
我:沒問題,隨便問,

MyBatis框架篇

面試官:你知道${}#{}在sql陳述句中使用的區別是什么嗎?
我:首先,#{}類似于jdbc中的占位符,這個主要的作用就是會將我們的string型別的資料自動在兩邊拼接上一個引號,但是如果是${}的話,則是直接拼接字串,我們為啥用#{}用的多,因為${}會造成sql注入的問題,打個比方,如果我們執行一條陳述句是select * from t_user where name=xxx,那如果我們使用的是${},在傳入引數的手傳個zhangsan;delete from t_user,那么這條陳述句最后就會變成select * from t_user where name=xxx;delete from t_user,我相信,你的技術經理看到你這么寫,你當天下午就要被掃地出門,但是這個也并不是沒有使用場景,當我們使用order by條件進行排序的時候可以使用這個,因為order by是一定在where之后生效,所以就算注入一條非法陳述句也不會產生影響,會被例外捕獲,

面試官:通常在xml檔案中,都會寫一個Dao層介面與之對應,請問這個Dao介面的作業原理是什么?如果方法引數不同,能多載嗎?
我:一般來說,介面的權全限定名就是namespace的值,介面的方法名就是MappedStatement的id值,Mapper介面是沒有實作類的,當呼叫介面方法時,介面全限定名+方法名拼接字串作為key,可以定位唯一一個MappedStatement,Dao介面的作業原理就是JDK動態代理,MyBatis運行時會使用JDK動態代理為Dao介面生成代理proxy物件,代理物件會攔截介面方法,轉而執行MappedStatement所代表的sql,然后回傳結果,方法是不能多載的,因為是按照全限定名+方法名去執行,

面試官:MyBatis的分頁插件你用過吧,他的基本原理是什么?
我:MyBatis使用RowBounds物件進行分頁,它是針對ResultSet結果集執行的記憶體分頁,而非物理分頁,可以在sql內直接書寫帶有物理分頁的引數來完成分頁功能,也可以使用分頁插件來完成屋里分頁,
分頁插件的基本原理是使用MyBatis提供的插件介面,實作自定義插件,在插件的攔截方法內攔截待執行的sql,然后重寫sql,添加分頁引數,

面試官:MyBatis中的插件介面有哪些?你怎么去自己撰寫一個插件?
我:MyBatis中提供了四種介面插件,分別是ParameterHandler,ResultSetHandler,StatementHandler,Executor,MyBatis使用JDK動態代理,為需要攔截的介面生成代理物件以實作介面方法攔截功能,每當執行這4種介面物件的方法時,就會進入攔截方法,具體就是invocationHandler中的invoke()方法,假如我們需要自己寫個插件,那么只需要去實作MyBatis的Interceptor介面并復寫intercept()方法,然后給插件撰寫注解,指定要攔截哪些方法就行,

面試官:MyBatis中將sql執行結果進行映射成物件的方式你是怎么做的?
我:一般就兩種形式,一種是使用sql的別名,另一種是使用resultMap

面試官:MyBatis中有哪些Executor執行器?他們的作用是什么?
我:SimpleExecutor,ResuseExecutor,BatchExecutor,SimpleExecutor每執行一次update或者select,就開啟一個Statement物件,用完就立刻關閉,ResuseExecutor:可以重復使用Statement,BatchExecutor:執行update(jdbc批處理不支持select),將所有sql都添加到批處理等待統一執行,

面試官:怎么去開啟mybatis的懶加載
我:通過設定組態檔中的lazyLoadingEnabled=true|false

面試官:聊了這么久了,喝杯水吧,小馨,麻煩倒杯水,
稚子:她上廁所去了,我給你們倒吧,
我:嗯?又是一個妹子,
說完,門開,走進來一個上身胸口一個可愛萌的短袖,下半身一個牛仔短褲,長發飄飄的仙女就走了進來,端著兩杯水,當然,處于禮貌的我還是站起身來幫她接了下來,嗯,妹子的身高剛好到我肩膀,
在這里插入圖片描述
面試官:嗯,我們接著聊聊MySQL如何?
我:嗯,當然可以,
面試官:今天也聊了這么多了,MySQL我就簡單的問問,
我:(信你個鬼)嗯,好的,

MySQL篇

面試官:講一下MySQL中的索引吧,
我:MySQL中使用的資料結構來說主要有BTree索引和哈希索引,對于哈希索引來說,底層的資料機構就是哈希表,因此在絕大多數需求為單條記錄查詢的時候,可以考慮采用哈希索引,性能最快,其余場景,還是建議選擇BTree索引,
MySQL的BTree索引使用的是B樹中的B+Tree,但是對于主要的兩種存盤引擎的實作方式還是不用的,
MyISAM:b+Tree葉子節點的data域存放的是資料記錄的地址,在索引檢索的時候,首先按照B+Tree的搜索演算法搜索索引,如果指定的key存在,則取出其data域中的值,然后以data域的值為地址讀取相應的資料記錄,這也被稱為ie“非聚簇索引”
INNODB:其資料檔案本身就是索引檔案,相比MyISAM來說,索引檔案和資料檔案是分離的,樹的葉子節點保存了完成的資料記錄,這個索引的key是資料表的主鍵,因此InnoDB表資料檔案本身就是主索引,這被成為“聚簇索引”,而其余的索引都作為輔助索引,輔助索引的data存盤相應記錄主鍵的值,而不是地址,這也是和MyISAM不同的地方,在根據主索引搜索時,直接找到key所在的節點即可取出資料;在根據輔助索引查找時,則需要先取出主鍵的值,在走一遍主索引,所以一般在設計表的時候,不建議使用很長的欄位去作為主鍵,

面試官:什么是事務?
我:事務是相對邏輯上的操作,簡而言之就是要么都執行,要么就都不執行,舉個例子:你和張三轉賬,你給他轉100,那么你的賬戶相應的要減少100塊,他的賬戶要多100,不能出現說你的少了,但是他的沒有增加,

面試官:事務的四個特性說一下,
我:ACID,分別是原子性,持久性,一致性和隔離性,

面試官:怎么看sql的執行有沒有走索引?
我:通過explain去查看陳述句,比如:explain select * from t_user

面試官:索引失效的十個原則你知道哪些?
我:
1、不要使用select *,這樣會導致全表掃描
2、如果索引了多列,比如(id,name,age),那么在查詢時,必須根據最左原則,中間的索引列不能斷開,也就是必須根據表的從左到右的順序
3、不能在索引列上做任何操作,這樣會導致索引失效
4、存盤引擎不能使用索引中范圍條件右邊的列,比如有個索引是x,y,那么只使用where y=xxx索引不會生效
5、盡量匹配精確的欄位值,不能全掃描
6、盡量不要使用!=或者<>
7、盡量不要使用is null,is not null
8、like查詢,%開頭會失效,%結尾不會
9、避免隱式轉換,字串要加單引號
10、少用or查詢

面試官:嗯,資料庫知識儲備還不錯,我們接著問問Redis
我:(mmp,這么多了還問)嗯,可以啊,

Redis篇

面試官:你們為什么要使用Redis來做快取,有沒有考慮過其他的快取,
我:除了redis,還有一種快取叫做Memcached,不過對于我自己的技術堆疊來說,我個人更偏向于使用redis,在系統沒有引入redis之前,系統的延遲很高,并且MySQL的壓力很大,之后引入了Redis之后,能很好的降低系統資料庫的壓力,我們將大部分不易變動的資料全部放在了Redis,這樣,由于Redis的查詢高效性,一方面能提高用戶的體驗度,另一方面也能降低資料庫的壓力,

面試官:你知道Redis的單執行緒模型嗎?能簡單的說說嗎?
我:Redis中使用了Reactor模式來開發了一套Redis 基于 Reactor 模式來設計開發了??的?套?效的事件處理模型 (Netty 的執行緒模型也基于 Reactor 模式,Reactor 模式不愧是?性能 IO 的基?),這套事件處理模型對應的是 Redis中的?件事件處理器(file event handler),由于?件事件處理器(file event handler)是單執行緒?式運?的,所以我們?般都說 Redis 是單執行緒模型,
(ps:具體的情況,如果有興趣的可以看看我這篇文章:https://blog.csdn.net/weixin_43581288/article/details/118939539)

面試官:Redis使用的是單執行緒還是多執行緒?
我:在Redis6.0之前一直都是使用的單執行緒,但是在6.0以后引入了多執行緒,
面試官:那為什么平時說的時候還是習慣說Redis是單執行緒,
我:那是因為Redis引入的多執行緒并不是用來處理事件IO,而是為了提高網路IO的讀寫性能從而引入的多執行緒,它的檔案事件處理器其實還是個單執行緒的,
面試官:你知道怎么開啟Redis的多執行緒嗎?
我:在redis.conf檔案中找到 io-threads-do-reads yes # 原本是no,需要改成yes,然后在設定它的執行緒數 io-threads 4 #官?建議4核的機器建議設定為2或3個執行緒,8核的建議設定為6個執行緒

面試官:如果我現在MySQL中有200w資料,需要在Redis中存20w資料,怎么保證這20w資料都是熱點資料?
我:這個得去考慮Redis中得淘汰策略;Redis中一共有6中資料淘汰策略,分別是:

  1. volatile-lru(least recently used):從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使?的資料淘汰
  2. volatile-ttl:從已設定過期時間的資料集(server.db[i].expires)中挑選將要過期的資料淘汰
  3. volatile-random:從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰
  4. allkeys-lru(least recently used):當記憶體不?以容納新寫?資料時,在鍵空間中,移除最近最少使?的 key(這個是最常?的)
  5. allkeys-random:從資料集(server.db[i].dict)中任意選擇資料淘汰
  6. no-eviction:禁?驅逐資料,也就是說當記憶體不?以容納新寫?資料時,新寫?操作會報錯,這個應該沒?使?吧!
    在4.0版本后增加以下兩種:
  7. volatile-lfu(least frequently used):從已設定過期時間的資料集(server.db[i].expires)中挑選最不經常使?的資料淘汰
  8. allkeys-lfu(least frequently used):當記憶體不?以容納新寫?資料時,在鍵空間中,移除最不經常使?的 key

面試官:你知道Redis中的持久化機制嗎?
我:在redis中有兩種持久化機制,分別是rdb和aof機制,redis默認是開啟的rdb機制,如果要開啟aof機制,則需要去改動redis.conf檔案中的appendonly yes

面試官:怎么開啟Redis的事務?
我:使用multi命令開啟一組事務,使用multi之后,書寫的命令并不會馬上執行,而是將這一組命令放在佇列中,然后使用exec命令去執行 如果不想執行這些命令 則可以使用discard命令取消這一組事務 我們還可以使用watch去監視key的變化,這種場景經常用來實作分布式事務控制,
面試官:你剛才提到了分布式事務,你平時處理分布式事務怎么做的?
我:我個人比較喜歡使用Redisson這個框架,在之前的時候我習慣是用一個key來當作標志位,然后使用watch命令去監視這個key是否有發生變化,如果是這個key發生了變化,那么事務則取消,

面試官:你知道快取穿透、擊穿以及雪崩嗎?
我:快取穿透是指大量不存在的key請求redis,然后直接打到了資料庫,快取擊穿是指某一個key在過期的時候,同時有這個key的大量請求,然后這個時候又會去設定資料到快取中,但是大量的并發還是可能會把db擊垮,快取雪崩是指大量的key在同一個時間段大面積過期,造成大量資料直接打到資料庫,造成雪崩,
面試官:那你能說說具體的解決方式嗎?
我:快取穿透我們可以考慮采用布隆過濾器去解決,快取擊穿我們可以考慮采用Redis得setnx去設定一個互斥鎖,當操作成功后回傳的時候在進行load db并重設快取,或者我們可以考慮采用邏輯過期的策略,快取雪崩我們可以將這些key的過期時間設定一個隨機值,不讓他們在同一個時間段過期,

面試官:嗯,今早上我們暫時先聊到這里,下午我們繼續?
我:啊,下午我們聊什么?
面試官:嗯,今早上問你的這些都是比較基礎的問題,下午我們聊一聊你專案中的分布式和微服務怎么樣?
我:ok,沒問題,那我們先去吃個飯?
面試官:走吧,一起,樓下有家餐館還不錯,(喊了喊小馨)
小馨:來了,
我:(os:這個妹子越看越好看)嗯,這位是叫小馨吧,
小馨:嗯,你好,我叫小馨,
我:你好,我叫彭于晏,那我們就一起吃個飯?
面試官、小馨:走吧,
(ps:給大家看看小馨吃飯的圖片)
在這里插入圖片描述

分布式、微服務篇

面試官:談談你對分布式的理解
我:所謂的分布式就是將不同的業務分散到不同的地方,在我的理解中,第一種分布式就是將不同的服務,比如MySQL服務和web服務部署到不同的機器上供用戶訪問,第二種分布式就像我們現在的微服務架構,一個大型的專案拆分成為各個小服務,對于這個單個服務,可以交給不同的技術團隊去完成,最后完成部署上線,這也是分布式的一種,

面試官:我看你最近的專案用了SpringCloud做微服務,那你講講他的幾個組件,
我:用通俗的話講,既然是微服務,那么肯定是眾多的服務了,這些服務得有一個統一的管理中心去做為管理,就比如說你作為一個公民,你的身份證肯定是要受統一的管理,這個管理呢就叫做注冊中心,一般就是使用eureka或者nacos,consul我們很少用,
當我們專案業務中有一個服務壓力過于龐大,那么這個時候我們會考慮采用集群化部署,但是集群化部署也有一個問題,那就是我們的A服務怎么去呼叫這個B服務的集群,所以這里就會涉及到另一個組件,那就是feign,feign組件的最大作用就是做服務的呼叫,但是光服務的呼叫肯定是不行,我們的服務集群肯定是得考慮一種負載均衡的方式去讓其他服務呼叫,所以這個組件就叫做ribbon,在feign的依賴中默認是集成了ribbon組件,
當我們的單個服務接受到龐大的用戶量之后,難免在服務之間呼叫的時候會引發服務雪崩的現象,從而給用戶造成不良好的體驗,所以這個時候我們會考慮引入一個叫做服務的熔斷,限流和降低的組件,這個組件叫做Hystrix或者sentinel,
在眾多的服務中,可能有一些配置是分散在各個地方,以后我要修改,比如說我要在這個服務中加一個資料源配置,那么這樣對于我們來說我很不友好的,所以這個時候我們會去使用一個全域配置中心,比如說config,或者nacos,
在我們的這些微服務中,最終是要交給前端進行呼叫的,如果我們每一個服務就有一個地址,那這樣對于前端來說肯定是不友好的,所以我們引入了另一個組件叫做網關,也叫gateway,這個網關最大的作用就是可以提供一個路由地址,根據這些路由地址來匹配對應的服務,然后供前端進行呼叫,網關的另一個作用就是可以對前端訪問的用戶做統一的鑒權操作,就好比小區門衛,你要進小區你肯定得先經過門衛,然后做身份資訊認證,
目前的spring cloud技術選型:注冊中心:nacos;遠程呼叫:openfeign;負載均衡:ribbon;服務熔斷:sentinel;配置中心:nacos;網關:gateway

面試官:你剛才說到了微服務的鑒權操作,你一般會怎么去做這個鑒權的設計,
我:權限的設計,現在用的比較多的就是叫rabc,就是基于角色的權限設計,就是我們會先去考慮一個用戶有哪些角色,然后根據這個角色去分配對應的權限,這樣來說,我們并不會一開始給用戶就賦予權限,而是先給用戶角色,然后再根據角色去設計用戶的權限,所以我在設計的時候一般就會去做三個物體,分別是用戶,角色和權限,這三者之間的關系呢,用戶和角色是一個多對多的關系,角色和權限的關系也是多對多的關系,

面試官:那你們現在用的比較多的權限框架是什么?
我:之前做的一個專案是前后端分離的單體架構,用的是shiro,微服務專案中用的比較多的是Spring Security,
面試官:那假如說你用了這個權限框架,提供給用戶的url,是任何人知道這個url的用戶都可以訪問嗎?
我:既然我們已經做了一個權限的設計,那我們肯定得先去判斷當前訪問這個url的用戶應該有哪些權限,如果說權限不夠的話,我們是不允許訪問的,
面試官:那我看你專案中使用的是jwt做的這個資源控制,你能具體說一下嗎?
我:jwt的話,在我們以前做的時候,都是用戶登錄成功之后,就回傳一個訊息,然后服務端保存好用戶的session,然后下次訪問資源的時候直接看這個session中是否有用戶,用戶是否有對應的權限,但是session存在一個問題,那就是在分布式中,session他只是保存在一個機器中,在另一個機器中就會出現session不存在,然后又提示用戶重新登陸的情況,用了jwt之后,我們在登錄的介面中,給用戶多回傳了一個東西叫做token,用戶下次去請求資源只需要攜帶上這個token,在token中存放好該用戶的權限資訊以及角色資訊,然后我們在攔截器中去判斷這個token是否合法以及將用戶的權限和角色拿出來去判斷,如果合法,那肯定就會允許用戶訪問這個資源,

面試官:分布式事務你是怎么做的?
我:我們考慮的是使用seata中的二階段提交,
面試官:二階段提交的話,效率比較低啊
我:確實比較低,但是我們在盡量對這個資料庫進行設計的時候就不要讓他產生分布式事務問題,如果真要處理分布式的事務問題,我們還可以考慮使用tcc,不過這種的話,會對我們的代碼造成侵害,所以我們一般都使用的是二階段提交這種方案,
面試官:那除了這個方案的話,有沒有其他的一些解決方案呢?
我:嗯,目前來說的話 第一種就是兩階段提交,第二種的話就是使用tcc,第三種就是最大努力通知,還有一個叫做最終訊息一致性,最大努力通知就是,在A服務中,我把訊息發給了B服務,能不能處理是你的事情,反正我是把訊息發給你了,還有一種就是,對方服務比較重要,然后對方服務來通知我,舉個例子就是在支付系統中,我要去調支付系統,那么我肯定是想讓支付系統以最大努力通知來告訴我說支付完成了,然后我這邊再去處理一些其他的業務,比如通知庫存服務去減庫存之類的,如果是說因為一些網路抖動的原因實在是造成了一些訊息通知不到位,那么也希望的是B服務提供一個訊息查詢的通道,然后供A服務去查詢這個最終的效果,用來保證最終一致性,還有一種比較好的理解就是,假如說因為一些網路原因,用戶在下單之后我們可以提供給用戶一種選擇,就說先下單,然后我們明天發貨,這樣,不管怎樣,最終用戶也會收到訊息,
其實除了這個,還有一個叫做三階段提交,三階段提交就是在二階段提交的程序中加了一個超時機制,如果說服務的提供方和協調方因為一些原因,造成了服務之間遲遲不呼叫,那么在二階段提交中就會存在一些問題,比如說服務阻塞,三階段提交引入了超時機制之后,如果說超出了一定的時間,那么就先不處理這個服務,
面試官:CAP理論和BASE理論你知道嗎?
我:CAP理論的話,其實這是幾個合在一起的縮寫,大致就是一致性,可用性和磁區容忍性,但是由于在分布式系統中,這三者是不可兼得的,所以一般的選擇方案就是保證AP,達到最終一致性,放棄掉暫時的一致性,
BASE是Basically Available(基本可用)、Soft state(軟狀態)和Eventually consistent(最終一致性)三個短語的縮寫,BASE理論是對CAP中一致性和可用性權衡的結果,其來源于對大規模互聯網系統分布式實踐的總結, 是基于CAP定理逐步演化而來的,BASE理論的核心思想是:即使無法做到強一致性,但每個應用都可以根據自身業務特點,采用適當的方式來使系統達到最終一致性,

面試官:你除了用Redis做快取,還有沒有考慮過其他的做快取,比如Map
我:Redis做快取,最大的一個好處其實就是他是一個中央快取,什么意思呢?其實就是比如我們的各個服務之間的資料需要共通,如果是用Map做本地快取的話,那其實還是走的資料庫,沒有去使用快取,

面試官:有對資料庫做優化嗎?
我:資料庫優化的話,你有一個前提,你的資料表沒有設計錯,你的sql也沒有寫錯,那么這個時候如果還是有問題的話,會去考慮采用分庫分表來做這個優化,
面試官:你有用一些命令去查看sql執行嗎?
我:嗯,有一個命令叫做explain,可以用這個命令去查看一些sql的執行程序,看看有沒有用索引啊,快取之類的,

面試官:你剛才說到了分庫分表,你們分庫分表一般會去怎么分?
我:嗯,一般來說的話,就兩種嘛,一種是垂直劃分,一種是水平劃分,垂直劃分的話就類似于說我們的欄位有很多,那么在查詢的是時候由于欄位過多,導致查詢效率緩慢,我們就去考慮將這些欄位隔開,然后進行分表,但是這樣做有一個壞處就是主鍵id的設定,我們得去考慮一個問題就是id的唯一性,水平劃分的話,那就是比如說我們的表中的資料太大了,超過了200w條,那我們就可以使用水平劃分,比如說分成四張表,每張表50w條資料之類的,
面試官:那你怎么保證分表之后,我們查詢的資料一定是在這個表里面呢?
我:這個的話,肯定是在分表的時候就會設定好嘛,會利用一定的分表策略來實作,比如說我可以按照id的區間來查,1-10w分一張,然后10.1到20作為一張表,那我們在查詢的時候就可以指定區間來查詢了,第二個就是使用時間來劃分嘛,比如說一個月資料量很大的情況下,我可以按照每個月分一張表,第三個就是使用hash演算法,根據hash演算法計算hash值來實作分表,
面試官:那如果我有一個查詢,但是這個查詢的結果跨表了,怎么去處理
我:首先,我們會去根據這個范圍,計算出這個查詢需要去哪些表里面查詢,然后找到這些表以后,MySQL中有個查詢叫做union查詢,這個查詢可以將不同表的結果查詢放在一起然后作為一個回傳值,
面試官:那除了這種查詢,還有什么其他的解決方案嗎?
我:嗯,假如說我們計算出來的資料在兩張表中,然后我們可以考慮將這兩張表的資料放在es中,然后利用搜索引擎去做,(ps:這塊我也不太熟悉,,,需要的小伙伴得去自行百度了,我可能解釋的不是很清楚)

面試官:你知道MySQL中的union和union all的區別嗎/
我:union會兩個結果取并集,會將結果進行去重,按照默認規則進行排序;union all會包括重復行,

面試官:嗯,最后一個問題,你知道InnoDB和MyISAM的區別嗎?
我:這個啊,這個簡單,

  1. InnoDB支持事務,MyISAM不支持,對于InnoDB每一條SQL語言都默認封裝成事務,自動提交,這樣會影響速度,所以最好把多條SQL語言放在begin和commit之間,組成一個事務;
  2. InnoDB支持外鍵,而MyISAM不支持,對一個包含外鍵的InnoDB表轉為MYISAM會失敗;
  3. InnoDB是聚集索引,使用B+Tree作為索引結構,資料檔案是和(主鍵)索引綁在一起的(表資料檔案本身就是按B+Tree組織的一個索引結構),必須要有主鍵,通過主鍵索引效率很高,但是輔助索引需要兩次查詢,先查詢到主鍵,然后再通過主鍵查詢到資料,因此,主鍵不應該過大,因為主鍵太大,其他索引也都會很大,
  4. InnoDB不保存表的具體行數,執行select count(*) from table時需要全表掃描,而MyISAM用一個變數保存了整個表的行數,執行上述陳述句時只需要讀出該變數即可,速度很快(注意不能加有任何WHERE條件);
  5. Innodb不支持全文索引,而MyISAM支持全文索引,在涉及全文索引領域的查詢效率上MyISAM速度更快高
    (ps):5.7以后的InnoDB支持全文索引了
  6. MyISAM表格可以被壓縮后進行查詢操作
  7. InnoDB支持表、行(默認)級鎖,而MyISAM支持表級鎖
  8. InnoDB表必須有唯一索引(如主鍵)(用戶沒有指定的話會自己找/生產一個隱藏列Row_id來充當默認主鍵),而Myisam可以沒有
  9. Innodb存盤檔案有frm、ibd,而Myisam是frm、MYD、MYI
    Innodb:frm是表定義檔案,ibd是資料檔案
    Myisam:frm是表定義檔案,myd是資料檔案,myi是索引檔案

面試官:那怎么去選擇呢?有什么好的方案嗎?
我:如果是要支持事務的話,就選擇InnoDB,否則就考慮MyISAM,第二個的話就是如果你的表大多數都是讀查詢,可以考慮采用MyISAM,如果有讀也有寫,那就采用InnoDB,第三個的話就是MyISAM本身不支持資料容災,也就是說你資料庫崩了的話,那就涼涼了,所以這一點得考慮好,其實個人建議還是采用InnoDB比較好,

面試官扶了扶眼鏡,說:嗯,小伙子很不錯嘛,面一天了都能這么從容,
我:嘿嘿,夸獎夸獎,主要是平時積累比較多,
面試官:你平時讀一些書嗎?
我:讀啊,比如說周志明老師的《深入理解Java虛擬機》,還有《Spring Cloud Alibaba微服務原理與實戰》,《Head First設計模式》,《Java并發編程的藝術》,《高性能MySQL》和《計算機網路7》
ps:收到讀者私信,這個不建議直接放百度網盤鏈接,不好意思了,如果有需要,可以私信我領取,謝謝,

小結

兩天時間,不負厚望,終于還是更新完了,其中的一些集合類的面試題我這兒就沒去整理,如果有需要的,可以留言或者私信我整理一套集合類的面試題免費分享給大家,
還是到了大家心心念念的小姐姐環節🍄
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/290073.html

標籤:java

上一篇:【開源軼事00】Redis 是親生的懶漢 Java 庶出終歸是庶出

下一篇:java-零錢通(專案)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more