2020年三、四月java面試題彙總

一名小碼農發表於2020-03-27

文章目錄

2020年3月份一些中小型網際網路或計算機軟體公司的社招題目,我會盡最大努力確保答案的正確性,但是我也才疏學淺,如果有錯誤或者遺漏的地方,歡迎指出。持續更新中……。

一、JavaSE基礎

談談對OOP的理解

  1. 首先物件導向是一種程式設計思想
  2. 萬物皆物件。我的電腦是一個物件,我的手機是一個物件等等,OOP可以理解為使用程式碼來模擬現實生活
  3. 三大特性:封裝、繼承和多型
    1. 封裝就是隱藏類的內部資訊,不允許外部程式直接訪問,而是通過getter(獲取)和setter(設定)方法完成,提高資料的安全性
    2. 繼承是指:父類的基本特徵和行為,子類也會有,子類也可以改變這些特徵和行為。
    3. 多型就是多個物件呼叫同一個方法,可能會得到的是不同的結果。

Java基本型別所佔位元組和範圍

型別位元組數位數取值範圍
byte18-27 ~ 27-1
short216-215 ~ 215-1
char216
int432-231 ~ 231-1
long864-263 ~ 263-1
float4323.402823e+38 ~ 1.401298e-45
double8641.797693e+308 ~ 4.9000000e-324
boolean1(前7位是0)10或者1

int(long) 和 float(double) 哪個表示的範圍大?為什麼?

float表示的 範圍更大floatint 都是4個位元組,而 float 還要表示小數,為什麼 float 表示的數字範圍大?

  • int 底層使用的是補碼的方式表示一個數:1 位符號位 + 31 位二進位制數
  • float 底層使用的是IEEE 754 浮點單精度數字格式,簡單來說就是用指數的形式去表示一個數:1 位符號位 + 8 位指數 + 23位尾數

Int和Integer的區別,在使用時有什麼區別?

  1. Integerint的包裝類,int則是java的一種基本資料型別
  2. Integer變數必須例項化後才能使用,而int變數不需要
  3. Integer實際是物件的引用,當new一個Integer時,實際上是生成一個指標指向此物件;而int則是直接儲存資料值
  4. Integer的預設值是nullint的預設值是0

重寫,過載

  • 方法過載是指同一個類中的多個方法具有相同的名字,但這些方法具有不同的引數列表,即引數的數量或引數型別不能完全相同
  • 方法重寫是存在子父類之間的,子類定義的方法與父類中的方法具有相同的方法名字,相同的參數列和相同的返回型別
    • 子類中不能重寫父類中的final方法
    • 子類如果不是抽象類,則必須重寫父類中的abstract方法

==與equals()

  • ==號在比較基本資料型別時比較的是值,而在比較兩個物件時比較的是兩個物件的地址值
  • equals()方法存在於Object類中,其底層依賴的是==號,如果類沒有重寫Object中的equals()方法的類中,則呼叫equals()方法其實和使用==號的效果一樣

介面與抽象類

相同點:

  1. 都不能被例項化
  2. 介面的實現類或抽象類的子類都只有實現了抽象方法後才能例項化。

不同點:

  1. 意義不同:介面是功能的封裝,解決物件能幹什麼;抽象類是事物的抽象,解決物件到底是什麼。
  2. 內容不同:介面中的內容只能有:抽象方法(public abstract修飾),常量(public static final修飾),在JDK8中,介面還可以定義static靜態方法和default方法;而抽象類可以有普通類的所有內容和抽象方法。
  3. 介面支援多繼承,類只允許單繼承

String底層使用什麼實現的?為什麼不可變?

String的底層使用的是char陣列。這個char陣列和String這個類都是final修飾的,所以不可變。

String,StringBuilder,StringBuffer的區別?

String是隻讀字串,它並不是基本資料型別,而是一個物件。從底層原始碼來看是一個final型別的字元陣列,所引用的字串不能被改變,一經定義,無法再增刪改。每次對String的操作都會生成新的String物件。

String的每次+操作 : 隱式在堆上new了一個跟原字串相同的StringBuilder物件,再呼叫append方法 拼接
+後面的字元,最後再呼叫toString方法,返回String物件

StringBufferStringBuilder他們兩都繼承了AbstractStringBuilder抽象類,從AbstractStringBuilder抽象類中我們可以看到他們的底層都是可變的字元陣列,所以在進行頻繁的字串操作時,建議使用StringBufferStringBuilder來進行操作。

另外StringBuffer 對方法加了同步鎖或者對呼叫的方法加了同步鎖,所以是執行緒安全的。StringBuilder 並沒有對方法進行加同步鎖,所以是非執行緒安全的

什麼是值傳遞?什麼是引用傳遞?

  • 值傳遞:是對 基本型別 變數而言的,傳遞的是該變數的一個副本,改變副本不影響原變數.
  • 引用傳遞:一般是對於 物件型別 變數而言的,傳遞的是該物件首地址的一個副本,並不是原物件本身,改變副本會影響原變數(本質都是同一個物件)。

格式化日期用的什麼?

使用的是 SimpleDateFormat

  • 把日期轉換為指定格式的字串
    //使用SimpleDateFormat類,在構造方法中指定日期的格式:y年,M月,d日,H小時,m分鐘,s秒
    SimpleDateFormat sdf = new SimpleDateFormat("yyy y-MM-dd HH:mm:ss");
    //呼叫SimpleDateFormat例項方法format(Date)可以把日期轉換為字串
    String text = sdf.format(date);
    
  • 把字串轉換為日期
    text = "2068年5月12日 8:28:58";
    //先建立SimpleDateFormat物件, 指定的格式串要與字串匹配
    SimpleDateFormat anotherSdf = new SimpleDateFormat("yyyy年M月dd日 H:mm:ss");
    //呼叫SimpleDateFormat的例項方法parse(String)可以把字串轉換為日期物件, 如果格式串與字串不匹配就會產生異常
    date2 = anotherSdf.parse(text);
    

IO流中都有哪些類?

節點流(介質流)

父類InputStream(輸入流、位元組流)OutputStream(輸出流、位元組流)Reader(輸入流、字元流)Writer(輸出流、字元流)
檔案FileInputStreamFileOutputStreamFileReaderFileWriter
陣列ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
字串StringReaderStringWriter
管道PipedInputStreamPipedOutputStreamPipedReaderPipedWriter

處理流(裝飾流)

父類InputStream(輸入流、位元組流)OutputStream(輸出流、位元組流)Reader(輸入流、字元流)Writer(輸出流、字元流)
轉換流InputStreamReaderOutputStreamWriter
緩衝流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
資料流DataInputStreamDataOutputStream
物件流ObjectInputStreamObjectOutputStream
列印流PrintStreamPrintWriter

如何建立檔案和目錄?

File 物件可以表示檔案或目錄:

//第一個引數為父路徑字串,第二個引數為子路徑名字
File f = new File("D:", "test");
f.mkdir();           //建立資料夾, 如果已存在重名的檔案(夾),建立失敗
f.createNewFile();   //建立檔案

f.delete();          //刪除檔案

物件導向程式設計的六大原則

  1. 開閉原則

    開閉原則的意思是:對擴充套件開放,對修改關閉。在程式需要進行擴充的時候,不能去修改原有的程式碼,實現一個熱插拔的效果。簡言之,是為了使程式的擴充套件性好,易於維護和升級。想要達到這樣的效果,我們需要使用介面和抽象類。

  2. 里氏替換原則

    所有引用基類的地方必須能透明地使用其子類的物件,也就是說子類可以擴充套件父類的功能,但不能改變父類原有的功能

    里氏替換原則是對開閉原則的補充。實現開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關係就是抽象化的具體實現,所以里氏替換原則是對實現抽象化的具體步驟的規範。

  3. 依賴倒轉原則

    高層模組不應該依賴低層模組,二者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象。簡單的說就是儘量面向介面程式設計.

  4. 介面隔離原則

    客戶端不應該依賴它不需要的介面;一個類對另一個類的依賴應該建立在最小的介面上。介面最小化,過於臃腫的介面依據功能,可以將其拆分為多個介面。

  5. 迪米特法則,又稱最少知道原則

    一個實體應當儘量少地與其他實體之間發生相互作用,使得系統功能模組相對獨立。簡單的理解就是高內聚,低耦合,一個類儘量減少對其他物件的依賴。

  6. 單一職責原則

    單一職責原則通俗來說就是一個類只負責一項職責。如果一個類有多個職責,這些職責就耦合在了一起。當一個職責發生變化時,可能會影響其它的職責。另外,多個職責耦合在一起會影響複用性。

瞭解哪些設計模式?具體說說其中的某個?

  1. 單例模式:懶漢式、餓漢式、雙重校驗鎖、靜態載入,內部類載入、列舉類載入。保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。
  2. 代理模式:動態代理和靜態代理,動態代理有jdk動態代理和cglib動態代理。
  3. 介面卡模式:將一個類的介面轉換成客戶希望的另外一個介面。介面卡模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。
  4. 裝飾者模式:動態給類加功能。
  5. 觀察者模式:有時被稱作釋出/訂閱模式,觀察者模式定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件。這個主題物件在狀態發生變化時,會通知所有觀察者物件,使它們能夠自動更新自己。
  6. 策略模式:定義一系列的演算法,把它們一個個封裝起來, 並且使它們可相互替換。
    外觀模式:為子系統中的一組介面提供一個一致的介面,外觀模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。
  7. 命令模式:將一個請求封裝成一個物件,從而使您可以用不同的請求對客戶進行引數化。
  8. 建立者模式:將一個複雜的構建與其表示相分離,使得同樣的構建過程可以建立不同的表示。
  9. 抽象工廠模式:提供一個建立一系列相關或相互依賴物件的介面,而無需指定它們具體的類。

二、集合

List,Set,Map區別(擴容過程)

(1)List集合

儲存特點:有序,可重複。儲存順序與新增順序一致,可以儲存重複的資料

  • ArrayList
    底層資料結構是陣列,訪問快,新增,刪除慢
    初始化容量:10
    擴容:1.5倍
  • Vector
    底層資料結構是陣列,它是執行緒安全的,ArrayList不是執行緒安全的
    初始化容量:10
    擴容:2倍
  • LinkedList
    底層是雙向連結串列,訪問慢,新增和刪除效率高

(2)Set集合

儲存特點:無序,不可重複。儲存順序與新增順序可能不一樣;不能儲存重複(通過equals()方法來判斷)的資料

  • HashSet
    • 底層是HashMap
    • HashSet新增元素就是把元素作為鍵新增到底層的HashMap
  • TreeSet
    • TreeSet實現了SortedSet介面,可以根據元素自然排序,要求集合中的元素必須是可比較的(ComparatorComparable
    • TreeSet底層是TreeMap
    • TreeSet新增元素就是把元素作為鍵新增到底層的TreeMap

(3)Map

Map是按<鍵,值>對的形式儲存資料,Mapkey不允許重複

  • HashMap

    • 底層是陣列+連結串列,鍵與值都可以為null,執行緒不安全
    • JDK8中,當連結串列長度超過 8 時,連結串列轉換為紅黑樹;插入時,新增的結點插入到連結串列的尾部
    • 初始化容量為16
    • 載入因子:0.75,當鍵值對的數量 > 容量*載入因子時,按2倍大小擴容
    • 在建立時可以指定初始化容量,HashMap會把初始化容量自動調整為2的冪次方
    • 這邊也可以引申到一個問題就是HashMap是先插入資料再進行擴容的,但是如果是剛剛初始化容器的時候是先擴容再插入資料。
  • CurrentHashMap

    • 底層採用陣列+連結串列+紅黑樹,利用CAS+Synchronized來保證執行緒安全

    詳見:
    深入淺出ConcurrentHashMap1.8
    HashMap和ConcurrentHashMap的知識總結

  • HashTable

    • 底層陣列+連結串列,無論key還是value都不能為null,執行緒安全,實現執行緒安全的方式是在修改資料時鎖住整個HashTable,效率低,ConcurrentHashMap做了相關優化
    • 初始容量為11,擴容:2倍+1

HashMap遍歷怎麼遍歷?

兩種方式遍歷:使用EntrySet遍歷;使用KeySet 遍歷

平時什麼情況下使用HashMap?

比如說讀取系統引數,在服務之間傳遞一些鍵值對時使用。

簡單說一下什麼是hash碰撞?怎麼解決的?

首先根據鍵的雜湊碼,經過hash函式計算hash值,然後根據hash值計算陣列下標,當下標相同時,就會出現hash碰撞。

HashMap採用連結串列法,將陣列下標相同的鍵值對用連結串列存起來,解決了hash碰撞。

TreeMap和LinkedHashMap有什麼區別?

  • HashMap 相較來說寫入慢,讀取快,上面介紹過了,就不贅述了;
  • LinkedHashMap它內部有一個連結串列,保持Key插入的順序,寫入快,讀取慢。迭代的時候,也是按照插入順序迭代;
  • TreeMap可以根據Key的自然順序(如整數從小到大)或者自己定義的比較器,實現 Key 的有序排列。

如何刪除一些集合中滿足條件的元素?

不能使用for或者foreach迴圈加list.remove()進行刪除,因為使用remove方法刪除後,集合的長度會變,導致一些元素被漏掉。

  1. 使用外部迭代器刪除集合中的所有奇數:

    List<Integer> list = new ArrayList<>();
    
    Iterator<Integer> iterator = list.iterator();
    
    while (iterator.hasNext()) {
        Integer i = iterator.next();
        if (i % 2 == 1) {
            iterator.remove();
        }
    }
    
  2. 使用內部迭代器(流),刪除集合中的所有奇數:

    List<Integer> list = new ArrayList<>();
    
    list = list.stream().
                filter(x -> x % 2 == 0).
                collect(Collectors.toList());
    

三、多執行緒

1. 建立執行緒有哪些方法(4種)?

  1. 繼承Thread類,重寫run方法(其實Thread類本身也實現了Runnable介面)
  2. 實現Runnable介面,重寫run方法
  3. 實現Callable介面,重寫call方法(有返回值)
  4. 使用執行緒池(有返回值)

2. Runnable和Callable有什麼不同?

  1. 最大的不同點:實現Callable介面的任務執行緒能返回執行結果;而實現Runnable介面的任務執行緒不能返回結果;
  2. Callable介面的call()方法允許丟擲異常;而Runnable介面的run()方法的異常只能在內部消化,不能繼續上拋;

    Callable介面支援返回執行結果,此時需要呼叫FutureTask.get()方法實現,此方法會阻塞主執行緒直到獲取結果;當不呼叫此方法時,主執行緒不會阻塞!

3. start()和run()方法有什麼區別?

  • start()方法來啟動執行緒,而啟動後的執行緒執行的是run()方法中的程式碼。
  • run()方法當作普通方法的方式呼叫時,並不會開啟新的執行緒。

4. 執行緒池有哪些引數

最常用的三個引數:

  • corePoolSize:核心執行緒數
  • queueCapacity:任務佇列容量(阻塞佇列)
  • maxPoolSize:最大執行緒數

三個引數的作用:

  1. 當執行緒數小於核心執行緒數時,建立執行緒。
  2. 當執行緒數大於等於核心執行緒數,且任務佇列未滿時,將任務放入任務佇列。
  3. 當執行緒數大於等於核心執行緒數,且任務佇列已滿
    1. 若執行緒數小於最大執行緒數,建立執行緒
    2. 若執行緒數等於最大執行緒數,丟擲異常,拒絕任務

5. 執行緒池有幾種(5種)?拒絕策略有幾種(4種)?阻塞佇列有幾種(3種)?

五種執行緒池:

ExecutorService threadPool = null;
threadPool = Executors.newCachedThreadPool();//有緩衝的執行緒池,執行緒數 JVM 控制
threadPool = Executors.newFixedThreadPool(3);//固定大小的執行緒池
threadPool = Executors.newScheduledThreadPool(2);//一個能實現定時、週期性任務的執行緒池
threadPool = Executors.newSingleThreadExecutor();//單執行緒的執行緒池,只有一個執行緒在工作
threadPool = new ThreadPoolExecutor();//預設執行緒池,可控制引數比較多   

四種拒絕策略:

RejectedExecutionHandler rejected = null;
rejected = new ThreadPoolExecutor.AbortPolicy();//預設,佇列滿了丟任務丟擲異常
rejected = new ThreadPoolExecutor.DiscardPolicy();//佇列滿了丟任務不異常
rejected = new ThreadPoolExecutor.DiscardOldestPolicy();//將最早進入佇列的任務刪,之後再嘗試加入佇列
rejected = new ThreadPoolExecutor.CallerRunsPolicy();//如果新增到執行緒池失敗,那麼主執行緒會自己去執行該任務

三種阻塞佇列:

BlockingQueue<Runnable> workQueue = null;
workQueue = new ArrayBlockingQueue<>(5);//基於陣列的先進先出佇列,有界
workQueue = new LinkedBlockingQueue<>();//基於連結串列的先進先出佇列,無界
workQueue = new SynchronousQueue<>();//無緩衝的等待佇列,無界

6. 如何檢測死鎖?

利用Java自帶工具JConsole
Java執行緒死鎖檢視分析方法

7. volatile底層是怎麼實現的?

當一個變數定義為volatile後,它將具備兩種特性:1. 可見性,2. 禁止指令重排序。

可見性:編譯器為了加快程式執行速度,對一些變數的寫操作會現在暫存器或CPU快取上進行,最後寫入記憶體。而在這個過程中,變數的新值對其它執行緒是不可見的。當對volatile標記的變數進行修改時,先當前處理器快取行的資料寫回到系統記憶體,然後這個寫回記憶體的操作會使其他CPU裡快取了該記憶體地址的資料無效。

處理器使用嗅探技術保證它的內部快取、系統記憶體和其他處理器的快取的資料在匯流排上保持一致。如果一個正在共享的狀態的地址被嗅探到其他處理器打算寫記憶體地址,那麼正在嗅探的處理器將使它的快取行無效,在下次訪問相同記憶體地址時,強制執行快取行填充。

8. volatile與synchronized有什麼區別?

  1. volatile僅能使用在變數上,synchronized則可以使用在方法、類、同步程式碼塊等等。
  2. volatile只能保證可見性和有序性,不能保證原子性。而synchronized都可以保證。
  3. volatile不會造成執行緒的阻塞,而synchronized可能會造成執行緒的阻塞.

9. java中的執行緒有幾種狀態(6種)

  • NEW:新建狀態,建立了一個執行緒物件
  • RUNNABLE:可以執行狀態,執行緒呼叫了start()方法。
  • BLOCKED:阻塞狀態,遇到了阻塞事件,或者等待鎖物件.
  • WAITING:等待狀態。wait()join()
  • TIMED_WAITING:等待狀態,sleep()
  • TERMINATED:終止狀態

在這裡插入圖片描述

更多多執行緒常見面試題及答案

四、MySql

sql優化

  1. sql優化主要是對查詢進行優化,應儘量避免全表掃描,首先應考慮在 whereorder by 涉及的列上建立索引
  2. 任何地方都不要使用 select * from t ,用具體的欄位列表代替“*”,不要返回用不到的任何欄位。
  3. 避免在 where 子句中使用!=<>操作符,否則將引擎放棄使用索引而進行全表掃描,
  4. 應儘量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描。可以在null上設定預設值,確保表中沒有null值。
  5. 複合索引必須指定索引中第一個欄位作為檢索條件,否則不使用索引
  6. innot in 也要慎用,否則會導致全表掃描。對於連續的數值,能用 between 就不要用 in 了。
  7. 模糊查詢時,以"%"開頭,會導致全表掃描

更多sql優化的幾種方法

索引

什麼是索引

索引(Index)是幫助MySQL高效獲取資料的資料結構,可以理解為一本字典的目錄,提高程式的檢索 和查詢效率

索引的型別

  • 主鍵索引:資料列不允許重複,不允許為NULL,一個表只能有一個主鍵。

  • 唯一索引:資料列不允許重複,允許為NULL值,一個表允許多個列建立唯一索引。

    #建立唯一索引
    ALTER TABLE table_name ADD UNIQUE (column);
    #建立唯一組合索引
    ALTER TABLE table_name ADD UNIQUE (column1,column2);
    
  • 普通索引:基本的索引型別,沒有唯一性的限制,允許為NULL值。

    #建立普通索引
    ALTER TABLE table_name ADD INDEX index_name (column);
    #建立組合索引
    ALTER TABLE table_name ADD INDEX index_name(column1, column2, column3);
    
  • 全文索引:是目前搜尋引擎使用的一種關鍵技術。

    #建立全文索引
    ALTER TABLE table_name ADD FULLTEXT (column);
    

如何判斷一條sql語句是否使用了索引(執行計劃)

通過執行計劃,可以判斷本次查詢中對於資料行定位是否是從索引獲取的

EXPLAIN  查詢語句 

對於查詢的評級由好到壞:const > primary(主鍵) > ref > range > index > all

  • 平時進行SQL優化時,最低要求達到range級別,儘可能達到ref級別

MySQL 服務佔用 cpu 100%,如何排查問題?

  1. 一般情況下,mysql佔用過高多是有慢查詢,開啟慢查詢日誌,找到執行較慢的語句。另一種方法:也可以在mysql命令列中呼叫 show processlist;,檢視當前 mysql 使用頻繁的 sql 語句。
  2. 分析查詢效率低的原因,一般是因為沒有使用索引,要麼sql優化,要麼給欄位加上索引。

事務

Mysql事務的隔離級別

事務隔離級別說明髒讀不可重複讀幻讀
讀未提交(read-uncommitted)讀取資料的事務物件,可以讀取到另一個事務物件尚未提交的資料
不可重複讀(read-committed)讀取資料的事務物件,只能讀取另一個事務物件提交過後的資料
可重複讀(repeatable-read)
(預設)
讀取資料的事務物件,在另一個事務物件提交前後讀取的內容要保持一致
序列化(serializable)序列讀取

什麼是髒讀?什麼是幻讀?

  • 髒讀:事務A讀取了事務B更新的資料,然後B回滾操作,那麼A讀取到的資料是髒資料

  • 不可重複讀:事務 A 多次讀取同一資料,事務 B 在事務A多次讀取的過程中,對資料作了更新並提交,導致事務A多次讀取同一資料時,結果 不一致。

  • 幻讀:系統管理員A將資料庫中所有學生的成績從具體分數改為ABCDE等級,但是系統管理員B就在這個時候插入了一條具體分數的記錄,當系統管理員A改結束後發現還有一條記錄沒有改過來,就好像發生了幻覺一樣,這就叫幻讀。事務1在兩次查詢的過程中,事務2對該表進行了插入、刪除操作,從而事務1第二次查詢的結果發生了變化。

總結:不可重複讀的和幻讀很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。解決不可重複讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表

儲存引擎

儲存引擎對比

  • MyIsam:

    • 使用三個檔案表示每個表:

      • 格式檔案 — 儲存表的結構(mytable.frm)
      • 資料檔案 — 儲存表的資料(mytable.MYD)
      • 索引檔案 — 儲存表的索引(mytable.MYI)
    • 對錶檔案中資料行查詢提速,有很大幫助。

    • 靈活的AUTO_INCREMENT欄位處理

    • 可被轉換為壓縮、只讀表來節省空間

    • 但是MyIsam不支援事務,因此在進行資料行新增,修改,刪除時,無法保證資料安全性

  • INNODB:MySQL資料庫的預設引擎;

    • 每個InnoDB表在資料庫目錄中以.frm格式檔案表示
    • InnoDB表空間tablespace被用於儲存表的內容
    • 提供一組用來記錄事務性活動的日誌檔案
    • 支援事務處理
    • 提供全ACID相容
    • MySQL伺服器崩潰後提供自動恢復
    • 多版本(MVCC)和行級鎖定
    • 支援外來鍵及引用的完整性,包括級聯更新和刪除
  • MEMORY:其資料儲存在記憶體中,且行的長度固定,這兩個特點使得MEMORY儲存引擎非常快;

    • 在資料庫目錄內,每個表均以.frm格式檔案表示;
    • 表資料及索引被儲存在記憶體中;
    • 表級鎖機制;
    • 不能包含TEXT或BLOB欄位;

如何選擇合適的儲存引擎

  1. MyISAM表最適合於大量的資料讀而少量資料更新的混合操作。MyISAM表的另一種適用情形是使用壓縮的中讀表。
  2. 如果查詢中包含較多的資料更新操作,應使用InnoDB。其行級鎖機制和多版本的支援為資料讀取和更新的混合提供了良好的併發機制。
  3. 可使用MEMORY儲存引擎儲存非永久需要的資料,或者是能夠從基於磁碟的表中重新生成的資料。

Innodb有哪些特性?

  1. 插入緩衝
  2. 二次寫
  3. 自適應雜湊
  4. 預讀

innodb引擎的4大特性

Innodb的事務鎖有哪些?(4種)

行級鎖

  • 共享鎖(S Lock) : 允許事務讀一行資料
  • 排它鎖(X Lock) : 允許事務刪除或更新一行資料

表級鎖

  • 意向共享鎖(IS Lock):事務想要獲得一張表中某幾行的共享鎖
  • 意向排它鎖(IX Lock):事務想要獲得一張表中某幾行的排它鎖

由於Innodb引擎支援的均為行鎖,所以意向鎖其實不會阻塞除全表掃描之外的任何請求

資料庫的樂觀鎖怎麼實現的?

樂觀鎖:假設不會發生併發衝突,只在提交操作時檢查是否違反資料完整性。

實現方式

  1. 在資料庫中加入一個版本號欄位,每次在執行資料的修改操作時,先查出相應的版本號,然後帶上查出來的這個版本號去修改資料庫,一旦該版本號和資料的版本號一致就可以執行修改操作並對版本號執行+1操作,否則就執行失敗
  2. 上述方法中的版本號也可以用時間戳替換,時間戳不需要 +1 操作。

使用場景
樂觀鎖適用於寫比較少的情況下(多讀場景),即衝突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。但如果是多寫的情況,一般會經常產生衝突,這就會導致上層應用會不斷的進行retry,這樣反倒是降低了效能,所以一般多寫的場景下用悲觀鎖就比較合適。

使用什麼工具建立表?

使用Power Designer來建立表

表結構是怎麼設計的?

  1. 首先,表結構的設計儘量要滿足資料庫三正規化
  2. 但是還是根據實際需求進行取捨,有時可能會拿冗餘換速度,最終用目的要滿足客戶需求。

第一正規化:每一個欄位是原子性不能再分;
第二正規化:在第一正規化基礎之上,要求資料庫中所有非主鍵欄位完全依賴主鍵,不能產生部分依賴;
第三正規化:在第二正規化基礎之上,要求非主鍵欄位不能產生傳遞依賴於主鍵欄位

資料庫叢集是怎麼同步的?

在這裡插入圖片描述

  1. 當 master 主伺服器上的資料發生改變時,則將其改變寫入二進位制事件日誌檔案中
  2. salve 從伺服器會在一定時間間隔內對 master 主伺服器上的二進位制日誌進行探測,探測其是否發生過改變,如果探測到 master 主伺服器的二進位制事件日誌發生了改變,則開始一個 I/O Thread 請求 master 二進位制事件日誌
  3. 同時 master 主伺服器為每個 I/O Thread 啟動一個dump Thread,用於向其傳送二進位制事件日誌
  4. slave 從伺服器將接收到的二進位制事件日誌儲存至自己本地的中繼日誌檔案中
  5. salve 從伺服器將啟動 SQL Thread 從中繼日誌中讀取二進位制日誌,在本地重放,使得其資料和主伺服器保持一致;
  6. 最後 I/O Thread 和 SQL Thread 將進入睡眠狀態,等待下一次被喚醒

如何防止SQL隱碼攻擊?

SQL隱碼攻擊產生的原因,就是使用者提交的資料,被資料庫系統編譯而產生了開發者預期之外的動作。也就是,SQL隱碼攻擊是使用者輸入的資料,在拼接SQL語句的過程中,超越了資料本身,成為了SQL語句查詢邏輯的一部分,然後這樣被拼接出來的SQL語句被資料庫執行,產生了開發者預期之外的動作。

解決辦法:

  1. 最簡單的就是採用預編譯語句集(PreparedStatement),在SQL語句中放置佔位符’?’,在執行前,資料庫會先對含有佔位符的SQL語句進行校驗和預編譯;執行時才會把引數傳遞進去,防止引數參與編譯變成sql語句的一部分。
  2. 採用正規表示式將包含有 單引號('),分號(;) 和 註釋符號(--)的語句給替換掉來防止SQL隱碼攻擊

五、Redis

1. Redis有哪些優勢?

  1. 單執行緒,利用redis佇列技術並將訪問變為序列訪問,消除了傳統資料庫序列控制的開銷

  2. 支援資料持久化,支援AOF和RDB兩種持久化方式。

  3. 分散式 讀寫分離模式

  4. 支援豐富資料型別

  5. 支援事務,操作都是原子性,所謂原子性就是對資料的更改要麼全部執行,要不全部不執行。

2. 有幾種資料型別?你用到了哪些資料型別(5種)?在哪兒用到的?

String型別

  • 計數:由於Redis單執行緒的特點,我們不用考慮併發造成計數不準的問題,通過 incrby 命令,我們可以正確的得到我們想要的結果。
  • 限制次數:比如登入次數校驗,錯誤超過三次5分鐘內就不讓登入了,每次登入設定key自增一次,並設定該key的過期時間為5分鐘後,每次登入檢查一下該key的值來進行限制登入。

Hash型別

  • 快取一些資訊

List型別

  • 可以用作棧,佇列等等

Set型別

  • 利用集合的交併集特性,比如在社交領域,我們可以很方便的求出多個使用者的共同好友,共同感興趣的領域等。

Zset型別

  • 和set資料結構一樣,zset也可以用於社交領域的相關業務,並且還可以利用zset 的有序特性,還可以做類似排行榜的業務。

3. redis的過期策略有哪些?

定期刪除+惰性刪除

定期刪除redis 預設是每隔 100ms 就會掃描一定數量的資料庫的expires字典中的key,檢查其是否過期,如果過期就刪除。定期刪除可能會導致很多過期 key 到了時間並沒有被刪除掉,所以就使用惰性刪除。

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

惰性刪除:獲取 key 的時候,如果此時 key 已經過期,就刪除,不會返回任何東西。

4. 持久化

(1)RDB

Redis Database(RDB),就是在指定的時間間隔內將記憶體中的資料集快照寫入磁碟,資料恢復時將快照檔案直接再讀到記憶體。

RDB 儲存了在某個時間點的資料集(全部資料)。儲存在一個二進位制檔案中,只有一個檔案。預設是 dump.rdb。 RDB 技術非常適合做備份,可以儲存最近一個小時,一天,一個月的全部資料。儲存資料是在單獨的程式中寫檔案,不影響 Redis 的正常使用。 RDB 恢復資料時比其他 AOF 速度快。

  • 優點:由於儲存的是資料快照檔案,恢復資料很方便,也比較快
  • 缺點:
    • 會丟失最後一次快照以後更改的資料。 如果你的應用能容忍一定資料的丟失,那麼使用 rdb 是不錯的選擇; 如果你不能容忍一定資料的丟失,使用 rdb 就不是一個很好的選擇。
    • 由於需要經常操作磁碟, RDB 會分出一個子程式。如果你的 redis 資料庫很大的話,子程式佔用比較多的時間,並且可能會影響 Redis 暫停服務一段時間(millisecond 級別),如果你的資料庫超級大並且你的伺服器 CPU 比較弱,有可能是會達到一秒。

(2)AOF

Append-only File(AOF), Redis 每次接收到一條改變資料的命令時,它將把該命令寫到一個 AOF 檔案中(只記錄寫操作,讀操作不記錄),當 Redis 重啟時,它通過執行 AOF 檔案中所有的命令來恢復資料。

  • append-only 檔案是另一個可以提供完全資料保障的方案;
  • AOF 檔案會在操作過程中變得越來越大。比如,如果你做一百次加法計算,最後你只會在資料庫裡面得到最終的數值,但是在你的 AOF 裡面會存在 100 次記錄,其中 99 條記錄對最終的結果是無用的;但 Redis 支援在不影響服務的前提下在後臺重構 AOF 檔案,讓檔案得以整理變小
  • 可以同時使用這兩種方式,redis預設優先載入 aof檔案(aof資料最完整);

5. Redis叢集保證了CAP中的什麼?

CAP原則又稱CAP定理,指在一個分散式系統不可能同時滿足 Consistency(一致性)、 Availability(可用性)、Partition tolerance(分割槽容錯性)。由於分割槽容錯性是分散式系統中必須要保證的,因此我們只能在A和C之間進行權衡

  • 一致性(C):在分散式系統中的所有資料備份,在同一時刻是否同樣的值。(等同於所有節點訪問同一份最新的資料副本)
  • 可用性(A):在叢集中一部分節點故障後,叢集整體是否還能響應客戶端的讀寫請求。(對資料更新具備高可用性)
  • 分割槽容錯性(P):以實際效果而言,分割槽相當於對通訊的時限要求。系統如果不能在時限內達成資料一致性,就意味著發生了分割槽的情況,必須就當前操作在C和A之間做出選擇。

Redis叢集是通過犧牲一致性來保證後兩者(AP)。無法保證強一致性(主從伺服器始終保持資料一致),只能保證最終一致性(最終從伺服器與主伺服器資料一致)

6. Redis主從複製原理

  1. 當啟動一個 從結點 的時候,它會傳送一個 PSYNC 命令給 主結點

  2. 如果這是 從結點 初次連線到 主結點,那麼會觸發一次全量複製:此時 主結點 會啟動一個後臺執行緒,開始生成一份 RDB 快照檔案,同時還會將從客戶端新收到的所有寫命令快取在記憶體中。RDB 檔案生成完畢後, 主結點 會將這個 RDB 傳送給 從結點從結點 會先寫入本地磁碟,然後再從本地磁碟載入到記憶體中,接著 主結點 會將記憶體中快取的寫命令傳送到 從結點從結點 也會同步這些資料。

  3. 從結點 如果跟 主結點 有網路故障,斷開了連線,會自動重連,連線之後 主結點 僅會複製給 從結點 部分缺少的資料。

7. Redis叢集會有寫操作丟失嗎?為什麼?

Redis並不能保證資料的強一致性,這意味這在實際中叢集在特定的條件下可能會丟失寫操作。

更多:面了BAT,我總結了他們會問的Redis基礎知識
Redis面試題(2020最新版)
Redis持久化與叢集 (Redis高頻面試點: AOF,RDB,CAP,主從複製,哨兵,叢集)

六、Linux

1. 什麼是管道?

管道就是用“|”連線兩個命令,將前面一個命令的輸出作為後面命令的輸入,用於把管道左邊的輸出作為右邊的輸入。

2. 檢視cpu,記憶體佔用的命令

Linux top命令詳解
Linux 常用命令-- top

七、Spring

1. IoC

IoC 就是控制反轉,是指建立物件的控制權的轉移,以前建立物件的主動權和時機是由自己把控的,而現在這種權力轉移到 Spring 容器中,並由容器根據配置檔案去建立例項和管理各個例項之間的依賴關係,物件與物件之間鬆散耦合,也利於功能的複用。DI 依賴注入,和控制反轉是同一個概念的不同角度的描述,即 應用程式在執行時依賴 IoC 容器來動態注入物件需要的外部資源。

2. Aop

AOP,一般稱為面向切面,作為物件導向的一種補充,用於將那些與業務無關,但卻對多個物件產生影響的公共行為和邏輯,抽取並封裝為一個可重用的模組,這個模組被命名為 “切面”(Aspect),減少系統中的重複程式碼,降低了模組間的耦合度,同時提高了系統的可維護性。可用於許可權認證、日誌、事務處理。AOP 實現的關鍵在於 代理模式.

3. 解釋一下 AOP 裡面的幾個名詞:

  1. 切面(Aspect):被抽取的公共模組(類),可能會橫切多個物件。例如銀行系統,先檢測是否安全,再執行核心(業務)層,再記錄日誌,再清空快取記錄,除了核心層都是切面

  2. 連線點(Join point):指方法(可以被切面織入的具體方法),通常業務介面中的方法均為連線點。例如上面的銀行系統中,核心類(目標業務方法)就是連線點

  3. 通知(Advice):Advice也叫增強。切面為類,而切面裡的方法叫通知。有前置通知,後置通知,環繞通知等等。

  4. 切入點(Pointcut):切入點就是一個或多個連線點的集合。通過切入點表示式,來獲取滿足條件的連線點的集合。

  5. 目標物件(Target Object): 目標物件指被增強了的物件(包含主業務邏輯的類的物件),即被一個或者多個切面所通知的物件。這個物件永遠是一個被代理(proxied) 物件。

  6. 織入(Weaving):指把增強應用到目標物件來建立新的代理物件的過程(將通知與連線點連線起來的過程)。

4. Aop在專案中用在什麼地方?

Aop一般用來處理許可權控制,日誌記錄,載入事務等非主要業務邏輯的事情。底層是採用cglib和jdk的動態代理實現的。

5. IoC和Aop有什麼作用?

  • IoC 這個指的就是我們獲取物件的方式進行反轉了。不使用IoC時,物件是需要手動new出來的,是我們主動獲取的。使用IoC之後,是將這個獲取的過程交給spring來管理,我們只需要告訴spring你需要什麼就行了,它就會把東西給你。
  • Aop 就是用來增強功能,在執行主業務的時可以順帶執行其他業務。

Sring 的AOP和IoC都是為了解決系統程式碼耦合度過高的問題。使程式碼重用度高、易於維護。

6. AspectJ的Aop和Spring的Aop有什麼區別?

  1. Spring AOP採用的是動態織入(執行時織入),而Aspectj是靜態織入(編譯時期就織入),通過修改程式碼來實現。

  2. AspectJ由於是在編譯時期就織入,故效能更優。

  3. Spring AOP的通知是基於該物件是SpringBean物件才可以,而AspectJ可以在任何Java物件上應用通知。

  4. Spring AOP僅支援方法切入點,而AspectJ支援所有切入點

7. Spring如何實現事務的控制?

事務就是對一系列的資料庫操作(比如插入多條資料)進行統一的提交或回滾操作,如果插入成功,那麼一起成功,如果中間有一條出現異常,那麼回滾之前的所有操作。這樣可以防止出現髒資料,防止資料庫資料出現問題。開發中為了避免這種情況一般都會進行事務管理。

Spring 提供了宣告式事務和程式設計式事務。

  • 宣告式事務:通常是指在配置檔案中對事務進行配置宣告,其中包括了很多宣告屬性,它是通過AOP實現的,自己不用額外的寫程式碼,只要在Spring配置檔案中宣告即可;通常用在資料庫的操作裡面;
  • 程式設計式事務:就是指通過硬編碼的方式做事務處理,這種處理方式需要寫程式碼,事務中的邏輯可以自己定製;可以是資料庫的操作,也可以是其他的操作。

Spring中也有自己的事務管理機制,一般是使用TransactionMananger進行管理,可以通過Spring的注入來使用此功能。

Spring面試總結(全)

八、SpringMVC

1. SpringMVC執行流程

在這裡插入圖片描述

  1. 瀏覽器提交請求到中央排程器
  2. 中央排程器直接將請求轉給處理器對映器。
  3. 處理器對映器會根據請求,找到處理該請求的處理器,並將其封裝為處理器執行鏈後返回給中央排程器。
  4. 中央排程器根據處理器執行鏈中的處理器,找到能夠執行該處理器的處理器介面卡。
  5. 處理器介面卡呼叫執行處理器。
  6. 處理器將處理結果及要跳轉的檢視封裝到一個物件 ModelAndView 中,並將其返回給處理器介面卡。
  7. 處理器介面卡直接將結果返回給中央排程器。
  8. 中央排程器呼叫檢視解析器,將 ModelAndView中的檢視名稱封裝為檢視物件。
  9. 檢視解析器將封裝了的檢視物件返回給中央排程器
  10. 中央排程器呼叫檢視物件,讓其自己進行渲染,即進行資料填充,成響應物件。
  11. 中央排程器響應瀏覽器。

2. @RequestParam和@PathVariable

  • @RequestParam:將請求引數繫結到控制器的方法引數上,同時可以給引數一些限定,比如說指定引數的預設值,是否允許引數為空等等。
  • @PathVariable:是將Rest風格的請求中{}中的值取出來

3. @ResponseBody和@RequestBody

  • @ResponseBody:作用在方法上,表示該方法的返回結果直接寫入響應體中,如果不使用@ResponseBody,則返回值會解析為跳轉路徑。
  • @RequestBody:作用在形參列表上,用於將前臺傳送過來固定格式的資料【xml 格式或者 json等】封裝為對應的 JavaBean 物件,封裝時使用到的一個物件是系統預設配置的 HttpMessageConverter進行解析,然後封裝到形參上。

4. 如何攔截請求?

  1. 使用過濾器方式攔截請求(實現javax.servlet.Filter介面)
  2. 使用SpringMVC提供的攔截器(實現HandlerInterceptor介面)
  3. 利用Spring的切面(AOP)實現攔截器。

九、MyBatis

1. #{}和${}都用過嗎?有什麼區別?

#{}是預編譯處理,${}是字串替換

  • #{}Mybatis在處理#{}時,會將sql中的#{}替換為?號,對sql語句預編譯後,再呼叫PreparedStatementset方法來賦值佔位符,告訴 mybatis 使用實際的引數值代替。可以有效的防止SQL隱碼攻擊,提高系統安全性

  • ${}:字串替換, 告訴 mybatis 使用$包含的“字串”替換所在位置。使用的是 Statement 物件,有SQL注入的風險。

2. 模糊查詢怎麼使用?

<select id="selectLikeTwo" resultType="com.bjpowernode.domain.Student">
    select * from student where name like "%" #{name} "%"
</select>

3. 動態查詢的sql怎麼寫?

MyBatis學習記錄(3)之動態SQL

4. MyBatis的快取機制

MyBatis提供查詢快取,用於減輕資料庫壓力,提高資料庫效能,MyBatis提供了一級快取和二級快取

  • 一級快取:SqlSession級別的快取

    在運算元據庫時,需要構造SqlSession物件,在物件中有一個資料結構(HashMap)用於儲存快取資料

    不同的SqlSession之間的快取區域是互相不影響的。

  • 二級快取:mapper級別的快取

    多個SqlSession去操作同一個mapper的sql語句,多個SqlSession可以共用二級快取,所得到的資料會存在二級快取區域,二級快取是跨SqlSession

    二級快取預設是沒有開啟的。需要在setting全域性引數中配置開啟二級快取

二級快取的具體流程:

  1. 當一個SqlSession執行了一次select後,在關閉此session的時候,會將查詢結果快取到二級快取

  2. 當另一個SqlSession執行select時,首先會在他自己的一級快取中找,如果沒找到,就回去二級快取中找,找到了就返回,就不用去資料庫了,從而減少了資料庫壓力提高了效能

  3. 如果SqlSession執行了DML操作(insert、update、delete),並commit了,那麼mybatis就會清空當前mapper快取中的所有快取資料,這樣可以保證快取中的存的資料永遠和資料庫中一致,避免出現髒讀

十、SpringBoot

1. SpringBoot配置檔案載入順序

在 Spring Boot 裡面,可以使用以下幾種方式來載入配置,他們的載入優先順序從高到低為:

  1. properties檔案;
  2. YAML檔案;
  3. 系統環境變數;
  4. 命令列引數;

不同目錄下的配置檔案(properties檔案與yaml檔案)優先順序從高到低順序:(高優先順序配置會覆蓋低優先順序配置,多個配置檔案互補

  1. file:./config/ - 優先順序最高(專案根路徑下的config
  2. file:./ - 優先順序第二 -(專案根路徑下
  3. classpath:/config/ - 優先順序第三(專案resources/config下
  4. classpath:/ - 優先順序第四(專案resources根目錄

2. SpringBoot的核心註解?由哪些子註解組成?

啟動類上面的註解是@SpringBootApplication,它也是 Spring Boot 的核心註解,主要組合包含了以下 3 個註解:

  • @SpringBootConfiguration:組合了 @Configuration 註解,實現配置檔案的功能。

  • @EnableAutoConfiguration:開啟自動配置的功能

  • @ComponentScan:Spring元件掃描。

3. SpringBoot的自動配置

註解 @EnableAutoConfiguration@Configuration@ConditionalOnClass 就是自動配置的核心。

@EnableAutoConfiguration 給容器匯入META-INF/spring.factories 裡定義的自動配置類,並根據一定的條件篩選有效的自動配置類,然後每一個自動配置類結合對應的 xxxProperties.java 讀取配置檔案進行自動配置功能

Spring Boot面試殺手鐗——自動配置原理

4. Spring Boot Starter

Starters可以理解為啟動器,它包含了一系列可以整合到應用裡面的依賴包,你可以一站式整合Spring及其他技術,而不需要到處找示例程式碼和依賴包。例如,你想使用Spring JPA訪問資料庫,只要加入springboot-starter-data-jpa啟動器依賴就能使用了。Starters包含了許多專案中需要用到的依賴,它們能快速持續的執行,都是一系列得到支援的管理傳遞性依賴。

Starter簡單來講就是引入了一些相關依賴和一些初始化的配置。然後通過自動配置,完成這些bean的自動配置。

Spring Boot面試題(2020最新版)

十一、RabbitMQ

RabbitMQ的組成

在這裡插入圖片描述

  1. Publisher:訊息的生產者,也是一個向交換器釋出訊息的客戶端應用程式。
  2. Broker:表示訊息佇列伺服器實體(就是個伺服器)。
  3. Virtual Host:虛擬主機,表示一批交換器、訊息佇列和相關物件。虛擬主機是共享相同的身份認證和加密環境的獨立伺服器域。每個 vhost 本質上就是一個 mini 版的 RabbitMQ 伺服器,擁有自己的佇列、交換器、繫結和許可權機制。vhost 是 AMQP 概念的基礎,必須在連線時指定,RabbitMQ 預設的 vhost 是 /
  4. Exchange:交換器,用來接收生產者傳送的訊息並將這些訊息路由給伺服器中的佇列。
  5. Binding:繫結,用於訊息佇列和交換器之間的關聯。一個繫結就是基於路由鍵將交換器和訊息佇列連線起來的路由規則,所以可以將交換器理解成一個由繫結構成的路由表。
  6. Queue:訊息佇列,用來儲存訊息直到傳送給消費者。它是訊息的容器,也是訊息的終點。一個訊息可投入一個或多個佇列。訊息一直在佇列裡面,等待消費者連線到這個佇列將其取走。
  7. Connection:網路連線,比如一個TCP連線。
  8. Channel:通道,多路複用連線中的一條獨立的雙向資料流通道。通道是建立在真實的TCP連線內地虛擬連線,AMQP 命令都是通過通道發出去的,不管是釋出訊息、訂閱佇列還是接收訊息,這些動作都是通過通道完成。因為對於作業系統來說建立和銷燬 TCP 都是非常昂貴的開銷,所以引入了通道的概念,以複用一條 TCP 連線。
  9. Consumer:訊息的消費者,表示一個從訊息佇列中取得訊息的客戶端應用程式。

RabbitMQ使用場景

  1. 解耦:A 系統傳送資料到 BCD 三個系統,通過介面呼叫傳送。如果 E 系統也要這個資料呢?那如果C 系統現在不需要了呢?A 系統負責人幾乎崩潰…A 系統跟其它各種亂七八糟的系統嚴重耦合,A 系統產生一條比較關鍵的資料,很多系統都需要 A 系統將這個資料傳送過來。如果使用 MQ,A 系統產生一條資料,傳送到 MQ 裡面去,哪個系統需要資料自己去 MQ 裡面消費。如果新系統需要資料,直接從MQ 裡消費即可;如果某個系統不需要這條資料了,就取消對 MQ 訊息的消費即可。這樣下來,A 系統壓根兒不需要去考慮要給誰傳送資料,不需要維護這個程式碼,也不需要考慮人家是否呼叫成功、失敗超時等情況。就是一個系統或者一個模組,呼叫了多個系統或者模組,互相之間的呼叫很複雜,維護起來很麻煩。但
    是其實這個呼叫是不需要直接同步呼叫介面的,如果用 MQ 給它非同步化解耦。
  2. 非同步:A 系統接收一個請求,需要在自己本地寫庫,還需要在 BCD 三個系統寫庫,自己本地寫庫要 3ms,BCD 三個系統分別寫庫要 300ms、450ms、200ms。最終請求總延時是 3 + 300 + 450 +200 = 953ms,接近 1s,使用者感覺搞個什麼東西,慢死了慢死了。使用者通過瀏覽器發起請求。如果使用MQ,那麼 A 系統連續傳送 3 條訊息到 MQ 佇列中,假如耗時 5ms,A 系統從接受一個請求到返回響應給使用者,總時長是 3 + 5 = 8ms。
  3. 削峰:減少高峰時期對伺服器壓力

如何保證訊息必達?

資料丟失可能有下面幾種情況:生產者丟失訊息、訊息列表丟失訊息、消費者丟失訊息;

  1. 生產者丟失訊息:從生產者弄丟資料這個角度來看,RabbitMQ提供transactionconfirm模式來確保生產者不丟訊息;

    transaction機制:同步,傳送訊息前,開啟事務(channel.txSelect()),然後傳送訊息,如果傳送過程中出現什麼異常,事務就會回滾(channel.txRollback()),如果傳送成功則提交事務(channel.txCommit())。然而,這種方式有個缺點:吞吐量下降;

    confirm模式(推薦):非同步,一旦channel進入confirm模式,所有在該通道上釋出的訊息都將會被指派一個唯一的ID(從1開始),一旦訊息被投遞到所有匹配的佇列之後;rabbitMQ就會傳送一個ACK給生產者(包含訊息的唯一ID),這就使得生產者知道訊息已經正確到達目的佇列了;如果rabbitMQ沒能處理該訊息,則會傳送一個Nack訊息給你,你可以進行重試操作。

  2. 訊息佇列丟資料:訊息持久化。

處理訊息佇列丟資料的情況,一般是開啟持久化磁碟的配置。

這個持久化配置可以和confirm機制配合使用,你可以在訊息持久化磁碟後,再給生產者傳送一個Ack訊號。這樣,如果訊息持久化磁碟之前,rabbitMQ陣亡了,那麼生產者收不到Ack訊號,生產者會自動重發。

  1. 消費者丟失訊息:關閉RabbitMQ的自動訊息確認,使用手動確認訊息

在程式碼中成功處理完訊息後,使用手動回覆確認訊息,避免消費者丟失訊息。

如何保證訊息不被重複消費?

保證訊息的唯一性,就算是多次傳輸,不要讓訊息的多次消費帶來影響;比如:在寫入訊息佇列的資料做唯一標示,消費訊息時,根據唯一標識判斷是否消費過;

假設你有個系統,消費一條訊息就往資料庫裡插入一條資料,要是你一個訊息重複兩次,你不就插入了兩條,這資料不就錯了?但是你要是消費到第二次的時候,自己判斷一下是否已經消費過了,若是就直接扔了,這樣不就保留了一條資料,從而保證了資料的正確性。

更多:
面試官問我:什麼是訊息佇列?什麼場景需要他?用了會出現什麼問題?
訊息中介軟體MQ與RabbitMQ面試題(2020最新版)

十二、web程式設計

簡單說一下HTTP

【HTTP協議】—HTTP協議詳解

簡單說一下http請求四次揮手的過程?

http三次握手四次揮手詳解

什麼是Cookie,什麼是Session,有什麼區別?

SessionCookie功能效果相同,都是記錄一系列狀態。

SessionCookie的區別在於:

  • Session是記錄在服務端的記憶體中,而Cookie是記錄在客戶端的瀏覽器記憶體,硬碟中。
  • Cookie中資料名和資料,只能String,而Session中資料名必須是String,但是資料可以是Object型別

Session的工作原理

HTTP協議是非連線性的,取完當前瀏覽器的內容,然後關閉瀏覽器後,連結就斷開了,而沒有任何機制去記錄取出後的資訊。而當需要訪問同一個網站的另外一個頁面時(就好比如在第一個頁面選擇購買的商品後,跳轉到第二個頁面去進行付款)這個時候取出來的資訊,就讀不出來了。所以必須要有一種機制讓頁面知道原理頁面的session內容。

Tomcat在建立一個Session物件時,自動為這個session物件提供一個唯一編號。開發人員將這個唯一編號稱為【sessionId】。Tomcatsessionid儲存到一個Cookie物件,在響應時將cookie寫入到響應頭交給當前客戶的瀏覽器上。瀏覽器收到sessionId之後將其儲存在瀏覽器記憶體中。

等到使用者再次通過瀏覽器發起請求時,其擁有sessionId作為Cookie寫入到請求頭髮送回服務端。此時Tomcat接收到命令之後,就可以根據使用者請求協議包中請求頭是否擁有sessionId,來判斷使用者在服務端是否存在Session

如果客戶端Cookie被禁用,Session還能使用嗎?

使用URL重寫,就是通過傳送請求時加入sessionId的引數

Tomcat是如何配置記憶體的?

  1. Windows下,在檔案/bin/catalina.bat,Linux下,在檔案/bin/catalina.sh的前面,增加如下設定:

    # 配置JAVA_OPTS,後面跟需要配置的引數,變數名必須是JAVA_OPS,例如:
    JAVA_OPTS='-Xms256m -Xmx512m'
    # 表示初始化記憶體為256MB,可以使用的最大記憶體為512MB。
    
  2. 環境變數中進行配置,例如(變數名必須是JAVA_OPS):
    變數名:JAVA_OPTS
    變數值:-Xms512m -Xmx512m

防止表單重複提交的方法有哪些?

  1. 通過JavaScript遮蔽提交按鈕(不推薦):

    通過js程式碼,當使用者點選提交按鈕後,遮蔽提交按鈕使使用者無法點選提交按鈕或點選無效,從而實現防止表單重複提交。但是如果使用者通過重新整理頁面方式,或使用postman等工具繞過前段頁面仍能重複提交表單。因此不推薦此方法。

  2. 給資料庫增加唯一鍵約束(會增加資料庫的負載):

    在資料庫建表的時候在ID欄位新增主鍵約束,使用者名稱、郵箱、電話等欄位加唯一性約束。確保資料庫只可以新增一條資料。

  3. 利用Session防止表單重複提交(推薦):

    在伺服器端響應表單頁面之前,先生成一個唯一的識別符號,將它存入session,同時將它寫入表單的隱藏欄位中,然後將表單頁面發給瀏覽器,使用者錄入資訊後點選提交,在伺服器端,獲取表單中隱藏欄位的值,與session中的唯一識別符號比較,相等說明是首次提交,就處理本次請求,然後將session中的唯一識別符號移除;不相等說明是重複提交,就不再處理。

什麼情況下會出現跨域問題,如何解決?

跨域問題的產生原因和相應的解決方式

十三、其它

1. maven配置檔案有哪些主要的元素?

  1. 如果maven專案有繼承關係(Spring Boot),用<parent>標籤指明父專案gav座標。
  2. 當前專案的gav座標(<groupId>/<artifactId>/<version>
  3. 當前專案產生的構件型別(<packaging>
  4. 自定義一些變數,通過${}來引用(<properties>
  5. 依賴(<dependencies>
  6. 資源路徑列表(<resources>
  7. 外掛(<plugins>

Maven配置檔案POM屬性最全詳解

自我介紹+專案介紹(你用了那些技術,專案用了哪些技術)

還有什麼想問的

  1. 工作地點
  2. 崗位職責
  3. 多久回覆

你的期望薪資是多少?

你的離職原因是什麼?

如果對方是大公司,你是小公司,你可以說,在小公司待的時間長了,想到大公司見識一下。

如果對方是小公司,你是大公司,你可以說,在大公司待的時間長了,職業天花板就在眼前,想在小公司試一下,有沒有發揮自己才華的空間。

如果對方是外包公司,你可以說,自己還年輕,想進外包公司鍛鍊一下,多積累專案經驗,提升自己的技術能力

如果對方不是外包公司,你就可以說上家公司是外包公司,每天都把自己外派到別的公司,感覺派來派去,沒有歸屬感

如果對方是創業公司,你可以說自己想要更有挑戰性的工作。

如果對方是國企,你可以說自己想有一份穩定一些的工作。

如果對方是外企,你可以說自己想要學習和感受一下國外的氛圍。

相關文章