1.Java 類的加載流程是怎樣的?什么是雙親委派機制?
類加載的主要任務:根據(jù)一個類的全限定名讀取該類的二進制字節(jié)流到JVM內(nèi)部,然后轉換為一個對應的java.lang.Class對象實例。
類從被加載到JVM中到卸載總共經(jīng)歷7個階段:加載、驗證、準備、解析、初始化、使用和卸載。

加載:類加載階段是由類加載器根據(jù)類文件全限定類名,來讀取這個類文件的二進制字節(jié)流到JVM中,并存儲在內(nèi)存的方法區(qū)中,然后將其裝換為一個對應的java.lang.Object對象實例。
驗證:驗證是否符合class文件規(guī)范;檢查final類是否有子類;檢查final方法是否被子類重寫;檢查父類和子類聲明方法是否兼容等。
準備:為類中static變量分配內(nèi)存空間并初始化;被final修飾的靜態(tài)變量會直接賦予原值。
解析:將常量池中所有符號引用轉換為直接飲用,得到類、字段或者方法在內(nèi)存中的指針或偏移量,以便直接調用。
初始化:賦值static變量,執(zhí)行static塊;先初始化父類再初始化子類。
雙親委派:當某個類加載器需要加載類文件時,會首先把這個任務委托給他的上級類加載器,遞歸這個操作。如果上級的類加載器無法加載時,將這個任務退回給下一級類加載器。判斷任何一級加載器加載過類文件直接返回。作用是保證每個類文件只被加載一次。
2.簡述 MVC 與 MVVM 的區(qū)別,MVVM 的優(yōu)點是什么?
VM:在前端頁面中,把Model用純JavaScript對象表示,View負責顯示,兩者做到了最大限度的分離,把Model和View關聯(lián)起來的就是ViewModel。
區(qū)別:MVC和MVVM的區(qū)別并不是VM瓦全取代C,只是在MVC的基礎上增加了一層VM,弱化了C的概念,VM存在的目的在于抽離C中展示的業(yè)務邏輯,而不是替代C,ViewModel負責把Model的數(shù)據(jù)同步到View顯示出來,還負責把View的修改同步回Model。
優(yōu)點:
低耦合:MVVM模式中,數(shù)據(jù)是獨立于UI,VM只負責處理和提供數(shù)據(jù)。
自動同步數(shù)據(jù):VM通過雙向數(shù)據(jù)綁定把V和M連接起來,V和M兩者可以自動同步。
可重用:可以封裝視圖邏輯,其他V也可以重用。
獨立開發(fā):可以獨立開發(fā)VM,設計人員可專注于頁面設計。
可測試:VM處理數(shù)據(jù)和業(yè)務邏輯,V關注UI,方便單獨測試。
3.簡述 Netty 線程模型,Netty 為什么如此高效?
Netty是一款基于NIO(Nonblocking I/O,非阻塞I/O)開發(fā)的網(wǎng)絡通信框架,對比與BIO(Blocking I/O,阻塞I/O),并發(fā)性能得到很大提升。


NIO的單線程處理連接的數(shù)量比BIO高很多,原因就是Selector。
當建立一個連接后,第一步是接收完客戶端發(fā)過來的全部數(shù)據(jù),第二步是服務端處理完請求業(yè)務之后返回response給客戶端。NIO和BIO的區(qū)別主要在于第一步。
BIO中,等待客戶端發(fā)數(shù)據(jù)過程是阻塞的,造成一個線程只能處理一個請求,而機器支持的最大線程數(shù)有限,所以并發(fā)能力差。
NIO中,一個線程接收數(shù)據(jù)不會阻塞,而是將請求交給Selector,Selector會不斷遍歷socket狀態(tài),一旦建立完成就會交給線程處理請求,這樣能讓一個線程處理多個請求。
4.HashMap 實現(xiàn)原理,為什么使用紅黑樹?
HashMap是使用頻率最高的處理鍵值對的數(shù)據(jù)結構,無序、允許插入null的key和value。
HashMap基于數(shù)組+鏈表實現(xiàn),使用拉鏈法處理碰撞,在JDK8中,當鏈表長度大于8(默認值)時轉為紅黑樹,當長度小于6轉為鏈表。

HashMap有一個Node<K,V>[] table字段,即哈希桶數(shù)組,數(shù)組元素是Node對象

哈希桶會在首次使用時初始化,默認大小是16,并根據(jù)需要調整大小,大小總為2的n次冪。如果構造函數(shù)傳入大小不是2的n次冪,那么初始化時會根據(jù)算法得出最接近且大于這個值的2的次冪的值。

HashMap屬性字段

影響HashMap性能的主要參數(shù)是:初始容量和負載因子。當數(shù)組元素數(shù)量超過容量值,會發(fā)生擴容,容量為原來的兩倍,并對key重新散列。
初始容量過小會多次觸發(fā)擴容和 rehash,所以預分配一個足夠大的容量更加有效
負載因子默認值是 0.75f,它是對時間和空間成本的一個很好的平衡,一般不用修改,較高的值會減少空間開銷,但會增加查找的成本
不管多么合理的散列算法,也免不了鏈表過長的情況,從而影響 HashMap 的性能,所以,JDK8 在鏈表長度大于 8 時,將其轉為紅黑樹,以利用紅黑樹快速增刪改查的特點。
先了解一下二叉樹

(1)左子樹上所有結點的值均小于或等于它的根結點的值。
(2)右子樹上所有結點的值均大于或等于它的根結點的值。
(3)左、右子樹也分別為二叉排序樹
二叉樹查找原理:找到數(shù)組中間位置元素v,將數(shù)組分為>v和<v兩部分,然后將v和要查找的數(shù)進行比較,小于找左邊,大于找右邊,直至找到元素,查找次數(shù)是目標元素所在二叉樹的層數(shù)。
紅黑樹
紅黑樹是一種自平衡的二叉樹。自平衡就是對HashMap鏈表可能很長最初的優(yōu)化,平衡能保證單側樹不會過長,導致查詢效率低,這也是相比于二叉樹的優(yōu)勢。

性質:
1.包含二叉樹的性質。
2.節(jié)點只能是紅色或黑色。
3.根結點是黑色。
4.每個葉節(jié)點是黑色(NIL節(jié)點,空節(jié)點)。
5.從每個葉節(jié)點到根結點路徑上,不會出現(xiàn)兩個相連的紅色節(jié)點。
6.從任意節(jié)點到葉子節(jié)點的所有路徑上,黑色節(jié)點數(shù)量相同。
紅黑樹通過“變色”和“旋轉”維護其平衡。
HashMap中怎么使用紅黑樹
JDK7中HashMap利用鏈表解決沖突,這可能導致鏈表過長,由于每次put/set都會遍歷鏈表,導致效率下降。
HashMap中的紅黑樹節(jié)點用TreeNode類

TreeNode和Entry都是Node的子類,也就說Node可能是鏈表結構,也可能是紅黑樹結構。
5.Java 中接口和抽象類的區(qū)別
定義
抽象類:抽象類必須用abstract修飾,子類必須實現(xiàn)抽象類中的抽象方法,如果未實現(xiàn)的,那么子類葉必須用abstract修飾。抽象類的默認權限是public,也可以聲明為protected,如果定義private,那么子類無法繼承。抽象類不能實例化。
接口:接口中的變量隱式的使用public static final修飾,并且必須初始化。方法隱式的使用public abstract修飾,只能是public否則會編譯報錯。從JDK8開始接口中方法允許有默認實現(xiàn)。
區(qū)別
1.抽象類只能繼承一次,但是可以實現(xiàn)多個接口。
2.接口和抽象類必須實現(xiàn)其中所有方法,抽象類中如果有為實現(xiàn)的方法,那么子類必須也定義為抽象方法。抽象類中可以有具體方法。
3.接口中的變量必須用public static final定義,并且必須初始化,實現(xiàn)類不能修改,也不能重新定義。
4.接口中的方法只能是public abstract,不能是static,接口中的方法不允許實現(xiàn)類重寫,抽象類可以有static方法。
6.成員變量和方法的區(qū)別?
成員變量作用域整個類,類當中任何方法都可以訪問。方法作用域也是整個類,其他方法可以調用,但是方法中定的變量作用域只在方法內(nèi)。
7.CAS 實現(xiàn)原理是什么?
在計算機科學中,比較和交換(Compare And Swap)是用于實現(xiàn)多線程同步的原子指令。它將內(nèi)存中的值與給定的值對比,相同情況下將內(nèi)存中的值修改為新值,否則更新失敗。比如線程1更新時發(fā)現(xiàn),線程2修改過內(nèi)存中的值,那么線程1會讀取內(nèi)存中的最新值(volatitl類型)然后再更新,這保證線程1不會死循環(huán)。
8.ThreadLocal 實現(xiàn)原理是什么?
ThreadLocal俗稱線程本地變量。ThreadLocal為每個線程維護了一份變量,每個線程可以訪問自己的變量副本,不涉及變量線程之間共享。
ThreadLocal維護了一個ThreadLocalMap,是Thread的一個屬性,key是ThreadLocal變量,value是線程本地的數(shù)據(jù)。
由于ThreadLocalMap維護的Entry繼承自WeakReference<ThreadLocal>,也就是說ThreadLocal自身的回收不受ThreadLocalMap的這個弱引用的影響。但是Entry被ThreadLocalMap強引用,所以Entry不能被GC。ThreadLocalMap的expungeStaleEntry這個方法,這個方法在ThreadLocalMap get、set、remove、rehash等方法都會調用到。方法有兩個作用:第一將remove的Entry置空,第二找到已經(jīng)被GC的ThreadLocal,然后清理掉ThreadLocalMap對Entry的引用。這樣Entry就會被后續(xù)GC回收。
如果數(shù)據(jù)初始化好之后,一直不調用get、set等方法,這樣Entry就一直不能回收,導致內(nèi)存泄漏。所以一旦數(shù)據(jù)不使用最好主動remove。
9.Java 常見鎖有哪些?ReentrantLock 是怎么實現(xiàn)的?? ?
樂觀鎖和悲觀鎖
對于同一個數(shù)據(jù)的并發(fā)操作,悲觀鎖認為自己在使用數(shù)據(jù)的時候一定有其他線程來修改數(shù)據(jù),因此在獲取數(shù)據(jù)的時候會先加鎖,確保數(shù)據(jù)不會被別的線程修改。Java中,synchronized和Lock實現(xiàn)的都是悲觀鎖。
樂觀鎖認為自己在使用數(shù)據(jù)的時候,不會有其他線程修改數(shù)據(jù),所以不會添加鎖,只是在更新數(shù)據(jù)的時候去判斷之前有沒有別的線程修改了這個數(shù)據(jù)。如果沒有被更新,當前線程將自己修改的數(shù)據(jù)成功寫入。如果數(shù)據(jù)被修改了,則可能報錯或者發(fā)起重試。樂觀鎖在Java中是通過無鎖編程實現(xiàn),最長采用的是CAS算法,Java原子類中的遞增操作就是通過CAS自旋實現(xiàn)的。
悲觀鎖適合寫操作多的場景,樂觀鎖適合讀操作多的場景。
自旋鎖和適應性自旋鎖

自旋鎖本身有一定缺陷,它不能代替阻塞。自旋鎖雖然避免了切換線程的開銷,但是會占用處理器的時間。如果鎖被占用的時間很短,自旋等待的效果就會很好。如果鎖被占用的時間很長,那自旋的線程只會浪費CPU資源。所以自旋等待的時間必須有一定的限度,如果自旋超過了一定次數(shù)(默認10次,可以用-XX:PreBlockSpin來更改)沒有成功獲的鎖,就應當掛起線程。
CAS實現(xiàn)的原理也是資源鎖,AtomicInteger中進行自增操作,代碼中的do-while循環(huán)就是自旋操作,如果修改失敗則通過循環(huán)來執(zhí)行自旋,知道修改成功。
JDK6中引入了適應性自旋鎖。自適應意味著自旋等待次數(shù)不固定,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態(tài)來決定。如果在同一個兌現(xiàn)鎖上,自旋等待成功獲取鎖,并且線程正在運行中,那么虛擬機認為這次自旋獲得鎖很可能成功,所以允許自旋等待更長時間。如果自旋很少成功獲得鎖,那么以后嘗試獲得鎖很可能放棄自旋,直接將阻塞線程,避免浪費CPU資源。
無鎖、偏向鎖、輕量級鎖、重量級鎖
這四種鎖是指鎖的狀態(tài),專門針對synchronized。
無鎖:所有線程都能訪問并修改同一個資源,但同時只有一個線程能修改成功,參考CAS原理;
偏向鎖:當一段同步代碼一直被一個線程鎖訪問,不存在線程競爭,那么該線程就會自動獲得鎖降低獲取鎖的代價。
輕量級鎖:當有線程競爭時,偏向鎖會升級為輕量級鎖,其他線程會通過自旋形式嘗試獲取鎖,不會阻塞,從而提高性能。
重量級鎖:如果自旋一定次數(shù)還未獲得鎖,輕量級鎖就會升級重量級。將擁有所以外的所有線程阻塞。
公平鎖和非公平鎖
公平鎖:多個線程按照申請鎖順序來獲取鎖,線程直接進入隊列中排隊,隊列中的第一個線程才能獲得鎖。公平鎖的優(yōu)點是不會讓線程一直等待下去,缺點是整體吞吐效率比非公平鎖低,等待隊列中第一個線程以外的線程都要阻塞,CPU喚醒阻塞線程的開銷比非公平鎖大。
非公平鎖:多個線程加鎖時直接嘗試獲取鎖,獲取不到才到等待隊列的隊尾等待。如果此時剛好能獲得鎖,那么這個線程可以無需阻塞直接獲得鎖,所以非公平鎖有可能出現(xiàn)后申請鎖得線程先獲得鎖。優(yōu)點是可以減少喚起線程的CPU開銷,整體吞吐效率高,缺點是處于等待隊列的線程可能會餓死,或者很久才能獲得鎖。ReentrantLock默認是非公平鎖,可通過構造函數(shù)參數(shù)設置公平鎖。
可重入鎖和非可重入鎖
可重入鎖:又名遞歸鎖,是指在同一個線程在外層方法獲得鎖的時候,再進入該線程的內(nèi)層方法會自動獲得鎖(前提是鎖對象必須是同一個對象或者class),不會因為之前獲得過還沒釋放而阻塞。Java中ReentrantLock和synchronized都是可重入鎖,可重入的在一定成都可避免死鎖:

在上面的代碼中,類中的兩個方法都是被內(nèi)置鎖synchronized修飾的,doSomething()方法中調用doOthers()方法。因為內(nèi)置鎖是可重入的,所以同一個線程在調用doOthers()時可以直接獲得當前對象的鎖,進入doOthers()進行操作。
如果是一個不可重入鎖,那么當前線程在調用doOthers()之前需要將執(zhí)行doSomething()時獲取當前對象的鎖釋放掉,實際上該對象鎖已被當前線程所持有,且無法釋放。所以此時會出現(xiàn)死鎖。
非可重入鎖相反。
獨享鎖和共享鎖
獨享鎖:也叫排他鎖,是指該鎖一次只能被一個線程所持有。如果線程T對數(shù)據(jù)A加上排它鎖后,則其他線程不能再對A加任何類型的鎖。獲得排它鎖的線程即能讀數(shù)據(jù)又能修改數(shù)據(jù)。JDK中的synchronized和JUC中Lock的實現(xiàn)類就是互斥鎖。
共享鎖:是指該鎖可被多個線程所持有。如果線程T對數(shù)據(jù)A加上共享鎖后,則其他線程只能對A再加共享鎖,不能加排它鎖。獲得共享鎖的線程只能讀數(shù)據(jù),不能修改數(shù)據(jù)。ReentrantReadWriteLock有兩把鎖:ReadLock和WriteLock,讀鎖和寫鎖的加鎖方式不一樣。讀鎖是共享鎖,寫鎖是獨享鎖讀鎖的共享鎖可保證并發(fā)讀非常高效,而讀寫、寫讀、寫寫的過程互斥,因為讀鎖和寫鎖是分離的。
10.GC回收算法
標記-清除法:標記沒用的對象,然后一個一個回收
? ? 缺點:標記和清除兩個過程效率不高,產(chǎn)生內(nèi)存隨便導致大對象需要非配內(nèi)存空間時無法找到連續(xù)內(nèi)存而需要進行一次GC
復制法:將內(nèi)存空間平均分成兩份,當一塊區(qū)域用完之后,將有用的對象復制到另一塊區(qū)域,然后把已使用的區(qū)域一次性清理
? ? 缺點:內(nèi)存空間變小
標記-整理法:標記處沒用的對象,讓活著的對象向一邊移動,然后直接清理掉邊界以外的垃圾
? ? 有點:解決了標記-清除法導致的內(nèi)存碎片問題和存活率較高時復制算法效率低的問題
分代回收法:根據(jù)對象存活周期的不同將內(nèi)存劃分為幾塊,一般是新生代和老年代,新生代基本用復制算法,老年代采用標記-整理算法。
11.hashMap 1.7 / 1.8 的實現(xiàn)區(qū)別
·put時出現(xiàn)hash沖突,1.7把數(shù)據(jù)存放在鏈表里,1.8是先存放在鏈表中,鏈表長度超過8轉為紅黑樹
·1.7的擴容條件是數(shù)組長度大于閾值且存在hash沖突,1.8擴容條件是數(shù)組長度大于閾值或鏈表轉為空黑樹
使用 hashmap 時,一開始最好指定下長度,畢竟擴容時,需要重新根據(jù) key 計算數(shù)組下標,還是很影響效率的。
12.Java 如何高效進行數(shù)組拷貝
JDK中提供了一個高效的API來實現(xiàn)數(shù)組復制。

System.arraycopy(array, 0, arraydst, 0, size);
System.arraycopuy()函數(shù)是native函數(shù),通常native函數(shù)的性能要優(yōu)于普通函數(shù)
13.簡述 Spring 的初始化流程
加載流程是:初始化環(huán)境 --> 加載配置文件 --> 實例化Bean --> 調用Bean顯示信息
a、加載Spring配置信息,并標記配置文件的資源。
b、讀取配置文件資源,然后進行解析。解析配置文件中每一個<bean>標簽。
d、進行Bean實例化操作,完成Bean屬性的設置。
e、對完成屬性設置的Bean進行后續(xù)加工,直接裝配出一個準備就緒的Bean。
14.簡述 synchronized,volatile,可重入鎖的不同使用場景及優(yōu)缺點
synchronized:適用于同步代碼執(zhí)行時間較長;優(yōu)點是線程不會進行自旋,不占用CPU資源;缺點是線程阻塞,響應時間長
volatile:單例模式的雙重檢查;線程能直接讀取內(nèi)存中最新的數(shù)據(jù);不能保證線程安全
可重入鎖:用在定時任務,如果定時任務執(zhí)行時間超過下次計劃執(zhí)行時間,確保只有一個線程正在執(zhí)行;優(yōu)點是有公平和非公平兩種模式,還可以避免死鎖;缺點是需要在finally塊中釋放鎖
15.Java 線程間有多少通信方式?
a 、通過synchronized實現(xiàn):兩個線程鎖定同一個對象,線程1執(zhí)行對象的A方法,線程2執(zhí)行對象的B方法,線程2等線程線程1執(zhí)行完A方法才能執(zhí)行

b、通過while循環(huán)實現(xiàn):兩個線程鎖定同一個對象,線程1修改對象的屬性,線程2檢查對象屬性
c、通過wait/notify實現(xiàn):生產(chǎn)者消費者例子。生產(chǎn)者往容器中添加,消費者從容器中往外拿,當容器滿了停止生產(chǎn)開始消費,當容器空了停止消費開始生產(chǎn)。
d、管道通信:通過管道,將一個線程中的消息發(fā)送給另一個。java.io.PipedInputStream 和 java.io.PipedOutputStream
16.hashmap 和 hashtable 的區(qū)別是什么?
1.繼承的父類不同:HashTable繼承自Dictionary類,而HashMap繼承AbstractMap。但二者都實現(xiàn)了Map接口。
2.線程安全性不同:HashTable這是線程安全的,其方法是Synchronized修飾的,HashMap是線程不安全的,可以用Collections.synchronizedMap()包裝。因為多線程并發(fā)put元素,或導致數(shù)據(jù)覆蓋問題。
3.是否提供contains方法:HashMap值提供containsKey和containsValue方法,HashTable多提供了contains方法,但與containsValue功能相同。
4.key和value是否允許null:HashMap允許一個key為null,允許多個value為null;HashTable不存于存在key或value為null,編譯通過,但運行或報空指針。
5.遍歷方式實現(xiàn)不同:HashMap、HashTable都使用了Iterator,但HashTable還使用了Enumeration的方式 。
6.hash值不同:HashTable直接使用key的hashCode。而HashMap重新計算hash值
7.內(nèi)部實現(xiàn)使用的數(shù)組初始化和擴容方式不同:HashTable初始容量默認11,HashMap默認16;HashTable不要求容量是2的次冪,HahsMap要求;Hashtable擴容時,將容量變?yōu)樵瓉淼?倍加1,而HashMap擴容時,將容量變?yōu)樵瓉淼?倍。
17.synchronized 關鍵字底層是如何實現(xiàn)的?它與 Lock 相比優(yōu)缺點分別是什么?
實現(xiàn):Java對象的鎖信息存在對象頭里,因此Java識別對象是否持有鎖是通過檢查對象頭中的鎖標志來判斷。monitor(監(jiān)視器)存在與對象頭中,當嘗試獲取鎖是,判斷鎖計數(shù)器是否等于0.等于0獲取鎖然后鎖計數(shù)器加1,釋放鎖時鎖計數(shù)器減1.
對比:
synchronized:優(yōu)點是實現(xiàn)簡單,自動上鎖釋放鎖;缺點是悲觀鎖,效率低
Lock:提供了讀寫鎖、公平鎖、非公平鎖,更靈活;缺點是需要手動釋放鎖
18.簡述 Redis 中如何防止緩存雪崩和緩存擊穿
緩存穿透:查詢一個本身不存在數(shù)據(jù)庫的數(shù)據(jù),這樣每次查詢都會訪問到數(shù)據(jù)庫,這樣會對數(shù)據(jù)庫造成很大壓力。
避免:可以將數(shù)據(jù)庫不存在的值也加入緩存,存儲空值并設置過期時間。
緩存擊穿:查詢一個數(shù)據(jù),恰巧這個數(shù)據(jù)在redis中失效了,會訪問數(shù)據(jù)庫,如果請求量很大,會給數(shù)據(jù)庫造成很大壓力。
避免:比如針對熱點數(shù)據(jù)短時間內(nèi)訪問量較大數(shù)據(jù),適當設置大一點過期時間。
緩存雪崩:在某一時間段,緩存集中過期失效或者重啟緩存服務,所有的請求都會訪問到數(shù)據(jù)庫。
避免:可以根據(jù)業(yè)務數(shù)據(jù)不同,設置不同有效期,甚至可以對同一類數(shù)據(jù)設置有效期是加入隨機參數(shù),這樣可以避免同一時間全部數(shù)據(jù)或同一類數(shù)據(jù)全部過期。
19.簡述 Redis 持久化中 rdb 以及 aof 方案的優(yōu)缺點
RDB:將Redis中緩存的數(shù)據(jù)定時記錄到磁盤上的dump文件中??梢耘渲脝挝粫r間修改若干key進行持久化。
優(yōu)點:定時fork一個子進程,現(xiàn)將數(shù)據(jù)寫入臨時文件,成功之后再替換之前文件,效率更高
缺點:如果在定時持久化之前服務器宕機,會導致從上次持久化現(xiàn)在時間點數(shù)據(jù)丟失
AOF:將Redis的操作日志追加到文件中。發(fā)生修改數(shù)據(jù)時、每秒、從不同步三種策略。
優(yōu)點:數(shù)據(jù)安全性高,當文件過大時還會進行rewrite操作來壓縮文件,先寫入最新的keys數(shù)據(jù)到臨時文件,最終替換aof文件;文件內(nèi)容清晰易懂
缺點:同等數(shù)據(jù)量AOF恢復數(shù)據(jù)比RDB慢
20.數(shù)據(jù)庫有哪些常見索引?數(shù)據(jù)庫設計的范式是什么?
索引類型:
唯一索引:在創(chuàng)建唯一索引時要不能給具有相同的索引值。
主鍵索引:在我們給一個字段設置主鍵的時候,它就會自動創(chuàng)建主鍵索引,用來確保每一個值都是唯一的。
聚集索引:我們在表中添加數(shù)據(jù)的順序,與我們創(chuàng)建的索引鍵值相同,而且一個表中只能有一個聚集索引。
普通索引:它的結構主要以B+樹和哈希索引為主,主要是對數(shù)據(jù)表中的數(shù)據(jù)進行精確查找。
全文索引:它的作用是搜索數(shù)據(jù)表中的字段是不是包含我們搜索的關鍵字,就像搜索引擎中的模糊查詢。
三大范式:三大范式只是一般設計數(shù)據(jù)庫的基本理念
第一范式:每一列屬性都是不可再分的屬性值;兩列的屬性相近或相似或一樣,盡量合并屬性一樣的列;
第二范式:每一行的數(shù)據(jù)只能與其中一列相關,即避免同一列出現(xiàn)重復數(shù)據(jù),比如維護一個人的訂單信息,每行是一個訂單,那么人員信息每行都一樣,需要拆開成訂單表和人員信息表;
第三范式:數(shù)據(jù)不能存在傳遞關系,即每個屬性都跟主鍵有直接關系而不是間接關系。比如Student表(學號,姓名,年齡,性別,所在院校,院校地址,院校電話),這樣一個表結構,就存在上述關系。 學號--> 所在院校 --> (院校地址,院校電話)。這樣的表結構,我們應該拆開來,如下:(學號,姓名,年齡,性別,所在院校)--(所在院校,院校地址,院校電話)。
21.Redis 如何實現(xiàn)分布式鎖?
滿足條件:任意時刻是能有一個節(jié)點持有鎖;鎖只能被持有的節(jié)點刪除;持有鎖節(jié)點宕機不影響其他節(jié)點獲取鎖。
實現(xiàn):setNX,只在key不存在時設置;設置key的過期時間;在catch或者finally塊中釋放鎖。
22.簡述 Redis 的哨兵機制
目的:為了解決在主從復制架構中出現(xiàn)宕機的情況。
Redis的Sentinel系統(tǒng)用于管理多個Redis服務器,系統(tǒng)執(zhí)行三項任務:
·監(jiān)控:Sentinel會不斷的定期檢查你的主服務器和從服務器是否運行正常;
·提醒:當被監(jiān)控的某臺服務器出現(xiàn)故障,Sentinel可以通過API向管理員或者其他應用程序發(fā)送通知;
·自動故障遷移:當一個主服務器不能正常工作時,它會在從服務器中選擇一個升級為新的主服務器,并讓其他從服務器都改為復制新的主服務器;當客戶端連接已經(jīng)故障的服務器時,集群也會向客戶端返回新的主服務器地址(此操作需要客戶端連接sentinel節(jié)點,而不能連接固定ip端口的主機,否則不能實現(xiàn)自動切換)。
23.簡述數(shù)據(jù)庫中的 ACID 分別是什么?
A:原子性:事務中各項操作,要么全做要么全不做,任何一項操作的失敗都會導致整個事務的失敗
C:一致性:事務結束后系統(tǒng)狀態(tài)是一致的;參考守恒定律,5個賬戶互相轉賬,賬戶總余額不能變
I:隔離性:并發(fā)執(zhí)行的事務彼此無法看到對方的中間狀態(tài)
D:持久性:事務完成后所做的改動都會被持久化,即使發(fā)生災難性的失敗
24.什么情況下會發(fā)生死鎖,如何解決死鎖?
條件:
互斥:同一時間內(nèi)資源僅能被一個線程占有;
不可剝奪:線程占有資源未釋放前不能被其他線程占有;
占有和等待:線程已經(jīng)占有了一個資源,但又請求新的資源,但是新的資源被其他線程占有
循環(huán)等待:前三個條件的結果。
避免:
線程有順序加鎖

線程加鎖有條件釋放,不能無限占有

25.Cookie和Session的關系和區(qū)別是什么?
作用:http請求是無狀態(tài)的,一旦請求數(shù)據(jù)提交完畢就會關閉請求,再次提交數(shù)據(jù)需要再發(fā)起請求,所以服務器無法追蹤和判斷請求管理,確定身份,而cookie和session可以幫助服務器確定用戶身份。
cookie:第一次登陸時,服務器會返回一段數(shù)據(jù)(cookie)給瀏覽器,瀏覽器會把cookie保存起來,再次發(fā)送請求會攜帶cookie發(fā)送給服務器,1以讓服務端確認用戶身份。
session:session存在服務端,而cookie則是存儲在客戶端本地。第一次登陸服務器會存儲session同時生成一個session_id,通過http響應頭返回給瀏覽器,然后瀏覽器會把session_id保存在cookie中,下一次請求,瀏覽器會發(fā)送session_id到服務器,服務器通過session_id獲取到對應數(shù)據(jù)來判斷用戶的身份。
26.Redis 有幾種數(shù)據(jù)結構?Zset 是如何實現(xiàn)的?
數(shù)據(jù)類型:
String:最基本的數(shù)據(jù)類型,一個key對應一個value。
Hash(哈希):是一個HashMap,指值本身是一種鍵值對結構,如value={{field1,value1},......fieldN,valueN}}
鏈表:List就是鏈表(redis使用的是雙端鏈表),是有序的,value可以重復,可以通過下標取出對應的value值,左右兩邊都能進行插入和刪除操作。

Set(集合):集合類型也是用來保存多個字符串的元素,但和列表不同的是集合中:1.不允許有重復元素,2.集合中元素無序,不能通過索引下標獲取,3.支持集合間的操作,可以去多個集合的交集、并集、差集
zset(有序集合):去集合區(qū)別是元素可以排序,為每個元素設置一個分數(shù),作為排序的依據(jù)。有序集合的元素不允許重復,但是分數(shù)可以重復。

27.Redis 序列化有哪些方式?
JDKSerializationRedisSerializer:redis默認的序列化方式。序列化的對象需要實現(xiàn)Serializer接口。
Jackson2JsonRedisSerializer:如果存儲對象為json的話推薦使用,不僅可以將對象序列化,還可以將對象轉為json字符串保存到redis中,被序列化對象不需要實現(xiàn)Serializable接口。序列化結果清晰,容易閱讀,存儲字節(jié)少,速度快。
StringRedisSerializer:如果key-value都是string的話,將RedisTemplate序列化方式改為Jackson2JsonRedisSerializer,StringRedisTemplate序列化方式不需要改。
28.MySQL 為什么使用 B+ 樹來作索引,對比 B 樹它的優(yōu)點和缺點是什么?
區(qū)別:
優(yōu)點:
IO次數(shù)少:B+樹的非葉節(jié)點不存儲數(shù)據(jù)信息,因此在內(nèi)存頁中能存粗更鎖的key,一次查詢IO次數(shù)等于B+樹的高度
遍歷更加方便:B+樹的葉子節(jié)點都是相連的,因此對于整棵樹的遍歷只需要一次線性遍歷葉子幾點。由于數(shù)據(jù)順序排列且相連,便于區(qū)間查找和搜索。而B樹需要對每一層遞歸遍歷,相鄰的元素可能在內(nèi)存中不相鄰。
缺點:
B樹每個節(jié)點都存key和value,如果訪問量高的數(shù)據(jù)接近根節(jié)點,那么查詢更迅速。
使用B+樹的理由:
B+樹查詢磁盤的IO次數(shù)更少,且查詢效率更穩(wěn)定,由于葉子節(jié)點數(shù)據(jù)排列有序且相連,便于區(qū)間查找。
29.簡述 CAP 理論
CAP原理指的是,在分布式系統(tǒng)中這三個要素最多只能同時實現(xiàn)兩個。因此在進行分布式系統(tǒng)設計是,必須作出取舍。而對于分布式系統(tǒng),分區(qū)容忍性是基本要求,否則就失去了價值。因此設計分布式系統(tǒng),就是在一致性可高可用性之間做取舍。對于大多數(shù)Web應用,其實并不去要強一致性,因此犧牲一致性換取高可用性,是目前多數(shù)分布式系統(tǒng)產(chǎn)品的方向。
一致性(Consistency):數(shù)據(jù)在多個副本之間是否能夠保持一致。(當一個系統(tǒng)在一致狀態(tài)下更新后,應保持系統(tǒng)中所有數(shù)據(jù)仍處于一致的狀態(tài))
高可用性(Availability):系統(tǒng)提供的服務必須一直處于可用狀態(tài),對每個操作的請求必須在有限時間內(nèi)返回結果。
分區(qū)容錯性(Tolerance of network Partition):分布式系統(tǒng)在遇到網(wǎng)絡分區(qū)故障時,仍然需要保證對外提供一致性和可用性的服務,除非整個網(wǎng)絡發(fā)生故障。
為什么只能同時滿足兩個:
例如,有ABC三臺服務器,value初始都收0,A服務器收到了修改value=1的請求,這是C服務器發(fā)生故障。為了滿足分區(qū)容錯,此時服務對外必須提供服務。
? ? 如果滿足一致性,則A服務器的修改將不會提交成功,因為C服務器故障不能完成修改
? ? 如果滿足高可用性,則允許AB服務器修改value=1
30.簡述 HTTPS 的認證過程
1.服務器生成一對公鑰和私鑰
2.把公鑰放到證書里發(fā)送給客戶端,私鑰自己保存
3.客戶端首先向一個權威的服務器檢查證書的合法性,如果證書合法,客戶端會產(chǎn)生一段隨機數(shù),這個隨機數(shù)就是通信密鑰,用公鑰加密這段隨機數(shù),然后發(fā)送到服務器
4.服務器解密獲取通信密鑰,然后雙方就用通信密鑰進行加密揭秘通信
31.