|
一鍵注冊,加入手機圈
您需要 登錄 才可以下載或查看,沒有帳號?立即注冊 
x
這里是參考B站上的大佬做的面試題筆記。大家也可以去看視頻講解?。?!
文章目錄
- 2、JDK、JRE、JVM區(qū)別和聯(lián)系
- 5、String 、StringBuffer、StringBuilder區(qū)別及使用場景
- 10、ArrayList和Linkedlist
- 11、HashMap和HashTable的區(qū)別及底層實現(xiàn)
- 12、ConcurrentHashMap原理簡述,jdk7和jdk8的區(qū)別
- 13、如何實現(xiàn)一個IOC容器
- 14、什么是字節(jié)碼,作用是什么
- 15、java類加載器有哪些
- 18、GC如何判斷對象可以被回收
- 19、線程的生命周期及狀態(tài)
- 20、sleep、wait、join、yield的區(qū)別
- 22、Thread和Runnable的區(qū)別
- 23、說說你對守護線程的理解
- 24、ThreadLocal的原理和使用場景
- 25、ThreadLocal內(nèi)存泄漏問題,如何避免
- 28、為什么使用線程池?解釋下線程池解釋?
- 30、線程池中阻塞隊列的作用?為什么是先添加隊列而不是先創(chuàng)建最大線程?
- 35、BeanFactory和ApplicationContext有什么什么區(qū)別?
- 36、簡述spring bean的生命周期
- 37、spring支持的幾種bean作用域
- 38、Spring框架中的單例Bean是線程安全的嗎
- 39、spring框架中使用了哪些設(shè)計模式及應(yīng)用場景
- 40、spring事務(wù)的實現(xiàn)方式原理以及隔離級別
- 41、spring的事務(wù)傳播機制
- 42 、spring事務(wù)什么時候會失效
- 43 、什么的是bean的自動裝配、有哪些方式?
- 44 、spring 、 springmvc 、springboot的區(qū)別
- 45 、springmvc的工作流程
- 46 、Spring MVC的主要組件
- 47 、springboot自動配置原理
- 48 、 如何理解springboot的starter
- 49 、 什么是嵌入式服務(wù)器,為什么使用嵌入式服務(wù)器
- 51、mybatis和hibernate的對比
- 53 、mybatis插件運行原理及開發(fā)流程
- 55 、mysql聚簇和非聚簇索引的區(qū)別
- 56 、mysql索引結(jié)構(gòu),各自的優(yōu)劣
- 58 、mysql鎖的類型有哪些
- 59 、mysql執(zhí)行計劃怎么看
- 60 、事務(wù)的基本特性和隔離級別
- 65、簡述Myisam和innodb的區(qū)別
- 66、簡述MySQL中索引類型對數(shù)據(jù)庫的性能的影響
- 68、Redis的過期鍵的刪除策略
- 69、Redis線程模型,單線程為什么這么快
- 70、緩存雪崩、緩存穿透、緩存擊穿
- 73、redis主從復(fù)制的核心原理
- 76、分布式架構(gòu)下,Session共享有什么方案
- 77、簡述你對RPC、RMI的理解
- 80、分布式事務(wù)解決方案
- 81、如何實現(xiàn)接口冪等性
1、面向?qū)ο?/strong>
1.1、 什么是面向?qū)ο螅?/strong>
對比面向過程、是兩種不同的處理問題的角度
面向過程更注重事情的沒一個步驟及順序,面向?qū)ο蟾⒅厥虑橛心男﹨⑴c者(對象),及各自需要做什么。
例如:洗衣機洗衣服
- 面向過程會將任務(wù)拆解成一系列的步驟(函數(shù)),1、打開洗衣機---->2、放衣服------>3、放洗衣粉---->4、清洗------>5、烘干
- 面向?qū)ο髸鸪鋈撕拖匆聶C兩個對象:人:打開洗衣機,放衣服,放洗衣粉。洗衣機:清洗、烘干
從以上例子可以看出:面向過程比較直觀高效,而面向?qū)ο蟾子趶?fù)用,擴展和維護。
1.2 、面向?qū)ο?/strong>
封裝
- 封裝的意義,在于明確標識出允許外部使用的所有成員函數(shù)和數(shù)據(jù)項,內(nèi)部細節(jié)對外部調(diào)用透明,外部調(diào)用無需修改或者關(guān)心內(nèi)部實現(xiàn)。
- 1、javabean的屬性私有,提供get/set對外訪問,因為屬性的賦值或者獲取邏輯只能由javabean本身決定,而不能由外部胡亂修改。
- 2、orm框架:操作數(shù)據(jù)庫,我們不需要關(guān)心鏈接時如何建立的,sql時如何執(zhí)行的,只需要引入mybatis,調(diào)方法即可。
繼承
- 繼承基類的方法,并做出自己的改變或擴展
- 子類共性的方法或者屬性直接使用父類的,而不需要自己再定義,只需擴展自己個性化的
多態(tài)
- 基于對象所屬類的不同,外部對同一個方法的調(diào)用,實際執(zhí)行的邏輯不同。
- 繼承,方法重寫,父類引用指向子類對象
- 父類類型 變量名 = new 子類對象;
- 變量名.方法名();
無法調(diào)用子類特有的功能
2、JDK、JRE、JVM區(qū)別和聯(lián)系
JDK:
- java Develpment Kit java 開發(fā)工具
JRE:
- java Runtime Environment java 運行時環(huán)境
JVM:
- java Virtual Machine java 虛擬機
2022年整理最詳細的java面試題、掌握這一套八股文、面試 ...-1.jpg (32.41 KB, 下載次數(shù): 6)
下載附件
2023-2-12 13:27 上傳
2022年整理最詳細的java面試題、掌握這一套八股文、面試 ...-2.jpg (42.84 KB, 下載次數(shù): 7)
下載附件
2023-2-12 13:27 上傳
3、==和equals
- 對比的時棧中的值,基本數(shù)據(jù)類型是變量值,引用類型是堆中內(nèi)存對象的地址
- equals:object中默認也是采用比較,通常會重寫
- String類中被復(fù)寫的equals()方法其實是比較兩個字符串的內(nèi)容
舉例
@Test void test(){ String str1 = "hello"; String str2 = new String("hello"); String str3 = str2;//引用傳遞 System.out.println("123"); System.out.println("(str1 == str2):"+(str1 == str2));//false System.out.println("(str1 == str3):"+(str1 == str3));//false System.out.println("(str2 == str3):"+(str2 == str3));//true System.out.println("str1.equals(str2):"+str1.equals(str2));//true System.out.println("str1.equals(str3):"+str1.equals(str3));//true System.out.println("str2.equals(str3):"+str2.equals(str3));//true }
2022年整理最詳細的java面試題、掌握這一套八股文、面試 ...-3.jpg (19.71 KB, 下載次數(shù): 7)
下載附件
2023-2-12 13:27 上傳
4、final
4.1、簡述final作用
最終的
- 修飾類:表示類不可被繼承
- 修飾方法:表示方法不可被子類覆蓋,但是可以重載
- 修飾變量:表示變量一旦被賦值就不可以更改它的值。
(1) 、 修飾成員變量
- 如果final修飾的類變量,只能在靜態(tài)初始化塊中指定初始值或聲明該類變量時指定初始值
- 如果final修飾的是成員變量,可以在非靜態(tài)初始化塊,聲明該變量或者構(gòu)造器中執(zhí)行初始值。
(2) 、修飾局部變量
系統(tǒng)不會為局部變量進行初始化,局部變量必須由程序員顯示初始化,因此使用final修飾局部變量時,即可以在定義時指定默認值(后面的代碼不能對變量再賦值),也可以不指定默認值,而在后面的代碼中對final變量賦初值(僅一次)
(3) 、修飾基本類型數(shù)據(jù)和引用類型數(shù)據(jù)
- 如果是基本數(shù)據(jù)類型的變量,則其數(shù)值一旦在初始化之后便不能更改
- 如果是引用類型的變量,則在對其初始化之后便不能再讓其指向另一個對象,但是引用的值是可以改變的
4.2 、為什么局部內(nèi)部類和匿名內(nèi)部類只能訪問局部final變量?
首先需要知道的一點是:內(nèi)部類和外部類是處于同一個級別的,內(nèi)部類不會因為定義在方法中就會隨著方法的執(zhí)行完畢就被銷毀。
這里就會產(chǎn)生問題:當外部類的方法結(jié)束時,局部變量就會被銷毀,但是內(nèi)部類對象可能還存在(只有沒有人再引用它時,才會死亡)。這里就出現(xiàn)了一個矛盾:內(nèi)部類對象訪問了一個不存在的變量。為了解決這個問題,就將局部變量復(fù)制了一份作為內(nèi)部類的成員變量,這樣當局部變量死亡后,內(nèi)部類仍可以訪問它,實際訪問的是局部變量的copy,這樣好像就延長了局部變量的生命周期。
將局部變量復(fù)制為內(nèi)部類的成員變量時,必須保證這兩個變量是一樣的,也就是如果我們再內(nèi)部類中修改了成員變量,方法中的局部變量也得跟著改變,怎樣解決問題呢?
就將局部變量設(shè)置為final、對它初始化之后,我就不讓你再去修改這個變量,就保證了內(nèi)部類的成員變量和方法的局部變量的一致性,這實際上也是一種妥協(xié),使得局部變量與內(nèi)部類內(nèi)建立的拷貝保持一致性。
5、String 、StringBuffer、StringBuilder區(qū)別及使用場景
- String是final修飾的、不可變、每次操作都會產(chǎn)生新的String對象
- StringBuffer和StringBuilder都是在原對象上操作
- StringBuffer是線程安全的,StringBuilder線程不安去的
- StringBuffer方法都是synchronized修飾的
性能:StringBuilder > StringBuffer > String
場景:經(jīng)常需要改變字符串內(nèi)容時使用后面兩個
優(yōu)先使用StringBuilder,多線程使用共享變量時使用StringBuffer
6、重載和重寫的區(qū)別
- 重載 :發(fā)生在同一個類中,方法名必須相同,參數(shù)類型不同,順序不同,方法的返回值和訪問修飾符可以不同,發(fā)生在編譯時期。
- 重寫 :發(fā)生在父子類中,方法名,參數(shù)列表必須相同,返回值范圍小于等于父類,拋出的異常范圍小于等于父類。訪問修飾符范圍大于等于父類;如果父類方法訪問修飾符為private則子類就不能重寫該方法。
7、接口和抽象類
- 抽象類可以存在普通成員函數(shù),而接口中只能存在public abstract方法
- 抽象類中的成員變量可以是各種類型的,而接口中的成員變量只能是public static final 類型的
- 抽象類只能繼承一個,接口可以實現(xiàn)多個。
接口的設(shè)計目的,是對類的行為進行約束(更準確的說是一種“有”約束,因為接口不能規(guī)定類不可以有什么行為),也就是提供一種機制,可以強制要求不同的類具有相同的行為,它只約束了行為的有無,但不對如何實現(xiàn)行為進行限制。
而抽象類的設(shè)計目的,是代碼復(fù)用。當不同的類具有某些相同的行為(記為行為集合),且其中一部分行為的實現(xiàn)方式一致時(A的非真子集,記為B)可以讓這些類都派生與一個抽象類,在這個抽象類中實現(xiàn)了B,避免讓所有的子類來實現(xiàn)B,這就達到了代碼復(fù)用的目的。而A減去B的部分,留給各個子類自己實現(xiàn),正是因為A-B在這里沒有實現(xiàn),所以抽象類不允許實例化出來(否則當調(diào)用到A-B時,無法執(zhí)行)。
抽象類是對類本質(zhì)的抽象,表達的是is-a的關(guān)系,比如:BMW is a car 。抽象類包含并實現(xiàn)子類的通用特性,將子類存在差異化的特性進行抽象,交由子類去實現(xiàn)。
而接口是對行為的抽象,表達的是like a的關(guān)系,比如:bird like a aircraft(像飛行器一樣飛),但其本質(zhì)上 is a bird 。接口的核心是定義行為,至于實現(xiàn)主體是誰,是如何實現(xiàn),接口并不關(guān)心。
使用場景:當你關(guān)注一個事物的本質(zhì)的時候,用抽象類;當你關(guān)注一個操作的時候,用接口
抽象類的能力要遠超過接口,但是,定義抽象類的代價高,因為高級語言來說(從實際設(shè)計上來說也是)每個類只能繼承一個類,在這個類中,你必須繼承或編寫出其所有子類的所有共性。雖然接口在功能上會弱化許多,但是它只是針對一個動作的描述,而且你可以在一個類中同時實現(xiàn)多個接口,在設(shè)計階段會降低難度
8、List和Set
- List:有序、按對象進入的順序保存對象,可重復(fù),允許多個Null元素對象,可以使用iterator取出所有元素,在逐一遍歷,還可以使用get(int index)獲取指定下的元素。
- Set:無序、不可重復(fù)、最多允許有一個Null元素對象,取元素時只能用iterator接口取得所有的元素,在逐一遍歷各個元素。
**鐵汁們、鐵汁們、鐵汁們、都看到這里了、動動可愛的小手、點個贊唄、鼓勵一下。哈哈哈**
9、hashcode和equals
hashcode介紹
hashcode()的作用是獲取哈希碼,也稱為散列碼;它實際上是返回一個int整數(shù)。這個哈希碼的作用是確定該對象在哈希表中的索引位置。hashcode()定義在JDK的Object.java中,java中的任何類都包含有hashcode()函數(shù)。散列表存儲的是鍵值對(key-value),它的特點是:能根據(jù)“鍵”快速的檢索出對應(yīng)的“值”。這其中就利用到了散列碼!(可以快速找到所需要的對象)
為什么要有hashcode
以“HashSet如何檢查重復(fù)”為例子來說明為什么要有hashcode:
對象加入HashSet時,HashSet會先計算對象的hashcode值來判斷對象加入的位置,看該位置是否有值,如果沒有、HashSet會假設(shè)對象沒有重復(fù)出現(xiàn)。但是如果發(fā)現(xiàn)有值,這時會調(diào)用equals()方法來檢查兩個對象是否真的相同。如果兩者相同,HashSet就不會讓其加入操作成功。如果不同的話,就會重新散列到其他位置。這樣就大大減少了equals的次數(shù),相應(yīng)就大大提高了執(zhí)行速度。
- 如果兩個對象相等,則hashcode一定也是相同的。
- 兩個對象相等,對兩個對象分別調(diào)用equals方法都返回true
- 兩個對象有相同的hashcode值,它們也不一定是相等的
- 因此、equals方法被覆蓋過,則hashcode方法也必須被覆蓋
- hashcode()的默認行為是對堆上的對象產(chǎn)生獨特值,如果沒有重寫hashcode(),則該class的兩個對象無論如何都不會相等(即使這兩個對象指向相同的數(shù)據(jù))
10、ArrayList和Linkedlist
ArrayList:
- 基于動態(tài)數(shù)組,連續(xù)內(nèi)存存儲,適合下標訪問(隨機訪問),擴容機制:因為數(shù)組長度固定,超出長度存數(shù)據(jù)時需要新建數(shù)組,然后將老數(shù)組的數(shù)據(jù)拷貝到新數(shù)組,如果不是尾部插入數(shù)據(jù)還會涉及到元素的移動(往后復(fù)制一份,插入新元素),使用尾插法并指定初始容量可以極大提升性能甚至超過LinkedList(需要創(chuàng)建大量的index對象)
LinkedList:
- 基于鏈表,可以存儲在分散的內(nèi)存中,適合做數(shù)據(jù)插入及刪除操作,不適合查詢;需要逐一遍歷,遍歷LinkedList必須使用iterator不能使用for循環(huán),因為每次for循環(huán)體內(nèi)通過get(i)取得某一元素時都需要對list重新遍歷,性能消耗極大。
另外不要試圖使用index()等返回元素索引,并利用其進行遍歷,使用indexof對list進行了遍歷,當結(jié)果為空時會遍歷整個列表
11、HashMap和HashTable的區(qū)別及底層實現(xiàn)
區(qū)別:
- HashMap方法沒有synchronized修飾、線程非安全,HashTable線程安全
- HashMap允許key和value為null,而HashTable不允許
底層實現(xiàn):數(shù)組+鏈表實現(xiàn)
jdk8開始鏈表高度到8,數(shù)組長度超過64,鏈表轉(zhuǎn)變?yōu)榧t黑樹,元素內(nèi)部類Node節(jié)點存在
- 計算key的hash值,二次hash然后對數(shù)組長度取模,對應(yīng)到數(shù)組下標
- 如果沒有產(chǎn)生hash沖突(下標位置沒有元素),則直接創(chuàng)建Node存入數(shù)組
- 如果產(chǎn)生hash沖突,先進性equal比較,相同則取代該元素不同,則判斷鏈表高度插入鏈表,鏈表高度達到8,并且數(shù)組長度到64則轉(zhuǎn)變?yōu)榧t黑樹,長度低于6則將紅黑樹傳回鏈表
- key為null,存在下標0的位置
數(shù)組擴容
12、ConcurrentHashMap原理簡述,jdk7和jdk8的區(qū)別
jdk7:
- 數(shù)據(jù)結(jié)構(gòu):ReentrantLock+Segment+HashEntry,一個Segment中包含一個HashEntry數(shù)組,每個HashEntry又是一個鏈表結(jié)構(gòu)
- 元素查詢:二次hash,第一次Hash定位到Segment,第二次hash定位到元素所在的鏈表的頭部
- 鎖:Segment分段鎖 Segment繼承了ReentrantLock,鎖定操作的Segment,其他的Segment不受影響,并發(fā)度為Segment個數(shù),可以通過構(gòu)造函數(shù)指定,數(shù)組擴容不會影響到其他的segment
- get方法無需加鎖,volatile保證
jdk8:
- 數(shù)據(jù)結(jié)構(gòu):synchronized+CAS+Node+紅黑樹,Node的val和next都用volatile修飾,保證可見性
- 查找,替換,賦值操作都使用CAS
- 鎖:鎖鏈表的head節(jié)點,不影響其他元素的讀寫,鎖粒度更細,效率更高,擴容時,阻塞所有的讀寫操作、并發(fā)擴容
- 讀操作無鎖:Node的val和next使用volatile修飾,讀寫線程對該變量互相可見。數(shù)組用volatile修飾,保證擴容時被讀線程感知
13、如何實現(xiàn)一個IOC容器
1、配置文件配置包掃描路徑
2、遞歸包掃描獲取 .class 文件
3、反射、確定需要交給IOC管理的類
4、對需要注入的類進行依賴注入
.class
14、什么是字節(jié)碼,作用是什么
java中的編譯器和解釋器
java中引入了虛擬機的概念,即在機器和編譯程序之間加入了一層抽象的虛擬機的機器。這臺虛擬機的機器在任何平臺上都提供給編譯程序一個共同的接口。
編譯程序只需要面向虛擬機,生成虛擬機能夠理解的代碼,然后由解釋器來將虛擬機代碼轉(zhuǎn)化為特定系統(tǒng)的機器碼執(zhí)行。在java中,這種提供虛擬機理解的代碼叫做字節(jié)碼(即擴展名為.class的文件),它不面向任何特定的處理器,只面向虛擬機。
每一種平臺的解釋器是不同的,但是實現(xiàn)的虛擬機是相同的。java源程序經(jīng)過編譯器編譯后變成字節(jié)碼,字節(jié)碼由虛擬機解釋執(zhí)行,虛擬機將每一條要執(zhí)行的字節(jié)碼送給解釋器,解釋器將其翻譯成特定機器上的機器碼,然后再特定的機器上運行,這也就是解釋了java的編譯與解釋并存的特點。
java源代碼----->編譯器------->jvm可執(zhí)行的java字節(jié)碼(即虛擬機指令)------>jvm------->jvm中的解釋器-------->機器可以執(zhí)行的二進制機器碼------>程序運行。
采用字節(jié)碼的好處:
java語言通過字節(jié)碼的方式,在一定程度上解決了傳統(tǒng)解釋型語言執(zhí)行效率低的問題,同時保留了解釋型語言可移植的特定。所以java程序運行時比較高效,而且,由于字節(jié)碼并不專對一種特定的機器,因此,java程序無需重新編譯便可在多種不同的計算機上運行。
15、java類加載器有哪些
JDK自帶有三個類加載器:bootstrap ClassLoader、ExtClassLoader、AppClassLoader.
- BootstrapClassLoader 是ExtClassLoader的父類加載器,默認負責(zé)加載 %JAVA_HOME% 下的jar包和class文件。
- ExtClassLoader是AppCLassLoader的父類加載器,負責(zé)加載 %JAVA_HOME%/lib/ext 文件夾下的jar包和class類。
- AppClassLoader是自定義加載器的父類,負責(zé)加載classpath下的類文件
- 繼承ClassLoader實現(xiàn)自定義類加載器
16、雙親委派模型
2022年整理最詳細的java面試題、掌握這一套八股文、面試 ...-4.jpg (67.17 KB, 下載次數(shù): 7)
下載附件
2023-2-12 13:27 上傳
雙親委派模型的好處:
- 主要是為了安全性,避免用戶自己編寫的類動態(tài)替換java的核心類,比如String。
- 同時也避免了類的重復(fù)加載,因為JVM中區(qū)分不同類,不僅僅是根據(jù)類名,相同的class文件被不同的ClassLoader加載就是不同的兩個類。
17、java中的異常體系
- java中的所有的異常都來自頂級父類Throwable
- Throwable下有兩個子類Exception和Error
- Error是程序無法處理的錯誤,一旦出現(xiàn)這個錯誤,則程序?qū)⒈黄韧V惯\行。
- Exception不會導(dǎo)致程序停止,又分為兩個部分RunTimeException運行時異常和CheckException檢查異常。
- RunTimeException常常發(fā)生在程序運行過程中,會導(dǎo)致程序當前線程執(zhí)行失敗。CheckedException常常發(fā)生在程序編譯過程中,會導(dǎo)致程序編譯不通過
鐵汁們、鐵汁們、鐵汁們、都看到這里了。拜托、拜托、拜托、一鍵三連唄
18、GC如何判斷對象可以被回收
- 引用計數(shù)法:每個對象有一個引用計數(shù)屬性,新增一個引用計數(shù)加1,引用釋放時計數(shù)減1,計數(shù)為0時可以回收。
- 可達性分析法:從GC Root開始向下搜索,搜索所走過的路徑稱為引用鏈。當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的,那么虛擬機就判斷是可回收對象。
引用計數(shù)法,可能會出現(xiàn)A引用lB,B又引用了A,這時候就算他們都不在使用了,但因為相互引用計數(shù)器=1、永遠無法完成回收。
GC Toots的對象有哪些:
- 虛擬機棧(棧幀中的本地變量表)中引用的對象
- 方法區(qū)中類靜態(tài)屬性引用的對象
- 方法區(qū)中常量引用的對象
- 本地方法棧中國JNI(即一般說的Native方法)引用的對象。
可達性算法中的不可達對象并不是并不是立即死亡的,對象擁有一次自我拯救的機會。對象被系統(tǒng)宣告死亡至少要經(jīng)歷兩次標記過程:第一次是經(jīng)過可達性分析發(fā)現(xiàn)沒有GC Roots相連接的引用鏈,第二次是在由虛擬機自動建立的Finalizer隊列中判斷是否需要執(zhí)行finalize()方法。
當對象變成(GC ROOts)不可達時,GC會判斷該對象是否覆蓋了finalize()方法,若未覆蓋,則直接將其回收。否則,若對象未執(zhí)行過finalize()方法,將其放入F-Queue隊列,由一低優(yōu)先級線程執(zhí)行該隊列中對象的finalize()方法。執(zhí)行finalize()方法完畢后,GC會再次判斷該對象是否可達,若不可達,則進行回收,否則,對象“復(fù)活"
每個對象只能觸發(fā)一次finalize()方法
由于finalize()方法運行代價高昂,不確定性大,無法保證各個對象的調(diào)用順序,不推薦大家使用,建議遺忘它
19、線程的生命周期及狀態(tài)
1、線程通常有五種狀態(tài), 創(chuàng)建 、 就緒 、 運行 、 阻塞 和 死亡狀態(tài) 。
2、阻塞的情況又分為三種:
- 等待阻塞:運行的線程執(zhí)行wait()方法,該線程會釋放占用的所有資源,JVM會把該線程放入”等待池中“。進入這個狀態(tài)后,是不能自動喚醒的,必須依靠其他線程調(diào)用notify或notifyAll方法才能被喚醒,wait是object類的方法
- 同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則JVM會把該線程放入”鎖池“中
- 其他阻塞:運行的線程執(zhí)行sleep或join方法,或者發(fā)出來I/O請求時,JVM會把該線程設(shè)置為阻塞狀態(tài)。當sleep狀態(tài)超時、join等待線程終止或者超時、或者I/O處理完畢時,線程重新轉(zhuǎn)入就緒狀態(tài)。sleep是Thread類的方法
1、新建狀態(tài)(new):新建了一個線程對象
2、就緒狀態(tài)(Runnable):線程對象創(chuàng)建后、其他線程調(diào)用了該對象的start()方法。該狀態(tài)的線程位于
可運行線程池中,變得可運行,等待獲取cpu的使用權(quán)。
3、運行狀態(tài)(Runnbaleing):就緒狀態(tài)的線程獲取了CPU,執(zhí)行程序代碼。
4、阻塞狀態(tài)(Blocked):阻塞狀態(tài)是線程因為某種原因放棄CPU的使用權(quán),暫時停止運行。直到線程進入就緒狀態(tài),才有機會轉(zhuǎn)到運行狀態(tài)。
5、死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常推出了run方法,該線程結(jié)束生命周期
20、sleep、wait、join、yield的區(qū)別
1、鎖池
所有需要競爭同步鎖的線程都會放在鎖池中,比如當前對象的鎖已經(jīng)被其中一個線程得到,則其他線程需要在這個鎖池進行等待,當前面的線程釋放同步鎖后鎖池中的線程區(qū)競爭同步鎖,當某個線程得到后會進入就緒隊列進行等待CPU資源分配。
2、等待池
當我們調(diào)用wait()方法后,線程會放到等待池當中,等待池的線程是不會去競爭同步鎖的。只有調(diào)用了notify()或notifyAll()后等待池的線程才會去競爭鎖,notify()是隨機從等待池選出一個線程放到鎖池,而notifyAll()是將等待池中的所有線程放到鎖池中。
1、sleep是Thread類的靜態(tài)本地方法,wait是object類的本地方法
2、sleep方法不會釋放lock,但是wait會釋放,而且會加入到等待隊列中。
sleep就是把cpu的執(zhí)行資格和執(zhí)行權(quán)釋放出去,不再運行此線程,當定時時間結(jié)束再取回cpu資源,參與cpu調(diào)度,獲取到cpu資源后就可以繼續(xù)運行了。而如果sleep時該線程有鎖,那么sleep不會釋放這個鎖,而是把鎖帶著進入了凍結(jié)狀態(tài),也就是說其他需要這個鎖的線程根本不可能獲取到這個鎖。也就是說無法執(zhí)行程序。如果在睡眠期間其他線程調(diào)用了這個線程的interrupt方法,那么這個線程也就會拋出interruptexception異常返回,這點和wait是一樣的
3、sleep方法不依賴于同步器synchronized,但是wait需要依賴synchronized關(guān)鍵字
4、sleep不需要被喚醒(休眠之后推出阻塞),但是wait需要(不指定時間需要被別人中斷)。
5、sleep一般用于當前線程休眠,或者輪循暫停操作,wait則多用于多線程之間的通信
6、sleep會讓出CPU執(zhí)行時間且強制上下文切換,而wait則不一定,wait后可能還是有機會重新競爭到鎖繼續(xù)執(zhí)行的。
yield()執(zhí)行后線程直接進入就緒狀態(tài),馬上釋放了cpu的執(zhí)行權(quán),但是依然保留了cpu的執(zhí)行資格,所以有可能cpu下次進行線程調(diào)度還會讓這個線程獲取到執(zhí)行權(quán)繼續(xù)執(zhí)行。
join()執(zhí)行后線程進入阻塞狀態(tài),例如在線程B中調(diào)用線程A的join(),那么線程B會進入到阻塞隊列,直到線程A結(jié)束或中斷線程
21、對線程安全的理解
不是線程安全、應(yīng)該是內(nèi)存安全,堆是共享內(nèi)存,可以被所有線程訪問
當多個線程訪問一個對象時,如果不用進行額外的同步控制或其他的協(xié)調(diào)操作,調(diào)用這個對象的行為都可以獲得正確的結(jié)果,我們就說這個對象是線程安全的。
- 堆是進程和線程共有的空間,分全局堆和局部堆。全局堆就是所有沒有分配的空間,局部堆就是用戶分配的空間。堆在操作系統(tǒng)對進程初始化的時候分配,運行過程中也可以向系統(tǒng)要額外的堆,但是用完了要還給操作系統(tǒng),要不然就是內(nèi)存泄漏
在java中,堆是java虛擬機所管理的內(nèi)存中最大的一塊,是所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機啟動時創(chuàng)建。堆所存在的內(nèi)存區(qū)域的唯一目的就是存放對象實例,幾乎所有的對象實例以及數(shù)組都在這里分配內(nèi)存。
- 棧是每個線程獨有的,保存其運行狀態(tài)和局部自動變量的。棧在線程開始的時候初始化,每個線程的棧相互獨立,因此,棧是線程安全的。操作系統(tǒng)在切換線程的時候會自動切換棧。??臻g不需要在高級語言里面顯示的分配和釋放。
目前主流操作系統(tǒng)都是多任務(wù)的,即多個進程同時運行。為了保證安全,每個進程只能訪問分配給自己的內(nèi)存空間,而不能訪問別的進程的,這是由操作系統(tǒng)保障的。
在每個進程的內(nèi)存空間中都會有一塊特殊的公共區(qū)域,通常稱為堆(內(nèi)存)。進程的所有線程都可以訪問到該區(qū)域,這就是造成問題的潛在原因。
22、Thread和Runnable的區(qū)別
Thread和Runnable的實質(zhì)是繼承關(guān)系,沒有可比性。無論使用Runnable還是Thread,都會new Thread,然后執(zhí)行run()方法。用法上,如果有復(fù)雜的線程操作需求,那就選擇繼承Thread,如果知識簡單的執(zhí)行一個任務(wù),那就實現(xiàn)Runnable
23、說說你對守護線程的理解
守護線程:為所有非守護線程提供服務(wù)的線程;任何一個守護線程都是整個JVM中所有非守護線程的保姆;
守護線程類似于整個進程的一個默默無聞的小嘍啰;它的生死無關(guān)重要,它卻依賴整個進程而運行;哪天其他線程結(jié)束了,沒有要執(zhí)行的了,程序就結(jié)束了,理都沒理守護線程,就把它中斷了;
注意:由于守護線程的終止是自身無法控制的,因此千萬不要把IO、File等重要操作邏輯分配給它;因為它不靠譜;
守護線程的作用是什么?
舉例,GC垃圾回收線程:就是一個經(jīng)典的守護線程,當我們的程序中不再有任何運行的Thread,程序就不會再產(chǎn)生垃圾,垃圾回收器也就無事可做,所以當垃圾回收線程是JVM上僅剩的線程時,垃圾回收線程會自動離開。它開始終再低級別的狀態(tài)中運行,用于實時監(jiān)控和管理系統(tǒng)中的可回收資源。
應(yīng)用場景:
- 1、來為其它線程提供服務(wù)支持的情況;
- 2、或者在任何情況下,程序結(jié)束時,這個線程必須正常且立刻關(guān)閉,就可以作為守護線程來使用;反之,如果一個正在執(zhí)行某個操作的線程必須要正確地關(guān)閉掉否則就會出現(xiàn)不好的后果的話,那么這個線程就不能是守護線程,而是用戶線程。通常都是些關(guān)鍵的事務(wù),比方說,數(shù)據(jù)庫錄入或者更新,這些操作都是不能中斷的。
thread.setDaemon(true) 必須在 thread.start() 之前設(shè)置,否則會拋出一個 IllegalThreadException 異常。你不能把正在運行的常規(guī)線程設(shè)置為守護線程。
在Daemon線程中產(chǎn)生的新線程也是Daemon的。
守護線程不能用于去訪問固有資源,比如讀寫操作或者計算邏輯。因為它會在任何時候甚至在一個操作的中間發(fā)生中斷
java自帶的多線程框架,比如ExecutorService,會將守護線程轉(zhuǎn)換為用戶線程,所以如果要使用后臺線程就不能用java的線程池。
24、ThreadLocal的原理和使用場景
每一個Thread對象均含有一個ThreadLocalMap類型的成員變量threadLocals,它存儲本線程中所有ThreadLocal對象及其對應(yīng)的值
ThreadLocalMap由一個個Entry對象構(gòu)成。
Entry 繼承自 WeakReference<ThreadLocal<?>> ,一個 Entry 由 ThreadLocal 對象和 object 構(gòu)成。由此可見, Entry 的key是 ThreadLocal 對象,并且是一個弱引用,當沒指向key的強引用后,該key就會被垃圾收集器回收。
當執(zhí)行set方法時,ThreadLocal首先會獲取當前線程對象,然后獲取當前線程的ThreadLocalMap對象。再以當前ThreadLocal對象為key,將值存儲進ThreadLocalMap對象中。
get方法執(zhí)行過程類似,ThreadLocal首先會獲取當前線程對象,然后獲取當前線程的ThreadLocalMap對象。再以當前ThreadLocal對象為key,獲取對應(yīng)的value。
由于每一條線程均含有各自私有的ThreadLocalMap容器,這些容器相互獨立互不影響,因此不會存在線程安全性問題,從而也無需使用同步機制來保證多條線程訪問容器的互斥性。
使用場景:
- 1、在進行對象跨層傳遞的時候,使用ThreadLocal可以避免多次傳遞,打破層次間的約束
- 2、線程間數(shù)據(jù)隔離
- 3、進行事務(wù)操作,用于存儲線程事務(wù)信息。
25、ThreadLocal內(nèi)存泄漏問題,如何避免
內(nèi)存泄漏為程序在申請內(nèi)存后,無法釋放已申請的內(nèi)存空間,一次內(nèi)存泄漏危害可以忽略,但內(nèi)存泄漏堆積后果很嚴重,無論多少內(nèi)存,遲早會被占光。
不再會被使用的對象或者變量占用的內(nèi)存不能被回收,就是內(nèi)存泄漏。
強引用:使用最普遍的引用(new),一個對象具有強引用,不會被垃圾回收器回收。當內(nèi)存空間不足,java虛擬機寧愿拋出 OutOfMemoryError 錯誤,使程序異常終止,也不回收這種對象。
如果想取消強引用和某個對象之間的關(guān)聯(lián),可以顯式地將引用賦值為null,這樣就可以使JVM在合適的時間就會回收該對象。
弱引用:JVM進行垃圾回收時,無論內(nèi)存是否充足,都會回收被弱引用關(guān)聯(lián)的對象。在java中,用 java.lang.ref.WeakReference 類來表示。可以在緩存中使用弱引用。
ThreadLocal的實現(xiàn)原理,每一個Thread維護一個ThreadLocalMap,key為使用弱引用的ThreadLocal實例,value為線程變量的副本
2022年整理最詳細的java面試題、掌握這一套八股文、面試 ...-5.jpg (30.1 KB, 下載次數(shù): 7)
下載附件
2023-2-12 13:27 上傳
hreadLocalMap使用ThreadLocal的弱引用作為key,如果一個ThreadLocal不存在外部強引用時,key(ThreadLocal)勢必會被GC回收,這樣就會導(dǎo)致ThreadLocalMap中key為null,而value還存在這強引用,只有thead線程退出以后value的強引用鏈條才會斷掉,但如果當前現(xiàn)線程再遲遲不結(jié)束的話,這些key為null的Entry的value就會一直存在一條強引用鏈(紅色鏈條)
key使用強引用
當threadLocalMap的key為強引用回收ThreadLocal時,因為ThreadLocalMap還持有ThreadLocal的強引用,如果沒有手動刪除,ThreadLocal不會被回收,導(dǎo)致Entry內(nèi)存泄漏。
key的弱引用
當ThreadLocalMap的key為弱引用回收ThreadLocal時,由于ThreadLocalMap持有ThreadLocal的弱引用,即使沒有手動刪除,ThreadLocal也會被回收。當key為null,在下一次ThreadLocalMap調(diào)用set(),get(),remove()方法的時候會被清除value值
ThreadLocal正確的使用方法
- 每次使用完ThreadLocal都調(diào)用它的remove()方法清除數(shù)據(jù)
- 將ThreadLocal變量定義成private static,這樣就一直存在ThreadLocal的強引用,也就能保證任何時候都能通過ThreadLocal的弱引用訪問到Entry的value值,進而清除掉。
26、并發(fā)、并行、串行
- 串行在時間上不可能發(fā)生重疊,前一個任務(wù)沒搞定,下一個任務(wù)就只能等著
- 并行在時間上是重疊的,兩個任務(wù)在同一個時刻互不干擾的同時執(zhí)行
- 并發(fā)允許兩個任務(wù)彼此干擾。統(tǒng)一時間點,只有一個任務(wù)運行,交替執(zhí)行。
27、并發(fā)三大特性
原子性
- 原子性是指在一個操作中cpu不可以在中途暫停然后再調(diào)度,即不被中斷操作,要不全部執(zhí)行完成,要不都不執(zhí)行。就好比轉(zhuǎn)賬,從賬戶A向賬戶B轉(zhuǎn)1000元,那么必然包括兩個操作:從賬戶A減去1000元,往賬戶B加上1000元。2個操作必須全部完成。
可見性
- 當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其他線程能夠立即看得到修改的值。
若兩個線程在不同的cpu,那么線程A改變了i的值還沒刷新到主存,線程2又使用了i,那么這個i值肯定還是之氣的,線程1對變量的修改線程2沒有看到這就是可見性問題
有序性
- 虛擬機在進行代碼編譯時,對于那些改變順序之后不會對最終結(jié)果造成影響的代碼,虛擬機不一定會按照我們寫的代碼的順序來執(zhí)行,有可能將他們重新排序。實際上,對于有些代碼進行排序之后,雖然對變量的值沒有造成影響,但由肯能會出現(xiàn)線程安全問題
28、為什么使用線程池?解釋下線程池解釋?
1、降低資源消耗;提高線程利用率,降低創(chuàng)建和銷毀線程的消耗
2、提高響應(yīng)速度;任務(wù)來了,直接有線程可用可執(zhí)行,而不是先創(chuàng)建線程,再執(zhí)行。
3、提高線程的可管理性;線程是稀缺資源,使用線程池可以統(tǒng)一分配調(diào)優(yōu)監(jiān)控
- corepoolsize 代表核心線程數(shù),也就是正常情況下創(chuàng)建工作線程的線程數(shù),這些線程創(chuàng)建后并不會消除,而是一種常駐線程。
- maxinumpoolsize 代表的是最大的線程數(shù),它與核心線程數(shù)相對應(yīng),表示最大允許被創(chuàng)建的線程數(shù),比如當前任務(wù)較多,將核心線程數(shù)都用完了,還無法滿足需求時,此時就會創(chuàng)建新的線程,但是線程池內(nèi)線程總數(shù)不會超過最大線程數(shù)
- keepAliveTime、unit 表示超出核心線程數(shù)之外的線程的空閑存活時間,也就是核心線程不會消除,但是超出核心線程的部分線程如果空閑一定的時間會被消除,我們可以通過setKeepAliveTime來設(shè)置空閑時間
- workQueue 用來存放待執(zhí)行的任務(wù),假設(shè)我們現(xiàn)在核心線程都已被停用,還有任務(wù)進來則全部放入隊列,直到整個隊列被放滿但任務(wù)還再持續(xù)進入則會開始創(chuàng)建新的線程。
- ThreadFactory 實際上是一個線程工廠,用來生產(chǎn)線程執(zhí)行任務(wù)。我們可以選擇使用默認的創(chuàng)建工廠,產(chǎn)生的線程都會在同一個組內(nèi),擁有相同的優(yōu)先級,且都不是守護線程。當然我們也可以選擇自定義線程工廠,一般我們會根據(jù)業(yè)務(wù)來指定不同的線程工廠
- handler任務(wù)拒絕策略,有兩種情況,第一種是當我們調(diào)用shutdown等方法關(guān)閉線程池后,這時候即使線程池內(nèi)部還有執(zhí)行完的任務(wù)正在執(zhí)行,但是由于線程池已經(jīng)關(guān)閉,我們在繼續(xù)向線程池提交任務(wù)就會遭到拒絕。另一種情況就是當達到最大線程數(shù),線程池已經(jīng)沒有能力繼續(xù)處理新提交的任務(wù)時,這也是拒絕
29、線程池處理流程
2022年整理最詳細的java面試題、掌握這一套八股文、面試 ...-6.jpg (72.58 KB, 下載次數(shù): 8)
下載附件
2023-2-12 13:27 上傳
30、線程池中阻塞隊列的作用?為什么是先添加隊列而不是先創(chuàng)建最大線程?
1、一般的隊列只能保證作為一個有限長度的緩沖區(qū),如果超出了緩沖長度,就無法保留當前的任務(wù)了,阻塞隊列通過阻塞可以保留住當前想要繼續(xù)入隊的任務(wù)。
阻塞隊列可以保證任務(wù)隊列中沒有任務(wù)時阻塞獲取任務(wù)的線程,使得線程進入wait狀態(tài),釋放cpu資源。
阻塞隊列自帶阻塞和喚醒功能,不需要額外處理,無任務(wù)執(zhí)行時,線程池利用阻塞隊列的take方法掛起,從而維持核心線程的存活,不至于一直占用cpu資源
2、在創(chuàng)建新線程的時候,是要獲取全局鎖的,這個時候其它的就得阻塞,影響了整體效率。
就好比一個企業(yè)里有10個(core)正式工的名額,最多招10個正式工,要是任務(wù)超過正式人工數(shù)(task>core)的情況下、工廠領(lǐng)導(dǎo)(線程池)不是首先擴招工人,還是這10個人,但是任務(wù)可以稍微積壓一下,即先放到隊列區(qū)(代價低)。10個正式工慢慢干,遲早會干完的。要是任務(wù)還在繼續(xù)增加,超過正式工的加班忍耐極限了(隊列滿了),就的招外包幫忙了(注意是臨時工)要是正式工加上外包還是不能完成任務(wù),那新來的任務(wù)就會被領(lǐng)導(dǎo)拒絕了(線程池的拒絕策略)
31、線程池復(fù)用的原理
- 線程池將線程和任務(wù)進行解耦,線程是線程,任務(wù)是任務(wù),擺脫了之前通過Thread創(chuàng)建線程時的一個線程必須對應(yīng)一個任務(wù)的限制。
- 在線程池中,同一個線程可以從阻塞隊列中不斷獲取新任務(wù)來執(zhí)行,其核心原理在于線程池對Thread進行了封裝,并不是每次執(zhí)行任務(wù)都會調(diào)用Thread.start()來創(chuàng)建線程,而是讓每個線程去執(zhí)行一個“循環(huán)任務(wù)”,在這個“循環(huán)任務(wù)”中不停檢查是否有任務(wù)需要被執(zhí)行,也就是調(diào)用任務(wù)中的run方法,將run方法當成一個普通的方法執(zhí)行,通過這種方式只使用固定的線程就將所有任務(wù)的run方法串聯(lián)起來
32、spring是什么?
輕量級的開源的J2EE框架,它是一個容器框架,用來裝javabean(java對象),中間層框架(萬能膠)可以起一個連接作用,比如說把Structs和hibernate粘合在一起運用,可以讓我們的企業(yè)開發(fā)更快,更簡潔
Spring是一個輕量級的控制反轉(zhuǎn)(IOC)和面向切面(AOP)的容器框架
- 從大小與開銷兩方面而言Spring都是輕量級的。
- 通過控制反轉(zhuǎn)(IOC)的技術(shù)達到松耦合的目的。
- 提供了面向切面編程的豐富支持,允許通過分離應(yīng)用的業(yè)務(wù)邏輯與系統(tǒng)級服務(wù)進行內(nèi)聚性的開發(fā)。
- 包含并管理應(yīng)用對象(Bean)的配置和生命周期,這個意義上是一個容器
- 將簡單的組件配置,組合成為復(fù)雜的應(yīng)用,這個意思上是一個框架。
33、對Aop的理解
- 系統(tǒng)是由許多不同的組件所組成的每一個組件各負責(zé)一塊特定功能。除了實現(xiàn)自身核心功能之外,這些組件還經(jīng)常承擔(dān)著額外的職責(zé)。例如日志,事務(wù)管理和安全這樣的核心服務(wù)經(jīng)常融入到自身具有核心業(yè)務(wù)邏輯的組件中去。這些系統(tǒng)服務(wù)經(jīng)常被稱為橫切關(guān)注點,因為它們會跨越系統(tǒng)的多個組件。
- 當我們需要為分散的對象引入公共行為的時候,O0P則顯得無能為力。也就是說,OOP允許你定義從上到下的關(guān)系,但并不適合定義從左到右的關(guān)系,例如日志功能。
- 日志代碼往往水平地散布在所有對象層次中,而與它所散布到的對象的核心功能毫無關(guān)系
- 在OOP設(shè)計中,它導(dǎo)致了大量代碼的重復(fù),而不利于各個模塊的復(fù)用
- AOP:將程序中的交叉業(yè)務(wù)邏輯(比如安全、日志、事務(wù)等),封裝成了一個切面,然后注入到目標對象(具體業(yè)務(wù)邏輯)中去,AOP可以對某個對象或某些對象的功能進行增強,比如對象中的方法進行增強,可以在執(zhí)行某個方法之前額外的做一些事情,在某個方法執(zhí)行之后額外的做一些事情
34、對IOC的理解
容器概念、控制反轉(zhuǎn)、依賴注入
IOC容器:
- 實際上就是個map(key,value),里面存的是各種對象(在xml里配置的bean節(jié)點,@repository、@service、@controller、@component),在項目啟動時的時候會讀取配置文件里面的bean節(jié)點,根據(jù)全限定類名使用反射創(chuàng)建對象放到map里,掃描到打上上述注解的類還是通過反射創(chuàng)建對象放到map里。
- 這個時候map里就有各種對象了,接下來我們在代碼里需要用到里面的對象時,再通過DI注入(autowired、resource等注解,xml里面bean節(jié)點內(nèi)的ref屬性,項目啟動時候會讀取xml節(jié)點ref屬性更具DI注入,也會掃描這些注解,根據(jù)類型或DI注入,id就是對象名)
控制反轉(zhuǎn)
- 沒有引入IOC容器之前,對象A依賴與對象B,那么對象A在初始化或者在運行到某一點的時候,自己必須主動去創(chuàng)建對象B或者使用已經(jīng)創(chuàng)建的對象B。無論是創(chuàng)建還是使對象B,控制權(quán)都在自己手上。
- 引入IOC容器之后,對象A與對象B之間失去了直接關(guān)系,當對象A運行到需要對象B的時候,IOC容器會主動去創(chuàng)建一個對象B注入到對象A需要的地方
- 通過前后的對比,不難看出:對象A獲得依賴對象B的過程由主動行為變?yōu)榱吮粍有形?,控制?quán)顛倒過來了,這就是“控制反轉(zhuǎn)”這個名稱的由來
全部對象的控制權(quán)全部上繳給“第三方“IOC容器、所以,IOC容器成了整個系統(tǒng)的關(guān)鍵核心,它起到了一種類似“粘合劑” 的作用,把系統(tǒng)中的所有對象粘合在一起發(fā)揮作用,如果沒有這個“粘合劑”,對象與對象之間會彼此失去聯(lián)系,這就是有人把IOC容器比喻成“粘合劑”的由來
依賴注入
- 獲得依賴對象的過程被反轉(zhuǎn)了。控制被反轉(zhuǎn)之后,獲得對象依賴對象的過程由自身管理變?yōu)榱擞蒊OC容器主動注入。依賴注入是實現(xiàn)IOC的方法,就是由IOC容器在運行期間,動態(tài)地將某種依賴關(guān)系注入到對象中。
35、BeanFactory和ApplicationContext有什么什么區(qū)別?
ApplicationContext是BeanFactory的子接口
ApplicationContext提供了更完整的功能:
- 繼承了MessageSource,因此支持國際化
- 統(tǒng)一的資源文件訪問方式
- 提供在監(jiān)聽器中注冊bean的事件
- 同時加載多個配置文件
- 載入多個(有繼承關(guān)系)上下文,使得每一個上下文都專注與一個特定的層次,比如應(yīng)用的web層
BeanFactory 采用的是延遲加載形式來注入Bean的,即只有在使用到某個Bean時(調(diào)用getBean()),才對該Bean進行加載實例化。這樣、我們就不能發(fā)現(xiàn)一些存在的Spring配置問題。如果Bean的某一個屬性沒有注入,BeanFactory加載后,直至第一次使用調(diào)用getBean()方法才會拋出異常
ApplicationContext ,它是在容器啟動時,一次性創(chuàng)建了所有的Bean。這樣,在容器啟動時,我們就可以發(fā)現(xiàn)Spring中存在的配置錯誤,這樣有利于檢查所依賴屬性是否注入。 ApplicationContext 啟動后預(yù)載入所有的單實例Bean,確保當你需要的時候,你就不用在等待,因為他們已經(jīng)創(chuàng)建好了。
相對于基本的 BeanFactory , ApplicationContext 唯一的不足是占用內(nèi)存空間。當應(yīng)用程序配置Bean較多時,程序啟動較慢。
BeanFactory 通常以編程的方式被創(chuàng)建, ApplicationContext 還能以聲明的方式創(chuàng)建,如使用 ContextLoader
BeanFactory 和 ApplicationContext 都支持 BeanPostProcessor 、 BeanFactoryPostProcessor 的使用,但兩者之間的區(qū)別是: BeanFactory 需要手動注冊,而 ApplicationContext 則是自動注冊
36、簡述spring bean的生命周期
- 1、解析類得到的 BeanDefinition
- 2、如果有多個構(gòu)造方法后,則要推斷構(gòu)造方法
- 3、確定好構(gòu)造方法后,進行實例化得到一個對象
- 4、對對象中的家里 @Autowired 注解的屬性進行屬性填充
- 5、回調(diào)Aware方法,比如 BeanNameAware , BeanFactoryAware
- 6、調(diào)用 BeanPostProcessor 的初始化前的方法
- 7、調(diào)用初始化方法
- 8、調(diào)用 BeanPostProcessor 的初始化后的方法,在這里進行AOP
- 9、如果當前創(chuàng)建的bean是單例的則會把bean放入單例池
- 10、使用bean
- 11、Spring容器關(guān)閉時調(diào)用 DisposableBean 中destory()方法
37、spring支持的幾種bean作用域
- singleton:默認,每個容器中只有一個bean的實例,單例的模式由BeanFactory自身來維護。該對象的生命周期是與Spring IOC容器一致的(但是第一次被注入時才會創(chuàng)建)
- prototype:為每一個bean請求提供一個實例。在每次注入時都會創(chuàng)建一個新的對象
- request:bean被定義為在每個HTTP請求中創(chuàng)建一個單例對象,也就是說在單個請求中都會復(fù)用這一個單例對象
- session:與request范圍類似,確保每個session中有一個bean的實例,在sesssion過期后,bean會隨之失效
- application:bean被定義為在servletContext的生命周期中復(fù)用一個單例對象。
- websocket:bean被定義為在websocket的生命周期中復(fù)用一個單例對象。
global-session:全局作用域,global-session和Portlet應(yīng)用相關(guān)。當你的應(yīng)用部署在Portlet容器中工作時,它包含很多portlet。如果你想要聲明讓所有的portlet共用全局的存儲變量的話,那么這全局變量需要存在 global-session 中。全局作用域與servlet中的session作用域效果相同。
38、Spring框架中的單例Bean是線程安全的嗎
Spring中的Bean默認是單例模式的,框架并沒有對bean進行多線程的封裝處理。
如果Bean是有狀態(tài)的 那就需要開發(fā)人員自己來進行線程安全的保證,最簡單的方法就是改變bean的作用域 把”singleton&#34;改為“protopyte&#34;這樣每次請求Bean就相當于是 new Bean()這樣就可以保證線程安全了。
- 有效狀態(tài)就是有數(shù)據(jù)存儲功能
- 無效狀態(tài)就是不會保存數(shù)據(jù) controller、service和dao層本身并不是線程安全的,只是如果只是調(diào)用里面的方法,而且多線程調(diào)用一個實例的方法,會在內(nèi)存中復(fù)制變量,這是自己的線程的工作內(nèi)存,是安全的。
Dao會操作數(shù)據(jù)庫Connection、Connection是帶有狀態(tài)的,比如說數(shù)據(jù)庫事務(wù)、spring的事務(wù)管理器使用
Threadlocal為不同線程維護了一套獨立的connection副本,保證線程之間不會相互影響(spring是如何保證事務(wù)獲取同一個Connection的)
不要在bean中聲明任何有狀態(tài)的實例變量或類變量,如果必須如此,那么就使用Threadlocal把變量變?yōu)樗接芯€程有的,如果bean的實例變量或類變量需要在多個線程之間共享,那么就只能使用synchronized、lock、CAS等這些實現(xiàn)線程同步的方法了。
39、spring框架中使用了哪些設(shè)計模式及應(yīng)用場景
簡單工廠:由一個工廠類根據(jù)傳入的參數(shù),動態(tài)決定應(yīng)該創(chuàng)建哪一個產(chǎn)品類
- Spring中的BeaFactory就是簡單工廠模式的體現(xiàn),根據(jù)傳入一個唯一的標識符來獲得Bean對象,但是否是在傳入?yún)?shù)后創(chuàng)建還是傳入?yún)?shù)前創(chuàng)建這個要根據(jù)具體情況來定
工廠方法:
- 實現(xiàn)了FactoryBean接口的bean是一類叫做factory的bean,其特點是,spring會再使用getBean()調(diào)用獲得該bean時,會自動調(diào)用該bean的getObject()方法,所以返回的不是factory這個bean,而是這個bean.getObject()方法的返回值。
單例模式:保證一個類僅有一個實例,并提供了一個訪問它的全局訪問點
- spring對單例的實現(xiàn):spring中的單例模式完成了后半句話,即提供了全局的訪問點BeanFactory,但沒有從構(gòu)造器級別去控制單例,這是因為spring管理的時任意的java對象
適配器模式
- spring定義了一個適配接口,使得每一個Controller有一種對應(yīng)的適配器實現(xiàn)類,讓適配器代替controller執(zhí)行相應(yīng)的方法,這樣在擴展controller時,只需要增加一個適配器類就完成了spirngmvc的擴展了
裝飾模式:動態(tài)地給一個對象添加一些額外的職責(zé)。就是增加功能來說,Decorator模式相比生成子類更為靈活。
- spring中用到的包裝器模式在類名上有兩種表現(xiàn),一種是類名中含有 wrapper ,另一種是類名中含有 Decorator 。
動態(tài)代理:
- 切面在應(yīng)用運行時的時刻被織入,在織入切面時,AOP容器會為目標對象創(chuàng)建動態(tài)的創(chuàng)建一個代理對象。SpringAOP就是以這種方式織入切面的。
- 織入:把切面應(yīng)用到目標對象并創(chuàng)建新的代理對象的過程
觀察者模式:
- spring的事件驅(qū)動模型使用的是:觀察者模式,Spring中obserber模式常用的地方是listener的實現(xiàn)
策略模式:
- spring框架的資源訪問Resource接口,該接口提供了更強的資源訪問能力,Spring框架本身大量使用了Resource接口來訪問底層資源
40、spring事務(wù)的實現(xiàn)方式原理以及隔離級別
在使用Spring框架時,可以有兩種使用事務(wù)的方式,一種是=編程式的,一種是申明式的,@Transactiona注解就是申明式的。
首先,事務(wù)這個概念是數(shù)據(jù)庫層面的,Spring只是基于數(shù)據(jù)庫中的事務(wù)進行了擴展,以及提供一些能讓程序員更加方便操作事務(wù)的方式。
比如我們可以提通過在某個方法上增加了 @Transactionanl 注解,就可以開啟事務(wù),這個方法中所有的的sql都會在一個事務(wù)中執(zhí)行,統(tǒng)一超過或失敗。
在一個方法上加了 @Transactional 注解后,Spring會基于這個類生成一個代理對象,會將這個代理對象作為bean,當在使用這個代理對象的方法時,如果這個方法存在 @Transactional 注解,那么代理邏輯就會先把事務(wù)的自動提交設(shè)置為false,然后再去執(zhí)行原本的業(yè)務(wù)邏輯方法,如果執(zhí)行業(yè)務(wù)邏輯方法沒有出現(xiàn)異常,那么代理邏輯中就會將事務(wù)進行提交,如果執(zhí)行業(yè)務(wù)邏輯方法出現(xiàn)了異常,那么會將事務(wù)進行回滾。
當然,針對哪些異?;貪L事務(wù)是可以配置的,可以利用 @Transactional 注解中的rollbackFor屬性進行配置,默認情況下會對 RuntimeException 進行回滾。
spring事務(wù)隔離級別就是數(shù)據(jù)庫的隔離級別:外加一個默認級別
- read uncommitted(未提交讀)
- read committed (提交讀、不可重復(fù)讀)
- repeatable read (可重復(fù)讀)
- serializable (可串行化)
數(shù)據(jù)庫的配置隔離級別是 Read Committed,而spirng配置的隔離級別是Repeatable Read,請問這時隔離級別是以哪個為準?
以Spring配置的為準,如果spring設(shè)置的隔離級別數(shù)據(jù)庫不支持,效果取決于數(shù)據(jù)庫。
41、spring的事務(wù)傳播機制
多個事務(wù)方法相互調(diào)用時,事務(wù)如何在這些方法間傳播
方法A是一個事務(wù)的方法,方法A執(zhí)行過程中調(diào)用了方法B,那么方法B有無事務(wù)以及方法B對事務(wù)的要求不同都會對方法A的事務(wù)具體執(zhí)行造成影響,同時方法A的事務(wù)對方法B的事務(wù)執(zhí)行也有影響,這種影響具體是什么就由兩個方法所定義的事務(wù)傳播類型所決定。
- REQUIRED (spring默認的事務(wù)傳播類型):如果當前沒有事務(wù),則自己新建一個事務(wù),如果當前存在事務(wù),則加入這個事務(wù)
- SUPPORTS :當前存在事務(wù),則加入當前事務(wù),如果當前沒有事務(wù),就以非事務(wù)方法執(zhí)行
- MANDATORY :當前存在事務(wù),則加入當前事務(wù),如果當前事務(wù)不存在,則拋出異常
- REQUIRES_NEW :創(chuàng)建一個新事務(wù),如果存在當前事務(wù),則掛起該事務(wù)
- NOT_SUPPORTED :以非事務(wù)方式執(zhí)行,如果當前存在事務(wù),則掛起當前事務(wù)
- NEVER :不適用事務(wù),如果當前事務(wù)存在,則拋出異常
- NESTED :如果當前事務(wù)存在,則在嵌套事務(wù)中執(zhí)行,否則REQUIRED的操作一樣(開啟一個事務(wù))
和 REQUIRES_NEW 的區(qū)別:
- REQUIRES_NEW 是新建一個事務(wù)并且新開啟的這個事務(wù)與原有事務(wù)無關(guān),而 NESTED 則是當前存在事務(wù)時(我們把當前事務(wù)稱之為父事務(wù))會開啟一個嵌套事務(wù)(稱之為一個子事務(wù))。在 NESTED 情況下父事務(wù)回滾時,子事務(wù)也會回滾,而在 REQUIRES_NEW 情況下,原有事務(wù)回滾,不會影響新開啟的事務(wù)
和REQUIRED的區(qū)別
- REQUIRED情況下,調(diào)用方法存在事務(wù)時,則被調(diào)用方和調(diào)用方使用同一事務(wù),那么被調(diào)用方法出現(xiàn)異常時,由于共用一個事務(wù),所有無論調(diào)用方是否catch其異常,事務(wù)都會回滾 。而在NESTED情況下,被調(diào)用方發(fā)生異常時,調(diào)用方可以catch其異常,這樣只有子事務(wù)回滾,父事務(wù)不受影響。
42 、spring事務(wù)什么時候會失效
spring事務(wù)的原理AOP,進行了切面增強,那么失效的根本原因是這個AOP不起作用了!常見情況有如下幾種
- 1 、 發(fā)生自調(diào)用,類里面使用this調(diào)用本類的方法(this通常省略),此時這個this對象不是代理類,而是UserService對象本身!
解決辦法很簡單:讓那個this變成UserService的代理類即可。
- 2、方法不是public的
@Transactional 只能用于public 的方法上,否則事務(wù)不會失效,如果要用在非public方法上,可以開啟Aspectj代理模式。
- 3 、數(shù)據(jù)庫不支持事務(wù)
- 4 、沒有被sprin管理
- 5 、異常被吃掉,事務(wù)不會回滾(或者拋出的異常沒有被定義,默認為RuntimeException)
43 、什么的是bean的自動裝配、有哪些方式?
開啟自動裝配,只需要在xml配置文件 <bean> 中定義“autowire&#34;屬性。
<bean id=&#34;cutomer&#34; class = &#34;com.xxx.xxx.Customer&#34; autowire=&#34; &#34; />
autowire屬性有五種裝配的方式:
- 1 、 no -缺省情況下,自動配置是通過”ref&#34;屬性手動設(shè)定。
手動裝配:以value或ref的方式明確指定屬性值都是手動裝配 需要通過ref屬性來連接bean
- 2 、byName-根據(jù)bean的屬性名稱進行自動裝配。
Customer的屬性名稱是person,Spring會將bean id為person的bean通過setter方法進行自動裝配。 <bean id = &#34;customer&#34; class = &#34;com.xxx.xxx.Cutomer&#34; autowire =&#34;byName&#34; /> <bean id =&#34;person&#34; class =&#34;com.xxx.xxx.Person&#34; />
- 3 、byType-根據(jù)bean的類型進行自動裝配
Cutomer的屬性person的類型為Person,Spring會將Person類型通過setter方法進行自動裝配。 <bean id =&#34;cutomer&#34; class = &#34;com.xxx.xxx.Cutomer&#34; autowire =&#34;byType&#34; /> <bean id = &#34;person&#34; class =&#34;com.xxx.xxx.Person&#34; />
- 4 、 constructor - 類似byType,不過是應(yīng)用于構(gòu)造器的參數(shù)。如果一個bean與構(gòu)造器參數(shù)的類型相同,則進行自動裝配,否則導(dǎo)致異常。
Cutomer構(gòu)造函數(shù)的參數(shù)person的類型為Person,Spring會將Person類型通過構(gòu)造方法進行自動裝配。 <bean id =&#34;cutomer&#34; class =&#34;com.xxx.xxx.Cutomer&#34; autowire =&#34;construtor&#34; /> <bean id =&#34;person&#34; class =&#34;com.xxx.xxx.Person&#34; />
- 5 、autodetect -如果有默認的構(gòu)造器,則通過constructor方式進行自動裝配,否則使用byType方式進行自動裝配。
如果有默認的構(gòu)造器,則通過constructor方式進行自動裝配,否則使用byType方式進行自動裝配。
@Autowired自動裝配bean,可以在字段、setter方法,構(gòu)造函數(shù)上使用。
44 、spring 、 springmvc 、springboot的區(qū)別
- spring是一個IOC容器,用來管理Bean,使用依賴注入實現(xiàn)控制反轉(zhuǎn),可以很方便的整合各種框架,提供AOP機制彌補OOP的代碼重復(fù)問題、更方便將不同類不同方法中的共同處理抽取成切面、自動注入給方法執(zhí)行,比如日志、異常等
- springmvc是spring對web框架的一個解決方案,提供了一個總的前端控制器servlet,用來接受請求,然后定義了一套路由策略(url到handle的映射)及適配執(zhí)行handle,將handle結(jié)果使用視圖解析器技術(shù)生成視圖展現(xiàn)給前端
- springboot是spring提供的一個快速開發(fā)工具包,讓程序員能更方便、更快速的開發(fā)spring+springmvc應(yīng)用,簡化了配置(約定了默認配置),整合了一些列的解決方案(starter機制)、redis、mongodb、es、可以開箱即用。
45 、springmvc的工作流程
- 1 、用戶發(fā)送請求至前端控制器DispatcherServlet.
- 2 、DispatcherServlet收到請求調(diào)用HandlerMapping處理器映射器
- 3 、處理器映射器找到具體的處理器(可以根據(jù)xml配置,注解進行查找),生成處理器及處理器攔截器(如果有則生成)一并返回給DispatcherServlet
- 4、DispatcherServlet調(diào)用HandlerAdapter處理器適配器
- 5、HandlerAdapter經(jīng)過適配器調(diào)用具體的處理器(Controller,也叫后端控制器)
- 6、 Controller執(zhí)行完成返回ModelAndView。
- 7、 HandlerAdapter將controller執(zhí)行結(jié)果ModelAndView返回給DispatcherServlet。
- 8、DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器
- 9、ViewReslover解析后返回具體View
- 10、DispatcherServlet根據(jù)View進行渲染視圖(即將模型數(shù)據(jù)填充至視圖中)
- 11、DispatcherServlet響應(yīng)用戶
46 、Spring MVC的主要組件
Handler:也就是處理器。它直接應(yīng)對著MVC中的C也就是Controller層,它的具體表現(xiàn)形式有很多,可以是類,可以是方法。在Controller層中 @RequestMapping 標注的所有方法都可以看成一個Handler,只要可以實際處理請求就可以是Handler。
- 1 、HandlerMapping
initHandlerMappings(context),處理器映射器,根據(jù)用戶請求的資源url來查找Handler的,在springMVC中會有很多請求,每個請求都需要一個Handler處理,具體接收到一個請求之后使用哪個Handler進行,這個就是HandlerMapping需要做的事。
- 2 、HandlerAdapter
initHandlerAdapters(context),適配器。因為SpringMVC中的Handler可以是任意形式,只要能處理請求就ok,但是servlet需要的處理方法的結(jié)構(gòu)卻是固定的,都是以request和response為參數(shù)的方法。如何讓固定的Servlet處理方法調(diào)用靈活的Handler來進行處理呢?這就是HandlerAdapter要做的事情。
Handler是用來干活的工具;HanderMapping用于根據(jù)需要干的活找到相應(yīng)的工具;HandlerAdapter是使用工具干活的人。
- 3 、HandlerExceptionResolver
initHandlerExceptionResolvers(context),其它組件都是用來干活的。在干活的過程中難免會出現(xiàn)問題,出問題后咋辦?這就需要有一個專門的角色對異常情況進行處理,在SpringMVC中就是HandlerExceptionResolver。具體來說,此組件的作用是根據(jù)異常設(shè)置ModelAndView,之后再交給rende方法進行渲染。
- 4 、ViewResolver
initViewResolver(context),ViewResolver用來將String類型的視圖名和Local解析為View類型的視圖。View是用來渲染頁面的,也就是將程序返回的參數(shù)填入模板里,生成html(也可能是其他類型)文件。這里就有兩個關(guān)鍵問題:使用哪個模板?用什么技術(shù)(規(guī)則)填入?yún)?shù)?這其實是ViewResolver主要做的工作,ViewResolver需要找到渲染所用的模板和所用的技術(shù)(也就是視圖的類型)進行渲染,具體的渲染過程則交由不同的視圖自己完成。
- 5 、RequestToViewNameTranslator
initRequestToViewNameTranslator(context),ViewResolver是根據(jù)ViewName查找View,但有的Handler處理完后并沒有設(shè)置ViewName,這時就需要從request獲取ViewName了,如何從request中獲取ViewName就是RequesToViewNameTranslator要做的事情。RequestToViewNameTranslator再SpringMVC容器里只可以配置一個,所以所有rqeust到ViewName的轉(zhuǎn)換規(guī)則都要在一個Translator里面全部實現(xiàn)。
- 6 、LocalResolver
initLocalResolver(context),解析視圖需要兩個參數(shù):一是視圖名,另一個是Locale。視圖名是處理器返回的,Locale是從哪里來的?這就是LocaleResolver要做的事情。LocalResolver用于從reqeust解析出Locale,locale就是zh-cn之類,表示一個區(qū)域,有了這個就可以對不同的區(qū)域的用戶顯示不同的結(jié)果。SpringMVC主要有兩個地方用到了Locale:一層是ViewResolver視圖解析的時候,二是用到國際化資源或者主題的時候。
- 7 、ThemeResolver
iniThemeResolver(context),用于解析主題。springMVC中一個主題對應(yīng)一個properties文件,里面存放著跟當前主題相關(guān)的所有資源、如圖片、css樣式等。SpringMVC的主題也支持國際化,同一個主題不同區(qū)域也可以顯示不同的風(fēng)格。SpringMVC中跟主題相關(guān)的類有ThemeResolver、ThemeSource和Theme。主題是通過一系列資源來具體體現(xiàn)的,要得到要給主題的資源,首先要得到資源的名稱,這是ThemeResover的工作。然后通過主題名稱找到對應(yīng)的主題(可以理解為一個配置)文件,這是ThemeSource的工作。最后從主題中獲取資源就可以了
- 8 、MultipartResolver
- initMultipartResolver(context),用于處理上傳請求。處理方法是將普通的rrequest包裝成MultipartHttpServletRequest,后者可以直接調(diào)用getFile方法獲取File,如果上傳多個文件,還可以調(diào)用getFileMap得到FileName->File結(jié)構(gòu)的Map.此組件中一共有三個方法,作用分別是判斷是不是上傳請求,將reqeust包裝成MultipartHttpServletRequest、處理完后清理上傳過程中產(chǎn)生的臨時資源
- 9 、FlashMapManager
initFlashMapManager(context),用來管理FlashMap的,F(xiàn)lashMap主要用在redirect中傳遞參數(shù)。
47 、springboot自動配置原理
@import+@Configuration+Spring spi
自動配置類由各個starter提供,使用@Configuration+@Bean定義配置類,放到META-INF/spring。factories下使用Spring spi 掃描META-INF/spring.factories下的配置類
使用@import導(dǎo)入自動裝配類。
48 、 如何理解springboot的starter
使用spring+springmvc使用,如果需要引入mybatis等框架,需要到xml中定義mybatis需要的bean
starter就是定義要給starter的jar包,寫一個@Configuration配置類、將這些bean定義在里面,然后再starter包的META-INF/spring.factories中寫入該配置類,springboot會按照約定來加載該配置類。
開發(fā)人員只需要將相應(yīng)的starter包依賴進應(yīng)用,進行相應(yīng)的屬性配置(使用默認配置時,不許要配置),就可以直接進行代碼開發(fā),使用對應(yīng)的功能了,比如mybatis-spring-boot-starter,spring-boot-starter-redis
49 、 什么是嵌入式服務(wù)器,為什么使用嵌入式服務(wù)器
節(jié)省了下載安裝tomcat,應(yīng)用也不再需要打war包,然后放到webapp目錄下再運行,只需要一個安裝了java的虛擬機,就可以直接在上面部署應(yīng)用程序了。springboot已經(jīng)內(nèi)置了tomcat.jar。運行main方法時會去啟動tomcat,并利用tomcat的spi機制加載springmvc
50、mybatis的優(yōu)缺點
優(yōu)點:
- 1 、基于sql語句編程,相當靈活,不會對應(yīng)用程序或者數(shù)據(jù)庫的現(xiàn)有設(shè)計造成任何影響,sql寫在XML文件里,解除sql與程序代碼的耦合,便于統(tǒng)一管理;提供了XML標簽,支持編寫動態(tài)sql語句,并可重用。
- 2 、與JDBC相比,減少了50%以上的代碼量,消除了大量冗余的代碼,不需要手動開關(guān)連接;
- 3 、很好的與各種數(shù)據(jù)庫兼容(因為MyBatis使用JDBC來連接數(shù)據(jù)庫,所以只要JDBC支持的數(shù)據(jù)庫MyBatis都支持)
- 4 、能夠與Spring很好的集成
- 5 、提供映射標簽,支持對象與數(shù)據(jù)庫的ORM字段映射;提供對象關(guān)系映射標簽,支持對象關(guān)系組件維護。
缺點:
- 1、SQL語句的編寫工作量較大,尤其字段多、關(guān)聯(lián)表多時,對開發(fā)人員編寫SQl語句的功底有一定的要求。
- 2、SQL語句依賴于數(shù)據(jù)庫,導(dǎo)致數(shù)據(jù)庫移植性差,不能隨意更換數(shù)據(jù)庫
51、mybatis和hibernate的對比
面向?qū)ο?/strong>
SQL和ORM的爭論,永遠都不會終止
開發(fā)速度的對比:
- Hibernate的真正掌握要比Mybatis難些。Mybatis框架相對簡單容易上手,但也相對簡陋些。
比起兩者的開發(fā)速度,不僅僅要考慮到兩者的特性及性能,更要根據(jù)項目需求去考慮究竟哪一個更適合項目開發(fā),比如:一個項目中用到的復(fù)雜查詢基本沒有,就是簡單的增刪改查,這樣選擇Hibernate
效率就很快了,因為基本的sql語句已經(jīng)被封裝好了,根本不需要你去寫sql語句,這就節(jié)省了大量的時間,但是對于一個大型項目,復(fù)雜語句較多,這樣再去選擇Hibernate就不是一個太好的選擇,選擇mybatis就會加快許多,而且語句的管理也比較方便。
開發(fā)工作量的對比:
- Hibernate和Mybatis都有相應(yīng)的代碼生成工具??梢陨珊唵蔚腄AO方法。針對高級查詢,Mybatis需要手動編寫sql語句,以及ResultMap。而Hibernate有良好的映射機制,開發(fā)者無需關(guān)心sql的生成與結(jié)果映射,可以更加專注于業(yè)務(wù)流程。
sql優(yōu)化方面:
- Hibernate的查詢會將表中的所有字段查詢出來,這一點會有性能消耗。Hibernate也可以自己寫SQL來指定需要查詢的字段,但這樣就破壞了Hibernate開發(fā)的簡潔性。而Mybatis的SQL是手動編寫的,所以可以按需求指定查詢的字段。
Hibernate HQL語句的調(diào)優(yōu)需要將SQL打印出來,而Hibernate的SQL被很多人嫌棄因為太丑了。Mybatis的sql是自己手動寫的所以調(diào)整方便。但Hibernate具有自己的日志系統(tǒng)。mybatis本身不帶日志系統(tǒng),使用log4進行日志記錄。
對象管理的對比
- Hibernate是完整的對象/關(guān)系映射解決方案,它提供了對象狀態(tài)管理(state management)的功能,使開發(fā)者不再需要理會底層數(shù)據(jù)庫系統(tǒng)的細節(jié)。也就是說,相對于常見的JDBC/SQL持久層方案中需要管理SQL語句,Hibernate采用了更自然的面向?qū)ο蟮囊暯莵沓志没痡ava應(yīng)用中的數(shù)據(jù)。
換句話說,使用Hibernate的開發(fā)者應(yīng)該總是關(guān)注對象的狀態(tài)(state),不必考慮SQL語句的執(zhí)行。這部分細節(jié)已經(jīng)由Hibernate掌管妥當,只有當開發(fā)者在進行系統(tǒng)性能調(diào)優(yōu)的時候才需要進行了解。而Mybatis在這一塊沒有文檔說明,用戶需要對對象自己進行詳細的管理。
緩存機制對比:
- 相同點:都可以實現(xiàn)自己的緩存或使用其他第三方緩存方案,創(chuàng)建適配器來完全覆蓋緩存行為。
- 不同點:Hibernate的二級緩存配置在SessionFactory生成的配置文件中進行詳細配置,然后再具體的表-對象映射中配置是哪種緩存。
Mybatis的二級緩存配置都是在每個具體的表–對象映射中進行詳細配置,這樣針對不同的表可以自定義不同的緩存機制。并且Mybatis可以在命名空間中共享相同的緩存配置和實例,通過Cache-ref來實現(xiàn)。
兩者比較:因為Hibernate對查詢對象有著良好的管理機制,用戶無需關(guān)心SQL,所以在使用二級緩存時如果出現(xiàn)臟數(shù)據(jù),系統(tǒng)會報出錯誤并提示。
而mybatis在這一方面,使用二級緩存時需要特別小心。如果不能完全確定數(shù)據(jù)更新操作的波及范圍,避免Cache的盲目使用。否則,臟數(shù)據(jù)的出現(xiàn)會給系統(tǒng)的正常運行帶來很大的隱患。
- Hibernate功能強大,數(shù)據(jù)庫無關(guān)性好,O/R映射能力強,如果你對Hibernate相當精通,而且對Hibernate進行了適當?shù)姆庋b,那么你的整個項目持久性代碼會相當簡單,需要寫的代碼少,開發(fā)速度很快,很爽。
- Hibernate的缺點就是學(xué)習(xí)門檻不低,要精通門檻更高,而且怎么設(shè)計O/R映射,在性能和對象模型之間如何權(quán)衡取得平衡,以及怎樣用好Hibernate方面需要你的經(jīng)驗和能力都很強才行。
- IBATIS入門簡單,即學(xué)即用,提供了數(shù)據(jù)庫查詢的自動對象綁定功能,而且延續(xù)了很好的SQL使用經(jīng)驗,對于沒有那么高的對象模型要求的項目來說,相當完美。
- IBATIS的缺點就是框架還是比較簡陋,功能尚有缺失,雖然簡化了數(shù)據(jù)綁定代碼,但是整個底層數(shù)據(jù)庫查詢實際還是要自己寫的,工作量也比較大,而且不太容易適應(yīng)快速數(shù)據(jù)庫修改。
52 、#{}和${}的區(qū)別
- #{} 是預(yù)編譯處理、是占位符, ${} 是字符串替換,是拼接符
- Mybatis在處理 #{} 時,會將sql中的 #{} 替換為?,調(diào)用 PrepareStatement 來賦值;
- Mybatis在處理 ${} 時,就是把 ${} 替換成變量的值,調(diào)用 Statement 來賦值
- #{}的變量替換是在DBMS中,變量替換后,#{}對應(yīng)的變量自動加上單引號
- ${} 的變量替換是在DBMS外,變量替換后, ${}對應(yīng)的變量不會加上單引號
- 使用#{}可以有校的防止SQL注入,提高系統(tǒng)安全性。
53 、mybatis插件運行原理及開發(fā)流程
Mybatis只支持針對 ParameterHandler 、 ResultSetHandler 、 StatementHandler 、 Executor 這四種接口的插件,Mybatis使用JDK的動態(tài)代理,為i需要攔截的接口生成代理對象以實現(xiàn)接口方法攔截功能,每當執(zhí)行這四種接口對象的方法時,就會進入攔截方法,具體就是InvocationHandler的invoke()方法,攔截那些你指定需要攔截的方法。
編寫插件:實現(xiàn)Mybatis的interceptor接口并復(fù)寫 Intercept() 方法,然后在給插件編寫注解,指定要攔截哪一個接口的哪些方法即可,在配置文件中配置編寫的插件
54 、索引的基本原理
索引用來快速地尋找那些具有特定值的記錄。如果沒有索引,一般來說執(zhí)行查詢時遍歷整張表。
索引的原理:就是把無序的數(shù)據(jù)變成有序的查詢。
- 1 、把創(chuàng)建了索引的列內(nèi)容進行排序
- 2 、對排序結(jié)果生成倒排表
- 3 、在倒排表內(nèi)容上拼上數(shù)據(jù)地址鏈
- 4 、在查詢的時候,先拿到倒排表內(nèi)容,再取出數(shù)據(jù)地址鏈,從而拿到具體數(shù)據(jù)。
55 、mysql聚簇和非聚簇索引的區(qū)別
都是B+樹的數(shù)據(jù)結(jié)構(gòu)
- 聚簇索引:將數(shù)據(jù)存儲與索引放到了一塊、并且是按照一定的順序組織的、找到了索引也就找到了數(shù)據(jù),數(shù)據(jù)的物理存放順序與索引順序是一致的,即:只要索引是相鄰的,那么對應(yīng)的數(shù)據(jù)一定也是相鄰地存放在磁盤上的。
- 非聚簇索引:葉子節(jié)點不存儲數(shù)據(jù)、存儲的是數(shù)據(jù)行地址,也就是說根據(jù)索引查到數(shù)據(jù)行的位置再取磁盤查找數(shù)據(jù),這個就有點類似一本書的目錄,比如我們要找第三章第一節(jié),那我們先在這個目錄里找,找到對應(yīng)的頁碼后再去對應(yīng)的頁碼查看文章。
優(yōu)勢:
- 1 、查詢通過聚簇索引可以直接獲取數(shù)據(jù),相比非聚簇索引需要第二次查詢(非覆蓋索引的情況下)效率要高
- 2 、聚簇索引對于范圍查詢的效率很高,因為其數(shù)據(jù)是按照大小排列的
- 3 、聚簇索引適合用在排序的場合,非聚簇索引不適合
劣勢:
- 1 、維護索引很昂貴,特別是插入新行或者主鍵被更新導(dǎo)至要分頁(page split)的時候。建議在大量插入新行后,選在負載較低的時間段,通過OPTIMIZE TABLE優(yōu)化表,因為必須被移動的數(shù)據(jù)可能造成碎片。使用獨享表空間可以弱化碎片。
- 2 、表因為使用UUID(隨機ID)作為主鍵,使數(shù)據(jù)存儲稀疏,這就會出現(xiàn)聚簇索引有可能有比全表掃更慢,所以建議使用int的auto_increment作為主鍵
- 3、如果主鍵比較大的話,那輔助索引將會變得更大,因為輔助索引的葉子存儲的是主鍵值;過長的主鍵值,會導(dǎo)致非葉子節(jié)點占用更多的物理空間。
innoDB中一定有主鍵,主鍵一定是聚簇索引,不手動設(shè)置、則會使用unique索引,沒有unique索引,則會使用數(shù)據(jù)庫內(nèi)部的一個行的隱藏id來當作主鍵索引。在聚簇索引之上創(chuàng)建的索引稱之為輔助索引,輔助索引訪問數(shù)據(jù)總是二次查找,非聚簇索引都是輔助索引,想復(fù)合索引、前綴索引、唯一索引、輔助索引葉子節(jié)點存儲的不再是行的物理位置、而是主鍵值。
MyISM使用的是非聚簇索引,沒有聚簇索引,非聚簇索引的兩顆B+樹上看上去沒什么不同,節(jié)點的結(jié)構(gòu)完全一致只是存儲的內(nèi)容不同而已,主鍵索引B+樹的節(jié)點存儲了主鍵,輔助索引B+樹存儲了輔助鍵。表數(shù)據(jù)存儲在獨立的地方,這兩顆B+樹的葉子節(jié)點都使用一個地址指向真正的表數(shù)據(jù),對于表數(shù)據(jù)來說,這兩個鍵沒有任何差別。由于索引樹是獨立的,通過輔助鍵檢索無需訪問主鍵的索引樹。
如果設(shè)計到大數(shù)據(jù)量的排序,全表掃描、count之類的操作的話,還是MyISAM占優(yōu)勢些,因為索引所占空間小,這些操作是需要在內(nèi)存中完成的
56 、mysql索引結(jié)構(gòu),各自的優(yōu)劣
索引的數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)和具體存儲引擎的實現(xiàn)有關(guān),在MySQL中使用較多的索引有hash索引,B+樹索引等,innoDB存儲引擎的默認索引實現(xiàn)為:B+樹索引。對于哈希索引來說,底層的數(shù)據(jù)結(jié)構(gòu)就是哈希表,因此在絕大多數(shù)需求為單條記錄查詢的時候,可以選擇哈希索引,查詢性能最快;其余大部分場景,建議選擇BTree索引。
B+樹:
- B+樹是一個平衡的多叉樹,從根節(jié)點到每個葉子節(jié)點的高度差值不超過1,而且同層級的節(jié)點間有指針相互鏈接。在B+樹上的常規(guī)檢索,從根節(jié)點到葉子節(jié)點的搜索效率基本相當,不會出現(xiàn)大幅波動,而且基于索引的順序掃描時,也可以利用雙向指針快速快速左右移動,效率非常高。因此B+樹索引被廣泛應(yīng)用于數(shù)據(jù)庫、文件系統(tǒng)等場景。
哈希索引:
- 哈希索引就是采用一定的哈希算法,把鍵值換算成新的哈希值,檢索時不需要類似B+樹那樣從根節(jié)點到葉子節(jié)點逐級查找,只需一次哈希算法即可立刻定位到相應(yīng)的位置,速度非??臁?br />
- 如果時等值查詢,那么哈希索引明顯有絕對優(yōu)勢,因為只需要經(jīng)過一次算法即可找到相應(yīng)的鍵值;前提是鍵值都是唯一的。如果鍵值不是唯一的,就需要先找到該鍵值所在位置,然后再根據(jù)鏈表往后掃描,直到找到相應(yīng)的數(shù)據(jù);
- 若果是范圍查詢檢索,這時候哈希索引就毫無用武之地了,因為原先是有序的鍵值,經(jīng)過哈希算法后,有可能變成不連續(xù)的了,就沒辦法再利用索引完成范圍查詢檢索。
- 哈希索引也沒辦法利用索引完成排序,以及l(fā)ike‘xxx%’這樣的部分模糊查詢(這種部分模糊查詢,其實質(zhì)上也是范圍查詢);
- 哈希索引也不支持多列聯(lián)合索引的最左匹配規(guī)則。
- B+樹索引的關(guān)鍵字檢索效率比較平均,不像B樹那樣波動幅度大,在有大量重復(fù)鍵值情況下,哈希索引的效率也是極低的,因為存在哈希碰撞問題。
57 、索引的設(shè)計原則
查詢更快、占用空間小
- 1 、適合索引的列是出現(xiàn)在where子句中的列,或者連接子句中指定的列
- 2 、基數(shù)較小的表,索引效果較差,沒有必要在此建立索引
- 3 、使用短索引,如果對長字符串進行索引,應(yīng)該指定一個前綴長度,這樣能夠節(jié)省大量索引空間,如果搜索詞超過索引前綴長度,則使用索引排除不匹配的行,然后檢索其余行是否可能匹配。
- 4 、不要過度索引。索引需要額外的磁盤空間,并降低寫操作的性能。在修改表內(nèi)容的時候,索引會進行更新甚至重構(gòu),索引列越多,這個時間就會越長。所以只保持需要的索引有利于查詢即可。
- 5 、定義有外鍵的數(shù)據(jù)列一定要建立索引
不適合建立索引的
- 1 、更新頻繁字段不適合創(chuàng)建索引
- 2 、若是不能有效區(qū)分數(shù)據(jù)的列不適合做索引(如性別、男女未知、最多也就三種,區(qū)分度實在太低)
- 3 、盡量的擴展索引,不要新建索引。比如表中已經(jīng)有a索引,現(xiàn)在要加(a,b)的索引,那么只需要修改原來的索引即可。
- 4 、對于那些查詢中很少涉及的列,重復(fù)值比較多的列不要建立索引。
- 5 、對于定義為text、image和bit的數(shù)據(jù)類型的列不要建立索引。
58 、mysql鎖的類型有哪些
基于鎖的 屬性 分類:共享鎖、排他鎖
基于鎖的 粒度 分類:行級鎖(INNODB)、表級鎖(INNODB、MYISAM)、頁級鎖(BDB引擎)、記錄所、間隙鎖、臨建鎖。
基于鎖的 狀態(tài) 分類:意向共享鎖,意向排他鎖。
共享鎖又稱讀鎖,簡稱S鎖:當一個事務(wù)為數(shù)據(jù)加上讀鎖之后,其他事務(wù)只能對該鎖加讀鎖,而不能對數(shù)據(jù)加寫鎖,直到所有的讀鎖釋放之后其他事務(wù)才能對其進行加持寫鎖。共享鎖的特性主要是為了支持并發(fā)的讀取數(shù)據(jù),讀取數(shù)據(jù)的時候不支持修改,避免出現(xiàn)重復(fù)讀的問題。
排他鎖又稱寫鎖,簡稱X鎖:當一個事務(wù)為數(shù)據(jù)加上寫鎖時,其他請求將不再為數(shù)據(jù)加任何鎖,直到該鎖釋放之后,其他事務(wù)才能對數(shù)據(jù)進行加鎖。排他鎖的目的是在數(shù)據(jù)修改時,不允許其他人同時修改,也不允許其他人讀取,避免了出現(xiàn)臟數(shù)據(jù)和臟讀的問題。
表鎖是指上鎖的時候鎖住的是整個表,當下一個事務(wù)訪問該表的時候,必須等前一個事務(wù)釋放了鎖才能進行對表進行訪問:
特點:粒度大、加鎖簡單、容易沖突
行鎖是指上鎖的時候鎖住的是表的某一行或多行記錄,其他事務(wù)訪問同一張表時,只有被鎖住的記錄不能訪問,其他的記錄可正常訪問
特點:粒度小、加鎖比表鎖麻煩,不容易沖突,相比表鎖支持的并發(fā)要高。
記錄鎖也屬于行鎖中的一種,只不過記錄鎖的范圍只是表中的某一條記錄,記錄鎖是說事務(wù)在加鎖后鎖住的只是表的某一條記錄。
精準條件名中,并且名中的條件字段是唯一索引
加了記錄鎖之后數(shù)據(jù)可以避免數(shù)據(jù)在查詢的時候被修改的重復(fù)讀問題,也避免了在修改的事務(wù)未提交前被其他事務(wù)讀取的臟讀問題。
- 頁鎖
-頁級鎖是MySQL中鎖定粒度介于行級鎖和表級鎖中間的一種鎖。表級鎖速度快,但沖突多,行級沖突少,但速度慢。所以取了折衷的頁級,一次鎖定相鄰的一組記錄。
特點:開銷和加鎖時間界于表鎖和行鎖之間;會出現(xiàn)死鎖:鎖定粒度界于表鎖和行鎖之間,并發(fā)讀一般。
- 間隙鎖(Gap Lock)
屬于行鎖中的一種,間隙鎖是在事務(wù)加鎖后其鎖住的是表記錄的某一個區(qū)間,當表的相鄰ID之間出現(xiàn)空隙則會形成一個區(qū)間,遵循左開右閉原則。
范圍查詢并且查詢未名中記錄,查詢條件必須名中索引、間隙鎖只會出現(xiàn)在REPEATABLE_READ(重復(fù)讀)的事務(wù)隔離級別中。
觸發(fā)條件:防止幻讀問題,事務(wù)并發(fā)的時候,如果沒有間隙鎖,就會發(fā)生如下的問題,在同一個事務(wù)里,A事務(wù)的兩次查詢出的結(jié)果會不一樣。
比如表里面的數(shù)據(jù)ID為 1,4,5,7,10,那么會形成以下幾個間隙區(qū)間-n-1區(qū)間,1-4區(qū)間、7-10區(qū)間、10-n區(qū)間(-n:代表負無窮大,n代表正無窮大)
也屬于行鎖的一種,并且它是INNODB的行鎖默認算法,總結(jié)來說它就是記錄鎖和間隙鎖的組合,臨建鎖會把查詢出來的記錄鎖住,同時也會把該范圍查詢內(nèi)的所有空隙空間也會鎖住,再之它會把相鄰的下一個區(qū)間也會鎖住
觸發(fā)條件:范圍查詢并命中,查詢命中了索引。
結(jié)合記錄鎖和間隙鎖的特性,臨建鎖避免了在范圍查詢時出現(xiàn)臟讀、重復(fù)度、幻讀問題。加了臨建鎖之后,在范圍區(qū)間內(nèi)數(shù)據(jù)不允許被修改和插入
如果當事務(wù)A加鎖成功之后就設(shè)置一個狀態(tài)告訴后面的人,已經(jīng)有人對表里的行加了一個排他鎖了,你們不能對整個表加共享鎖或排它鎖了,那么后面需要對整個表加鎖的人只需要獲取這個狀態(tài)就知道自己是不是可以對表加鎖,避免了對整個索引樹的每個節(jié)點掃描是否加鎖,而這個狀態(tài)就是意向鎖。
當一個事務(wù)試圖對整個表進行加共享鎖之前,首先需要獲得這個表的意向共享鎖。
當一個事務(wù)試圖對整個表進行加排它鎖之前,首先需要獲得這個表的意向排它鎖。
59 、mysql執(zhí)行計劃怎么看
執(zhí)行計劃就是sql的執(zhí)行查詢順序,以及如何使用索引查詢,返回的結(jié)果集的行數(shù)
1 、 id:是一個有順序的編號,是查詢的順序號,有幾個select就顯示幾行。id的順序是按select出現(xiàn)的順序增長的。id列的值越大執(zhí)行優(yōu)先級越高越先執(zhí)行,id列的值相同則從上往下執(zhí)行,id列的值為NULL最后執(zhí)行。
2 、selectType表示查詢中每個select子句的類型
- SIMPLE:表示查詢中每個select子句的類型
- PRIMARY:表示此查詢是最外層的查詢(包括子查詢)
- SUBQUERY:子查詢中的第一個SELECT
- UNION:表示此查詢是UNION的第二或后面的查詢語句,取決于外面的查詢
- UNION RESULT,UNION 的結(jié)果
- EEPENDENT SUBQUERY:子查詢中的第一個SELECT,取決于外面的查詢,即子查詢依賴于外層查詢的結(jié)果。
- DERIVED:衍生,表示導(dǎo)出表的SELECT(FROM子句的子查詢)
3 、table:表示該語句查詢的表
4、 type :優(yōu)化sql的重要字段,也就是我們判斷sql性能和優(yōu)化程度的重要指標。他的取值類型范圍:
- const:通過索引一次名中,匹配一行數(shù)據(jù)
- system:表中只有一行記錄,相當于系統(tǒng)表
- eq_ref:唯一性索引掃描,對于一個索引鍵,表中只有一條記錄與之匹配
- ref:非唯一性索引掃描,返回匹配某個值的所有
- range:只檢索給定范圍的行,使用一個索引來選擇行,一般用于between、<>;
- index:只遍歷索引樹;
- ALL:表示全表掃描,這個類型的查詢是性能最差的查詢之一。那么基本就是隨著表的數(shù)量增多,執(zhí)行效率越慢
執(zhí)行效率 ALL < index < range < ref < eq_ref < const <system。最好是避免ALL和index
5 、possible_key:它表示MySQL在執(zhí)行該sql語句的時候,可能用到的索引信息,僅僅是可能,實際不一定會用到。
6、 key :此字段是mysql在當前查詢時所真正使用到的索引。它是possible_keys的子集
60 、事務(wù)的基本特性和隔離級別
事務(wù)的 基本特性 和 隔離級別
原子性:指的是一個事務(wù)中的操作要么全部成功,要么全部失敗。
一致性:指的是:數(shù)據(jù)庫總是從一個一致性的狀態(tài)轉(zhuǎn)換到另外一個一致性的狀態(tài)。比如A轉(zhuǎn)賬給B100塊錢,假設(shè)A只有90塊,支付之前我們數(shù)據(jù)庫里的數(shù)據(jù)都是符合約束的,但是如果事務(wù)執(zhí)行成功了,我們的數(shù)據(jù)庫數(shù)據(jù)就破壞約束了,因此事務(wù)不能成功,這里我們說事務(wù)提供了一致性的保證。
隔離性:指的是一個事務(wù)的修改在最終提交前,對其他事務(wù)是不可見的。
持久性指的是:一旦事務(wù)提交,所做的修改就會永久保存到數(shù)據(jù)庫中。
隔離性有 四個隔離級別 ,分別是:
- read uncommit 讀未提交 ,可能會讀到其他事務(wù)未提交的數(shù)據(jù),也叫臟讀。
用戶本來應(yīng)該讀取到id=1的用戶age應(yīng)該是0,結(jié)果讀取到了其他事務(wù)還沒有提交的事務(wù),結(jié)果讀取結(jié)果age=20,這就是臟讀。
- read commit 讀已提交 ,兩次讀取結(jié)果不一致,叫做不可重復(fù)讀。
不可重復(fù)讀解決了臟讀的問題,他只會讀取已經(jīng)提交的事務(wù)。
用戶開啟事務(wù)讀取id=1用戶,查詢到age=10,再次讀取發(fā)現(xiàn)結(jié)果=20,在同一個事務(wù)里同一個查詢讀取到不同的結(jié)果叫做不可重復(fù)度。
- repeatable read 可重復(fù)度 ,這是 mysql的默認級別 ,就是每次讀取結(jié)果都一樣,但是有可能產(chǎn)生幻讀。
- serializable串行 ,一般是不會使用的,他會給每一行讀取的數(shù)據(jù)加鎖,會導(dǎo)致大量超時和鎖競爭的問題。
臟讀(Drity Read):某個事務(wù)已更新一份數(shù)據(jù),另一個事務(wù)在此時讀取了同一份數(shù)據(jù),由于某些原因,前一個RollBack了操作,則后一個事務(wù)所讀取的數(shù)據(jù)就會是不正確的。
不可重復(fù)讀(Non-repeatable read):在一個事務(wù)的兩次查詢之中數(shù)據(jù)不一致,這可能是兩次查詢過程中間插入了一個事務(wù)更細的原有的數(shù)據(jù)。
幻讀(Phantom Read):在一個事務(wù)的兩次查詢中數(shù)據(jù)筆數(shù)不一致,例如有一個事務(wù)查詢了幾列(Row)數(shù)據(jù),而另一個事務(wù)卻在此時插入了新的幾列數(shù)據(jù),先前的事務(wù)在接下來的查詢中,就會發(fā)現(xiàn)有幾列數(shù)據(jù)是它先前所沒有的。
61、怎么處理慢查詢
關(guān)心過業(yè)務(wù)系統(tǒng)里面的sql耗時嗎?統(tǒng)計過慢查詢嗎?對慢查詢都怎么優(yōu)化過?
在業(yè)務(wù)系統(tǒng)中,除了使用主鍵進行的查詢,其他的都會在測試庫上測試其耗時,慢查詢的統(tǒng)計主要由運維在做,會定期將業(yè)務(wù)中的慢查詢反饋給我們。
慢查詢的優(yōu)化首先要搞明白慢的原因是什么? 是查詢條件沒有名中索引? 是load了不需要的數(shù)據(jù)列?還是數(shù)據(jù)量太大?
所以優(yōu)化也是針對這三個方向來的,
- 首先分析語句,看看是否load了額外的數(shù)據(jù),可能是查詢了多余的行并且拋棄掉了,可能是加載了許多結(jié)果中并不需要的列,對語句進行分析以及重寫。
- 分析語句的執(zhí)行計劃,然后獲得其使用索引的情況,之后修改語句或者修改索引,使得語句可以盡可能的名中索引。
- 如果對語句的優(yōu)化已經(jīng)無法進行,可以考慮表中的數(shù)據(jù)量是否太大,如果是的話可以進行橫向或者縱向的分表。
62、ACID靠什么保證的
- A原子性由undo log日志保證,它記錄了需要回滾的日志信息,事務(wù)回滾撤銷已經(jīng)執(zhí)行成功的sql
- C一致性由其他三大特性保證、程序代碼要保證業(yè)務(wù)上的一致性
- I隔離性由MVCC來保證
- D持久層由內(nèi)存+redo log來保證,mysql修改數(shù)據(jù)同時在內(nèi)存和redo log記錄這次操作,宕機的時候可以從redo log恢復(fù)
InnoDB redo log 寫盤,InnoDB 事務(wù)進入 prepare狀態(tài)。
如果前面prepare 成功,binlog 寫盤,再繼續(xù)將事務(wù)日志持久化到binlog,如果持久化成功,那么 InnoDB 事務(wù)則進入commit狀態(tài) redo log里面寫一個 commit 記錄)
redolog的刷盤會在系統(tǒng)空閑時進行
63、什么是MVCC
多版本并發(fā)控制:讀取數(shù)據(jù)數(shù)據(jù)時通過一種類似快照的方式將數(shù)據(jù)保存下來,這樣讀鎖和寫鎖就不沖突了,不同的事務(wù)session會看到自己特定版本的數(shù)據(jù),版本鏈
MVVC只在 READ COMMITTED 和 REPEATABLE READ 兩個隔離級別下工作。其他兩個隔離級別和MVCC不兼容,因為 READ UNCOMMITTED9 總是讀取最新的數(shù)據(jù)行,而不是符合當前事務(wù)版本的數(shù)據(jù)行。而SERIALIZABLE則會對所有讀取的行都加鎖。
聚簇索引記錄中有兩個必要的隱藏列:
- trx_id: 用來存儲每次對某條聚簇索引記錄進行修改的時候的事務(wù)id。
- roll_pointer: 每次對哪條聚簇索引記錄有修改的時候,都會把老版本寫入undo日志中。這個roll_pointer就是存了一個指針,它指向這條聚簇索引記錄上一個版本的位置,通過它來獲得上一個版本的記錄信息。(注意插入操作的undo日志沒有這個屬性,因為它沒有老版本。
已提交讀和可重復(fù)讀的區(qū)別就在于它們生成ReadView策略不同。
- 開始事務(wù)時創(chuàng)建 readview , readView 維護當前活動的事務(wù)id,即未提交的事務(wù)id,排序成一個數(shù)組
- 訪問數(shù)據(jù),獲取數(shù)據(jù)中的事務(wù)id(獲取的時事務(wù)id最大的記錄),對比readview:
- 如果在readview的左邊(比readview都?。梢栽L問(在左邊意味著該事務(wù)已經(jīng)提交)
- 如果在readview的右邊(比readview都大)或者就在readview中,不可以訪問,獲取roll_pointer,取上一版本重新對比(在右邊意味著,該事務(wù)在readview生成之后出現(xiàn),在readview中意味著該事務(wù)還未提交)
已提交讀隔離級別下的事務(wù)在每次查詢的開始都會生成一個獨立的ReadView,而可重復(fù)讀隔離級別則在第一次讀的時候生成一個ReadView,之后的讀都復(fù)用之前的ReadView。
這就是Mysql的MVVC,通過版本鏈,實現(xiàn)多版本,可并發(fā)讀-寫,寫-讀。通過ReadView生成策略的不同實現(xiàn)不同的隔離級別
64、mysql主從同步原理
MySQL主從同步的過程:
MySQL的主從復(fù)制中有三個線程:master(binlog dumo thread)、slave(I/0 thread、SQL thread),Master一條線程和Slave中的兩條線程。
- 主節(jié)點binlog,主從復(fù)制的基礎(chǔ)是主庫記錄數(shù)據(jù)庫的所有變更記錄到binlog。binlog是數(shù)據(jù)庫啟動的那一刻起,保存所有修改數(shù)據(jù)庫結(jié)構(gòu)或內(nèi)容的一個文件。
- 主節(jié)點log dump 線程,當binlog有變動時,log dump線程讀取其內(nèi)容并發(fā)送給從節(jié)點。
- 從節(jié)點I/O線程接受binlog內(nèi)容,并將其寫入到relay log文件中。
- 從節(jié)點的SQL線程讀取relay log 文件內(nèi)容對數(shù)據(jù)更新進行重放,最終保證主從數(shù)據(jù)庫的一致性。
注意:從節(jié)點使用binlog文件+position偏移量來定位主從同步的位置,從節(jié)點會保存其已接收到的偏移量,如果從節(jié)點發(fā)生宕機重啟,則會自動從position的位置發(fā)起同步。
由于mysql默認的復(fù)制方式是異步的,主庫把日志發(fā)送給從庫后不關(guān)心從庫是否已經(jīng)處理,這樣會產(chǎn)生一個問題就是假設(shè)主庫掛了,從庫處理失敗了,這時候從庫上升為主庫后,日志就丟失了。由此產(chǎn)生兩個概念。
全同步復(fù)制
- 主庫寫入binlog后強制同步日志到從庫,所有的從庫都執(zhí)行完成后才返回給客戶端,但是很顯然這個方式的話性能會受到嚴重影響。
半同步復(fù)制
- 和全同步不同的是,半同步復(fù)制的邏輯是這樣的,從庫寫入日志成功后返回ACK確認給主庫,主庫收到至少一個從庫的確認就認位寫操作完成。
65、簡述Myisam和innodb的區(qū)別
MyISAM:
- 不支持事務(wù),但是每次查詢都是原子的;
- 支持表級鎖,即每次操作是對整個表加鎖
- 存儲表的行總數(shù);
- 一個MYISAM表有三個文件:索引文件、表結(jié)構(gòu)文件、數(shù)據(jù)文件;
- 采用非聚簇索引,索引文件的數(shù)據(jù)域存儲指向數(shù)據(jù)文件的指針。輔助索引與主索引基本一致,但是輔助索引不用保證唯一性。
innoDb:
- 支持ACID事務(wù),支持事務(wù)的四種隔離級別;
- 支持行級鎖及外鍵約束:因此可以支持寫并發(fā);
- 不存儲行總數(shù);
- 一個InnoDb引擎存儲在一個文件空間(共享表空間,表大小不受操作系統(tǒng)控制,一個表可能分布在多個文件里),也有可能為多個(設(shè)置為獨立表空,表大小受操作系統(tǒng)文件大小限制,一般為2G),受操作系統(tǒng)文件大小的限制;
- 主鍵索引采用聚集索引(索引的數(shù)據(jù)域存儲數(shù)據(jù)文件本身,輔助索引的數(shù)據(jù)域存儲主鍵的值;因此從輔助索引查找數(shù)據(jù),需要先通過輔助索引找到主鍵值,再訪問輔助索引;最好使用自增主鍵,防止插入數(shù)據(jù)時,為維持B+樹結(jié)構(gòu),文件的大調(diào)整。
66、簡述MySQL中索引類型對數(shù)據(jù)庫的性能的影響
- 普通索引:允許被索引的數(shù)據(jù)列包含重復(fù)的數(shù)據(jù)
- 唯一索引:可以保證數(shù)據(jù)記錄的唯一性
- 主鍵:是一種特殊的唯一索引,在一張表中只能定義一個主鍵索引,主鍵用于唯一標識一條記錄,使用關(guān)鍵字primary key來創(chuàng)建。
- 聯(lián)合索引:索引可以覆蓋多個數(shù)據(jù)列,如像INDEX(columnA,columnB)索引。
- 全文索引:通過建立倒排索引,可以極大的提升檢索效率,解決判斷字段是否包含的問題,是目前搜索引擎使用的一種關(guān)鍵技術(shù)??梢酝ㄟ^ALTER TABLE table_name ADD FULLTEXT(column);創(chuàng)建全文索引
優(yōu)點:
- 索引可以極大的提高數(shù)據(jù)的查詢速度。
- 通過使用索引,可以在查詢的過程中,使用優(yōu)化隱藏器,提高系統(tǒng)的性能。
缺點:
- 但是會減低插入、刪除、更新表的速度,因為在執(zhí)行這些寫操作時,還要操作索引文件
- 索引需要占用物理空間,除了數(shù)據(jù)表占數(shù)據(jù)空間之外,每一個索引還要占一定的物理空間,如果要建立聚簇索引,那么需要的空間就會更大,如果非聚簇索引很多,一旦聚簇索引改變,那么所有非聚集索引都會跟著變。
67、RDB和AOF機制
RDB:Redis DataBase
在指定的時間間隔內(nèi)將內(nèi)存中的數(shù)據(jù)集快照寫入磁盤,實際操作過程就是fork一個子進程,先將數(shù)據(jù)集寫入臨時文件,寫入成功后,再替換之前的文件,用二進制壓縮存儲。
優(yōu)點:
- 1、整個Redis數(shù)據(jù)庫只包含一個文件dump.rdb,方便持久化
- 2、容災(zāi)性好,方便備份
- 3、性能最大化,fork子進程來完成寫操作,讓主進程繼續(xù)處理命令,所以是IO最大化。使用單獨子進程來進行持久化,主進程不會進行任何IO操作,保證了redis的高性能。
- 4、相對于數(shù)據(jù)集大時,比AOF的啟動效率更高
缺點:
- 1、數(shù)據(jù)安全性低。RDB是間隔一段時間進行持久化,如果持久化之間redis發(fā)生故障,會發(fā)生數(shù)據(jù)丟失。所以這種方式更適合數(shù)據(jù)要求不嚴謹?shù)臅r候)
- 2、由于RDB是通過fork子進程來協(xié)助完成數(shù)據(jù)持久化工作的,因此,如果當數(shù)據(jù)集較大時,可能會導(dǎo)致整個服務(wù)器停止服務(wù)幾百毫秒,甚至是一秒鐘。
AOF:Append Only File
以日志的形式記錄服務(wù)器所處理的每一個寫、刪除操作,查詢操作不會記錄,以文本的方式記錄,可以打開文件看到詳細的操作記錄。
優(yōu)點:
- 1、數(shù)據(jù)安全,Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事實上,每秒同步也是異步完成的,其效率也是非常高的,所差的是一旦系統(tǒng)發(fā)生宕機現(xiàn)象,那么這一秒鐘之內(nèi)修改的數(shù)據(jù)將會丟失。而每修改同步,我們可以將其視為同步持久化,即每次發(fā)生的數(shù)據(jù)變化都會被立刻記錄到磁盤中。
- 2、通過append模式寫文件,即使中途服務(wù)器宕機也不會破壞已經(jīng)存在的內(nèi)容,可以通過redis-check-aof工具解決數(shù)據(jù)一致性問題。
- AOF機制的rewrite模式。定期對AOF文件進行重寫,以達到壓縮的目的。
缺點:
- 1、AOF文件比RDB文件大,且恢復(fù)速度慢
- 2、數(shù)據(jù)集大的時候,比rdb啟動效率低
- 3、運行效率沒有RDB高
AOF文件比RDB更新頻率高,優(yōu)先使用AOF還原數(shù)據(jù)
AOF比RDB更安全也更大
RDB性能比AOF好
如果兩個都配置了優(yōu)先加載AOF
68、Redis的過期鍵的刪除策略
Redis是key-value數(shù)據(jù)庫,我們可以設(shè)置Redis中緩存的過期時間。Redis的過期策略就是指當Redis中緩存的key過期了,Redis如何處理。
- 惰性過期:只有當訪問一個key時,才會判斷該key是否已過期,過期則清除。該策略可以最大化地節(jié)省CPU資源,卻對內(nèi)存非常不友好。極端情況肯可能出現(xiàn)大量的過期key沒有被訪問,從而不會被清除,占用大量內(nèi)存
- 定期過期:每隔一定的時間,會掃描一定數(shù)量的數(shù)據(jù)庫的expires字典中一定數(shù)量的key,并清除其中已過期的key。該策略是前者的一個折中方案。通過調(diào)整定時掃描的時間間隔和每次掃描的限定耗時,可以在不同情況下使得CPU和內(nèi)存資源達到最優(yōu)的平衡效果。
(expires字典會保存所有設(shè)置了過期時間的key的過期時間數(shù)據(jù),其中,key是指向鍵空間中的某個鍵的指針,value是改鍵的毫秒精度的UNIX時間戳表示過期的時間。鍵空間是指該Redis集群中保存的所有的鍵。)
Redis中同時使用了惰性過期和定期過期兩種過期策略。
69、Redis線程模型,單線程為什么這么快
Redis基于Reactor模式開發(fā)了網(wǎng)路事件處理器,這個處理器叫做文件事件處理器 file event handler。這個文件事件處理器,它是單線程的,所以Redis才叫做單線程的模型,它采用IO多路復(fù)用機制來同時監(jiān)聽多個Socket,根據(jù)Socket上的事件類型來選擇對應(yīng)的事件處理器來處理這個事件??梢詫崿F(xiàn)高性能網(wǎng)絡(luò)通信模型,又可以跟內(nèi)部其他單線程的模塊進行對接,保證了Redis內(nèi)部的線程模型的簡單性。
文件事件處理器 的結(jié)構(gòu)包含4個部分:多個Socket、IO多路復(fù)用程序、文件事件分配器以及事件處理器(命令請求處理器、命令回復(fù)處理器、連接應(yīng)答處理器等)。
多個Socket可能并發(fā)的產(chǎn)生不同的操作,每個操作對應(yīng)不同的文件事件,但是IO多路復(fù)用程序會監(jiān)聽多個Socket,會將Socket放入一個隊列中排隊,每次從隊列中取出一個Socket給事件分派器,事件分派器把Socket給對應(yīng)的事件處理器。
然后一個Socket的事件處理完之后,IO多路復(fù)用程序會將隊列中的下一個Socket給事件分派器。文件事件分派器會根據(jù)每個Socket當前產(chǎn)生的事件,來選擇對應(yīng)的事件處理器來處理。
單線程快的原因:
- 1、純內(nèi)存操作
- 2、核心是基于非阻塞的IO多路復(fù)用機制
- 3、單線程反而避免了多線程的頻繁上下文切換帶來的性能問題
70、緩存雪崩、緩存穿透、緩存擊穿
緩存雪崩、緩存穿透、緩存擊穿
緩存雪崩是指緩存同一時間大面積的失效,所以,后面的請求都會落到數(shù)據(jù)庫上,造成數(shù)據(jù)庫短時間內(nèi)承受大量請求而崩掉。
解決方案:
- 1、緩存數(shù)據(jù)的過期時間設(shè)置隨機,防止同一時間大量數(shù)據(jù)過期現(xiàn)象發(fā)生。
- 2、給每一個緩存數(shù)據(jù)增加相應(yīng)的緩存標記,記錄緩存是否失效,如果緩存標記失效,則更新數(shù)據(jù)緩存
- 3、緩存預(yù)熱
- 4、互斥鎖
緩存穿透是指緩存和數(shù)據(jù)庫中都沒有的數(shù)據(jù),會導(dǎo)致所有的請求都落到數(shù)據(jù)庫上,造成數(shù)據(jù)庫短時間內(nèi)承受大量請求而崩掉。
解決方法:
- 1、接口層增加校驗,如用戶鑒權(quán)校驗,id做基礎(chǔ)校驗,id<=0的直接攔截;
- 2、從緩存取不到數(shù)據(jù),在數(shù)據(jù)庫中也沒有取到,這時也可以將key-value對寫為key-null,緩存有效時間可以設(shè)置短點,如30秒(設(shè)置太長會導(dǎo)致正常情況也沒法使用)。這樣就可以防止攻擊用戶反復(fù)用同一個id暴力攻擊
- 3、采用布隆過濾器,將所有可能存在的數(shù)據(jù)哈希到一個足夠大的bitmap中,一個一定不存在的數(shù)據(jù)會被這個bitmap攔截掉,從而避免了對底層存儲系統(tǒng)的查詢壓力
緩存擊穿是指緩存中沒有但數(shù)據(jù)庫中有的數(shù)據(jù)(一般是緩存時間到期),這時由于并發(fā)用戶特別多,同時讀緩存沒讀到數(shù)據(jù),又同時去數(shù)據(jù)庫去取數(shù)據(jù),引起數(shù)據(jù)庫壓力瞬間增大,造成過大壓力。和緩存雪崩不同的是,緩存擊穿指并發(fā)查同一條數(shù)據(jù),緩存雪崩式不同數(shù)據(jù)都過期了,很多數(shù)據(jù)都查不到從而查數(shù)據(jù)庫。
解決方法:
- 設(shè)置熱點數(shù)據(jù)永遠不過期
- 加互斥鎖
71、簡述Redis事務(wù)實現(xiàn)
1、事務(wù)開始
MULTI命令的執(zhí)行,標識著一個事務(wù)的開始。MULTI命令會將客戶端狀態(tài)的flag屬性中打開REDIS_MULTI標識來完成的。
2、命令入隊
當一個客戶端切換到事務(wù)狀態(tài)后,服務(wù)器會根據(jù)這個客戶端發(fā)送來的命令來執(zhí)行不同的操作。如果客戶端發(fā)送的命令為MULTI、EXEC、WATCH、DISCARD中的一個,立即執(zhí)行這個命令,否則將命令放入一個事務(wù)隊列里面,然后向客戶端返回QUEUED回復(fù)。
- 如果客戶端發(fā)送的命令為EXEC、DISCARD、WATCH、MULTI四個命令的其中一個,那么服務(wù)器立即執(zhí)行這個命令。
- 如果客戶端發(fā)送的是四個命令以外的其他命令,那么服務(wù)器并不立即執(zhí)行這個命令。
- 首先檢查命令的格式是否正確、如果不正確,服務(wù)器會在客戶端狀態(tài)(redisClient)的flags屬性關(guān)閉REDIS_MULTI標識,并且返回錯誤信息給客戶端。如果正確,將這個命令放入一個事務(wù)隊列里面,然后向客戶端返回QUEUED回復(fù)
事務(wù)隊列是按照FIFO的方式保存入隊的命令。
3、事務(wù)執(zhí)行
客戶端發(fā)送EXEC命令,服務(wù)器執(zhí)行EXEC命令邏輯
- 如果客戶端狀態(tài)的flags屬性不包含REDIS_MULTI標識,或者包含REDIS_DIRTY_CAS或者REDIS_DIRTY_EXEC標識,那么就直接取消事務(wù)的執(zhí)行。
- 否則客戶端處于事務(wù)狀態(tài)(flags有REDIS_MULTI標識),服務(wù)器會遍歷客戶端的事務(wù)隊列,然后執(zhí)行事務(wù)隊列中的所有的命令,最后將返回結(jié)果全部返回給客戶端;
redis不支持事務(wù)回滾機制,但是它會檢查每一個事務(wù)中的命令是否錯誤。
Redis事務(wù)不支持檢查哪些程序員自己的邏輯錯誤。例如對String類型的數(shù)據(jù)庫執(zhí)行對HashMap類型的操作?。?!
- WATCH命令 是一個樂觀鎖,可以為Redis事務(wù)提供check-and-set(CAS)行為,可以監(jiān)控一個或多個鍵,一旦其中有一個鍵被修改(或刪除),之后的事務(wù)就不會執(zhí)行,監(jiān)控一直持續(xù)到EXEC命令。
- MULTI命令 有用于開啟一個事務(wù),它總是返回OK,MULTI執(zhí)行之后,客戶端可以繼續(xù)向服務(wù)器發(fā)送任意多條命令,這些命令不會立即被執(zhí)行,而是被放到一個隊列中,當EXEC命令被調(diào)用時,所有隊列中的命令才會被執(zhí)行。
- EXEC :執(zhí)行所有事務(wù)塊內(nèi)的命令。返回事務(wù)塊內(nèi)所有命令的返回值,按命令執(zhí)行的先后順序排列。當操作被打斷時,返回空值NULL。
- 通過調(diào)用 DISCARD ,客戶端可以清空事務(wù)隊列,并放棄執(zhí)行事務(wù),并且客戶端會從事務(wù)狀態(tài)中退出。
- UNWATCH 命令可以取消watch對所有key的監(jiān)控。
72、redis集群方案
主從
哨兵模式:
sentinel,哨兵是redis集群中非常重要的一個組件,主要有一下功能:
- 集群監(jiān)控:負責(zé)監(jiān)控redis master 和slave進程是否正常工作。
- 信息通知:如果某個redis實例有故障,那么哨兵負責(zé)發(fā)送信息作為報警通知給管理員
- 故障轉(zhuǎn)移:如果master node掛掉了,會自動轉(zhuǎn)移到slave node上。
- 配置中心:如果故障轉(zhuǎn)移發(fā)生了,通知client客戶端新的master地址
哨兵用于實現(xiàn)redis集群的高可用,本身也是分布式的,作為一個哨兵集群去運行,互相協(xié)同工作。
- 故障轉(zhuǎn)移時,判斷一個master node是否宕機了,需要大部分的哨兵都同意才行,涉及到了分布式選舉
- 即使部分哨兵節(jié)點掛掉了,哨兵集群還是能正常工作的
- 哨兵通常需要3個實例,來保證自己的健壯性。
- 哨兵+redis主從的部署架構(gòu),是不保證數(shù)據(jù)零丟失的,只能保證redis集群的高可用性。
- 對于哨兵+redis主從這種復(fù)雜的部署架構(gòu),盡量在測試環(huán)境和生產(chǎn)環(huán)境,都進行充足的測試和演練。
redis Cluster是一種服務(wù)端Sharding技術(shù),3.0版本開始正式提供。采用==slot(槽)==的概念,一共分成16384個槽。將請求發(fā)送到任意節(jié)點,接收到請求的節(jié)點會將查詢請求發(fā)送到正確的節(jié)點上執(zhí)行
方案說明:
- 通過哈希的方式,將數(shù)據(jù)分片,每個節(jié)點均分存儲一定哈希槽(哈希值)區(qū)間的數(shù)據(jù),默認分配 了16384個槽位
- 每份數(shù)據(jù)分片會存儲在多個互為主從的節(jié)點上
- 數(shù)據(jù)寫入先寫主節(jié)點,再同步到從節(jié)點(支持配置為阻塞同步)
- 同一分片多個節(jié)點間的數(shù)據(jù)不保持一致性
- 讀取數(shù)據(jù)時,當客戶端操作的key沒有分配在該節(jié)點上時,redis會返回轉(zhuǎn)向指令,指向正確的節(jié)點
- 擴容時需要把舊節(jié)點的數(shù)據(jù)遷移一部分到新節(jié)點
在redis cluster架構(gòu)下,每個redis要開放兩個端口號,比如一個是6379,另外一個就是加1w的端口號,比如16379.
16379端口號是用來進行節(jié)點間通信的,也就是cluster bus的通信,用來進行故障檢測、配置更新、故障轉(zhuǎn)移授權(quán)。cluster bus用了另外一種二進制的協(xié)議,gossip協(xié)議,用于節(jié)點間進行高效的數(shù)據(jù)交換,占用更少的網(wǎng)絡(luò)帶寬和處理時間。
優(yōu)點:
- 無中心架構(gòu),支持動態(tài)擴容,對業(yè)務(wù)透明
- 具備Sentine的監(jiān)控和自動Failover(故障轉(zhuǎn)移)能力
- 客戶端不需要連接集群所有節(jié)點,連接集群中任何一個可用節(jié)點即可
- 高性能,客戶端直連redis服務(wù),免去了proxy代理的損耗
缺點:
- 運維也很復(fù)雜、數(shù)據(jù)遷移需要人工干預(yù)
- 只能使用0號數(shù)據(jù)庫
- 不支持批量操作(pipeline管道操作)
- 分布式邏輯和存儲模塊耦合等
Redis Sharding是Redis Cluster出來之前,業(yè)界普遍使用的多Redis實例集群方法。其主要思想是采用哈希算法將Redis數(shù)據(jù)的Key進行散列,通過hash函數(shù),特定的key會映射到特定的Redis節(jié)點上。java redis客戶端驅(qū)動jedis,支持Redis Sharing功能,即ShardedJedis以及結(jié)合緩存池的ShardegjedisPool
優(yōu)點:
優(yōu)勢在于非常簡單,服務(wù)端的redis實例彼此獨立,相互無關(guān)聯(lián),每個Redis實例像單服務(wù)器一樣運行,非常容易線性擴展,系統(tǒng)的靈活性很強。
缺點:
由于sharding處理放到客戶端,規(guī)模進一步擴大時給運維帶來挑戰(zhàn)。
客戶端sharding不支持動態(tài)增刪改節(jié)點。服務(wù)端redis實例群拓撲結(jié)構(gòu)有變化時,每個客戶端都需要更新調(diào)整。連接不能共享,當應(yīng)用規(guī)模增大時,資源浪費制約優(yōu)化
73、redis主從復(fù)制的核心原理
通過執(zhí)行slaveof命令或設(shè)置slaveof選項,讓一個服務(wù)器去復(fù)制另一個服務(wù)器的數(shù)據(jù)。主數(shù)據(jù)庫可以進行讀寫操作,當寫操作導(dǎo)致數(shù)據(jù)變化時會自動將數(shù)據(jù)同步給從數(shù)據(jù)庫。而從數(shù)據(jù)庫一般只是只讀的,并接受主數(shù)據(jù)庫同步過來的數(shù)據(jù)。一個主數(shù)據(jù)庫可以擁有多個從數(shù)據(jù)庫,而一個從數(shù)據(jù)庫只能擁有一個主數(shù)據(jù)庫。
全量復(fù)制:
bgrewriteaof
部分復(fù)制:
- 1、 復(fù)制偏移量 :執(zhí)行復(fù)制的雙方,主從節(jié)點,分別會維護一個復(fù)制偏移量offset
- 2、 復(fù)制積壓緩沖區(qū) :主節(jié)點內(nèi)部維護了一個固定長度的、先進先出(FIFO)隊列作為復(fù)制積壓緩沖區(qū),當主從節(jié)點offset的差距過大超過緩沖區(qū)長度時,將無法執(zhí)行部分復(fù)制,只能執(zhí)行全量復(fù)制。
- 3、 服務(wù)器運行ID(runid) :每個Redis節(jié)點,都有其運行ID,運行ID由節(jié)點在啟動時自動生成,主節(jié)點會將自己的運行ID發(fā)送給從節(jié)點,從節(jié)點會將主節(jié)點的運行ID存起來。從節(jié)點Redis斷開重連的時候,就是根據(jù)運行ID來判斷同步的進度:
如果從節(jié)點保存的runid與主節(jié)點現(xiàn)在的runid相同,說明主從節(jié)點之前同步過,主節(jié)點會繼續(xù)嘗試使用部分復(fù)制(到底能不能部分復(fù)制還要看offset和復(fù)制積壓緩沖區(qū)的情況)
如果從節(jié)點保存的runid與主節(jié)點現(xiàn)在的runid不同,說明從節(jié)點在斷線前同步的Redis節(jié)點并不是當前的主節(jié)點,只能進行全量復(fù)制。
2022年整理最詳細的java面試題、掌握這一套八股文、面試 ...-7.jpg (45.05 KB, 下載次數(shù): 7)
下載附件
2023-2-12 13:27 上傳
74、CAP理論,BASE理論
CAP理論
Consistency(一致性):
- 即更新操作成功并返回客戶端后,所有節(jié)點在同一時間的數(shù)據(jù)完全一致。
- 對于客戶端來說,一致性指的是并發(fā)訪問時更新過的數(shù)據(jù)如何獲取的問題。
- 從服務(wù)端來看,則是更新如何復(fù)制分布到整個系統(tǒng),以保證數(shù)據(jù)最終一致。
Availability(可用性):
- 即服務(wù)一直可用,而且是正常響應(yīng)時間。系統(tǒng)能夠很好的為用戶服務(wù),不出現(xiàn)用戶操作失敗或者訪問超時等用戶體驗不好的情況
Partition Tolerance(分區(qū)容錯性):
- 即分布式系統(tǒng)在遇到某節(jié)點或網(wǎng)絡(luò)分區(qū)故障的時候,依舊能夠?qū)ν馓峁M足一致性和可用性的服務(wù)。分區(qū)容錯性要求能夠使應(yīng)用雖然是一個分布式系統(tǒng),而看上去卻好像是在一個可以運轉(zhuǎn)正常的整體。比如現(xiàn)在的分布式系統(tǒng)中由某一個或幾個機器宕機了,其他剩下的機器還能夠正常運轉(zhuǎn)滿足系統(tǒng)需求,對于用戶而言并沒有什么體驗上的影響。
CP和AP:分區(qū)容錯是必須保證的,當發(fā)生網(wǎng)絡(luò)分區(qū)的時候,如果要繼續(xù)服務(wù),那么強一致性和可用性只能2選1.
BASE是Basically Available(基本可用)、soft state(軟狀態(tài))和Eventually consisten(最終一致性)
BASE理論是對CPA中一致性和可用性權(quán)衡的結(jié)果,其來源于對大規(guī)模互聯(lián)網(wǎng)系統(tǒng)分布式實踐的總結(jié),是基于CAP定理逐步演化而來的。BASE理論的核心思想是:即使無法做到強一致性,但每個應(yīng)用都可以根據(jù)自身業(yè)務(wù)特定,采用適當?shù)姆绞絹硎瓜到y(tǒng)達到最終一致性
基本可用:
- 響應(yīng)時間上的損失:正常情況下,處理用戶請求需要0.5s返回結(jié)果,但是由于系統(tǒng)出現(xiàn)故障,處理用戶請求的時間變?yōu)?s
- 系統(tǒng)功能上的損失:正常情況下,用戶可以使用系統(tǒng)的全部功能,但是由于系統(tǒng)訪問量突然劇增,系統(tǒng)的部分非核心功能無法使用。
軟狀態(tài):數(shù)據(jù)同步允許一定的延遲
最終一致性:系統(tǒng)中所有的數(shù)據(jù)副本,在經(jīng)過一段時間的同步后,最終能夠達到一個一致的狀態(tài),不要求實時
75、負載均衡算法、類型
1、輪循法
- 將請求按順序輪流分配到后端服務(wù)器上,它均衡地對待后端的每一臺服務(wù)器,而不關(guān)心服務(wù)器實際的連接數(shù)和當前的系統(tǒng)負載
2、隨機法
- 通過系統(tǒng)的隨機算法、根據(jù)后端服務(wù)器的列表大小值來隨機選取其中的一臺服務(wù)器訪問。由概率統(tǒng)計理論可以得知,隨著客戶端調(diào)用服務(wù)端的次數(shù)增多,其實際效果越來越接近于平均分配調(diào)用量到后端的每一臺服務(wù)器,也就是輪循的結(jié)果
3、源地址哈希法
- 源地址哈希的思想是根據(jù)獲取客戶端的IP地址,通過哈希函數(shù)計算得到的一個數(shù)值,用該數(shù)值對服務(wù)器列表的大小進行取模運算,得到的結(jié)果便是客戶端要訪問服務(wù)器的序號。采用源地址哈希法進行負載均衡,同一IP地址的客戶端,當 后端服務(wù)器列表不變時,它每次都會映射到同一臺后端服務(wù)器進行訪問。
4、加權(quán)輪詢法
- 不同的后端服務(wù)器可能機器的配置和當前系統(tǒng)的負載并不相同,因此它們的抗壓能力也不相同。給配置高、負載低的機器配置更高的權(quán)重,讓其處理更多的請求;而配置低、負載高的機器,給其分配較低的權(quán)重,降低其系統(tǒng)負載,加權(quán)輪循能很好地處理這一問題,并將請求順序且按照權(quán)重分配到后端。
5、加權(quán)隨機法
- 與加權(quán)輪詢法一樣,加權(quán)隨機法也可根據(jù)后端機器的配置,系統(tǒng)的負載分配不同的權(quán)重。不同的是,它是按照權(quán)重隨機請求后端服務(wù)器,而非順序
6、隨機連接數(shù)法
- 最小連接數(shù)算法比較靈活和智能,由于后端服務(wù)器的配置不盡相同,對于請求的處理有快有慢,它是根據(jù)后端服務(wù)器當前的連接情況,動態(tài)地選取其中當前積壓連接數(shù)最少的一臺服務(wù)器來處理當前的請求,盡可能地提高后端服務(wù)的利用效率,將負責(zé)合理地分流到每一臺服務(wù)器。
類型:
DNS方式實現(xiàn)負載均衡
硬件負載均衡:F5和A10
軟件負載均衡:
Nginx、HAproxy、Lvs。其中的區(qū)別:
- Nginx :七層負載均衡,支持HTTP、E-mail協(xié)議,同時也支持4層負載均衡;
- HAproxy :支持七層規(guī)則的,性能也很不粗。OpenStack默認使用的負載均衡軟件就是HPproxy
- LVS:運行在內(nèi)核態(tài),性能是軟件負載均衡中最高的,嚴格來說工作在三次(傳輸層),所以更通用一些,適用各種應(yīng)用服務(wù)。
76、分布式架構(gòu)下,Session共享有什么方案
1、采用無服務(wù)狀態(tài),拋棄session
2、存入cookie(有安全風(fēng)險)
3、服務(wù)器之間進行session同步,這樣可以保證每個服務(wù)器上都有全部的session信息,不過當服務(wù)器數(shù)量比較多的時候,同步是會有延遲甚至同步失??;
4、IP綁定策略
使用Nginx(或其他復(fù)雜負載均衡硬件)中的IP綁定策略,同一個IP只能在指定的同一個機器訪問,但是這樣做失去了負載均衡的意義,當掛掉一臺服務(wù)器的時候,會影響一批用戶的使用,風(fēng)險很大;
5、使用Redis存儲
把Session放到Redis中存儲,雖然架構(gòu)上變得復(fù)雜,并且訪問需要多訪問一次Redis,但是這種方案帶來的好處也是很大的:
- 實現(xiàn)了session共享
- 可以水平擴展(增加Redis服務(wù)器)
- 服務(wù)器重啟Session不丟失(不過也要注意Session在Redis中的刷新/失效機制);
- 不僅可以跨服務(wù)器Session共享,甚至可以跨平臺(例如網(wǎng)頁端和APP端)
77、簡述你對RPC、RMI的理解
RPC:在本地調(diào)用遠程的函數(shù),遠程過程調(diào)用,可以跨語言實現(xiàn), httpClient
RMI:遠程方法調(diào)用,java中用于實現(xiàn)RPC的一種機制,RPC的java版本,是J2EE的網(wǎng)路調(diào)用機制,跨JVM調(diào)用對象的方法,面向?qū)ο蟮乃季S方式
直接或間接實現(xiàn)接口 java.rmi.Remove 成為存在于服務(wù)端的遠程對象,供客戶端訪問并提供一頂?shù)姆?wù)
遠程對象必須實現(xiàn)java.rmi.server.UniCastRemoteObject類,這樣才能保證客戶端訪問獲得遠程對象時,該遠程對象將會把自身的一個拷貝以Socket的形式傳輸給客戶端。此時客戶端所獲得的這個拷貝稱為”存根“,而服務(wù)器端本身已存在的遠程對象則稱之為”骨架“,其實此時的存根是客戶端的一個代理,用于服務(wù)器端的通信,而骨架也可以認位是服務(wù)器端的一個代理,用于接收客戶端的請求之后調(diào)用遠程方法來響應(yīng)客戶端的請求。
78、分布式id生成方案
uuid
- 1、當前日期和時間 時間戳
- 時鐘序列。 計數(shù)器
- 全局唯一的IEEE機器識別號,如果有網(wǎng)卡,從網(wǎng)卡MAC地址獲得,沒有網(wǎng)卡以其他方式獲得。
優(yōu)點:代碼簡單,性能好(本地生成,沒有網(wǎng)絡(luò)消耗),保證唯一(相對而言,重復(fù)概率極低可以忽略)
缺點:
- 每次生成的ID都是無序的,而且不是全數(shù)字,且無法保證趨勢遞增
- UUID生成的是字符串,字符串存儲性能差,查詢效率慢,寫的時候由于不能產(chǎn)生順序的append操作,需要進行insert操作,導(dǎo)致頻繁的頁分裂,這種操作在記錄占用空間比較大的情況下,性能下降比較大,還會增加讀取磁盤次數(shù)
- UUID長度過長,不適用于存儲,耗費數(shù)據(jù)庫性能。
- ID無一定業(yè)務(wù)含義,可讀性差。
- 有信息安全問題,有可能泄漏mac地址
數(shù)據(jù)庫自增序列
單機模式:
優(yōu)點:
- 實現(xiàn)簡單,依靠數(shù)據(jù)庫即可,成本小。
- ID數(shù)字化,單調(diào)自增,滿足數(shù)據(jù)庫存儲和查詢性能。
- 具有一定的業(yè)務(wù)可讀性。(結(jié)合業(yè)務(wù)code)
缺點:
- 強依賴DB,存在單點問題,如果數(shù)據(jù)庫宕機,則業(yè)務(wù)不可用。
- DB生成ID性能有限,單點數(shù)據(jù)庫壓力大,無法抗高并發(fā)場景。
- 信息安全問題,比如暴露訂單量,url查詢改一下id查到別人的訂單
數(shù)據(jù)庫高可用:多主模式做負載,基于序列的起始值和步長設(shè)置,不同的初始值,相同的步長,步長大于節(jié)點數(shù)。
優(yōu)點:解決了ID生成的單點問題,同時平衡了負載。
缺點:
- 系統(tǒng)擴容困難:系統(tǒng)定義好步長之后,增加機器之后調(diào)整步長困難。
- 數(shù)據(jù)庫壓力大:每次獲取一個ID都必須讀寫一次數(shù)據(jù)庫。
- 主從同步的時候,電商下單---->支付insert master db select 數(shù)據(jù),因為數(shù)據(jù)同步延遲導(dǎo)致查不到這個數(shù)據(jù)。加cache(不是最好的解決方式)數(shù)據(jù)要求比較嚴謹?shù)脑挷閙aster主庫
leaf-sagmnet
-采用每次獲取一個ID區(qū)間段的方式來解決,區(qū)間段用完之后再去數(shù)據(jù)庫獲取新的號段,這樣一來就可以大大減輕數(shù)據(jù)庫的壓力
核心字段:biz_tag, max_id ,step
biz_tag用來區(qū)分業(yè)務(wù),max_id表示該biz_tag目前所被分配的ID號段的最大值,step表示每次分配的號段長度,原來每次獲取ID都需要訪問數(shù)據(jù)庫,現(xiàn)在只需要把Step設(shè)置的足夠合理如1000,那么現(xiàn)在可以在1000個ID用完之后再去訪問數(shù)據(jù)庫
優(yōu)點:
- 擴展靈活,性能強能撐起大部分業(yè)務(wù)場景。
- ID號碼是趨勢遞增的,滿足數(shù)據(jù)庫存儲和查詢性能要求
- 可用性高,即使ID生成服務(wù)器不可用,也能夠使得業(yè)務(wù)在短時間內(nèi)可用,為排查問題爭取時間。
缺點:
- 可能存在多個節(jié)點同時請求ID區(qū)間的情況,依賴DB
對buffer:將獲取一個號段的方式優(yōu)化成獲取兩個號段,在一個號段用完之后不用立馬去更新號段,還有一個緩存號段備用,這樣能夠有效解決這種沖突問題,而且采用雙buffer的方式,在當前號段消耗了10%的時候就去檢查下一個號段有沒有準備好,如果沒有準備好就去更新下一個號段,當前號段用完了就切換到下一個已經(jīng)緩存好的號段去使用,同時在下一個號段消耗到10%的時候,又去檢測下一個號段有沒有準備好,如此往復(fù)。
優(yōu)點:
- 基于JVM存儲雙buffer的號段,減少了數(shù)據(jù)庫查詢,減少了網(wǎng)絡(luò)依賴,效率更高。
缺點
segment號段長度是固定的,業(yè)務(wù)量大時可能會頻繁更新號段,因為原本分配的號段會一下用完,如果號段長度設(shè)置的過長,但凡緩存中有號段沒有消耗完,其他節(jié)點重新獲取的號段與之前相比可能跨度會很大,動態(tài)調(diào)整Step
基于redis、mongodb、zk等中間件生成
雪花算法:
生成一個64bit的整性數(shù)字
第一位符號位固定為0,41位時間戳,10位workid,12位序列號
位數(shù)可以有不同實現(xiàn)
優(yōu)點
- 每個毫秒值包含的ID值很大,不夠可以變動位數(shù)來增加,性能佳(依賴workId的實現(xiàn))。
- 時間戳值在高位,中間是固定的機器碼,自增的序列在低位,整個ID是趨勢遞增的。
- 能夠根據(jù)業(yè)務(wù)場景數(shù)據(jù)庫節(jié)點布置靈活挑戰(zhàn)bit位劃分,靈活度高。
缺點
- 強依賴于機器時鐘,如果時鐘回撥,會導(dǎo)致重復(fù)的ID生成,所以一般基于此的算法發(fā)現(xiàn)時鐘回撥,都會拋異常處理,阻止ID生成,這可能導(dǎo)致服務(wù)不可用。
79、分布式鎖解決方案
需要這個鎖獨立于每一個服務(wù)之外,而不是在服務(wù)里面。
數(shù)據(jù)庫:利用主鍵沖突控制一次只有一個線程能獲取鎖,非阻塞、不可重入、單點、失效時間
Zookeeper分布式鎖:
zk通過臨時節(jié)點,解決了死鎖的問題,一旦客戶端獲取到鎖之后突然掛掉(Session連接斷開),那么這個臨時節(jié)點就會自動刪除,其他客戶端自動獲取鎖。臨時順序節(jié)點解決驚群效應(yīng)。
Redis分布式鎖:setNX,單線程處理網(wǎng)絡(luò)請求,不需要考慮并發(fā)安全性
所有服務(wù)節(jié)點設(shè)置相同的key,返回為0,則鎖獲取失敗
setnx問題:
- 1、早期版本沒有超時參數(shù),需要單獨設(shè)置,存在死鎖問題(中途宕機)
- 2、后期版本提供加鎖與設(shè)置原子性操作,但是存在任務(wù)超時,鎖自動釋放,導(dǎo)致并發(fā)問題,加鎖與釋放鎖不是同一線程問題。
刪除鎖:判斷線程唯一標志,再刪除
可重入性及鎖續(xù)期沒有 實現(xiàn),通過redisson解決(類似AQS的實現(xiàn),看門狗機制)
redlock:意思的機制都只操作單節(jié)點、即使Redis通過sentinel保證高可用,如果這個master節(jié)點由于某些原因發(fā)生了主從切換,那么就會出現(xiàn)鎖丟失的情況(redis同步設(shè)置可能數(shù)據(jù)丟失)。redlock從多個節(jié)點申請鎖,當一半以上節(jié)點獲取成功,鎖才算獲取成功,redission有相應(yīng)的實現(xiàn)。
80、分布式事務(wù)解決方案
XA規(guī)范:分布式事務(wù)規(guī)范,定義了分布式事務(wù)模型
四個角色:事務(wù)管理器(協(xié)調(diào)者TM)、資源管理器(參與者RM)、應(yīng)用程序AP、通信資源管理器CRM
全局事務(wù):一個橫跨多個數(shù)據(jù)庫的事務(wù),要么全部提交、要么全部回滾
JTA事務(wù)時java對XA規(guī)范的實現(xiàn),對應(yīng)JDBC的單庫事務(wù)。
兩階段協(xié)議:
2022年整理最詳細的java面試題、掌握這一套八股文、面試 ...-8.jpg (44.03 KB, 下載次數(shù): 7)
下載附件
2023-2-12 13:27 上傳
第一階段(prepare):每個參與者執(zhí)行本地事務(wù)但不提交,進入ready狀態(tài),并通知協(xié)調(diào)者已經(jīng)準備就緒。
第二階段(commit)當協(xié)調(diào)者確認每個參與者都read后,通知參與者進行commit操作,如果有參與者fail,則發(fā)送rollback指令,各參與者做回滾。
問題:
- 單點故障:一旦事務(wù)管理器出現(xiàn)故障,整個系統(tǒng)不可用(參與者都會阻塞住)
- 數(shù)據(jù)不一致:在階段二,如果事務(wù)管理器只發(fā)送了部分commit消息,此時網(wǎng)絡(luò)發(fā)生異常,那么只有部分參與者接收到commit信息,也就是說只有部分參與者提交了事務(wù),使得系統(tǒng)數(shù)據(jù)不一致。
- 響應(yīng)時間較長:參與者和協(xié)調(diào)者資源都被鎖住,提交或者回滾之后才能釋放
- 不確定性:當協(xié)調(diào)者管理器發(fā)送commit之后,并且此時只有一個參與者收到了commit,那么當該參與者與事務(wù)管理器同時宕機之后,重新選舉的事務(wù)管理器無法確定該條消息是否提交成功。
三階段協(xié)議:主要是針對兩階段的優(yōu)化,解決了2PC單點故障的問題,但是性能問題和不一致問題依舊沒有根本解決
2022年整理最詳細的java面試題、掌握這一套八股文、面試 ...-9.jpg (50.19 KB, 下載次數(shù): 6)
下載附件
2023-2-12 13:27 上傳
引入了超時機制解決參與者阻塞的問題,超時后本地提交,2pc只有協(xié)調(diào)者有超時機制
- 第一階段:CanCommit階段,協(xié)調(diào)者詢問事務(wù)參與者,是否有能力完成此次事務(wù)。
如果都返回yes,則進入第二階段
有一個返回no或等待響應(yīng)超時,則中斷事務(wù),并向所有參與者發(fā)送abort請求
- 第二階段:precommit階段,此時協(xié)調(diào)者會向所有的參與者發(fā)送precommit請求,參與者收到后開始執(zhí)行事務(wù)操作。參與者執(zhí)行完事務(wù)操作后(此時屬于未提交事務(wù)的狀態(tài)),就會向協(xié)調(diào)者反饋”Ack“表示我已經(jīng)準備好提交了,并等待協(xié)調(diào)者的下一步指令。
- 第三階段:DoCommit階段,在階段二中如果所有的參與者節(jié)點都返回Ack,那么協(xié)調(diào)者就會從”預(yù)提交狀態(tài)“轉(zhuǎn)變?yōu)椤碧峤粻顟B(tài)“。然后向所有的參與者節(jié)點發(fā)送”doCommit&#34;請求,參與者節(jié)點在收到提交請求后就會各自執(zhí)行事務(wù)提交操作,并向協(xié)調(diào)者節(jié)點反饋“Ack”消息,協(xié)調(diào)者收到所有參與者的Ack消息后完成事務(wù)。相反,如果有一個參與者節(jié)點未完成PreCommit的反饋或者反饋超時,那么協(xié)調(diào)者都會向所有的參與者節(jié)點發(fā)送abort請求,從而中斷事務(wù)。
TCC(補償事務(wù)):Try、Confirm、Cancel
- 針對每個操作,都要注冊一個與其對應(yīng)的確認和補償(撤銷)操作
Try操作做業(yè)務(wù)檢查及資源預(yù)留,Confirm做業(yè)務(wù)確認,Cancel實現(xiàn)一個與Try相反的操作既回滾操作。TM首先發(fā)起所有的分支事務(wù)的try操作,任何一個分支事務(wù)的try操作執(zhí)行失敗,TM將會發(fā)起所有分支事務(wù)的Cancel操作,若try操作全部成功,TM將會發(fā)起所有分支事務(wù)的Confirm操作,其中Confirm/Cancel操作若執(zhí)行失敗,TM會進行重試。
TCC模型對業(yè)務(wù)的侵入性較強,改造的難度較大,每個操作都需要try、confirm、cancel三個接口實現(xiàn)confirm和cancel接口還必須實現(xiàn)冪等性。
消息隊列的事務(wù)信息:
- 發(fā)送prepare消息到消息中間件
- 發(fā)送成功后,執(zhí)行本地事務(wù)
如果事務(wù)執(zhí)行成功,則commit,消息中間件將消息下發(fā)至消費端(commit前,消息不會被消費)
如果事務(wù)執(zhí)行失敗,則回滾,消息中間件將這條prepare消息刪除
- 消費端接收到消息進行消費,如果消費失敗,則不斷重試
81、如何實現(xiàn)接口冪等性
- 唯一id。每次操作,都根據(jù)操作和內(nèi)容生成唯一的id,在執(zhí)行之前先判斷id是否存在,如果不存在則執(zhí)行后續(xù)操作,并且保存到數(shù)據(jù)庫或者redis等。
- 服務(wù)端提供發(fā)送token的接口,業(yè)務(wù)調(diào)用接口前先獲取token,然后調(diào)用業(yè)務(wù)邏輯接口請求時,把token攜帶過去,服務(wù)器判斷token是否存在redis中,存在表示第一次請求,可以繼續(xù)執(zhí)行業(yè)務(wù),執(zhí)行業(yè)務(wù)完成后,最后需要把redis中的token刪除
- 建去重表。將業(yè)務(wù)中有唯一標識的字段保存到去重表,如果表中存在,則表示已經(jīng)處理過了
- 版本控制。增加版本號,當版本號符合時,才能更新數(shù)據(jù)
- 控制狀態(tài)。例如訂單有狀態(tài)已支付 未支付 支付中 支付失敗,當處于未支付的時候才允許修改為支付中等
----------------------------- |
|