java-技能提升、性能優(yōu)化相關(guān)面試題

多線程

創(chuàng)建線程是幾種方式

方式一:繼承Thread類,覆寫(xiě)run方法,創(chuàng)建實(shí)例對(duì)象,調(diào)用該對(duì)象的start方法啟動(dòng)線程
方式二:創(chuàng)建Runnable接口的實(shí)現(xiàn)類,類中覆寫(xiě)run方法,再將實(shí)例作為此參數(shù)傳遞給Thread類有參構(gòu)造創(chuàng)建線程對(duì)象,調(diào)用start方法啟動(dòng)

方式三:創(chuàng)建Callable接口的實(shí)現(xiàn)類,類中覆寫(xiě)call方法,創(chuàng)建實(shí)例對(duì)象,將其作為參數(shù)傳遞給FutureTask類有參構(gòu)造創(chuàng)建FutureTask對(duì)象,再將FutureTask對(duì)象傳遞給Thread類的有參構(gòu)造創(chuàng)建線程對(duì)象,調(diào)用start方法啟動(dòng)

Thread有單繼承的局限性,Runnable和Callable三避免了單繼承的局限,使用更廣泛。Runnable適用于無(wú)需返回值的場(chǎng)景,Callable使用于有返回值的場(chǎng)景

Thread的start和run的區(qū)別

start是開(kāi)啟新線程, 而調(diào)用run方法是一個(gè)普通方法調(diào)用,還是在主線程里執(zhí)行。沒(méi)人會(huì)直接調(diào)用run方法

sleep 和 wait的區(qū)別

第一,sleep方法是Thread類的靜態(tài)方法,wait方法是Object類的方法

第二:sleep方法不會(huì)釋放對(duì)象鎖,wait方法會(huì)釋放對(duì)象鎖

第三:sleep方法必須捕獲異常,wait方法不需要捕獲異常

線程的幾種狀態(tài)

新建狀態(tài):線程剛創(chuàng)建,還沒(méi)有調(diào)用start方法之前

就緒狀態(tài):也叫臨時(shí)阻塞狀態(tài),當(dāng)調(diào)用了start方法后,具備cpu的執(zhí)行資格,等待cpu調(diào)度器輪詢的狀態(tài)

運(yùn)行狀態(tài):就緒狀態(tài)的線程,獲得了cpu的時(shí)間片,真正運(yùn)行的狀態(tài)

凍結(jié)狀態(tài):也叫阻塞狀態(tài),指的是該線程因某種原因放棄了cpu的執(zhí)行資格,暫時(shí)停止運(yùn)行的狀態(tài),比如調(diào)用了wait,sleep方法

死亡狀態(tài):線程執(zhí)行結(jié)束了,比如調(diào)用了stop方法

Synchronized 和 lock的區(qū)別

他們都是用來(lái)解決并發(fā)編程中的線程安全問(wèn)題的,不同的是

  • synchronized是一個(gè)關(guān)鍵字,依靠Jvm內(nèi)置語(yǔ)言實(shí)現(xiàn),底層是依靠指令碼來(lái)實(shí)現(xiàn);Lock是一個(gè)接口,它基于CAS樂(lè)觀鎖來(lái)實(shí)現(xiàn)的
  • synchronized在線程發(fā)生異常時(shí),會(huì)自動(dòng)釋放鎖,不會(huì)發(fā)生異常死鎖,Lock在異常時(shí)不會(huì)自動(dòng)釋放鎖,我們需要在finally中釋放鎖
  • synchronized是可重入,不可判斷,非公平鎖,Lock是可重入,可判斷的,可手動(dòng)指定公平鎖或者非公平鎖

你知道AQS嗎

AQS:AbstractQuenedSynchronizer抽象的隊(duì)列式同步器。是除了java自帶的synchronized關(guān)鍵字之外的鎖機(jī)制,它維護(hù)了一個(gè)volatile修飾的 int 類型的,state(代表共享資源)和一個(gè)FIFO線程等待隊(duì)列(多線程爭(zhēng)用資源被阻塞時(shí)會(huì)進(jìn)入此隊(duì)列)。

工作思想是如果被請(qǐng)求的資源空閑,也就是還沒(méi)有線程獲取鎖,將當(dāng)前請(qǐng)求資源的線程設(shè)置為有效的工作線程,并將共享資源設(shè)置為鎖定狀態(tài),如果請(qǐng)求的資源被占用,就將獲取不到鎖的線程加入隊(duì)列。

悲觀鎖和樂(lè)觀鎖

悲觀鎖和樂(lè)觀鎖,指的是看待并發(fā)同步問(wèn)題的角度

  • 悲觀鎖認(rèn)為,對(duì)同一個(gè)數(shù)據(jù)的并發(fā)操作,一定是會(huì)被其他線程同時(shí)修改的。所以在每次操作數(shù)據(jù)的時(shí)候,都會(huì)上鎖,這樣別人就拿不到這個(gè)數(shù)據(jù)。如果不加鎖,并發(fā)操作一定會(huì)出問(wèn)題。用陽(yáng)間的話說(shuō),就是總有刁民想害朕

  • 樂(lè)觀鎖認(rèn)為,對(duì)同一個(gè)數(shù)據(jù)的并發(fā)操作,是不會(huì)有其他線程同時(shí)修改的。它不會(huì)使用加鎖的形式來(lái)操作數(shù)據(jù),而是在提交更新數(shù)據(jù)的時(shí)候,判斷一下在操作期間有沒(méi)有其他線程修改了這個(gè)數(shù)據(jù)

悲觀鎖一般用于并發(fā)小,對(duì)數(shù)據(jù)安全要求高的場(chǎng)景,樂(lè)觀鎖一般用于高并發(fā),多讀少寫(xiě)的場(chǎng)景,通常使用版本號(hào)控制,或者時(shí)間戳來(lái)解決.

你知道什么是CAS嘛

CAS,compare and swap的縮寫(xiě),中文翻譯成比較并交換。它是樂(lè)觀鎖的一種體現(xiàn),CAS 操作包含三個(gè)操作數(shù) —— 內(nèi)存位置(V)、預(yù)期原值(A)和新值(B)。 如果內(nèi)存位置的值與預(yù)期原值相匹配,那么處理器會(huì)自動(dòng)將該位置值更新為新值 。否則,處理器不做任何操作。

Synchronized 加非靜態(tài)和靜態(tài)方法上的區(qū)別

實(shí)例方法上的鎖,鎖住的是這個(gè)對(duì)象實(shí)例,它不會(huì)被實(shí)例共享,也叫做對(duì)象鎖

靜態(tài)方法上的鎖,鎖住的是這個(gè)類的字節(jié)碼對(duì)象,它會(huì)被所有實(shí)例共享,也叫做類鎖

Synchronized(this) 和 Synchronized (User.class)的區(qū)別

Synchronized(this) 中,this代表的是該對(duì)象實(shí)例,不會(huì)被所有實(shí)例共享

Synchronized (User.class),代表的是對(duì)類加鎖,會(huì)被所有實(shí)例共享

Synchronized 和 volatitle 關(guān)鍵字的區(qū)別

這兩個(gè)關(guān)鍵字都是用來(lái)解決并發(fā)編程中的線程安全問(wèn)題的,不同點(diǎn)主要有以下幾點(diǎn)

第一:volatile的實(shí)現(xiàn)原理,是在每次使用變量時(shí)都必須重主存中加載,修改變量后都必須立馬同步到主存;synchronized的實(shí)現(xiàn)原理,則是鎖定當(dāng)前變量,讓其他線程處于阻塞狀態(tài)

第二:volatile只能修飾變量,synchronized用在修飾方法和同步代碼塊中

第三:volatile修飾的變量,不會(huì)被編譯器進(jìn)行指令重排序,synchronized不會(huì)限制指令重排序

第四:volatile不會(huì)造成線程阻塞,高并發(fā)時(shí)性能更高,synchronized會(huì)造成線程阻塞,高并發(fā)效率低

第五:volatile不能保證操作的原子性,因此它不能保證線程的安全,synchronized能保證操作的原子性,保證線程的安全

synchronized 鎖的原理

synchronized是基于JVM內(nèi)置鎖實(shí)現(xiàn),通過(guò)內(nèi)部對(duì)象Monitor(監(jiān)視器鎖)實(shí) 現(xiàn),基于進(jìn)入與退出Monitor對(duì)象實(shí)現(xiàn)方法與代碼塊同步,監(jiān)視器鎖的實(shí)現(xiàn)依賴 底層操作系統(tǒng)的Mutex lock(互斥鎖)實(shí)現(xiàn),它是一個(gè)重量級(jí)鎖性能較低,涉及到用戶態(tài)到內(nèi)核態(tài)的切換,會(huì)讓整個(gè)程序性能變得很差。

因此在JDK1.6及以后的版本中,增加了鎖升級(jí)的過(guò)程,依次為無(wú)鎖,偏向鎖,輕量級(jí)鎖,重量級(jí)鎖。而且還增加了鎖粗化,鎖消除等策略,這就節(jié)省了鎖操作的開(kāi)銷,提高了性能

synchronized 鎖升級(jí)原理

每個(gè)對(duì)象都擁有對(duì)象頭,對(duì)象頭由Mark World ,指向類的指針,以及數(shù)組長(zhǎng)度三部分組成,鎖升級(jí)主要依賴Mark Word中的鎖標(biāo)志位和釋放偏向鎖標(biāo)識(shí)位。

  • 偏向鎖(無(wú)鎖)

大多數(shù)情況下鎖不僅不存在多線程競(jìng)爭(zhēng),而且總是由同一線程多次獲得。偏向鎖的目的是在某個(gè)線程 獲得鎖之后(線程的id會(huì)記錄在對(duì)象的Mark Word鎖標(biāo)志位中),消除這個(gè)線程鎖重入(CAS)的開(kāi)銷,看起來(lái)讓這個(gè)線程得到了偏護(hù)。(第二次還是這個(gè)線程進(jìn)來(lái)就不需要重復(fù)加鎖,基本無(wú)開(kāi)銷),如果自始至終使用鎖的線程只有一個(gè),很明顯偏向鎖幾乎沒(méi)有額外開(kāi)銷,性能極高。

  • 輕量級(jí)鎖(CAS):

輕量級(jí)鎖是由偏向鎖升級(jí)來(lái)的,偏向鎖運(yùn)行在一個(gè)線程進(jìn)入同步塊的情況下,當(dāng)?shù)诙€(gè)線程加入鎖爭(zhēng)用的時(shí)候,偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖自旋鎖);沒(méi)有搶到鎖的線程將自旋,獲取鎖的操作。輕量級(jí)鎖的意圖是在沒(méi)有多線程競(jìng)爭(zhēng)的情況下,通過(guò)CAS操作嘗試將MarkWord鎖標(biāo)志位更新為指向LockRecord的指針,減少了使用重量級(jí)鎖的系統(tǒng)互斥量產(chǎn)生的性能消耗。

長(zhǎng)時(shí)間的自旋操作是非常消耗資源的,一個(gè)線程持有鎖,其他線程就只能在原地空耗CPU,執(zhí)行不了任何有效的任務(wù),這種現(xiàn)象叫做忙等(busy-waiting)

  • 重量級(jí)鎖:

如果鎖競(jìng)爭(zhēng)情況嚴(yán)重,某個(gè)達(dá)到最大自旋次數(shù)(10次默認(rèn))的線程,會(huì)將輕量級(jí)鎖升級(jí)為重量級(jí)鎖,重量級(jí)鎖則直接將自己掛起,在JDK1.6之前,synchronized直接加重量級(jí)鎖,很明顯現(xiàn)在得到了很好的優(yōu)化。

虛擬機(jī)使用CAS操作嘗試將MarkWord更新為指向LockRecord的指針,如果更新成功表示線程就擁有該對(duì)象的鎖;如果失敗,會(huì)檢查MarkWord是否指向當(dāng)前線程的棧幀,如果是,表示當(dāng)前線程已經(jīng)擁有這個(gè)鎖;如果不是,說(shuō)明這個(gè)鎖被其他線程搶占,此時(shí)膨脹為重量級(jí)鎖。

樂(lè)觀鎖的使用場(chǎng)景(數(shù)據(jù)庫(kù),ES)

場(chǎng)景一:ES中對(duì)version的控制并發(fā)寫(xiě)。

場(chǎng)景二:數(shù)據(jù)庫(kù)中使用version版本號(hào)控制來(lái)防止更新覆蓋問(wèn)題。

場(chǎng)景三:原子類中的CompareAndSwap操作

AtomicInterger怎么保證并發(fā)安全性的

通過(guò)CAS操作原理來(lái)實(shí)現(xiàn)的,就可見(jiàn)性和原子性兩個(gè)方面來(lái)說(shuō)

它的value值使用了volatile關(guān)鍵字修飾,也就保證了多線程操作時(shí)內(nèi)存的可見(jiàn)性

Unsafe這個(gè)類是一個(gè)很神奇的類,而compareAndSwapInt這個(gè)方法可以直接操作內(nèi)存,依靠的是C++來(lái)實(shí)現(xiàn)的,它調(diào)用的是Atomic類的cmpxchg函數(shù)。而這個(gè)函數(shù)的實(shí)現(xiàn)是跟操作系統(tǒng)有關(guān)的,比如在X86的實(shí)現(xiàn)就利用匯編語(yǔ)言的CPU指令lock cmpxchg,它在執(zhí)行后面的指令時(shí),會(huì)鎖定一個(gè)北橋信號(hào),最終來(lái)保證操作的原子性

什么是重入鎖,什么是自旋鎖,什么是阻塞

可重入鎖是指允許同一個(gè)線程多次獲取同一把鎖,比如一個(gè)遞歸函數(shù)里有加鎖操作

自旋鎖不是鎖,而是一種狀態(tài),當(dāng)一個(gè)線程嘗試獲取一把鎖的時(shí)候,如果這個(gè)鎖已經(jīng)被占用了,該線程就處于等待狀態(tài),并間隔一段時(shí)間后再次嘗試獲取的狀態(tài),就叫自旋

阻塞,指的是當(dāng)一個(gè)線程嘗試獲取鎖失敗了,線程就就進(jìn)行阻塞,這是需要操作系統(tǒng)切換CPU狀態(tài)的

你用過(guò)JUC中的類嗎,說(shuō)幾個(gè)

Lock鎖體系 ,ConcurrentHashMap ,Atomic原子類,如:AtomicInteger ;ThreadLoal ; ExecutorService

ThreadLocal的作用和原理

ThreadLocal,翻譯成中國(guó)話,叫做線程本地變量,它是為了解決線程安全問(wèn)題的,它通過(guò)為每個(gè)線程提供一個(gè)獨(dú)立的變量副本,來(lái)解決并發(fā)訪問(wèn)沖突問(wèn)題 - 簡(jiǎn)單理解它可以把一個(gè)變量綁定到當(dāng)前線程中,達(dá)到線程間數(shù)據(jù)隔離目的。

原理:ThredLocal是和當(dāng)前線程有關(guān)系的,每個(gè)線程內(nèi)部都有一個(gè)ThreadLocal.ThreadLocalMap類型的成員變量threadLocals,它用來(lái)存儲(chǔ)每個(gè)線程中的變量副本,key就是ThreadLocal變量,value就是變量副本。

當(dāng)我們調(diào)用get方法是,就會(huì)在當(dāng)前線程里的threadLocals中查找,它會(huì)以當(dāng)前ThreadLocal變量為key獲取當(dāng)前線程的變量副本

它的使用場(chǎng)景比如在spring security中,我們使用SecurityContextHolder來(lái)獲取SecurityContext,比如在springMVC中,我們通過(guò)RequestContextHolder來(lái)獲取當(dāng)前請(qǐng)求,比如在 zuul中,我們通過(guò)ContextHolder來(lái)獲取當(dāng)前請(qǐng)求

線程池的作用

請(qǐng)求并發(fā)高的時(shí)候,如果沒(méi)有線程池會(huì)出現(xiàn)線程頻繁創(chuàng)建和銷毀而浪費(fèi)性能的情況,同時(shí)沒(méi)辦法控制請(qǐng)求數(shù)量,所以使用了線程池后有如下好處

  • 主要作用是控制并發(fā)數(shù)量,線程池的隊(duì)列可以緩沖請(qǐng)求
  • 線程池可以實(shí)現(xiàn)線程的復(fù)用效果
  • 使用線程池能管理線程的生命周期

Executors創(chuàng)建四種線程池

  • CachedThreadPool:可緩存的線程池,它在創(chuàng)建的時(shí)候,沒(méi)有核心線程,線程最大數(shù)量是Integer最大值,最大空閑時(shí)間是60S

  • FixedThreadPool:固定長(zhǎng)度的線程池,它的最大線程數(shù)等于核心線程數(shù),此時(shí)沒(méi)有最大空閑時(shí)長(zhǎng)為0

  • SingleThreadPool:?jiǎn)蝹€(gè)線程的線程池,它的核心線程和最大線程數(shù)都是1,也就是說(shuō)所有任務(wù)都串行的執(zhí)行

  • ScheduledThreadPool:可調(diào)度的線程池,它的最大線程數(shù)是Integer的最大值,默認(rèn)最長(zhǎng)等待時(shí)間是10S,它是一個(gè)由延遲執(zhí)行和周期執(zhí)行的線程池

線程池的執(zhí)行流程

corePoolSize,maximumPoolSize,workQueue之間關(guān)系。

  1. 當(dāng)線程池中線程數(shù)小于corePoolSize時(shí),新提交任務(wù)將創(chuàng)建一個(gè)新線程(使用核心)執(zhí)行任務(wù),即使此時(shí)線程池中存在空閑線程。
  2. 當(dāng)線程池中線程數(shù)達(dá)到corePoolSize時(shí)(核心用完),新提交任務(wù)將被放入workQueue中,等待線程池中任務(wù)調(diào)度執(zhí)行 。
  3. 當(dāng)workQueue已滿,且maximumPoolSize > corePoolSize時(shí),新提交任務(wù)會(huì)創(chuàng)建新線程(非核心)執(zhí)行任務(wù)。
  4. 當(dāng)workQueue已滿,且提交任務(wù)數(shù)超過(guò)maximumPoolSize(線程用完,隊(duì)列已滿),任務(wù)由RejectedExecutionHandler處理。
  5. 當(dāng)線程池中線程數(shù)超過(guò)corePoolSize,且超過(guò)這部分的空閑時(shí)間達(dá)到keepAliveTime時(shí),回收這些線程。
  6. 當(dāng)設(shè)置allowCoreThreadTimeOut(true)時(shí),線程池中corePoolSize范圍內(nèi)的線程空閑時(shí)間達(dá)到keepAliveTime也將回收。

線程池執(zhí)行流程 : 核心線程 => 等待隊(duì)列 => 非核心線程 => 拒絕策略

線程池構(gòu)造器的7個(gè)參數(shù)

  • CorePoolSize:核心線程數(shù),它是不會(huì)被銷毀的

  • MaximumPoolSize :最大線程數(shù),核心線程數(shù)+非核心線程數(shù)的總和

  • KeepAliveTime:非核心線程的最大空閑時(shí)間,到了這個(gè)空閑時(shí)間沒(méi)被使用,非核心線程銷毀

  • Unit:空閑時(shí)間單位

  • WorkQueue:是一個(gè)BlockingQueue阻塞隊(duì)列,超過(guò)核心線程數(shù)的任務(wù)會(huì)進(jìn)入隊(duì)列排隊(duì)

  • ThreadFactory:它是一個(gè)創(chuàng)建新線程的工廠

  • Handler:拒絕策略,任務(wù)超過(guò)最大線程數(shù)+隊(duì)列排隊(duì)數(shù) ,多出來(lái)的任務(wù)該如何處理取決于Handler

線程池拒絕策略有幾種

拒絕策略,當(dāng)線程池任務(wù)超過(guò) 最大線程數(shù)+隊(duì)列排隊(duì)數(shù) ,多出來(lái)的任務(wù)該如何處理取決于Handler

  1. AbortPolicy丟棄任務(wù)并拋出RejectedExecutionException異常;
  2. DiscardPolicy丟棄任務(wù),但是不拋出異常;
  3. DiscardOldestPolicy丟棄隊(duì)列最前面的任務(wù),然后重新嘗試執(zhí)行任務(wù);
  4. CallerRunsPolicy由調(diào)用線程處理該任務(wù)

可以定義和使用其他種類的RejectedExecutionHandler類來(lái)定義拒絕策略。

你知道ScheduledThreadPool使用場(chǎng)景嗎

這是帶定時(shí)任務(wù)的線程池,EurekaClient拉取注冊(cè)表&心跳續(xù)約就是使用的這個(gè)線程池。

索引部分

什么是索引

索引是用來(lái)高效獲取數(shù)據(jù)的存儲(chǔ)結(jié)構(gòu)如同字典的目錄一樣,數(shù)據(jù)庫(kù)的索引通常使用b+tree來(lái)實(shí)現(xiàn),索引樹(shù)的節(jié)點(diǎn)和數(shù)據(jù)地址相關(guān)聯(lián),查詢的時(shí)候在索引樹(shù)種進(jìn)行高效搜索,然后根據(jù)數(shù)據(jù)地址獲取數(shù)據(jù)。索引提高了搜索的效率同時(shí)增加了索引維護(hù)的成本,濫用索引也會(huì)降低insert,update,delete的性能。

Mysql索引有哪些類型

普通索引:允許重復(fù)的值

唯一索引:不允許有重復(fù)的值

主鍵索引:數(shù)據(jù)庫(kù)自動(dòng)為我們的主鍵創(chuàng)建索引,如果我們沒(méi)有指定主鍵,它會(huì)根據(jù)沒(méi)有null的唯一索引創(chuàng)建主鍵索引,否則會(huì)默認(rèn)根據(jù)一個(gè)隱藏的rowId作為主鍵索引

全文索引,用來(lái)對(duì)文本域進(jìn)行索引,比如text,varchar,只針對(duì)MyISAM有效

索引方式有哪些

B+樹(shù)和hash,Myisam和innodb都不支持hash

Mysql的索引結(jié)構(gòu)原理

采用了B+樹(shù)的數(shù)據(jù)結(jié)構(gòu),采用B+樹(shù)的原因,B+樹(shù)是多叉樹(shù),適合存儲(chǔ)大量數(shù)據(jù),B+樹(shù)的數(shù)據(jù)存儲(chǔ)在葉子節(jié)點(diǎn),內(nèi)部節(jié)點(diǎn)只存鍵值,因此B+樹(shù)每次查詢都要走到葉子節(jié)點(diǎn), 查詢性能更穩(wěn)定,同時(shí)它的非葉子節(jié)點(diǎn)只存儲(chǔ)key,因此每個(gè)節(jié)點(diǎn)能存儲(chǔ)更多的key,樹(shù)的高度變的更低,查詢性能更快,而且它的葉子節(jié)點(diǎn)能夠形成一個(gè)鏈表,支持范圍查詢,排序 。

InnoDB的索引結(jié)構(gòu)和MyIsam的索引結(jié)構(gòu)有什么區(qū)別

他們都是用的B+樹(shù),不同的是

  • innodb的葉子節(jié)點(diǎn)存放的是數(shù)據(jù),myisam的葉子節(jié)點(diǎn)存放的是數(shù)據(jù)的地址

  • innodb中輔助索引的葉子節(jié)點(diǎn)存放的是主鍵索引的鍵值,myisam中輔助索引的葉子節(jié)點(diǎn)存放的也是數(shù)據(jù)的地址

  • innodb的索引和數(shù)據(jù)都存放到一個(gè)文件中,myisam的索引和數(shù)據(jù)分別存放到不同的文件中

哪些列不適合創(chuàng)建索引

不經(jīng)常查詢的列不適合創(chuàng)建索引

不出現(xiàn)在where中的字段不適合創(chuàng)建索引

離散度太低的字段不適合創(chuàng)建索引,比如性別

更新非常頻繁的字段不適合創(chuàng)建索引

哪些因素會(huì)造成索引失效

模糊查詢時(shí),通配符放到左邊的時(shí)候,會(huì)導(dǎo)致索引失效 比如 like ''%keyword%''

列是字符串類型,查詢條件沒(méi)有用引號(hào),會(huì)導(dǎo)致索引失效

使用了or,in,not in,not exist, !=等,會(huì)導(dǎo)致索引失效

查詢null值,會(huì)導(dǎo)致索引失效

還有mySQL認(rèn)為全表掃描會(huì)比索引查找快,就不會(huì)使用索引,比如表里只有一條記錄

什么是輔助索引&什么是覆蓋索引

除了主鍵索引之外的其他索引都叫輔助索引,也叫二級(jí)檢索。輔助索引的葉子節(jié)點(diǎn)存儲(chǔ)的是主鍵索引的鍵值,因此輔助索引掃描完之后還會(huì)掃描主鍵索引,這也叫回表

但是如果查詢的列恰好包含在輔助索引的鍵值中,就不會(huì)再回表了,這也叫覆蓋索引

InnoDB輔助索引的葉子節(jié)點(diǎn)也存數(shù)據(jù)嗎

InnoDB輔助索引的葉子節(jié)點(diǎn)存放的是,主鍵索引的鍵值

因此輔助索引掃描完還會(huì)掃描主鍵索引,也叫回表

但是如果查詢的列恰好包含在輔助索引的鍵值中,就不會(huì)再回表了,這也叫覆蓋索引

組合索引的匹配原則

組合索引向左匹配,我們應(yīng)該優(yōu)先選擇組合索引,因?yàn)閷?duì)覆蓋索引命中率更高,查詢性能更高,但是應(yīng)該考慮列的順序,因?yàn)榻M合索引會(huì)向左匹配

Like一定會(huì)讓索引失效嗎

不一定,比如:like "值%" 一樣可以使用索引,向左匹配,而 like "%值"或 "_值"就不能命中索引。

索引創(chuàng)建的原則有哪些

查詢較頻繁的列應(yīng)該考慮創(chuàng)建索引

不經(jīng)常查詢的列不適合創(chuàng)建索引

不出現(xiàn)在where中的字段不適合創(chuàng)建索引

離散度太低的字段不適合創(chuàng)建索引,比如性別

更新非常頻繁的字段不適合創(chuàng)建索引

數(shù)據(jù)庫(kù)優(yōu)化

哪些因素可能會(huì)造成數(shù)據(jù)庫(kù)性能問(wèn)題

不合理的商業(yè)需求,比如實(shí)時(shí)更新總注冊(cè)人數(shù),總交易額等等,應(yīng)該考慮不要實(shí)時(shí)

對(duì)于熱點(diǎn)數(shù)據(jù)的查詢并發(fā)太高,應(yīng)該考慮用緩存

數(shù)據(jù)庫(kù)結(jié)構(gòu)設(shè)計(jì)不合理,比如幾十個(gè)字段集中在一張表,應(yīng)該考慮分表

SQL語(yǔ)句有問(wèn)題,比如太多JOIN,很多不需要的字段也要全部查詢出來(lái),應(yīng)該考慮優(yōu)化SQL

硬件和網(wǎng)絡(luò)方面的影響

Mysql的執(zhí)行流程是怎么樣的

客戶端發(fā)起SQL查詢,首先通過(guò)連接器,它會(huì)檢查用戶的身份,包括校驗(yàn)賬戶密碼,權(quán)限

然后會(huì)查詢緩存,如果緩存命中直接返回,如果沒(méi)有命中再執(zhí)行后續(xù)操作,但是MySQL8.0之后已經(jīng)刪除了緩存功能

接下來(lái)到達(dá)分析器,主要檢查語(yǔ)法詞法,比如SQL有沒(méi)有寫(xiě)錯(cuò),總共有多少關(guān)鍵字,要查詢哪些東西

然后到達(dá)優(yōu)化器,他會(huì)以自己的方式優(yōu)化我們的SQL

最后到達(dá)執(zhí)行器,調(diào)用存儲(chǔ)引擎執(zhí)行SQL并返回結(jié)果

優(yōu)化SQL你從哪些方面著手

不需要的字段就不要查詢出來(lái)

小結(jié)果集驅(qū)動(dòng)大結(jié)果集,將能過(guò)率更多數(shù)據(jù)的條件寫(xiě)到前面

in和not in盡量不要用,會(huì)導(dǎo)致索引失效

避免在where中使用or鏈接條件,這會(huì)導(dǎo)致索引失效

給經(jīng)常要查詢的字段建立索引

考慮如果不需要事務(wù),并且主要查詢的化,可以考慮使用MyISAM存儲(chǔ)引擎

如果表數(shù)據(jù)量實(shí)在太龐大了,考慮分表

如何去定位慢SQL

通過(guò)druid連接池的內(nèi)置監(jiān)控來(lái)定位慢SQL

通過(guò)MySQL的慢查詢?nèi)罩静榭绰齋QL

通過(guò)show processlist,查看當(dāng)前數(shù)據(jù)庫(kù)SQL執(zhí)行情況來(lái)定位慢SQL

頁(yè)面上發(fā)起的一個(gè)查詢很慢,你怎么去優(yōu)化

首先看一下硬件和網(wǎng)絡(luò)層面,有沒(méi)有什么異常

然后分析代碼有沒(méi)有什么問(wèn)題,算法有沒(méi)有什么缺陷,比如多層嵌套循環(huán)

最后我們?cè)俣ㄎ坏铰齋QL,比如

  • 通過(guò)druid連接池的內(nèi)置監(jiān)控來(lái)定位慢SQL
  • 通過(guò)MySQL的慢查詢?nèi)罩静榭绰齋QL
  • 通過(guò)show processlist,查看當(dāng)前數(shù)據(jù)庫(kù)SQL執(zhí)行情況來(lái)定位慢SQL

定位到慢SQL再考慮優(yōu)化該SQL,比如說(shuō)

  • 不需要的字段就不要查詢出來(lái)
  • 小結(jié)果集驅(qū)動(dòng)大結(jié)果集,將能過(guò)率更多數(shù)據(jù)的條件寫(xiě)到前面
  • in和not in盡量不要用,會(huì)導(dǎo)致索引失效
  • 避免在where中使用or鏈接條件,這會(huì)導(dǎo)致索引失效
  • 考慮如果不需要事務(wù),并且主要查詢的化,可以考慮使用MyISAM存儲(chǔ)引擎

如果優(yōu)化SQL后還是很慢,可以考慮給查詢字段建索引來(lái)提升效率

如果建立索引了還是慢,看一下是不是數(shù)據(jù)量太龐大了,應(yīng)該考慮分表了

你如何看SQL有沒(méi)有命中索引

在SQL語(yǔ)句前加上explain,結(jié)果中的key就是實(shí)際用到的索引

mysql存儲(chǔ)引擎有哪些,有什么區(qū)別,如何選擇

主要有innodb,memory,myisam

innodb支持事務(wù),速度相對(duì)較慢,支持外鍵,不支持全文索引

myisam 速度相對(duì)較快,支持全文索引,不支持外鍵,不支持事務(wù),

memory不支持事務(wù),基于內(nèi)存讀寫(xiě),速度快,支持全文索引

如果對(duì)事務(wù)要求不高,而且是查詢?yōu)橹鳎紤]用myisam

如果對(duì)事務(wù)要求高,保存的都是重要的數(shù)據(jù),建議使用innodb,它也是默認(rèn)的存儲(chǔ)引擎

如果數(shù)據(jù)頻繁變化的,不需要持久化,可以使用memory

下面SQL如何優(yōu)化

一個(gè)sql : select sum(amount) from recharge ,來(lái)查詢總充值,recharge 表數(shù)據(jù)量達(dá)到了上千萬(wàn),怎么優(yōu)化

可以考慮建個(gè)匯總表來(lái)統(tǒng)計(jì)總充值,總訂單數(shù),總?cè)藬?shù)等等等

或者采用日?qǐng)?bào)表,月報(bào)表,年報(bào)表,使用定時(shí)任務(wù)進(jìn)行結(jié)算的方式來(lái)統(tǒng)計(jì)

或者看數(shù)據(jù)能不能使用ES搜索引擎來(lái)優(yōu)化,如果非得要在這個(gè)上千萬(wàn)的表中來(lái)查詢,那就采用分表

事務(wù)相關(guān)

什么是事務(wù)

一組對(duì)數(shù)據(jù)庫(kù)的操作,把這一組看成一個(gè)再給你,要么全部成功,要么全部失敗。

舉個(gè)栗子,比如A向B轉(zhuǎn)賬,A賬戶的錢少了,B賬戶的錢就應(yīng)該對(duì)應(yīng)增加,這就轉(zhuǎn)賬成功了,如果A賬戶的錢少了,由于網(wǎng)絡(luò)波動(dòng)等因素轉(zhuǎn)賬失敗了,B賬戶的錢沒(méi)有增加,那么A賬戶就應(yīng)該恢復(fù)成原先的狀態(tài)

事務(wù)的四大特性

原子性:指的是一個(gè)事務(wù)應(yīng)該是一個(gè)最小的無(wú)法分割的單元,不允許部分成功部分失敗,只能同時(shí)成功,或者同時(shí)失敗

持久性:一旦提交事務(wù),那么數(shù)據(jù)就應(yīng)該持久化,保證數(shù)據(jù)不會(huì)丟失

隔離性:兩個(gè)事務(wù)修改同一個(gè)數(shù)據(jù),必須按順序執(zhí)行,并且前一個(gè)事務(wù)如果未完成,那么中間狀態(tài)對(duì)另一個(gè)事務(wù)不可見(jiàn)

一致性:要求任何寫(xiě)到數(shù)據(jù)庫(kù)的數(shù)據(jù)都必須滿足預(yù)先定義的規(guī)則,它基于其他三個(gè)特性實(shí)現(xiàn)的

InnoDB如何保證原子性和持久性的

通過(guò)undo log 保證事務(wù)的原子性,redo log保證事務(wù)的持久性

undo log是回滾日志,記錄的是回滾需要的信息,redo log記錄的是新數(shù)據(jù)的備份

當(dāng)事務(wù)開(kāi)始時(shí),會(huì)先保存一個(gè)undo log,再執(zhí)行修改,并保存一個(gè)redo log,最后再提交事務(wù)。如果系統(tǒng)崩潰數(shù)據(jù)保存失敗了,可以根據(jù)redo log中的內(nèi)容,從新恢復(fù)到最新?tīng)顟B(tài),如果事務(wù)需要回滾,就根據(jù)undo log 回滾到之前的狀態(tài)

事務(wù)并發(fā)問(wèn)題有哪些

臟讀:事務(wù)A讀到了事務(wù)B修改還未提交的數(shù)據(jù)

幻讀,也叫虛讀:事務(wù)A兩次讀取相同條件的數(shù)據(jù),兩次查詢到的數(shù)據(jù)條數(shù)不一致,是由于事務(wù)B再這兩次查詢中插入或刪除了數(shù)據(jù)造成的

不可重復(fù)讀:事務(wù)A兩次讀取相同條件的數(shù)據(jù),結(jié)果讀取出不同的結(jié)果,是由于事務(wù)B再這兩次查詢中修改了數(shù)據(jù)造成的

第一類丟失更新:也叫回滾丟失,事務(wù)A和事務(wù)B更新同一條數(shù)據(jù),事務(wù)B先完成了修改,此時(shí)事務(wù)A異常終止,回滾后造成事務(wù)B的更新也丟失了

第二類丟失更新:也叫覆蓋丟失,事務(wù)A和事務(wù)B更新同一條數(shù)據(jù),事務(wù)B先完成了修改,事務(wù)A再次修改并提交,把事務(wù)B提交的數(shù)據(jù)給覆蓋了

事務(wù)隔離級(jí)別有哪些,分別能解決什么問(wèn)題

讀未提交:事務(wù)讀不阻塞其他事務(wù)的讀和寫(xiě),事務(wù)寫(xiě)阻塞其他事務(wù)的寫(xiě)但不阻塞讀,能解決第一類丟失更新的問(wèn)題,

讀已提交:事務(wù)讀不會(huì)阻塞其他事務(wù)讀和寫(xiě),事務(wù)寫(xiě)會(huì)阻塞其他事務(wù)的讀和寫(xiě),能解決第一類丟失更新,臟讀的問(wèn)題

可重復(fù)讀:事務(wù)讀會(huì)阻塞其他事務(wù)的寫(xiě)但不阻塞讀,事務(wù)寫(xiě)會(huì)阻塞其他事務(wù)讀和寫(xiě),能解決第一類丟失更新,臟讀,不可重復(fù)讀,第二類丟失更新問(wèn)題

串行化:使用表級(jí)鎖,讓事務(wù)一個(gè)一個(gè)的按順序執(zhí)行,能解決以上所有并發(fā)安全問(wèn)題

MySql的InnoDB是如何保證原子性的

利用了undo log實(shí)現(xiàn)的

undo log記錄了這些回滾需要的信息,當(dāng)事務(wù)執(zhí)行失敗或調(diào)用了rollback,導(dǎo)致事務(wù)需要回滾,就可以利用undo log中的信息將數(shù)據(jù)回滾到修改之前的樣子

MySql的InnoDB是如何保證持久性的

利用了redo log實(shí)現(xiàn)的

redo log記錄的是新數(shù)據(jù)的備份,在事務(wù)提交前,需要將Redo Log持久化,當(dāng)系統(tǒng)崩潰時(shí),可以根據(jù)redo Log的內(nèi)容,將所有數(shù)據(jù)恢復(fù)到最新的狀態(tài)

說(shuō)一下事務(wù)的執(zhí)行流程(Undolog+Redolog)

假設(shè)有A=1,B=2,兩個(gè)數(shù)據(jù),現(xiàn)在有個(gè)事務(wù)把A修改為3,B修改為4,那么事務(wù)的執(zhí)行流程:

當(dāng)事務(wù)開(kāi)始時(shí),會(huì)首先記錄A=1到undo log,記錄A=3到redo log,和記錄B=2到undo log,記錄B=4到redo log,然后再將redo log寫(xiě)入磁盤,最終事務(wù)提交

解釋一下事務(wù)并發(fā)丟失更新問(wèn)題,·如何解決

第一類丟失更新:也叫回滾丟失,事務(wù)A和事務(wù)B更新同一條數(shù)據(jù),事務(wù)B先完成了修改,此時(shí)事務(wù)A異常終止,回滾后造成事務(wù)B的更新也丟失了

第二類丟失更新:也叫覆蓋丟失,事務(wù)A和事務(wù)B更新同一條數(shù)據(jù),事務(wù)B先完成了修改,事務(wù)A再次修改并提交,把事務(wù)B提交的數(shù)據(jù)給覆蓋了

SQL標(biāo)準(zhǔn)中的四種隔離級(jí)別,讀未提交,讀已提交,可重復(fù)讀,串行化,都能解決第一類數(shù)據(jù)更新丟失問(wèn)題

對(duì)于第二類丟失更新問(wèn)題,可以使用悲觀鎖也就是串行化來(lái)解決,也可以使用樂(lè)觀鎖的方式,比如加一個(gè)版本號(hào)管理來(lái)解決

InnoDB事務(wù)隔離的實(shí)現(xiàn)原理是什么

隔離的實(shí)現(xiàn)主要利用了讀寫(xiě)鎖和MVCC機(jī)制

讀寫(xiě)鎖,要求在每次讀操作時(shí)需要獲取一個(gè)共享鎖,寫(xiě)操作時(shí)需要獲取一個(gè)寫(xiě)鎖。共享鎖之間不會(huì)產(chǎn)生互斥,共享鎖和寫(xiě)鎖,寫(xiě)鎖與寫(xiě)鎖之間會(huì)產(chǎn)生互斥。當(dāng)產(chǎn)生鎖競(jìng)爭(zhēng)時(shí),需要等一個(gè)操作的鎖釋放,另一個(gè)操作才能獲得鎖

MVCC,多版本并發(fā)控制,它是在讀取數(shù)據(jù)時(shí)通過(guò)一種類似快照的方式將數(shù)據(jù)保存下來(lái),不同的事務(wù)看到的快照版本是不一樣的,即使其他事務(wù)修改了數(shù)據(jù),但是對(duì)本事務(wù)仍然是不可見(jiàn)的,它只會(huì)看到第一次查詢到的數(shù)據(jù)

可重復(fù)讀是只在事務(wù)開(kāi)始的時(shí)候生成一個(gè)當(dāng)前事務(wù)全局性的快照,而讀提交則是每次執(zhí)行語(yǔ)句的時(shí)候都重新生成一次快照

數(shù)據(jù)庫(kù)集群

Mysql主從解決什么問(wèn)題,不能解決什么問(wèn)題?

MySQL主從同步,主負(fù)責(zé)寫(xiě),從負(fù)責(zé)讀,使用一主多從,能減輕讀的壓力

但是這不能解決寫(xiě)的壓力和主庫(kù)的單點(diǎn)故障,如果主庫(kù)的寫(xiě)并發(fā)高,可以做成多個(gè)主庫(kù)

MySql主從復(fù)制原理?

主要依靠binlog來(lái)實(shí)現(xiàn)的,它記錄的是所有的DDL,DML,TCL操作

當(dāng)主庫(kù)的數(shù)據(jù)發(fā)生改變時(shí),會(huì)將改變記錄保存到binlog中

從庫(kù)新開(kāi)一個(gè)線程將binlog內(nèi)容發(fā)送到從庫(kù)

從庫(kù)會(huì)發(fā)起一個(gè)I/O線程請(qǐng)求主庫(kù)的binlog,并保存到中繼日志中

從庫(kù)新開(kāi)一個(gè)SQL線程,讀取中繼日志并解析成具體操作,從而將主庫(kù)更新的內(nèi)容寫(xiě)到了從庫(kù)中

MySql主從配置步驟?

安裝mySQL主從客戶端,并配置my.ini

主庫(kù)需要配置授權(quán)從庫(kù)使用的賬號(hào)和權(quán)限,啟動(dòng)后可以通過(guò)show 主庫(kù)名 status查看狀態(tài),我們需要記錄File和Position的值,F(xiàn)ile是對(duì)應(yīng)的binlog文件名,position是當(dāng)前同步數(shù)據(jù)的最新行

從庫(kù)需要配置主庫(kù)鏈接信息,包括賬號(hào)密碼和binlog文件名和最新行,然后啟動(dòng)。通過(guò)show 從庫(kù)名 status 檢查同步狀態(tài),Slave_IO_Running 和 Slave_SQL_Running 的值都為YES,說(shuō)明大功告成了

什么是垂直分表,垂直分庫(kù),水平分表,水平分庫(kù)

垂直分表,可以理解為按列分表,如果一個(gè)表的字段太多了,可以按照使用頻率分成不同的表,優(yōu)化查詢性能。比如商品表可以分為商品類型表,商品詳情表,商品促銷表等等

垂直分庫(kù),為了減輕單個(gè)數(shù)據(jù)庫(kù)壓力,我們可以按照業(yè)務(wù)類型,拆分成多個(gè)數(shù)據(jù)庫(kù),比如分布式架構(gòu),不同的模塊可以有不同的數(shù)據(jù)庫(kù)

水平分表,可以理解為按行分表,如果一個(gè)表的數(shù)據(jù)有千萬(wàn)行,查詢性能太低,可以拆分成10張小表,每張表保存一百萬(wàn)行數(shù)據(jù)

水平分庫(kù),我們做了水平分表后,表數(shù)量太多了也會(huì)影響數(shù)據(jù)庫(kù)查詢效率,我們可以將這些表分到多個(gè)數(shù)據(jù)庫(kù)中

分庫(kù)分表后會(huì)出現(xiàn)哪些問(wèn)題?怎么解決

會(huì)產(chǎn)生分布式事務(wù),以前本地事務(wù)就能結(jié)局的問(wèn)題現(xiàn)在要用上Seata分布式事務(wù)

垂直分庫(kù)后跨庫(kù)查詢會(huì)導(dǎo)致一個(gè)查詢結(jié)果來(lái)源于兩個(gè)庫(kù),可能要用到多線程調(diào)用多個(gè)庫(kù)查詢

水平分庫(kù)后一個(gè)分頁(yè)查詢的某一頁(yè)可能來(lái)自兩個(gè)庫(kù),可以將兩個(gè)庫(kù)的數(shù)據(jù)合并之后再執(zhí)行SQL

水平分表后不同的表出現(xiàn)主鍵重復(fù),可以通過(guò)雪花算法來(lái)解決

兩個(gè)庫(kù)都用到同一個(gè)表,那這個(gè)公共表的維護(hù)可能要用到MySQL主從同步

你們公司使用的是什么技術(shù)來(lái)水平分表?還可以有什么技術(shù)?有什么區(qū)別?

使用的是sharding-jdbc來(lái)實(shí)現(xiàn)的,它是由java開(kāi)發(fā)的關(guān)系型數(shù)據(jù)庫(kù)中間件,讀寫(xiě)分離,分庫(kù)分表操作簡(jiǎn)單

TDDL,淘寶業(yè)務(wù)框架,復(fù)雜而且分庫(kù)分表的部分還沒(méi)有開(kāi)源

Mycat,要安裝額外的環(huán)境,不穩(wěn)定用起來(lái)復(fù)雜

MySQL官方提供的中間件,不支持大數(shù)據(jù)量的分不分表,性能較差

你們使用什么規(guī)則來(lái)分庫(kù)分表的?還有哪些規(guī)則?

垂直分庫(kù),按照業(yè)務(wù)進(jìn)行垂直分庫(kù),比如課程表和用戶表放到不同數(shù)據(jù)庫(kù)

垂直分表,把多字段表拆分少量字段表,比如將課程表分為課程類型表,課程詳情表,課程促銷表等

水平分表,把海量數(shù)據(jù)表拆分為多個(gè)小表

把商品業(yè)務(wù)進(jìn)行水平分庫(kù),可以對(duì)水平分庫(kù)后每一個(gè)數(shù)據(jù)庫(kù)服務(wù)器進(jìn)行集群

你從哪些方面去優(yōu)化你的數(shù)據(jù)庫(kù)?

如果是并發(fā)高,可以考慮緩存,如果是數(shù)據(jù)量大可以考慮分庫(kù)分表,具體如下:

首先應(yīng)該考慮垂直分庫(kù),不同的業(yè)務(wù)使用不同的數(shù)據(jù)庫(kù)

然后進(jìn)行垂直分表,按照使用頻率把字段多的表拆分成若干個(gè)表

對(duì)經(jīng)常查詢的列建立索引,提高查詢效率

設(shè)計(jì)冗余字段,減少join表的次數(shù)

SQL優(yōu)化,比如盡量使用索引查詢

對(duì)熱點(diǎn)數(shù)據(jù)應(yīng)該考慮做緩存,比如首頁(yè)展示匯總數(shù)據(jù)

從海量數(shù)據(jù)中查詢數(shù)據(jù)應(yīng)該考慮用全文檢索

如果查詢并發(fā)高,可以對(duì)mySQL做集群

如果數(shù)據(jù)量實(shí)在太大了,可以考慮水平分表,

水平分表后,表數(shù)量還是太多了,可以考慮水平分庫(kù)

Mysql的集群有哪些模式?

一主一從;一主多從;雙主;環(huán)形多主;級(jí)聯(lián)同步

單機(jī)優(yōu)化到極致了,可以怎么優(yōu)化?

可以考慮做集群,比如一主多從模式,然后對(duì)應(yīng)用做讀寫(xiě)分離

多機(jī)優(yōu)化有哪些方式?

分表,分庫(kù),主從同步

解釋一下分庫(kù)分表的含義?

垂直分表,可以理解為按列分表,如果一個(gè)表的字段太多了,可以按照使用頻率分成不同的表,優(yōu)化查詢性能。比如商品表可以分為商品類型表,商品詳情表,商品促銷表等等

垂直分庫(kù),為了減輕單個(gè)數(shù)據(jù)庫(kù)壓力,我們可以按照業(yè)務(wù)類型,拆分成多個(gè)數(shù)據(jù)庫(kù),比如分布式架構(gòu),不同的模塊可以有不同的數(shù)據(jù)庫(kù)

水平分表,可以理解為按行分表,如果一個(gè)表的數(shù)據(jù)有千萬(wàn)行,查詢性能太低,可以拆分成10張小表,每張表保存一百萬(wàn)行數(shù)據(jù)

水平分庫(kù),我們做了水平分表后,表數(shù)量太多了也會(huì)影響數(shù)據(jù)庫(kù)查詢效率,我們可以將這些表分到多個(gè)數(shù)據(jù)庫(kù)中

水平分表有哪些分表規(guī)則?

按照區(qū)間范圍分表,比如把用戶按照年齡分為新生代表,青年代表,老年代表

按照時(shí)間分表,比如按照年來(lái)分表,比如登錄日志,分成今年的表,去年的表。。

hash分表,通過(guò)將某一列的值比如id,通過(guò)一定的hash算法來(lái)算出對(duì)應(yīng)那張表

雪花算法,通過(guò)雪花算法生成id,根據(jù)id來(lái)算出對(duì)應(yīng)那張表

能簡(jiǎn)單說(shuō)一下你怎么使用shardingjdbc做讀寫(xiě)分離的嘛

首先導(dǎo)入相關(guān)的依賴

然后在配置文件中配置datasource,包括主從數(shù)據(jù)庫(kù)的名字,主從數(shù)據(jù)庫(kù)的連接信息,配置負(fù)載均衡

項(xiàng)目中就可以正常使用datasource了,自動(dòng)做讀寫(xiě)分離

能簡(jiǎn)單說(shuō)一下你怎么使用shardingjdbc做讀分庫(kù)分表的嘛

首先,要改造數(shù)據(jù)庫(kù),比如水平分表,水平分庫(kù)

在配置文件中,需要做如下配置

  • datasource名字,多個(gè)數(shù)據(jù)源就配多個(gè)datasource

  • 分庫(kù)策略,比如按照哪一列分庫(kù),分庫(kù)規(guī)則

  • 分表策略,比如哪些庫(kù)下面的哪些表,按照那一列分表,分表規(guī)則

  • 配置公共的表

然后項(xiàng)目中就可以正常使用了

JVM篇

你們用什么工具監(jiān)控JVM

jconsule, jvisualvm

JVM類加載流程

loading加載:class文件從磁盤加載到內(nèi)存中

verification驗(yàn)證:校驗(yàn)class文件,包括字節(jié)碼驗(yàn)證,元數(shù)據(jù)驗(yàn)證,符號(hào)引用驗(yàn)證等等

preparation準(zhǔn)備:靜態(tài)變量賦默認(rèn)值,只有final會(huì)賦初始值

resolution解析:常量池中符號(hào)引用,轉(zhuǎn)換成直接訪問(wèn)的地址

initializing初始化:靜態(tài)變量賦初始值

JVM類加載器有幾種類型,分別加載什么東西,用到什么設(shè)計(jì)模式?

  1. BootStrap ClassLoader 啟動(dòng)類加載器,加載<JAVA_HOME>\lib下的類

  2. Extenstion ClassLoader 擴(kuò)展類加載器,加載<JAVA_HOME>\lib\ext下的類

  3. Application ClassLoader 應(yīng)用程序類加載器,加載Classpath下的類

  4. 自定義類加載器

這里是用到了雙親委派模式,從上往下加載類,在這過(guò)程中只要上一級(jí)加載到了,下一級(jí)就不會(huì)加載了,這麼做的目的

  • 不讓我們輕易覆蓋系統(tǒng)提供功能
  • 也要讓我們擴(kuò)展我們功能。

JVM組成,以及他們的作用

運(yùn)行時(shí)數(shù)據(jù)區(qū):

  • 堆:存放對(duì)象的區(qū)域,所有線程共享

  • 虛擬機(jī)棧:對(duì)應(yīng)一個(gè)方法,線程私有的,存放局部變量表,操作數(shù)棧,動(dòng)態(tài)鏈接等等

  • 本地方法棧:對(duì)應(yīng)的是本地方法,在hotspot中虛擬機(jī)棧和本地方法棧是合為一體的

  • 程序計(jì)數(shù)器:確定指令的執(zhí)行順序

  • 方法區(qū):存放虛擬機(jī)加載的類的信息,常量,靜態(tài)變量等等,JDK1.8后,改為元空間

執(zhí)行引擎:

  • 即時(shí)編譯器,用來(lái)將熱點(diǎn)代碼編譯成機(jī)器碼(編譯執(zhí)行)

  • 垃圾收集,將沒(méi)用的對(duì)象清理掉

本地方法庫(kù):融合不同的編程語(yǔ)言為java所用

在JVM層面,一個(gè)線程是如何執(zhí)行的

線程執(zhí)行,每個(gè)方法都會(huì)形成一個(gè)棧幀進(jìn)行壓榨保存到虛擬機(jī)棧中,方法調(diào)用結(jié)束就回出棧。調(diào)用過(guò)程中創(chuàng)建的變量在虛擬機(jī)棧,對(duì)象實(shí)例存放在堆內(nèi)存中,棧中的變量指向了對(duì)中的內(nèi)存。當(dāng)方法執(zhí)行完成就出棧,創(chuàng)建的變量會(huì)被銷毀,堆中的對(duì)象等待GC。

程序內(nèi)存溢出了,如何定位問(wèn)題出在哪兒?

增加啟動(dòng)參數(shù)-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:\ 可以把內(nèi)存溢出的日志輸出到文件,然后通過(guò)JVM監(jiān)視工具VisualVM來(lái)分析日志,定位錯(cuò)誤所在。在linux服務(wù)器也可以使用命令: jmap -dump 來(lái)下載堆快照。

垃圾標(biāo)記算法

垃圾標(biāo)記算法有:引用計(jì)數(shù)和可達(dá)性算法

  • 引用計(jì)數(shù) : 給每一個(gè)對(duì)象添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器值加1;每當(dāng)有一個(gè)地方不再引用它時(shí),計(jì)數(shù)器值減1,這樣只要計(jì)數(shù)器的值不為0,就說(shuō)明還有地方引用它,它就不是無(wú)用的對(duì)象. 這種算法的問(wèn)題是當(dāng)某些對(duì)象之間互相引用時(shí),無(wú)法判斷出這些對(duì)象是否已死
  • GC Roots :找到一個(gè)對(duì)象作為 CG Root , 當(dāng)一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈相連(GC Roots到這個(gè)對(duì)象不可達(dá))時(shí),就說(shuō)明此對(duì)象是不可用的

垃圾回收算法

  • 標(biāo)記清除算法 :分為標(biāo)記和清除兩個(gè)階段,首先標(biāo)記出所有需要回收的對(duì)象,標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象 ;缺點(diǎn):標(biāo)記和清除兩個(gè)過(guò)程效率都不高;標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片。
  • 復(fù)制算法 :把內(nèi)存分為大小相等的兩塊,每次存儲(chǔ)只用其中一塊,當(dāng)這一塊用完了,就把存活的對(duì)象全部復(fù)制到另一塊上,同時(shí)把使用過(guò)的這塊內(nèi)存空間全部清理掉,往復(fù)循環(huán) ,缺點(diǎn):實(shí)際可使用的內(nèi)存空間縮小為原來(lái)的一半,比較適合

  • 標(biāo)記整理算法 :先對(duì)可用的對(duì)象進(jìn)行標(biāo)記,然后所有被標(biāo)記的對(duì)象向一段移動(dòng),最后清除可用對(duì)象邊界以外的內(nèi)存

  • 分代收集算法 :把堆內(nèi)存分為新生代和老年代,新生代又分為Eden區(qū)、From Survivor和To Survivor。一般新生代中的對(duì)象基本上都是朝生夕滅的,每次只有少量對(duì)象存活,因此新生代采用復(fù)制算法,只需要復(fù)制那些少量存活的對(duì)象就可以完成垃圾收集;老年代中的對(duì)象存活率較高,就采用標(biāo)記-清除和標(biāo)記-整理算法來(lái)進(jìn)行回收。

垃圾回收器有哪些

  • 新生代:Serial :一款用于新生代的單線程收集器,采用復(fù)制算法進(jìn)行垃圾收集。Serial進(jìn)行垃圾收集時(shí),不僅只用一條線程執(zhí)行垃圾收集工作,它在收集的同時(shí),所有的用戶線程必須暫停(Stop The World

  • 新生代:ParNew : ParNew就是一個(gè)Serial的多線程版本`,其它與Serial并無(wú)區(qū)別。ParNew在單核CPU環(huán)境并不會(huì)比Serial收集器達(dá)到更好的效果,它默認(rèn)開(kāi)啟的收集線程數(shù)和CPU數(shù)量一致,可以通過(guò)-XX:ParallelGCThreads來(lái)設(shè)置垃圾收集的線程數(shù)。

  • 新生代:Parallel Scavenge(掌握) Parallel Scavenge也是一款用于新生代的多線程收集器,與ParNew的不同之處是,ParNew的目標(biāo)是盡可能縮短垃圾收集時(shí)用戶線程的停頓時(shí)間,Parallel Scavenge的目標(biāo)是達(dá)到一個(gè)可控制的吞吐量.Parallel Old收集器以多線程,采用標(biāo)記整理算法進(jìn)行垃圾收集工作。

  • 老年代:Serial Old ,Serial Old收集器是Serial的老年代版本,同樣是一個(gè)單線程收集器,采用標(biāo)記-整理算法。

  • 老年代CMS收集器是一種以最短回收停頓時(shí)間為目標(biāo)的收集器,以“最短用戶線程停頓時(shí)間”著稱。整個(gè)垃圾收集過(guò)程分為4個(gè)步驟

    • 初始標(biāo)記:標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對(duì)象,速度較快
    • 并發(fā)標(biāo)記:進(jìn)行GC Roots Tracing,標(biāo)記出全部的垃圾對(duì)象,耗時(shí)較長(zhǎng)
    • 重新標(biāo)記:修正并發(fā)標(biāo)記階段引用戶程序繼續(xù)運(yùn)行而導(dǎo)致變化的對(duì)象的標(biāo)記記錄,耗時(shí)較短
    • 并發(fā)清除:用標(biāo)記-清除算法清除垃圾對(duì)象,耗時(shí)較長(zhǎng)

    整個(gè)過(guò)程耗時(shí)最長(zhǎng)的并發(fā)標(biāo)記和并發(fā)清除都是和用戶線程一起工作,所以從總體上來(lái)說(shuō),CMS收集器垃圾收集可以看做是和用戶線程并發(fā)執(zhí)行的。

  • 老年代:Parallel Old ,Parallel Old收集器是Parallel Scavenge的老年代版本,是一個(gè)多線程收集器,采用標(biāo)記-整理算法。可以與Parallel Scavenge收集器搭配,可以充分利用多核CPU的計(jì)算能力

  • 堆收集:G1 收集器, G1 收集器是jdk1.7才正式引用的商用收集器,現(xiàn)在已經(jīng)成為jdk1.9默認(rèn)的收集器。前面幾款收集器收集的范圍都是新生代或者老年代,G1進(jìn)行垃圾收集的范圍是整個(gè)堆內(nèi)存,它采用“化整為零”的思路,把整個(gè)堆內(nèi)存劃分為多個(gè)大小相等的獨(dú)立區(qū)域(Region)在每個(gè)Region中,都有一個(gè)Remembered Set來(lái)實(shí)時(shí)記錄該區(qū)域內(nèi)的引用類型數(shù)據(jù)與其他區(qū)域數(shù)據(jù)的引用關(guān)系(在前面的幾款分代收集中,新生代、老年代中也有一個(gè)Remembered Set來(lái)實(shí)時(shí)記錄與其他區(qū)域的引用關(guān)系),在標(biāo)記時(shí)直接參考這些引用關(guān)系就可以知道這些對(duì)象是否應(yīng)該被清除,而不用掃描全堆的數(shù)據(jù)

Jdk1.7.18新生代使用Parallel Scavenge,老年代使用Parallel Old

Minor GC和Full GC

新生代的回收稱為Minor GC,新生代的回收一般回收很快,采用復(fù)制算法,造成的暫停時(shí)間很短 ,而Full GC一般是老年代的回收,并伴隨至少一次的Minor GC,新生代和老年代都回收,而老年代采用標(biāo)記-整理算法這種GC每次都比較慢造成的暫停時(shí)間比較長(zhǎng)`,通常是Minor GC時(shí)間的10倍以上。盡量減少 Full GC

JVM優(yōu)化的目的是什么?

優(yōu)化程序的內(nèi)存使用大小,以及減少CG來(lái)減少程序的停頓來(lái)提升程序的性能。

堆怎么調(diào),棧怎么調(diào)

-Xms : 初始堆,1/64 物理內(nèi)存

-Xmx : 最大堆,1/4物理內(nèi)存

-Xmn :新生代大小

-Xss : 棧大小

設(shè)計(jì)模式

什么是單例,如何實(shí)現(xiàn)

一個(gè)類只能有一個(gè)實(shí)例,主要用于需要頻繁使用的對(duì)象避免頻繁初始化和銷毀來(lái)提高性能,或者資源需要相互通信的環(huán)境

主要實(shí)現(xiàn)方式有,餓漢模式,懶漢模式,枚舉,靜態(tài)內(nèi)部類

餓漢模式,是在類加載過(guò)程中就將這個(gè)單例對(duì)象實(shí)例化,需要將構(gòu)造方法私有化,定義一個(gè)成員變量并new一個(gè)該類的實(shí)例作為初始值,提供一個(gè)公共的靜態(tài)方法獲取這個(gè)實(shí)例

懶漢模式,是在使用時(shí)才創(chuàng)建這個(gè)單例對(duì)象,需要將構(gòu)造方法私有化,定義一個(gè)該類的成員變量不賦初始值,提供一個(gè)獲取實(shí)例的公共靜態(tài)方法。特別注意這個(gè)方法需要保證多線程環(huán)境下的并發(fā)安全性,可以通過(guò)DCL加volatile關(guān)鍵字來(lái)解決

枚舉,直接在枚舉中定義字段,它就是單例并且線程安全的

靜態(tài)內(nèi)部類,在類中搞一個(gè)靜態(tài)內(nèi)部類,在靜態(tài)內(nèi)部類中搞一個(gè)目標(biāo)類的靜態(tài)成員變量并且new一個(gè)實(shí)例作為初始值。然后在目標(biāo)類中定義一個(gè)獲取實(shí)例的靜態(tài)方法,方法返回的就是靜態(tài)內(nèi)部類中的成員變量。這種方式能保證線程安全,也能實(shí)現(xiàn)延遲加載。缺點(diǎn)是這種方式傳參不太方便

模板模式的作用

定義一個(gè)算法骨架,而將某個(gè)或多個(gè)具體的實(shí)現(xiàn)延遲到子類中,使得子類可以在不修改當(dāng)前算法的結(jié)構(gòu)情況下,重新定義當(dāng)前算法的某些特定步驟

比如考試中所有考生的試卷都一樣,答案由每個(gè)考生自己完成

什么是適配器模式

將不兼容的接口轉(zhuǎn)換為可兼容的接口的中間類

比如HandlerInterceptorAdapter ,我們定義攔截器時(shí)不需要覆寫(xiě)HandlerInterceptor中的所有方法,因?yàn)檫m配器類幫我們做了空實(shí)現(xiàn)。但JDK1.8之后,給接口中增加了默認(rèn)方法,可以有方法體,因此這些適配器類已經(jīng)失去作用了

什么是代理模式?有幾種代理?

不直接使用實(shí)際對(duì)象,通過(guò)調(diào)用代理對(duì)象間接調(diào)用實(shí)際對(duì)象,主要用作對(duì)實(shí)際對(duì)象的增強(qiáng),分為靜態(tài)代理,JDK動(dòng)態(tài)代理,CGLIB動(dòng)態(tài)代理

JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理的區(qū)別?

JDK動(dòng)態(tài)代理是jdk提供的,我們可以直接使用,而CGLIB需要導(dǎo)入第三方庫(kù)

JDK動(dòng)態(tài)代理是利用反射機(jī)制生成一個(gè)實(shí)現(xiàn)代理接口的匿名類,在調(diào)用目標(biāo)方法前調(diào)用InvokeHandler來(lái)處理

CGLIB動(dòng)態(tài)代理是先加載目標(biāo)類的class文件,然后修改其字節(jié)碼生成子類來(lái)實(shí)現(xiàn)的

常見(jiàn)的設(shè)計(jì)模式說(shuō)一下

單例模式:一個(gè)類只能有一個(gè)實(shí)例,分為餓漢模式(迫切加載)和懶漢模式(延遲加載)和枚舉。

工廠模式:隱藏了產(chǎn)品的復(fù)雜創(chuàng)建過(guò)程,實(shí)現(xiàn)生產(chǎn)功能的復(fù)用,讓產(chǎn)品生產(chǎn)更加高效。分為簡(jiǎn)單工廠(需要來(lái)回切換生產(chǎn)線),工廠方法(開(kāi)設(shè)新的生產(chǎn)線),抽象工廠(制定創(chuàng)建產(chǎn)品的接口,讓子工廠選擇創(chuàng)建哪種產(chǎn)品)

在Spring中各種的BeanFactory創(chuàng)建bean都用到了

模板模式:定義一個(gè)算法骨架或者算法的流程,而不同的實(shí)例實(shí)現(xiàn)方式不同,將某個(gè)或多個(gè)具體的實(shí)現(xiàn)延遲到子類中,比如RedisTemplate實(shí)現(xiàn)了RedisOperations,ElasticSearchTemplate實(shí)現(xiàn)了ElasticsearchOperations

代理模式:不直接使用實(shí)際對(duì)象,通過(guò)調(diào)用代理對(duì)象間接調(diào)用實(shí)際對(duì)象,主要用作對(duì)實(shí)際對(duì)象的增強(qiáng),分為靜態(tài)代理,JDK動(dòng)態(tài)代理,CGLIB動(dòng)態(tài)代理比如Spring的AOP原理就是動(dòng)態(tài)代理,當(dāng)目標(biāo)對(duì)象實(shí)現(xiàn)了接口會(huì)使用JDK動(dòng)態(tài)代理,沒(méi)有實(shí)現(xiàn)接口會(huì)使用CGLIB動(dòng)態(tài)代理

適配器模式:將不兼容的接口轉(zhuǎn)換為可兼容的接口的中間類,比如HandlerInterceptorAdapter ,我們定義攔截器時(shí)不需要覆寫(xiě)HandlerInterceptor中的所有方法,因?yàn)檫m配器類幫我們做了空實(shí)現(xiàn)。但JDK1.8之后,給接口中增加了默認(rèn)方法,可以有方法體,因此這些適配器類已經(jīng)失去作用了

觀察者模式:當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新,比如Spring中的ApplicationListener

數(shù)據(jù)結(jié)構(gòu)

數(shù)據(jù)結(jié)構(gòu)有哪幾種分類

按照邏輯結(jié)構(gòu)分

  • 集合:沒(méi)有相互關(guān)系的一堆數(shù)據(jù)
  • 線性結(jié)構(gòu):元素存在一對(duì)一的相互關(guān)系
  • 樹(shù)形結(jié)構(gòu):元素存在一對(duì)多的相互關(guān)系
  • 圖形結(jié)構(gòu):元素存在多對(duì)多的相互關(guān)系

按照物理結(jié)構(gòu)分

  • 順序存儲(chǔ)結(jié)構(gòu):用一組地址連續(xù)的存儲(chǔ)空間依次存儲(chǔ)線性表的數(shù)據(jù)元素,也叫順序存儲(chǔ)結(jié)構(gòu),比如數(shù)組

  • 鏈接存儲(chǔ)結(jié)構(gòu):用一組任意的存儲(chǔ)空間來(lái)存儲(chǔ)線性表中的數(shù)據(jù)元素,不要求相鄰元素在物理位置上也相鄰,比如鏈表

  • 數(shù)據(jù)索引存儲(chǔ)結(jié)構(gòu):建立附加的索引來(lái)標(biāo)識(shí)節(jié)點(diǎn)的地址,通過(guò)索引,可以很快檢索數(shù)據(jù)

  • 數(shù)據(jù)散列存儲(chǔ)結(jié)構(gòu):將數(shù)據(jù)元素的存儲(chǔ)位置與關(guān)鍵字之間建立確定的對(duì)應(yīng)關(guān)系,加快查找的速度,又叫hash存儲(chǔ)

數(shù)組和鏈表在內(nèi)存中的存儲(chǔ)結(jié)構(gòu)有什么區(qū)別

數(shù)組在內(nèi)存中是一組連續(xù)的存儲(chǔ)空間,它隨機(jī)存取元素性能很高,但是插入和刪除操作,需要移動(dòng)其他元素,因此性能很低

鏈表在內(nèi)存中的存儲(chǔ)空間可以是不連續(xù)的,而在每一個(gè)元素中都保存相鄰節(jié)點(diǎn)的指針,因此它的存儲(chǔ)密度相對(duì)較小,查找的性能低,因?yàn)樾枰獜牡谝粋€(gè)元素依次遍歷,但是它的插入和刪除操作性能很高,因?yàn)樗恍枰苿?dòng)節(jié)點(diǎn),只需要改變相鄰節(jié)點(diǎn)指針就行了,同時(shí)它更容易造成內(nèi)存的碎片化

說(shuō)一下散列存儲(chǔ)(Hash存儲(chǔ)) , 什么是Hash沖突 , 有什么解決方案

散列存儲(chǔ),它通過(guò)把關(guān)鍵碼的值映射到表中的一個(gè)位置,來(lái)提高查詢的速度。而這個(gè)映射函數(shù)叫做散列函數(shù)。

哈希沖突,也叫哈希碰撞,指的是兩個(gè)不同的值,計(jì)算出了相同的hash,也就是兩個(gè)不同的數(shù)據(jù)計(jì)算出同一個(gè)下標(biāo),通常解決方案有:

  • 拉鏈法,把哈希碰撞的元素指向一個(gè)鏈表

  • 開(kāi)放尋址法,把產(chǎn)生沖突的哈希值作為值,再進(jìn)行哈希運(yùn)算,直到不沖突

  • 再散列法,就是換一種哈希算法重來(lái)一次

  • 建立公共溢出區(qū),把哈希表分為基本表和溢出表,將產(chǎn)生哈希沖突的元素移到溢出表

說(shuō)說(shuō) 數(shù)組,鏈表,循環(huán),嵌套循環(huán)的時(shí)間復(fù)雜度

時(shí)間復(fù)雜度是用來(lái)度量算法執(zhí)行的時(shí)間長(zhǎng)短,通常我們用O(f(n))漸進(jìn)時(shí)間復(fù)雜度來(lái)衡量,比如說(shuō)

  • 要在 hash 表中找到一個(gè)元素就是 O(1)
  • 要在無(wú)序數(shù)組中找到一個(gè)元素就是 O(n)
  • 訪問(wèn)數(shù)組的第 n 個(gè)元素是 O(1)
  • 二分搜索的時(shí)間復(fù)雜度最好的情況是 O(1),最壞情況(平均情況)下 O(log n)
  • 訪問(wèn)鏈表的第 n 個(gè)元素是 O(n)
  • 一個(gè)For循環(huán)是O(n)
  • 兩個(gè)For循環(huán)嵌套是O(n2)
  • 三個(gè)Foreach嵌套是O(n3)

JDK中線性結(jié)構(gòu)的集合有哪些

數(shù)組:按照順序物理結(jié)構(gòu)存儲(chǔ),ArrayList

鏈表:按照鏈?zhǔn)轿锢斫Y(jié)構(gòu)存儲(chǔ),LinkedList

棧:LIFO后進(jìn)先出的線性存儲(chǔ)結(jié)構(gòu),分為用數(shù)組實(shí)現(xiàn)的順序棧,用鏈表實(shí)現(xiàn)的鏈棧

隊(duì)列:FIFO先進(jìn)先出的線性存儲(chǔ)結(jié)構(gòu),分為順序隊(duì)列和鏈?zhǔn)疥?duì)列

串:特殊的線性存儲(chǔ)結(jié)構(gòu),String,StringBuffer,StringBuilder

你說(shuō)一下樹(shù)形結(jié)構(gòu)對(duì)比線性結(jié)構(gòu)的優(yōu)勢(shì)

線性結(jié)構(gòu),對(duì)于大量的輸入數(shù)據(jù),訪問(wèn)時(shí)間很長(zhǎng),效率很低,樹(shù)形結(jié)構(gòu)的優(yōu)勢(shì)在于它查找數(shù)據(jù)性能很高

說(shuō)一下樹(shù)的分類,以及你對(duì)它們的理解

樹(shù)有二叉樹(shù),多叉樹(shù),他們特點(diǎn)如下

  • 二叉樹(shù):樹(shù)中任意節(jié)點(diǎn)最多只有兩個(gè)分叉的樹(shù),它又分為二叉排序樹(shù),平衡二叉樹(shù),赫夫曼樹(shù),紅黑樹(shù)

  • 二叉排序樹(shù),它是一個(gè)有序的二叉樹(shù),優(yōu)勢(shì)在于查找插入數(shù)據(jù)的性能很高,但是可能會(huì)出現(xiàn)傾斜而變成數(shù)組

  • 平衡二叉樹(shù),二叉排序樹(shù)進(jìn)化形態(tài),要求任何節(jié)點(diǎn)的兩顆字?jǐn)?shù)高度差不大于1。它的查詢性能很高,但是每次增刪元素,會(huì)重排序?qū)е滦阅艿?/p>

  • 紅黑樹(shù),自平衡二叉樹(shù),要求根節(jié)點(diǎn)和葉子節(jié)點(diǎn)是黑色,其他節(jié)點(diǎn)紅黑交替,在任何一個(gè)子樹(shù)中,從根節(jié)點(diǎn)向下走到空姐點(diǎn)的路徑經(jīng)過(guò)的黑節(jié)點(diǎn)數(shù)相同。從而保證了平衡。它的查詢性能比平衡二叉樹(shù)稍低,插入和刪除元素的性能大幅提高。

多叉樹(shù):解決二叉樹(shù)存儲(chǔ)大規(guī)模數(shù)據(jù)時(shí),深度過(guò)大而導(dǎo)致IO性能低,查詢效率低的問(wèn)題,常見(jiàn)有B樹(shù)和B+樹(shù),字典樹(shù),后綴樹(shù)等等

  • B樹(shù),自平衡的樹(shù),一個(gè)節(jié)點(diǎn)可以存儲(chǔ)多個(gè)key,和擁有key數(shù)量+1個(gè)分叉,適用于讀寫(xiě)相對(duì)大的數(shù)據(jù)塊,比如文件系統(tǒng),數(shù)據(jù)庫(kù)索引。因?yàn)橄鄬?duì)二叉樹(shù)來(lái)說(shuō),節(jié)點(diǎn)存儲(chǔ)key越多,分叉越多,需要的節(jié)點(diǎn)越少,樹(shù)高越矮,IO次數(shù)少,查詢效率越高。

  • B+樹(shù),B樹(shù)升級(jí)版,它的內(nèi)部節(jié)點(diǎn)只存儲(chǔ)key,不存儲(chǔ)具體數(shù)據(jù),葉子節(jié)點(diǎn)存放key和具體數(shù)據(jù)。這就使得每個(gè)節(jié)點(diǎn)可以存更多的key,樹(shù)的高度更低,查詢更快,同時(shí)它每次查詢都會(huì)到葉子節(jié)點(diǎn),查詢速度更穩(wěn)定。并且所有的葉子節(jié)點(diǎn)會(huì)組成一個(gè)有序鏈表,方便區(qū)間查詢

有是二叉樹(shù)為什么要出現(xiàn)多叉樹(shù)

因?yàn)槎鏄?shù)在大規(guī)模的數(shù)據(jù)存儲(chǔ)中,樹(shù)會(huì)高的沒(méi)譜,這會(huì)導(dǎo)致IO讀寫(xiě)過(guò)于頻繁,查詢效率低下

多叉樹(shù)可以解決這個(gè)問(wèn)題,它每層可以存放更多的數(shù)據(jù),因此能大幅度降低樹(shù)的深度,提高查詢性能

B-tree和b+tree的區(qū)別

一是節(jié)點(diǎn)存儲(chǔ)內(nèi)容上的區(qū)別:B樹(shù)每個(gè)節(jié)點(diǎn)都可以存放key,存放數(shù)據(jù),而B(niǎo)+樹(shù)所有內(nèi)部節(jié)點(diǎn)只存放key,葉子節(jié)點(diǎn)存放key和數(shù)據(jù),因此它的節(jié)點(diǎn)能存放更多數(shù)據(jù),降低樹(shù)高,查詢性能更快

二是B+樹(shù)所有的葉子節(jié)點(diǎn)會(huì)構(gòu)成一個(gè)鏈表結(jié)構(gòu),方便區(qū)間查找和排序

說(shuō)一下ES用到了什么數(shù)據(jù)結(jié)構(gòu)

ES是使用了數(shù)據(jù)索引存儲(chǔ)結(jié)構(gòu),它是通過(guò)為關(guān)鍵字建立索引,通過(guò)索引找到對(duì)應(yīng)的數(shù)據(jù),這種索引也叫倒排索引,可以實(shí)現(xiàn)快速檢索

五.項(xiàng)目部分

瀏覽器輸入一個(gè)域名,它是怎么去執(zhí)行的?

  1. 首先帶著域名去hosts文件中看有沒(méi)有配置對(duì)應(yīng)的本地域名,如果有就以配置的ip進(jìn)行訪問(wèn)
  2. 如果hosts沒(méi)有配置,就會(huì)請(qǐng)求DNS服務(wù)器解析域名得到對(duì)應(yīng)的IP然后發(fā)起訪問(wèn)
  3. 這時(shí)候請(qǐng)求就會(huì)打到服務(wù)器上可能是Nginx也有可能直接打到Tomcat.

請(qǐng)求在你的項(xiàng)目中是怎么執(zhí)行的?

后端使用zuul網(wǎng)關(guān),請(qǐng)求先到達(dá)zuul網(wǎng)關(guān),zuul做登錄檢查,zuul網(wǎng)關(guān)底層整合ribbon把請(qǐng)求路由到下游微服務(wù),服務(wù)之間使用OpenFeign進(jìn)行通信。執(zhí)行成功后原路返回結(jié)果。

如果zuul網(wǎng)關(guān)掛了怎么辦?

可以做zuul集群,使用Nginx做負(fù)載均衡到zuul集群,然后Nginx可以采用雙機(jī)主備,或者雙機(jī)互備做集群防止單點(diǎn)故障。如果并發(fā)非常高可以加上LVS做負(fù)載。

如果有人用腳本刷你們的短信接口怎么辦

首先,可以設(shè)置圖形驗(yàn)證碼,流量錯(cuò)峰

其次,可以獲取請(qǐng)求的ip地址,手機(jī)號(hào),發(fā)送時(shí)間,并保存到發(fā)送短信記錄的日志中,對(duì)于短時(shí)間多次請(qǐng)求的ip地址,手機(jī)號(hào),可以攔截不執(zhí)行發(fā)送手機(jī)驗(yàn)證碼

再次,可以設(shè)置單位時(shí)間內(nèi)發(fā)送短信的總數(shù)量,比如設(shè)定1秒最多只發(fā)送10條驗(yàn)證碼。但這種方式會(huì)降低并發(fā)性

非對(duì)稱加密,什么是數(shù)字簽名

非對(duì)稱加密是一種算法,指的是加密和解密時(shí)使用不同的密鑰,其中私鑰不可公開(kāi),公鑰可以公開(kāi)。

數(shù)字簽名就是在非對(duì)稱加密的基礎(chǔ)上,使用私鑰加密,公鑰解密,主要用來(lái)防止數(shù)據(jù)被篡改,實(shí)現(xiàn)安全傳輸?shù)哪康?/p>

Oauth2的四種授權(quán)模式

oauth協(xié)議是一個(gè)安全的開(kāi)放授權(quán)標(biāo)準(zhǔn),與傳統(tǒng)的授權(quán)方式相比,它不會(huì)使第三方觸及到用戶的賬號(hào)信息,比如用戶名,密碼。Oauth2有四種授權(quán)模式

一、授權(quán)碼模式,它是功能最完整,流程最嚴(yán)密的授權(quán)模式

二、簡(jiǎn)化模式,直接從前端渠道獲取token,容易受安全攻擊

三、用戶名密碼模式,使用用戶名和密碼登錄的應(yīng)用,比如桌面APP

四、客戶端憑證模式,用戶直接向客戶端認(rèn)證,客戶端以自己的名義向第三方索取服務(wù)

要求每天早上 1點(diǎn)統(tǒng)計(jì)前一天的平臺(tái)注冊(cè)人數(shù),怎么做

使用定時(shí)任務(wù)每日結(jié)算即可。把結(jié)算的數(shù)據(jù)保存到一個(gè)統(tǒng)計(jì)表中

使用Quzrtz定時(shí)任務(wù)做訂單超時(shí)關(guān)單有什么問(wèn)題

數(shù)據(jù)量大的時(shí)候,定時(shí)任務(wù)掃描表性能會(huì)很差,而且多數(shù)都是空掃描,還有延遲問(wèn)題,

對(duì)于我們的小型項(xiàng)目,可以使用quartz定時(shí)器,使用起來(lái)也很簡(jiǎn)單方便,但如果是高并發(fā),比如秒殺等業(yè)務(wù),可以使用RabbitMQ的延遲隊(duì)列來(lái)實(shí)現(xiàn),也可以使用Redis來(lái)做延遲隊(duì)列。

講一下你做過(guò)的比較復(fù)雜的業(yè)務(wù)

省略...

什么是RBAC , 相關(guān)表怎么設(shè)計(jì)的?

RBAC:Role-Based Access Control首字母縮寫(xiě),意為基于角色的訪問(wèn)控制。基本思想是對(duì)系統(tǒng)操作的各種權(quán)限不是直接授予具體的用戶,而是在用戶集合與權(quán)限集合之間建立一個(gè)角色集合。

將權(quán)限與角色相關(guān)聯(lián),用戶通過(guò)成為適當(dāng)角色的成員而得到這些角色的權(quán)限。極大地簡(jiǎn)化了權(quán)限的管理。這樣管理都是層級(jí)相互依賴的,權(quán)限賦予給角色,而把角色又賦予用戶,這樣的權(quán)限設(shè)計(jì)很清楚,管理起來(lái)很方便。

實(shí)現(xiàn)RBAC,需要將用戶對(duì)權(quán)限的多對(duì)多關(guān)系,轉(zhuǎn)化為用戶對(duì)角色,角色對(duì)權(quán)限的多對(duì)多關(guān)系,因此在數(shù)據(jù)庫(kù)中,需要在用戶,角色,權(quán)限中分別加入中間表,即用戶表,用戶和角色關(guān)系表,角色表,角色和權(quán)限關(guān)系表,權(quán)限表

在VUE中,什么是MVVM

MVVM,Model–View–ViewModel首字母縮寫(xiě),是一種軟件架構(gòu)模式。

其中Model指的是模型,包括數(shù)據(jù)和一些基本操作

View指的是視圖,頁(yè)面渲染結(jié)果

ViewModel指的是模型與視圖間的雙向操作

MVVM的思想就是數(shù)據(jù)模型和視圖的雙向綁定,只要數(shù)據(jù)變化,視圖會(huì)跟著變化,只要視圖被修改,數(shù)據(jù)也會(huì)跟者變化

講幾個(gè)VUE的指令

v-text:給元素填充純文本內(nèi)容

v-html:給元素填充內(nèi)容,與v-text的區(qū)別是它會(huì)把內(nèi)容的html符號(hào)進(jìn)行渲染

v-for:遍歷數(shù)字,字符串,數(shù)組,對(duì)象

v-bind:將data中的數(shù)據(jù)綁定到標(biāo)簽上,作為標(biāo)簽的屬性

v-model:創(chuàng)建雙向綁定,表單的值被修改時(shí)會(huì)自動(dòng)修改data中的數(shù)據(jù),data中的值變化時(shí)頁(yè)面也會(huì)被修改

v-show:根據(jù)表達(dá)式的真假值,切換元素的css屬性

v-if:根據(jù)表達(dá)式的真假值,銷毀或重建元素

v-on:綁定事件

webpack的作用

VUE項(xiàng)目需要打包后才能部署

首先,它可以將ES6等高級(jí)語(yǔ)法,編譯成各個(gè)瀏覽器都認(rèn)識(shí)的語(yǔ)法

其次,它可以將相互依賴的許多散碎文件搞成一個(gè)整體,提高網(wǎng)頁(yè)訪問(wèn)的效率

再次,它可以將代碼壓縮,減小代碼體積

Vue中定義組件分為幾種,有什么區(qū)別

組件是一種自定義的元素標(biāo)簽,可以對(duì)功能封裝,提高代碼復(fù)用性,分為全局組件和局部組件兩種

  • 全局組件,是在所有vue掛載的標(biāo)簽中都有效,

  • 局部組件,只在當(dāng)前vue所掛載的標(biāo)簽中有效

講一下你用過(guò)ElementUI的哪些組件

基礎(chǔ)組件,比如按鈕Button,圖標(biāo)Icon

表單組件:比如表單Form,單選框Radio,多選框Checkbox,輸入框Input,選擇器Select,級(jí)聯(lián)選擇器Cascader

其他組件:比如Dialog對(duì)話框,消息提示Message

你們Redis做登錄是怎么處理登錄信息過(guò)期的?

給保存在Redis中的token設(shè)置過(guò)期時(shí)間來(lái)處理登錄過(guò)期的,為了防止已登錄用戶在訪問(wèn)后臺(tái)時(shí)突然遭遇登錄過(guò)期的情況,我們?cè)诤笈_(tái)接收到用戶訪問(wèn)時(shí),重新設(shè)置token的過(guò)期時(shí)間寫(xiě)入Redis,則用戶訪問(wèn)期間就不會(huì)突然過(guò)期了

講一下你們的登錄實(shí)現(xiàn)方案

當(dāng)用戶第一次發(fā)起登錄請(qǐng)求,后臺(tái)生成一個(gè)token保存到Redis中

將生成的token返回給用戶端

用戶端使用用瀏覽器中的localStorage保存token

通過(guò)axios的攔截器,給每次請(qǐng)求的請(qǐng)求頭都加上token

服務(wù)端收到token,就能在Redis中找到對(duì)應(yīng)的數(shù)據(jù)

三方登錄流程講一下

1.用戶發(fā)起微信登錄請(qǐng)求

2.后端獲取請(qǐng)求二維碼的連接,重定向到掃碼界面

3.用戶使用微信掃一掃并同意授權(quán)

4.后端回調(diào)獲取授權(quán)碼,并將授權(quán)碼作為參數(shù),重定向到前端跳轉(zhuǎn)頁(yè)面

5.前端將授權(quán)碼返回后端,后端根據(jù)授權(quán)碼獲取token

6.后端根據(jù)token獲取openId

7.根據(jù)openId查詢微信用戶表

  • 如果查到有用戶信息,且已關(guān)聯(lián)本地賬戶,就默認(rèn)登錄

  • 如果有查到用戶信息,但沒(méi)有關(guān)聯(lián)本地賬戶,就跳轉(zhuǎn)本地賬戶綁定頁(yè)面,

  • 如果沒(méi)有查到用戶信息,就向微信平臺(tái)發(fā)起請(qǐng)求查詢用戶基本信息,添加到微信用戶信息表,再跳轉(zhuǎn)本地賬戶綁定頁(yè)面

8.執(zhí)行綁定邏輯時(shí),根據(jù)手機(jī)號(hào)判斷是否有本地賬戶,如果有就直接綁定,如果沒(méi)有就自動(dòng)注冊(cè)再綁定,綁定成功后就默認(rèn)登錄

講一下什么是非對(duì)稱加密,什么是數(shù)字簽名,數(shù)字簽名的作用是什么?

非對(duì)稱加密是一種算法,指的是加密和解密時(shí)使用不同的密鑰,其中私鑰不可公開(kāi),公鑰可以公開(kāi)。

數(shù)字簽名就是在非對(duì)稱加密的基礎(chǔ)上,使用私鑰加密,公鑰解密,主要用來(lái)防止數(shù)據(jù)被篡改,實(shí)現(xiàn)安全傳輸?shù)哪康?/p>

京東的首頁(yè)的商品分類,讓你設(shè)計(jì)表,你怎么設(shè)計(jì)

首先可以看出表的結(jié)構(gòu)是自關(guān)聯(lián),三層的樹(shù)狀結(jié)構(gòu),分類表的字段可以有主鍵id,商品名,創(chuàng)建時(shí)間,修改時(shí)間,上架時(shí)間,下架時(shí)間,商品數(shù)量,排序,圖標(biāo),父級(jí)id

如何查詢出樹(shù)狀結(jié)構(gòu)的課程分類數(shù)據(jù)

首先,在entity中加入子分類字段children

查詢方式有四種

  • 第一,使用嵌套for循環(huán),循環(huán)體內(nèi)查詢每一層級(jí)的數(shù)據(jù),并關(guān)聯(lián)到children。當(dāng)然這也可以使用遞歸函數(shù)來(lái)實(shí)現(xiàn)

  • 第二,使用mybatis的嵌套查詢,也就是主查詢加額外子sql查詢的方式

  • 第三,使用mybatis的嵌套結(jié)果,也就是join連表查詢的方式

  • 第四,只使用一次查詢,將所有數(shù)據(jù)查詢出來(lái),通過(guò)一種算法來(lái)實(shí)現(xiàn):除了第一級(jí),其他所有數(shù)據(jù)都關(guān)聯(lián)到自己的父級(jí)分類,結(jié)果返回第一級(jí)數(shù)據(jù)就可以

第一,第二種方式,當(dāng)層級(jí)多的時(shí)候查詢性能極低,第三種方式一般只能查詢兩層結(jié)構(gòu),第四種方式性能最高,適用于數(shù)據(jù)量本身并不大但層級(jí)很多的場(chǎng)景

所有課程的數(shù)據(jù)本身體量小,層級(jí)多,因此采用了第四種方式。

你們系統(tǒng)使用Redis緩存了哪些東西?用Redis的什么結(jié)構(gòu)去存儲(chǔ)的?

登錄信息login,使用的是String結(jié)構(gòu)存儲(chǔ)

手機(jī)驗(yàn)證碼code,使用的是String結(jié)構(gòu)

課程分類course_type ,使用的是String結(jié)構(gòu)

購(gòu)物車保存,使用的是Hash結(jié)構(gòu)

課程發(fā)布流程講一下

發(fā)布課程兩大步

第一步,將課程的狀態(tài)改為上線并保存到數(shù)據(jù)庫(kù)中,

第二步,將課程信息保存到ES中,方便門戶網(wǎng)站展示

你們課程相關(guān)的表是怎么設(shè)計(jì)的?主要的字段說(shuō)一下

我們按照字段的使用頻次,垂直分表來(lái)設(shè)計(jì),分為課程主表,課程詳情表,課程類型表,課程市場(chǎng)詳情表。

課程主表,包括主鍵id,課程名稱,課程類型id,課程上下線狀態(tài),適用人群,課程等級(jí),課程所屬機(jī)構(gòu)等,并且冗余了課程類型名,課程價(jià)格字段來(lái)提高前臺(tái)的查詢性能

課程詳情表,包括課程簡(jiǎn)介,課程詳情

課程市場(chǎng)詳情表,包括課程價(jià)格,促銷活動(dòng),活動(dòng)過(guò)期時(shí)間

課程類型表,包括主鍵id,類型名,創(chuàng)建修改時(shí)間,課程數(shù)量,父級(jí)id

其中課程主表和課程詳情表、課程主表和課程市場(chǎng)詳情表,都是一對(duì)一的關(guān)系,他們采用相同的主鍵id來(lái)相互關(guān)聯(lián)。課程主表和課程類型表是多對(duì)一的關(guān)系,在課程主表添加類型id來(lái)相互關(guān)聯(lián)

講一下你們這個(gè)項(xiàng)目的主線業(yè)務(wù)

我們項(xiàng)目分為兩大版圖,

入駐我們平臺(tái)的培訓(xùn)機(jī)構(gòu),可以發(fā)布相關(guān)課程,入駐平臺(tái)的企業(yè),可以發(fā)布相關(guān)的就業(yè)招聘信息

門戶網(wǎng)站的大眾用戶,可以選擇培訓(xùn)機(jī)構(gòu)發(fā)布的課程來(lái)進(jìn)行學(xué)習(xí),可以選擇企業(yè)發(fā)布的招聘信息來(lái)就業(yè)

你們項(xiàng)目最大并發(fā)是多少

俺們項(xiàng)目是按照最高2000 QPS設(shè)計(jì)的,實(shí)際并發(fā)數(shù)運(yùn)維在統(tǒng)計(jì),俺也不太清楚

你們項(xiàng)目最大表數(shù)量是多少

俺們項(xiàng)目都有分庫(kù)分表,按服務(wù)拆分多個(gè)數(shù)據(jù)庫(kù),對(duì)于有些數(shù)據(jù)量大的表,我們也是按照字段的使用頻率,拆分成多個(gè)表,比如課程表拆分成課程主表,課程詳情表,課程分類表等等。

但是有些表比如日志,流水相關(guān)的表,數(shù)據(jù)量還是很大的

.說(shuō)一下你們課程搜索的那個(gè)業(yè)務(wù)方法的大致邏輯

首先,課程在發(fā)布的時(shí)候,就同時(shí)將課程信息存放到ES中,信息中包括了需要查詢的字段,如課程標(biāo)題,課程分類,課程等級(jí),機(jī)構(gòu)名,銷量,瀏覽量,上線時(shí)間,價(jià)格等等

接下來(lái),根據(jù)用戶在前臺(tái)發(fā)送的查詢條件,在ES中搜索對(duì)應(yīng)的課程,并作關(guān)鍵字高亮處理,排序和分頁(yè)處理,然后返回前臺(tái)

項(xiàng)目并發(fā)高處理過(guò)不過(guò)來(lái)怎么辦

前端優(yōu)化:

  • 使用頁(yè)面靜態(tài)化技術(shù)由Nginx實(shí)現(xiàn)動(dòng)靜分離、
  • CDN加速加快響應(yīng)速度、
  • 使用驗(yàn)證碼使流量錯(cuò)峰等手段最大限度的降低并發(fā)

后端優(yōu)化:

  • Nginx+LVS負(fù)載,也可以多機(jī)房部署,分流

  • 從架構(gòu)上使用分布式、集群分散并發(fā)量,

  • 從數(shù)據(jù)結(jié)構(gòu)上使用緩存如Redis減少數(shù)據(jù)讀寫(xiě)時(shí)間,

  • 從處理方式上采用如RabitMQ隊(duì)列實(shí)現(xiàn)異步響應(yīng),

  • 資源隔離比如使用Hystrix的信號(hào)量隔離來(lái)限流,同時(shí)做好備用方案比如Hystrix的熔斷降級(jí)策略等等

講一下你們的微服務(wù)授權(quán)方案 你還知道有哪些方案嗎?

我們使用的是SpringSecurity+Oauth2+JWT,認(rèn)證服務(wù)器負(fù)責(zé)頒發(fā)token,資源服務(wù)器負(fù)責(zé)認(rèn)證和授權(quán)

或者也可以將認(rèn)證工作交給網(wǎng)關(guān)zuul,資源服務(wù)器只負(fù)責(zé)授權(quán)工作。

另外常見(jiàn)的授權(quán)方案還有,單點(diǎn)登錄,用戶只用在某個(gè)服務(wù)上登錄,訪問(wèn)其他服務(wù)時(shí)就不需要登錄了,這就要求每個(gè)面向用戶的服務(wù)都必須于認(rèn)證服務(wù)交互,會(huì)產(chǎn)生大量重復(fù)的工作

分布式會(huì)話,它是將用戶認(rèn)證信息存儲(chǔ)在共享容器比如redis中,通常會(huì)以會(huì)話作為key,當(dāng)用戶訪問(wèn)微服務(wù)時(shí),就從redis中獲取認(rèn)證信息。這對(duì)安全存儲(chǔ)有較高的要求,復(fù)雜度高

講一下你們微服務(wù)認(rèn)證授權(quán)的整體流程

客戶端訪問(wèn)認(rèn)證服務(wù)器,認(rèn)證服務(wù)器驗(yàn)證用戶名密碼,然后頒發(fā)token

客戶端保存token,并且每次訪問(wèn)服務(wù)時(shí)都攜帶token

資源服務(wù)器接收到客戶端請(qǐng)求,會(huì)驗(yàn)證token信息,認(rèn)證通過(guò)后返回資源

你們?yōu)樯兑肑WT

一個(gè)字,安全

我們做了認(rèn)證授權(quán)后,每次客戶端訪問(wèn)資源服務(wù)器,都需要遠(yuǎn)程調(diào)用認(rèn)證服務(wù)器進(jìn)行token的校驗(yàn)和授權(quán),才能訪問(wèn)到資源。這是很好性能的,因此我們考慮將簽名信息直接保存到客戶端,那就不需要每次都向認(rèn)證服務(wù)器認(rèn)證授權(quán)了。

但是這有有一個(gè)新的問(wèn)題,這些敏感數(shù)據(jù)赤裸裸的存到客戶端不安全!而JWT就能解決這個(gè)問(wèn)題。它支持非對(duì)稱加密算法對(duì)信息加密,保證了信息安全

另外,JWT以json對(duì)象的形式傳遞信息,解析更方便

可以再令牌中定義內(nèi)容,方便擴(kuò)展

Oauth2的授權(quán)模式有哪些,分別使用在什么場(chǎng)景?

授權(quán)碼模式:它是功能最完整、流程最嚴(yán)密的授權(quán)模式

簡(jiǎn)化模式:跳過(guò)授權(quán)碼,直接再瀏覽器端申請(qǐng)令牌

用戶名密碼模式:客戶向客戶端提供用戶名密碼,建立在用戶對(duì)客戶端高度信賴的基礎(chǔ)上

客戶端模式:客戶端以自己的名義,要求服務(wù)提供商提供服務(wù)

Oauth2認(rèn)證,如果Token過(guò)期了你們是怎么處理的

首先,我們會(huì)在前端設(shè)置axios后置攔截,檢查是否是token過(guò)期,判斷一下如果返回401,就代表token過(guò)期了

然后從localStorage中獲取刷新refresh_token,并發(fā)送請(qǐng)求獲取新的token

后臺(tái)接收到前臺(tái)的刷新token請(qǐng)求,拼接完整的刷新token的url,發(fā)送http請(qǐng)求獲取到新的token并返回客戶端

客戶端收到新的token就把舊的token覆蓋掉,最后把之前的請(qǐng)求再重新發(fā)送一次

Oauth2認(rèn)證,如果Token被盜了怎么辦?

首先,我們需要對(duì)token設(shè)置過(guò)期時(shí)間,這個(gè)時(shí)間可以根據(jù)需要設(shè)置短一點(diǎn)

然后,可以在token中加入客戶身份標(biāo)識(shí),比如客戶的ip地址,如果短時(shí)間內(nèi)ip地址頻繁變動(dòng),就標(biāo)記為異常狀態(tài),并給用戶發(fā)送信息,提示賬戶有風(fēng)險(xiǎn)

秒殺的整體流程詳細(xì)說(shuō)一下

秒殺的商品和庫(kù)存是緩存到Redis的,庫(kù)存使用信號(hào)量,做的是秒殺預(yù)減庫(kù)存方案。用戶發(fā)起秒殺,直接走Redis秒殺商品,滿足資格就預(yù)減庫(kù)存,然后預(yù)創(chuàng)訂單寫(xiě)入Redis。整個(gè)秒殺流程是不做數(shù)據(jù)羅庫(kù)的。

此時(shí)把訂單號(hào)返回給客戶端,用戶帶著訂單號(hào)進(jìn)入訂單確認(rèn)頁(yè)面進(jìn)行下單,用戶確認(rèn)下單,再把Redis中的預(yù)創(chuàng)訂單寫(xiě)入訂單數(shù)據(jù),同時(shí)做庫(kù)存同步。緊接著就是調(diào)用支付接口做支付。

如果流量更高,比如:每秒10W請(qǐng)求,應(yīng)該怎么處理

Lvs+Nginx集群+下游服務(wù)集群。如果流量再高,就使用CDN分流。

說(shuō)一下支付超時(shí)處理方案?延遲隊(duì)列和死信隊(duì)列是什么意思?

支付超時(shí)使用MQ延遲隊(duì)列來(lái)處理,把消息投遞到一個(gè)設(shè)置了過(guò)期時(shí)間的隊(duì)列中,達(dá)到過(guò)期時(shí)間消息會(huì)被轉(zhuǎn)發(fā)給另外一個(gè)“死信隊(duì)列”

設(shè)置了過(guò)期時(shí)間的隊(duì)列就是延遲隊(duì)列,過(guò)期的消息叫著死信消息,存放死信消息的隊(duì)列叫死信隊(duì)列。

整個(gè)秒殺流程你用到了哪些隊(duì)列

下單業(yè)務(wù)中用到了一個(gè)低劣,訂單超時(shí)用到一個(gè)隊(duì)列,支付結(jié)果處理用到一個(gè)隊(duì)列。

秒殺成功,返回給用戶的數(shù)據(jù)是什么?

預(yù)創(chuàng)訂單號(hào),前臺(tái)通過(guò)這個(gè)訂單號(hào)來(lái)進(jìn)行下單。

你們?cè)趺刺幚沓u

Redisson分布式鎖,信號(hào)量來(lái)保證庫(kù)存不超賣

如何提高接口的qps

一方面:提高并發(fā)數(shù)

1.多線程,盡量用線程池 (線程個(gè)數(shù):CPU核數(shù) / (1 - 阻塞系數(shù)(IO密集型接近1,計(jì)算密集型接近0)))

2.適當(dāng)調(diào)整連接數(shù)(Tomcat,Redis,Mysql等連接數(shù))

3.集群

二方面:提高接口響應(yīng)速度

1.減少和數(shù)據(jù)庫(kù)交互,使用Redis代替

2.使用異步方案,比如MQ

3.使用并發(fā)編程,多個(gè)線程同時(shí)工作

4.減少服務(wù)的調(diào)用鏈

5.實(shí)在要連數(shù)據(jù)庫(kù),考慮數(shù)據(jù)庫(kù)優(yōu)化

你們這個(gè)前后端分離項(xiàng)目是怎么部署的

前后端分開(kāi)部署,前端使用Nginx部署,

后端使用Springboot內(nèi)嵌的tomcat部署,

分開(kāi)部署后,通過(guò)代理解決前后端域名不一致的跨域問(wèn)題

前后端分離的好處

第一,專人干專事,前后端同時(shí)開(kāi)發(fā),效率更高

第二,責(zé)任分離,避免了前后端相互踢皮球的現(xiàn)象

第三,前后端解耦合,一套后端可以處理不同的前端,包括app端,瀏覽器端

第四,分開(kāi)部署,減輕了服務(wù)器壓力

第五,頁(yè)面顯示東西再多也不怕,數(shù)據(jù)都是異步加載,就算后端服務(wù)器掛了,前端頁(yè)面也能訪問(wèn),雖然沒(méi)有數(shù)據(jù)

第六,前端分離出去,后端寫(xiě)一套接口就可以適用于web,app端

你們用什么做項(xiàng)目代碼管理的

使用主流的Git管理項(xiàng)目

講講Git相對(duì)于SVN的區(qū)別

第一。Git是每個(gè)攻城獅都有自己的版本庫(kù),可以在自己的庫(kù)上任意操作提交代碼

第二。Git在每個(gè)工程只產(chǎn)生一個(gè).git目錄,而SVN會(huì)在每個(gè)目錄下都生成.svn目錄

第三。Git能快速切換分支,且合并文件的速度比SVN快

第四。Git采用分布式版本庫(kù),內(nèi)容完整性更好

你們微服務(wù)項(xiàng)目怎么部署

docker 容器 ,使用Jnekins做持續(xù)集成。

講幾個(gè)Git的命令

git clone:從遠(yuǎn)程倉(cāng)庫(kù)克隆項(xiàng)目到本地

git add:添加代碼到本地倉(cāng)庫(kù)管理

git commit:提交add后的代碼到本地倉(cāng)庫(kù)

git push:推送本地倉(cāng)庫(kù)文件到遠(yuǎn)程倉(cāng)庫(kù)

git pull:拉取遠(yuǎn)程倉(cāng)庫(kù)中的代碼到本地倉(cāng)庫(kù)

六.運(yùn)維篇

linux

有使用過(guò)linux嗎 , 講幾個(gè)命令

  • 查看目錄 : ls
  • 切換目錄: cd
  • 拷貝:cp
  • 遠(yuǎn)程拷貝 :scp
  • 移動(dòng) : mv
  • 刪除:rm
  • 查看文本內(nèi)容:cat
  • 編輯文本: vi
  • 查找:find
  • 遠(yuǎn)程拷貝:scp
  • 創(chuàng)建目錄 : mkdir
  • 創(chuàng)建文件:touch

Linux根目錄下的幾個(gè)核心目錄

  • /bin : 二進(jìn)制文件
  • /dev : 設(shè)備文件
  • /etc : 配置文件
  • /home : 用戶的主目錄,在 Linux 中,每個(gè)用戶都有一個(gè)自己的目錄,一般該目錄名是以用戶的賬號(hào)命名的
  • /root: 該目錄為系統(tǒng)管理員,也稱作超級(jí)權(quán)限者的用戶主目錄。
  • /sbin : s 就是 Super User 的意思,是 Superuser Binaries (超級(jí)用戶的二進(jìn)制文件) 的縮寫(xiě),這里存放的是系統(tǒng)管理員使用的系統(tǒng)管理程序。

周日凌晨零點(diǎn)零分定期備份 /user/backup到 /tmp 目錄下,如何做?

使用crontab即可做到,如下配置:

crontab -e
0 0 * * 7 /bin/cp /user/backup /tmp

Linux中你怎么排查項(xiàng)目問(wèn)題?查看項(xiàng)目日志你一般怎么做?

查看tomcat日志,使用tail命令tail -n N filename.txt 。n是查看行數(shù)

怎么查看進(jìn)程

ps -ef | grep 軟件名

常用的壓縮命令

使用 : tar -zcvf壓縮 , tar -zxvf 解壓縮

或者使用: zip 壓縮成zip, unzip解壓

部署過(guò)項(xiàng)目么?大概講一講如何部署的

單體應(yīng)用的部署是比較簡(jiǎn)單的,前端打包上傳使用Nginx,后端打包成war,可以使用Tomcat來(lái)部署,如果是SpringBoot的可以默認(rèn)打包為jar,直接java -jar 啟動(dòng)。如果用到其他組件,比如Redis可以直接在服務(wù)器安裝,然后項(xiàng)目指向其IP即可。

如果項(xiàng)目的組成部分比較多,比如:項(xiàng)目后端,前端,Redis,Mysql等等都涉及到,那么可以使用Docker來(lái)部署,這樣更好管理應(yīng)用之間的內(nèi)存和資源分配。

你們這個(gè)服務(wù)器的配置是怎么樣的

我們微服務(wù)有20個(gè)服務(wù)器,業(yè)務(wù)系統(tǒng)是8核CPU,16G內(nèi)存,有些服務(wù)配置還要低一些,視頻處理系統(tǒng)是12核CPU,24G內(nèi)存。通過(guò)NFS方式共享20T硬盤。

Docker

講講什么是Docker

docker是一個(gè)容器技術(shù),最大的好處是做資源的分配和管理,傳統(tǒng)的linux部署項(xiàng)目不好管理內(nèi)存等資源的分配,造成了應(yīng)用之間資源競(jìng)爭(zhēng)的情況,Dcoker的出現(xiàn)解決了這一問(wèn)題。我們可以把我們的應(yīng)用打包成Docker的鏡像,然后啟動(dòng)成容器。容器和容器之間相互隔離也可以互相通信。就類似于有多個(gè)主機(jī)一樣。

講幾個(gè)Docker的命令

docker images :查看本地鏡像

docker search : 搜索鏡像

docker pull : 下載鏡像

docker push : 上傳鏡像到倉(cāng)庫(kù)

docker rmi : 刪除鏡像

docker run : 創(chuàng)建并啟動(dòng)一個(gè)容器

docker ps : 查看容器

docker rm :刪除容器

docker stop : 停止容器

docker kill :停止容器

docker start : 啟動(dòng)容器

docker exec -it 容器名 /bin/bash : 進(jìn)入容器

docker exit :退出容器

docker cp : 拷貝文件到容器,或者從容器中拷貝文件到linux

docker logs : 查看容器的日志

怎么把文件上傳到容器中

docker cp 或者在啟動(dòng)容器的時(shí)候增加 -v 做目錄映射

某個(gè)服務(wù)不可訪問(wèn)了你怎么排查

服務(wù)不可訪問(wèn),那就是容器出問(wèn)題了,我會(huì)去找到對(duì)應(yīng)的容器是不是掛了,或者使用docker logs 查看日志根據(jù)錯(cuò)誤日志來(lái)排錯(cuò)。

容器之間怎么通信

使用容器IP通信,但是容器重啟IP會(huì)變動(dòng),不建議

使用端口映射也可以通信,但是內(nèi)網(wǎng)部署的應(yīng)用不需要做端口映射,所以這個(gè)不建議用

使用--link 名字進(jìn)行通信

使用橋接網(wǎng)絡(luò)通信

對(duì)于Redis和zuul網(wǎng)關(guān)你怎么部署

首先肯定要下載一個(gè)redis的鏡像, 對(duì)于zuul的鏡像可以使用docker插件對(duì)zuul進(jìn)行打包。

redis是不需要暴露給外網(wǎng)的,所以不要做端口映射,可以使用--link或橋接網(wǎng)絡(luò)通信 ,而zuul是服務(wù)訪問(wèn)入口需要做端口映射進(jìn)行外網(wǎng)部署。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容