GitHub地址:github.com/zaiyunduan1…,若是對你有幫助歡迎Starcss
若是咱們定義了主鍵(PRIMARY KEY),那麼InnoDB會選擇主鍵做爲彙集索引、若是沒有顯式定義主鍵,則InnoDB會選擇第一個不包含有NULL值的惟一索引做爲主鍵索引、若是也沒有這樣的惟一索引,則InnoDB會選擇內置6字節長的ROWID做爲隱含的彙集索引(ROWID隨着行記錄的寫入而主鍵遞增,這個ROWID不像ORACLE的ROWID那樣可引用,是隱含的)。html
數據記錄自己被存於主索引(一顆B+Tree)的葉子節點上。這就要求同一個葉子節點內(大小爲一個內存頁或磁盤頁)的各條數據記錄按主鍵順序存放,所以每當有一條新的記錄插入時,MySQL會根據其主鍵將其插入適當的節點和位置,若是頁面達到裝載因子(InnoDB默認爲15/16),則開闢一個新的頁(節點)前端
若是表使用自增主鍵,那麼每次插入新的記錄,記錄就會順序添加到當前索引節點的後續位置,當一頁寫滿,就會自動開闢一個新的頁java
若是使用非自增主鍵(若是身份證號或學號等),因爲每次插入主鍵的值近似於隨機,所以每次新紀錄都要被插到現有索引頁得中間某個位置,此時MySQL不得不爲了將新記錄插到合適位置而移動數據,甚至目標頁面可能已經被回寫到磁盤上而從緩存中清掉,此時又要從磁盤上讀回來,這增長了不少開銷,同時頻繁的移動、分頁操做形成了大量的碎片,獲得了不夠緊湊的索引結構,後續不得不經過OPTIMIZE TABLE來重建表並優化填充頁面。node
B+樹是一個平衡的多叉樹,從根節點到每一個葉子節點的高度差值不超過1,並且同層級的節點間有指針相互連接,是有序的 mysql
哈希索引的優點:linux
哈希索引不適用的場景:git
一般,B+樹索引結構適用於絕大多數場景,像下面這種場景用哈希索引才更有優點:程序員
在HEAP表中,若是存儲的數據重複度很低(也就是說基數很大),對該列數據以等值查詢爲主,沒有範圍查詢、沒有排序的時候,特別適合採用哈希索引,例如這種SQL:github
select id,name from table where name='李明'; — 僅等值查詢
複製代碼
而經常使用的InnoDB引擎中默認使用的是B+樹索引,它會實時監控表上索引的使用狀況,若是認爲創建哈希索引能夠提升查詢效率,則自動在內存中的「自適應哈希索引緩衝區」創建哈希索引(在InnoDB中默認開啓自適應哈希索引),經過觀察搜索模式,MySQL會利用index key的前綴創建哈希索引,若是一個表幾乎大部分都在緩衝池中,那麼創建一個哈希索引可以加快等值查詢。
注意:在某些工做負載下,經過哈希索引查找帶來的性能提高遠大於額外的監控索引搜索狀況和保持這個哈希表結構所帶來的開銷。但某些時候,在負載高的狀況下,自適應哈希索引中添加的read/write鎖也會帶來競爭,好比高併發的join操做。like操做和%的通配符操做也不適用於自適應哈希索引,可能要關閉自適應哈希索引。
B+的磁盤讀寫代價更低 B+的內部結點並無指向關鍵字具體信息的指針。所以其內部結點相對B樹更小。若是把全部同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入內存中的須要查找的關鍵字也就越多。相對來講IO讀寫次數也就下降了。
B+-tree的查詢效率更加穩定 因爲非終結點並非最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。因此任何關鍵字的查找必須走一條從根結點到葉子結點的路。全部關鍵字查詢的路徑長度相同,致使每個數據的查詢效率至關。
表分區,是指根據必定規則,將數據庫中的一張表分解成多個更小的,容易管理的部分。從邏輯上看,只有一張表,可是底層倒是由多個物理分區組成。
分表:指的是經過必定規則,將一張表分解成多張不一樣的表。好比將用戶訂單記錄根據時間成多個表。
分表與分區的區別在於:分區從邏輯上來說只有一張表,而分表則是將一張表分解成多張表。
命令:show variables like '%partition%' 運行結果:
mysql> show variables like '%partition%'; +-------------------+-------+ | Variable_name | Value | +-------------------+-------+ | have_partitioning | YES | +-------------------+-------+ 1 row in set (0.00 sec) have_partintioning 的值爲YES,表示支持分區。
MySQL InnoDB存儲引擎,實現的是基於多版本的併發控制協議——MVCC (Multi-Version Concurrency Control) (注:與MVCC相對的,是基於鎖的併發控制,Lock-Based Concurrency Control)。MVCC最大的好處:讀不加鎖,讀寫不衝突。在讀多寫少的OLTP應用中,讀寫不衝突是很是重要的,極大的增長了系統的併發性能,現階段幾乎全部的RDBMS,都支持了MVCC。
在MVCC併發控制中,讀操做能夠分紅兩類:
簡單的說,就是一組SQL語句集,功能強大,能夠實現一些比較複雜的邏輯功能,相似於JAVA語言中的方法;
ps:存儲過程跟觸發器有點相似,都是一組SQL集,可是存儲過程是主動調用的,且功能比觸發器更增強大,觸發器是某件事觸發後自動調用;
有哪些特性
DROP PROCEDURE IF EXISTS `proc_adder`;
DELIMITER ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `proc_adder`(IN a int, IN b int, OUT sum int)
BEGIN
#Routine body goes here...
DECLARE c int;
if a is null then set a = 0;
end if;
if b is null then set b = 0;
end if;
set sum = a + b;
END
;;
DELIMITER ;
set @b=5;
call proc_adder(0,@b,@s);
SELECT @s as sum;
create table tab2(
tab2_id varchar(11)
);
DROP TRIGGER if EXISTS t_ai_on_tab1;
create TRAILING t_ai_on_tab1
AFTER INSERT ON tab1
for EACH ROW
BEGIN
INSERT INTO tab2(tab2_id) values(new.tab1_id);
end;
INSERT INTO tab1(tab1_id) values('0001');
SELECT * FROM tab2;
複製代碼
區別:
InnoDB支持事務,MyISAM不支持,對於InnoDB每一條SQL語言都默認封裝成事務,自動提交,這樣會影響速度,因此最好把多條SQL語言放在begin和commit之間,組成一個事務;
InnoDB支持外鍵,而MyISAM不支持。對一個包含外鍵的InnoDB錶轉爲MYISAM會失敗;
InnoDB是彙集索引,數據文件是和索引綁在一塊兒的,必需要有主鍵,經過主鍵索引效率很高。可是輔助索引須要兩次查詢,先查詢到主鍵,而後再經過主鍵查詢到數據。所以,主鍵不該該過大,由於主鍵太大,其餘索引也都會很大。而MyISAM是非彙集索引,數據文件是分離的,索引保存的是數據文件的指針。主鍵索引和輔助索引是獨立的。
InnoDB不保存表的具體行數,執行select count(*) from table時須要全表掃描。而MyISAM用一個變量保存了整個表的行數,執行上述語句時只須要讀出該變量便可,速度很快;
Innodb不支持全文索引,而MyISAM支持全文索引,查詢效率上MyISAM要高;
如何選擇:
是否要支持事務,若是要請選擇innodb,若是不須要能夠考慮MyISAM;
若是表中絕大多數都只是讀查詢,能夠考慮MyISAM,若是既有讀寫也挺頻繁,請使用InnoDB。
系統奔潰後,MyISAM恢復起來更困難,可否接受;
MySQL5.5版本開始Innodb已經成爲Mysql的默認引擎(以前是MyISAM),說明其優點是有目共睹的,若是你不知道用什麼,那就用InnoDB,至少不會差。
剔除關係不密切的字段
字段命名要有規則及相對應的含義(不要一部分英文,一部分拼音,還有相似a.b.c這樣不明含義的字段)
字段命名儘可能不要使用縮寫(大多數縮寫都不能明確字段含義)
字段不要大小寫混用(想要具備可讀性,多個英文單詞可以使用下劃線形式鏈接)
字段名不要使用保留字或者關鍵字
保持字段名和類型的一致性
慎重選擇數字類型
給文本字段留足餘量
添加刪除標記(例如操做人、刪除時間)
創建版本機制
多型字段的處理,就是表中是否存在字段可以分解成更小獨立的幾部分(例如:人能夠分爲男人和女人)
多值字段的處理,能夠將表分爲三張表,這樣使得檢索和排序更加有調理,且保證數據的完整性!
對於大數據字段,獨立表進行存儲,以便影響性能(例如:簡介字段)
使用varchar類型代替char,由於varchar會動態分配長度,char指定長度是固定的。
給表建立主鍵,對於沒有主鍵的表,在查詢和索引定義上有必定的影響。
避免表字段運行爲null,建議設置默認值(例如:int類型設置默認值爲0)在索引查詢上,效率立顯!
創建索引,最好創建在惟一和非空的字段上,創建太多的索引對後期插入、更新都存在必定的影響(考慮實際狀況來建立)。
單線程指的是網絡請求模塊使用了一個線程(因此不需考慮併發安全性),即一個線程處理全部網絡請求,其餘模塊仍用了多個線程。
內部實現採用epoll,採用了epoll+本身實現的簡單的事件框架。epoll中的讀、寫、關閉、鏈接都轉化成了事件,而後利用epoll的多路複用特性,不在io上浪費一點時間 這3個條件不是相互獨立的,特別是第一條,若是請求都是耗時的,採用單線程吞吐量及性能不好。redis爲特殊的場景選擇了合適的技術方案。
redis其實是採用了線程封閉的觀念,把任務封閉在一個線程,天然避免了線程安全問題,不過對於須要依賴多個redis操做的複合操做來講,依然須要鎖,並且有多是分佈式鎖。
過程原理:
缺點:全部的slave節點數據的複製和同步都由master節點來處理,會照成master節點壓力太大,使用主從從結構來解決
RDB的優勢:
RDB 是一個很是緊湊(compact)的文件,它保存了 Redis 在某個時間點上的數據集。 這種文件很是適合用於進行備份: 好比說,你能夠在最近的 24 小時內,每小時備份一次 RDB 文件,而且在每月的每一天,也備份一個 RDB 文件。 這樣的話,即便趕上問題,也能夠隨時將數據集還原到不一樣的版本。
RDB 很是適用於災難恢復(disaster recovery):它只有一個文件,而且內容都很是緊湊,能夠(在加密後)將它傳送到別的數據中心,或者亞馬遜 S3 中。
RDB 能夠最大化 Redis 的性能:父進程在保存 RDB 文件時惟一要作的就是 fork 出一個子進程,而後這個子進程就會處理接下來的全部保存工做,父進程無須執行任何磁盤 I/O 操做。
RDB 在恢復大數據集時的速度比 AOF 的恢復速度要快
例子:下述的Something和Apple均可以做爲root對象。
public AClass{
public static Something;
public static final Apple;
''''''
}
複製代碼
Java方法的參數和方法中的局部變量,能夠做爲root.
public Aclass{
public void doSomething(Object A){
ObjectB b = new ObjectB;
}
}
複製代碼
自定義類加載器的方法:繼承 ClassLoader 類,重寫 findClass()方法 。
字節碼能夠經過如下兩種方式轉換成合適的語言:
問題解決思路:
內存溢出是指應用系統中存在沒法回收的內存或使用的內存過多,最終使得程序運行要用到的內存大於虛擬機能提供的最大內存。爲了解決Java中內存溢出問題,咱們首先必須瞭解Java是如何管理內存的。Java的內存管理就是對象的分配和釋放問題。在Java中,內存的分配是由程序完成的,而內存的釋放是由垃圾收集器(Garbage Collection,GC)完成的,程序員不須要經過調用GC函數來釋放內存,由於不一樣的JVM實現者可能使用不一樣的算法管理GC,有的是內存使用到達必定程度時,GC纔開始工做,也有定時執行的,有的是中斷式執行GC。但GC只能回收無用而且再也不被其它對象引用的那些對象所佔用的空間。Java的內存垃圾回收機制是從程序的主要運行對象開始檢查引用鏈,當遍歷一遍後發現沒有被引用的孤立對象就做爲垃圾回收。
引發內存溢出的緣由有不少種,常見的有如下幾種:
內存中加載的數據量過於龐大,如一次從數據庫取出過多數據;
集合類中有對對象的引用,使用完後未清空,使得JVM不能回收;
代碼中存在死循環或循環產生過多重複的對象實體;
使用的第三方軟件中的BUG;
啓動參數內存值設定的太小;
內存溢出雖然很棘手,但也有相應的解決辦法,能夠按照從易到難,一步步的解決。
第一步,就是修改JVM啓動參數,直接增長內存。這一點看上去彷佛很簡單,但很容易被忽略。JVM默承認以使用的內存爲64M,Tomcat默承認以使用的內存爲128MB,對於稍複雜一點的系統就會不夠用。在某項目中,就由於啓動參數使用的默認值,常常報「OutOfMemory」錯誤。所以,-Xms,-Xmx參數必定不要忘記加。
第二步,檢查錯誤日誌,查看「OutOfMemory」錯誤前是否有其它異常或錯誤。在一個項目中,使用兩個數據庫鏈接,其中專用於發送短信的數據庫鏈接使用DBCP鏈接池管理,用戶爲不將短信發出,有意將數據庫鏈接用戶名改錯,使得日誌中有許多數據庫鏈接異常的日誌,一段時間後,就出現「OutOfMemory」錯誤。經分析,這是因爲DBCP鏈接池BUG引發的,數據庫鏈接不上後,沒有將鏈接釋放,最終使得DBCP報「OutOfMemory」錯誤。通過修改正確數據庫鏈接參數後,就沒有再出現內存溢出的錯誤。
查看日誌對於分析內存溢出是很是重要的,經過仔細查看日誌,分析內存溢出前作過哪些操做,能夠大體定位有問題的模塊。
第三步,找出可能發生內存溢出的位置。重點排查如下幾點:
檢查代碼中是否有死循環或遞歸調用。
檢查是否有大循環重複產生新對象實體。
檢查對數據庫查詢中,是否有一次得到所有數據的查詢。通常來講,若是一次取十萬條記錄到內存,就可能引發內存溢出。這個問題比較隱蔽,在上線前,數據庫中數據較少,不容易出問題,上線後,數據庫中數據多了,一次查詢就有可能引發內存溢出。所以對於數據庫查詢儘可能採用分頁的方式查詢。
檢查List、MAP等集合對象是否有使用完後,未清除的問題。List、MAP等集合對象會始終存有對對象的引用,使得這些對象不能被GC回收。
第四步,使用內存查看工具動態查看內存使用狀況。某個項目上線後,每次系統啓動兩天後,就會出現內存溢出的錯誤。這種狀況通常是代碼中出現了緩慢的內存泄漏,用上面三個步驟解決不了,這就須要使用內存查看工具了。
內存查看工具備許多,比較有名的有:Optimizeit Profiler、JProbe Profiler、JinSight和Java1.5的Jconsole等。它們的基本工做原理大同小異,都是監測Java程序運行時全部對象的申請、釋放等動做,將內存管理的全部信息進行統計、分析、可視化。開發人員能夠根據這些信息判斷程序是否有內存泄漏問題。通常來講,一個正常的系統在其啓動完成後其內存的佔用量是基本穩定的,而不該該是無限制的增加的。持續地觀察系統運行時使用的內存的大小,能夠看到在內存使用監控窗口中是基本規則的鋸齒形的圖線,若是內存的大小持續地增加,則說明系統存在內存泄漏問題。經過間隔一段時間取一次內存快照,而後對內存快照中對象的使用與引用等信息進行比對與分析,能夠找出是哪一個類的對象在泄漏。
經過以上四個步驟的分析與處理,基本能處理內存溢出的問題。固然,在這些過程當中也須要至關的經驗與敏感度,須要在實際的開發與調試過程當中不斷積累。
整體上來講,產生內存溢出是因爲代碼寫的很差形成的,所以提升代碼的質量是最根本的解決辦法。有的人認爲先把功能實現,有BUG時再在測試階段進行修正,這種想法是錯誤的。正如一件產品的質量是在生產製造的過程當中決定的,而不是質量檢測時決定的,軟件的質量在設計與編碼階段就已經決定了,測試只是對軟件質量的一個驗證,由於測試不可能找出軟件中全部的BUG。
若是monitor的進入數爲0,則該線程進入monitor,而後將進入數設置爲1,該線程即爲monitor的全部者。
若是線程已經佔有該monitor,只是從新進入,則進入monitor的進入數加1.
若是其餘線程已經佔用了monitor,則該線程進入阻塞狀態,直到monitor的進入數爲0,再從新嘗試獲取monitor的全部權。
CAS,compare and swap的縮寫,中文翻譯成比較並交換。樂觀鎖用到的機制就是CAS,每次不加鎖而是假設沒有衝突而去完成某項操做,若是由於衝突失敗就重試。
原理:
JDK文檔說cas同時具備volatile讀和volatile寫的內存語義。
缺點:
ABA問題。 由於CAS須要在操做值的時候檢查下值有沒有發生變化,若是沒有發生變化則更新,可是若是一個值原來是A,變成了B,又變成了A,那麼使用CAS進行檢查時會發現它的值沒有發生變化
循環時間長開銷大。 自旋CAS若是長時間不成功,會給CPU帶來很是大的執行開銷。
只能保證一個共享變量的原子操做。 對多個共享變量操做時,循環CAS就沒法保證操做的原子性,這個時候就能夠用鎖,或者有一個取巧的辦法,就是把多個共享變量合併成一個共享變量來操做。好比有兩個共享變量i=2,j=a,合併一下ij=2a,而後用CAS來操做ij。從Java1.5開始JDK提供了AtomicReference類來保證引用對象之間的原子性,你能夠把多個變量放在一個對象裏來進行CAS操做。
Executor線程池框架是一個根據一組執行策略調用,調度,執行和控制的異步任務的框架。
新建線程 -> 達到核心數 -> 加入隊列 -> 新建線程(非核心) -> 達到最大數 -> 觸發拒絕策略
兩個特性
要想併發程序正確地執行,必需要保證原子性、可見性以及有序性,鎖保證了原子性,而volatile保證可見性和有序性
Lock 是一個 接口,而 synchronized 是 Java 中的 關鍵字, synchronized 是 內置的語言實現;
synchronized 在 發生異常時,會 自動釋放線程佔有的鎖,所以 不會導 致死鎖現象發生;而 Lock 在發生異常時,若是沒有主動經過 unLock()去釋放 鎖,則很 可能形成死鎖現象,所以用 使用 Lock 時須要在 finally 塊中釋放鎖;
Lock 可讓 等待鎖的線程響應中斷 (可中斷鎖),而 synchronized 卻不行,使用 synchronized 時,等待的線程會一直等待下去, 不可以響應中 斷 (不可中斷鎖);
經過 Lock 能夠知道 有沒有成功獲取鎖 (tryLock ( ) 方法 : 若是獲取 了鎖 ,回 則返回 true ;回 不然返回 false e, , 也就說這個方法不管如何都會當即返回 。 在拿不到鎖時不會一直在那等待。 ),而 synchronized 卻沒法辦到。
Lock 能夠提升 多個線程進行讀操做的效率( 讀寫鎖)。
Lock 能夠實現 公平鎖,synchronized 不保證公平性。 在性能上來講,若是線程競爭資源不激烈時,二者的性能是差很少的,而 當競爭資源很是激烈時(即有大量線程同時競爭),此時 Lock 的性能要遠遠優 於 synchronized。因此說,在具體使用時要根據適當狀況選擇。
Synchronized實現內存共享,ThreadLocal爲每一個線程維護一個本地變量。 採用空間換時間,它用於線程間的數據隔離,爲每個使用該變量的線程提供一個副本,每一個線程均可以獨立地改變本身的副本,而不會和其餘線程的副本衝突。 ThreadLocal類中維護一個Map,用於存儲每個線程的變量副本,Map中元素的鍵爲線程對象,而值爲對應線程的變量副本。 ThreadLocal在Spring中發揮着巨大的做用,在管理Request做用域中的Bean、事務管理、任務調度、AOP等模塊都出現了它的身影。 Spring中絕大部分Bean均可以聲明成Singleton做用域,採用ThreadLocal進行封裝,所以有狀態的Bean就可以以singleton的方式在多線程中正常工做了。
Java 5在concurrency包中引入了java.util.concurrent.Callable 接口,它和Runnable接口很類似,但它能夠返回一個對象或者拋出一個異常。
Callable接口使用泛型去定義它的返回類型。Executors類提供了一些有用的方法去在線程池中執行Callable內的任務。因爲Callable任務是並行的,咱們必須等待它返回的結果。java.util.concurrent.Future對象爲咱們解決了這個問題。在線程池提交Callable任務後返回了一個Future對象,使用它咱們能夠知道Callable任務的狀態和獲得Callable返回的執行結果。Future提供了get()方法讓咱們能夠等待Callable結束並獲取它的執行結果。
在Java中有兩類線程:User Thread(用戶線程)、Daemon Thread(守護線程) 用個比較通俗的好比,任何一個守護線程都是整個JVM中全部非守護線程的保姆: 只要當前JVM實例中尚存在任何一個非守護線程沒有結束,守護線程就;只有當最後一個非守護線程結束時,守護線程隨着JVM一同結束工做。 JVM內部的實現是若是運行的程序只剩下守護線程的話,程序將終止運行,直接結束。因此守護線程是做爲輔助線程存在的,主要的做用是提供計數等等輔助的功能。
終止線程的三種方法:
thread.exit = true; // 終止線程thread
複製代碼
使用stop方法強行終止線程(這個方法不推薦使用,由於stop和suspend、resume同樣,也可能發生不可預料的結果)。 使用stop方法能夠強行終止正在運行或掛起的線程。咱們能夠使用以下的代碼來終止線程: thread.stop(); 雖然使用上面的代碼能夠終止線程,但使用stop方法是很危險的,就象忽然關閉計算機電源,而不是按正常程序關機同樣,可能會產生不可預料的結果,所以,並不推薦使用stop方法來終止線程。
使用interrupt方法中斷線程,使用interrupt方法來終端線程可分爲兩種狀況:
注意:在Thread類中有兩個方法能夠判斷線程是否經過interrupt方法被終止。一個是靜態的方法interrupted(),一個是非靜態的方法isInterrupted(),這兩個方法的區別是interrupted用來判斷當前線是否被中斷,而isInterrupted能夠用來判斷其餘線程是否被中斷。所以,while (!isInterrupted())也能夠換成while (!Thread.interrupted())。
HashSet
TreeSet
LinkedHashMap的實現就是HashMap+LinkedList的實現方式,以HashMap維護數據結構,以LinkList的方式維護數據插入順序
LinkedHashMap保存了記錄的插入順序,在用Iterator遍歷LinkedHashMap時,先獲得的記錄確定是先插入的。 在遍歷的時候會比HashMap慢TreeMap可以把它保存的記錄根據鍵排序,默認是按升序排序,也能夠指定排序的比較器
利用LinkedHashMap實現LRU算法緩存(
這裏的Hash算法本質上就是三步:取key的hashCode值、高位運算、取模運算。
元素在從新計算hash以後,由於n變爲2倍,那麼n-1的mask範圍在高位多1bit(紅色),所以新的index就會發生這樣的變化: hashMap 1.8 哈希算法例圖2
Set<Entry<String, String>> entrySet = map.entrySet();
Set<String> set = map.keySet();`
複製代碼
public V get(Object key) {
if (key == null)
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
複製代碼
final Entry<K,V> getEntry(Object key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
}
return null;
}
複製代碼
參數 | 抽象類 | 接口 |
---|---|---|
默認的方法實現 | 它能夠有默認的方法實現 | 接口徹底是抽象的。它根本不存在方法的實現 |
實現 | 子類使用extends關鍵字來繼承抽象類。若是子類不是抽象類的話,它須要提供抽象類中全部聲明的方法的實現。 | 子類使用關鍵字implements來實現接口。它須要提供接口中全部聲明的方法的實現 |
構造器 | 抽象類能夠有構造器 | 接口不能有構造器 |
與正常Java類的區別 | 除了你不能實例化抽象類以外,它和普通Java類沒有任何區別 | 接口是徹底不一樣的類型 |
訪問修飾符 | 抽象方法能夠有public、protected和default這些修飾符 | 接口方法默認修飾符是public。你不能夠使用其它修飾符。 |
main方法 | 抽象方法能夠有main方法而且咱們能夠運行它 | 接口沒有main方法,所以咱們不能運行它。 |
多繼承 | 抽象方法能夠繼承一個類和實現多個接口 | 接口只能夠繼承一個或多個其它接口 |
速度 | 它比接口速度要快 | 接口是稍微有點慢的,由於它須要時間去尋找在類中實現的方法。 |
添加新方法 | 若是你往抽象類中添加新的方法,你能夠給它提供默認的實現。所以你不須要改變你如今的代碼。 | 若是你往接口中添加方法,那麼你必須改變實現該接口的類。 |
Employee emp2 = (Employee)Class.forName("org.programming.mitra.exercises.Employee").newInstance();
複製代碼
Constructor<Employee> constructor = Employee.class.getConstructor();
Employee emp3 = constructor.newInstance();
複製代碼
ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"));
Employee emp5 = (Employee) in.readObject();
複製代碼
上圖所示的間接轉發請求的過程以下: 瀏覽器向Servlet1發出訪問請求; Servlet1調用sendRedirect()方法,將瀏覽器重定向到Servlet2; 瀏覽器向servlet2發出請求; 最終由Servlet2作出響應。
上圖所示的直接轉發請求的過程以下: 瀏覽器向Servlet1發出訪問請求; Servlet1調用forward()方法,在服務器端將請求轉發給Servlet2; 最終由Servlet2作出響應。
類型擦除的主要過程以下:
Object是全部類的根類,任何類的對象均可以設置給該Object引用變量,使用的時候可能須要類型強制轉換,可是用使用了泛型T、E等這些標識符後,在實際用以前類型就已經肯定了,不須要再進行類型強制轉換。
throw:(針對對象的作法) 拋出一個異常,能夠是系統定義的,也能夠是本身定義的
public void yichang(){
NumberFormatException e = new NumberFormatException();
throw e;
}
複製代碼
throws:(針對一個方法拋出的異常) 拋出一個異常,能夠是系統定義的,也能夠是本身定義的。
public void yichang() throws NumberFormatException{
int a = Integer.parseInt("10L");
}
複製代碼
class文件是一種8位字節的二進制流文件
父類實現了Serializable,子類不須要實現Serializable
相關注意事項 1. 序列化時,只對對象的狀態進行保存,而無論對象的方法; 2. 當一個父類實現序列化,子類自動實現序列化,不須要顯式實現Serializable接口; c)當一個對象的實例變量引用其餘對象,序列化該對象時也把引用對象進行序列化; 3. 並不是全部的對象均可以序列化,至於爲何不能夠,有不少緣由了,好比: 1.安全方面的緣由,好比一個對象擁有private,public等field,對於一個要傳輸的對象,好比寫到文件,或者進行rmi傳輸等等,在序列化進行傳輸的過程當中,這個對象的private等域是不受保護的。 2. 資源分配方面的緣由,好比socket,thread類,若是能夠序列化,進行傳輸或者保存,也沒法對他們進行從新的資源分配,並且,也是沒有必要這樣實現。
2,反過來父類未實現Serializable,子類實現了,序列化子類實例的時候,父類的屬性是直接被跳過不保存,仍是能保存但不能還原?(答案:值不保存)
解:父類實現接口後,全部派生類的屬性都會被序列化。子類實現接口的話,父類的屬性值丟失。
java中序列化之子類繼承父類序列化
標識符能夠包括這4種字符:字母、下劃線、$、數字;開頭不能是數字;不能是關鍵字
Integer i = 127的時候,使用Java常量池技術,是爲了方便快捷地建立某些對象,當你須要一個對象時候,就去這個池子裏面找,找不到就在池子裏面建立一個。可是必須注意 若是對象是用new 建立的。那麼無論是什麼對像,它是不會放到池子裏的,而是向堆申請新的空間存儲。Byte,Short,Integer,Long,Character這5種整型的包裝類也只是在對應值在-128到127之間的數時纔可以使用對象池。超過了就要申請空間建立對象了
int i1=128;
Integer i2=128;
Integer i3=new Integer(128);//自動拆箱
System.out.println(i1==i2);//true
System.out.println(i1==i3);//true
Integer i5=127;
Integer i6=127;
System.out.println(i5==i6);//true
Integer i5=127;
Integer ii5=new Integer(127);
System.out.println(i5==ii5);//false
Integer i7=new Integer(127);
Integer i8=new Integer(127);
System.out.println(i7==i8);//false
複製代碼
最好的單例模式是靜態內部類,不要寫雙重檢驗
private static class LazySomethingHolder {
public static Something something = new Something();
}
public static Something getInstance() {
return LazySomethingHolder.something;
}
複製代碼
Java的每一個對象中都有一個鎖(monitor,也能夠成爲監視器) 而且wait(),notify()等方法用於等待對象的鎖或者通知其餘線程對象的監視器可用。在Java的線程中並無可供任何對象使用的鎖和同步器。這就是爲何這些方法是Object類的一部分,這樣Java的每個類都有用於線程間通訊的基本方法
這兩個方法來自不一樣的類分別是Thread和Object
最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其餘線程能夠使用同步控制塊或者方法。
wait,notify和notifyAll只能在同步控制方法或者同步控制塊裏面使用,而sleep能夠在任何地方使用(使用範圍)
sleep必須捕獲異常,而wait,notify和notifyAll不須要捕獲異常
sleep方法屬於Thread類中方法,表示讓一個線程進入睡眠狀態,等待必定的時間以後,自動醒來進入到可運行狀態,不會立刻進入運行狀態,由於線程調度機制恢復線程的運行也須要時間,一個線程對象調用了sleep方法以後,並不會釋放他所持有的全部對象鎖,因此也就不會影響其餘進程對象的運行。但在sleep的過程當中過程當中有可能被其餘對象調用它的interrupt(),產生InterruptedException異常,若是你的程序不捕獲這個異常,線程就會異常終止,進入TERMINATED狀態,若是你的程序捕獲了這個異常,那麼程序就會繼續執行catch語句塊(可能還有finally語句塊)以及之後的代碼。
在有些狀況下,程序設計者在設計一個類的時候爲須要重寫equals方法,好比String類,可是千萬要注意,在重寫equals方法的同時,必須重寫hashCode方法。 也就是說對於兩個對象,若是調用equals方法獲得的結果爲true,則兩個對象的hashcode值一定相等; 若是equals方法獲得的結果爲false,則兩個對象的hashcode值不必定不一樣; 若是兩個對象的hashcode值不等,則equals方法獲得的結果一定爲false; 若是兩個對象的hashcode值相等,則equals方法獲得的結果未知。
Object方法:equals()、toString()、finalize()、hashCode()、getClass()、clone()、wait()、notify()、notifyAll()
值傳遞
public class TempTest {
private void test1(int a) {
a = 5;
System.out.println("test1方法中的a=" + a);
}
public static void main(String[] args) {
TempTest t = new TempTest();
int a = 3;
t.test1(11);
System.out.println("main方法中a=" + a);
}
}
複製代碼
test1方法中的a=5 main方法中a=3 值傳遞:傳遞的是值的拷貝,傳遞後就互不相關了 引用傳遞:傳遞的是變量所對應的內存空間的地址
public class TempTest {
private void test1(A a) {
a.age = 20;
System.out.println("test1方法中a=" + a.age);
}
public static void main(String[] args) {
TempTest t = new TempTest();
A a = new A();
a.age = 10;
t.test1(a);
System.out.println("main方法中a=" + a.age);
}
}
class A {
public int age = 0;
}
複製代碼
test1方法中a=20 main方法中a=20 傳遞前和傳遞後都指向同一個引用(同一個內存空間) 若是不互相影響,方法是在test1方法裏面新new一個實例就能夠了
netty經過Reactor模型基於多路複用器接收並處理用戶請求,內部實現了兩個線程池,boss線程和work線程池,其中boss線程池的線程負責處理請求的accept事件,當接收到accept事件的請求,把對應的socket封裝到一個NioSocketChannel中,並交給work線程池,其中work線程池負責請求的read和write事件
服務端和客戶端各自維護一個管理通道的對象,咱們稱之爲 selector,該對 象能檢測一個或多個通道(channel)上的事件。咱們以服務端爲例,若是服務 端的 selector 上註冊了讀事件,某時刻客戶端給服務端送了一些數據,阻塞 I/O 這時會調用 read()方法阻塞地讀取數據,而 NIO 的服務端會在 selector 中添加 一個讀事件。服務端的處理線程會輪詢地訪問 selector,若是訪問 selector 時發 現有感興趣的事件到達,則處理這些事件,若是沒有感興趣的事件到達,則處 理線程會一直阻塞直到感興趣的事件到達爲止。
緩衝區Buffer
通道Channel
選擇器(Selector )
Selector(選擇器)是Java NIO中可以檢測一到多個NIO通道,並可以知曉通道是否爲諸如讀寫事件作好準備的組件。這樣,一個單獨的線程能夠管理多個channel,從而管理多個網絡鏈接。
爲了實現Selector管理多個SocketChannel,必須將具體的SocketChannel對象註冊到Selector,並聲明須要監聽的事件(這樣Selector才知道須要記錄什麼數據),一共有4種事件:
每次請求到達服務器,都是從connect開始,connect成功後,服務端開始準備accept,準備就緒,開始讀數據,並處理,最後寫回數據返回。
因此,當SocketChannel有對應的事件發生時,Selector均可以觀察到,並進行相應的處理。
(GET) 請注意,查詢字符串(名稱/值對)是在 GET 請求的 URL 中發送的: /test/demo_form.asp?name1=value1&name2=value2
GET 請求可被緩存
GET 請求保留在瀏覽器歷史記錄中
GET 請求可被收藏爲書籤
GET 請求不該在處理敏感數據時使用
GET 請求有長度限制
GET 請求只應當用於取回數據 POST 方法 (POST) 請注意,查詢字符串(名稱/值對)是在 POST 請求的 HTTP 消息主體中發送的: POST /test/demo_form.asp HTTP/1.1 Host: w3schools.com name1=value1&name2=value2
POST 請求不會被緩存
POST 請求不會保留在瀏覽器歷史記錄中
POST 不能被收藏爲書籤
POST 請求對數據長度沒有要求
既使用TCP又使用UDP
一個冪等操做的特色是其任意屢次執行所產生的影響均與一次執行的影響相同。冪等函數,或冪等方法,是指能夠使用相同參數重複執行,並能得到相同結果的函數。這些函數不會影響系統狀態,也不用擔憂重複執行會對系統形成改變。例如,「getUsername()和setTrue()」函數就是一個冪等函數.
Cookies是一種可以讓網站服務器把少許數據儲存到客戶端的硬盤或內存,或是從客戶端的硬盤讀取數據的一種技術。Cookies是當你瀏覽某網站時,由Web服務器置於你硬盤上的一個很是小的文本文件,它能夠記錄你的用戶ID、密碼、瀏覽過的網頁、停留的時間等信息。 session: 當用戶請求來自應用程序的 Web 頁時,若是該用戶尚未會話,則 Web 服務器將自動建立一個 Session 對象。當會話過時或被放棄後,服務器將終止該會話。 cookie機制:採用的是在客戶端保持狀態的方案,而session機制採用的是在服務端保持狀態的方案。同時咱們看到因爲服務器端保持狀態的方案在客戶端也須要保存一個標識,因此session機制可能須要藉助cookie機制來達到保存標識的目的。
Session是服務器用來跟蹤用戶的一種手段,每一個Session都有一個惟一標識:session ID。當服務器建立了Session時,給客戶端發送的響應報文包含了Set-cookie字段,其中有一個名爲sid的鍵值對,這個鍵值Session ID。客戶端收到後就把Cookie保存瀏覽器,而且以後發送的請求報表都包含SessionID。HTTP就是經過Session和Cookie這兩個發送一塊兒合做來實現跟蹤用戶狀態,Session用於服務端,Cookie用於客戶端
第一次握手:創建鏈接時,客戶端發送syn包(syn=j)到服務器,並進入SYN_SEND狀態,等待服務器確認;
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時本身也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
完成三次握手,客戶端與服務器開始傳送數據
TIME_WAIT的狀態就是主動斷開的一方(這裏是客戶端),發送完最後一次ACK以後進入的狀態。而且持續時間還挺長的。客戶端TIME_WAIT持續2倍MSL時長,在linux體系中大概是60s,轉換成CLOSE狀態
TIME_WAIT 是主動關閉連接時造成的,等待2MSL時間,約4分鐘。主要是防止最後一個ACK丟失。 因爲TIME_WAIT 的時間會很是長,所以server端應儘可能減小主動關閉鏈接
CLOSE_WAIT是被動關閉鏈接是造成的。根據TCP狀態機,服務器端收到客戶端發送的FIN,則按照TCP實現發送ACK,所以進入CLOSE_WAIT狀態。但若是服務器端不執行close(),就不能由CLOSE_WAIT遷移到LAST_ACK,則系統中會存在不少CLOSE_WAIT狀態的鏈接。此時,多是系統忙於處理讀、寫操做,而未將已收到FIN的鏈接,進行close。此時,recv/read已收到FIN的鏈接socket,會返回0。
假設最終的ACK丟失,server將重發FIN,client必須維護TCP狀態信息以即可以重發最終的ACK,不然會發送RST,結果server認爲發生錯誤。TCP實現必須可靠地終止鏈接的兩個方向(全雙工關閉),client必須進入 TIME_WAIT 狀態,由於client可能面 臨重發最終ACK的情形。
若是 TIME_WAIT 狀態保持時間不足夠長(好比小於2MSL),第一個鏈接就正常終止了。第二個擁有相同相關五元組的鏈接出現,而第一個鏈接的重複報文到達,干擾了第二個鏈接。TCP實現必須防止某個鏈接的重複報文在鏈接終止後出現,因此讓TIME_WAIT狀態保持時間足夠長(2MSL),鏈接相應方向上的TCP報文要麼徹底響應完畢,要麼被 丟棄。創建第二個鏈接的時候,不會混淆。
若是服務器出了異常,百分之八九十都是下面兩種狀況:
1.服務器保持了大量TIME_WAIT狀態
2.服務器保持了大量CLOSE_WAIT狀態,簡單來講CLOSE_WAIT數目過大是因爲被動關閉鏈接處理不當致使的。
域名解析 --> 發起TCP的3次握手 --> 創建TCP鏈接後發起http請求 --> 服務器響應http請求,瀏覽器獲得html代碼 --> 瀏覽器解析html代碼,並請求html代碼中的資源(如js、css、圖片等) --> 瀏覽器對頁面進行渲染呈現給用戶
HTTP協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,是用於從萬維網(WWW:World Wide Web )服務器傳輸超文本到本地瀏覽器的傳送協議。
特色
簡單快速:客戶向服務器請求服務時,只需傳送請求方法和路徑。請求方法經常使用的有GET、HEAD、POST。每種方法規定了客戶與服務器聯繫的類型不一樣。因爲HTTP協議簡單,使得HTTP服務器的程序規模小,於是通訊速度很快。
靈活:HTTP容許傳輸任意類型的數據對象。正在傳輸的類型由Content-Type加以標記。
無鏈接:無鏈接的含義是限制每次鏈接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開鏈接。採用這種方式能夠節省傳輸時間。
無狀態:HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺乏狀態意味着若是後續處理須要前面的信息,則它必須重傳,這樣可能致使每次鏈接傳送的數據量增大。另外一方面,在服務器不須要先前信息時它的應答就較快。
支持B/S及C/S模式。
請求消息Request
響應消息Response
狀態碼
http的方法
URI,是uniform resource identifier,統一資源標識符,用來惟一的標識一個資源。 Web上可用的每種資源如HTML文檔、圖像、視頻片斷、程序等都是一個來URI來定位的
URI通常由三部組成:
URL是uniform resource locator,統一資源定位器,它是一種具體的URI,即URL能夠用來標識一個資源,並且還指明瞭如何locate這個資源。 URL是Internet上用來描述信息資源的字符串,主要用在各類WWW客戶程序和服務器程序上,特別是著名的Mosaic。 採用URL能夠用一種統一的格式來描述各類信息資源,包括文件、服務器的地址和目錄等。
URL通常由三部組成:
https實際就是在TCP層與http層之間加入了SSL/TLS來爲上層的安全保駕護航,主要用到對稱加密、非對稱加密、證書,等技術進行客戶端與服務器的數據加密傳輸,最終達到保證整個通訊的安全性。
動態規劃過程是:每次決策依賴於當前狀態,又隨即引發狀態的轉移。一個決策序列就是在變化的狀態中產生出來的,因此,這種多階段最優化決策解決問題的過程就稱爲動態規劃。
在數組中找到一個基準數(pivot) 分區,將數組中比基準數大的放到它的右邊,比基準數小的放到它的左邊 繼續對左右區間重複第二步,直到各個區間只有一個數,這時候,數組也就有序了。
27 23 27 3 以第一個27做爲pivot中心點,則27與後面那個3交換,造成 3 23 27 27,排序通過一次結束,但最後那個27在排序之初先於初始位置3那個27,因此不穩定。
利用大頂堆(小頂堆)堆頂記錄的是最大關鍵字(最小關鍵字)這一特性,使得每次從無序中選擇最大記錄(最小記錄)變得簡單。 其基本思想爲(大頂堆): 1)將初始待排序關鍵字序列(R1,R2....Rn)構建成大頂堆,此堆爲初始的無須區; 2)將堆頂元素R[1]與最後一個元素R[n]交換,此時獲得新的無序區(R1,R2,......Rn-1)和新的有序區(Rn),且知足R[1,2...n-1]<=R[n]; 3)因爲交換後新的堆頂R[1]可能違反堆的性質,所以須要對當前無序區(R1,R2,......Rn-1)調整爲新堆,而後再次將R[1]與無序區最後一個元素交換,獲得新的無序區(R1,R2....Rn-2)和新的有序區(Rn-1,Rn)。不斷重複此過程直到有序區的元素個數爲n-1,則整個排序過程完成。
字典樹主要有以下三點性質:
linkList reverse(linkList head){
linkList p,q,pr;
p = head->next;
q = NULL;
head->next = NULL;
while(p){
pr = p->next;
p->next = q;
q = p;
p = pr;
}
head->next = q;
return head;
}
複製代碼
通常來講,進程有三個狀態,即就緒狀態,運行狀態,阻塞狀態。
固然理論上上述三種狀態之間轉換分爲六種狀況;
如下兩種狀態是不可能發生的:
阻塞——>運行:即便給阻塞進程分配CPU,也沒法執行,操做系統在進行調度時不會從阻塞隊列進行挑選,而是從就緒隊列中選取
就緒——>阻塞:就緒態根本就沒有執行,談不上進入阻塞態。
ln -s source dist # 創建軟鏈接 ln source dist # 創建硬鏈接 創建硬連接時,連接文件和被連接文件必須位於同一個文件系統中,而且不能創建指向目錄的硬連接
單個線程,經過記錄跟蹤每一個I/O流(sock)的狀態,來同時管理多個I/O流 。儘可能多的提升服務器的吞吐能力
select, poll, epoll 都是I/O多路複用的具體的實現
設計模式主要分三個類型:建立型、結構型和行爲型。
Singleton,單例模式:保證一個類只有一個實例,並提供一個訪問它的全局訪問點
Abstract Factory,抽象工廠:提供一個建立一系列相關或相互依賴對象的接口,而無須指定它們的具體類。
Factory Method,工廠方法:定義一個用於建立對象的接口,讓子類決定實例化哪個類,Factory Method使一個類的實例化延遲到了子類。
Builder,建造模式:將一個複雜對象的構建與他的表示相分離,使得一樣的構建過程能夠建立不一樣的表示。
Prototype,原型模式:用原型實例指定建立對象的種類,而且經過拷貝這些原型來建立新的對象。
這種代理方式須要代理對象和目標對象實現同樣的接口。在程序運行前,代理類的.class文件就已經存在了。
優勢:
缺點:
動態代理利用了JDK API,運用反射機制動態地在內存中構建代理對象,從而實現對目標對象的代理功能。動態代理又被稱爲JDK代理或接口代理。
動態代理是實現JDK裏的InvocationHandler接口的invoke方法,但注意的是代理的是接口,也就是你的業務類必需要實現接口,經過Proxy裏的newProxyInstance獲得代理對象
優勢:
動態代理
JDK中的動態代理: 經過反射類Proxy以及InvocationHandler回調接口實現的,
動態代理缺點: JDK中所要進行動態代理的類必需要實現一個接口,也就是說只能對該類所實現接口中定義的方法進行代理,這在實際編程中具備必定的侷限性,並且使用反射的效率也並非很高。
CGLIB
CGLIB原理:動態生成一個要代理類的子類,子類重寫要代理的類的全部不是final的方法。在子類中採用方法攔截的技術攔截全部父類方法的調用,順勢織入橫切邏輯。它比使用java反射的JDK動態代理要快。
CGLIB底層:使用字節碼處理框架ASM,來轉換字節碼並生成新的類。不鼓勵直接使用ASM,由於它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉。
CGLIB優勢:它爲沒有實現接口的類提供代理,爲JDK的動態代理提供了很好的補充。一般能夠使用Java的動態代理建立代理,但當要代理的類沒有實現接口或者爲了更好的性能,CGLIB是一個好的選擇。
CGLIB缺點:對於final方法,沒法進行代理
分佈式系統事務一致性解決方案 MQ(事務消息)
舉個例子,Bob向Smith轉帳,那咱們究竟是先發送消息,仍是先執行扣款操做?
好像均可能會出問題。若是先發消息,扣款操做失敗,那麼Smith的帳戶裏面會多出一筆錢。反過來,若是先執行扣款操做,後發送消息,那有可能扣款成功了可是消息沒發出去,Smith收不到錢。除了上面介紹的經過異常捕獲和回滾的方式外,還有沒有其餘的思路呢?
下面以阿里巴巴的RocketMQ中間件爲例,分析下其設計和實現思路。
RocketMQ第一階段發送Prepared消息時,會拿到消息的地址,第二階段執行本地事物,第三階段經過第一階段拿到的地址去訪問消息,並修改狀態。細心的讀者可能又發現問題了,若是確認消息發送失敗了怎麼辦?RocketMQ會按期掃描消息集羣中的事物消息,這時候發現了Prepared消息,它會向消息發送者確認,Bob的錢究竟是減了仍是沒減呢?若是減了是回滾仍是繼續發送確認消息呢?RocketMQ會根據發送端設置的策略來決定是回滾仍是繼續發送確認消息。這樣就保證了消息發送與本地事務同時成功或同時失敗。以下圖:
使用樂觀鎖,樂觀鎖意思是不鎖定表的狀況下,利用業務的控制來解決併發問題,這樣既保證數據的併發 可讀性 ,又保證保存數據的 排他性,保證性能的同時解決了併發帶來 的髒數據問題。hibernate 中實現樂觀鎖。(樂觀鎖,使用版本標識來肯定讀到的數據與提交時的數據是否一致。提交後修改版本標識,不一致時能夠採起丟棄和再次嘗試的策略。)
分佈式:一個業務分拆多個子業務,部署在不一樣的服務器上 集羣:同一個業務,部署在多個服務器上
超賣是什麼
解決方案
update sk_goods_seckill set stock_count = stock_count - 1 where goods_id = #{goodsId} and stock_count > 0
u_uid_gid
(user_id
,goods_id
) USING BTREE,防止同一用戶同一商品下兩次訂單要實現分佈式鎖,最簡單的方式可能就是直接建立一張鎖表,而後經過操做該表中的數據來實現了。
咱們對method_name(方法名)作了惟一性約束,這裏若是有多個請求同時提交到數據庫的話,數據庫會保證只有一個操做能夠成功,那麼咱們就能夠認爲操做成功的那個線程得到了該方法的鎖,能夠執行方法體內容。當方法執行完畢以後,想要釋放鎖的話就刪除該記錄
緣由:
解決方法:
請求 ---> DispatcherServlet(前端控制器)---> 調用HandlerMapping(處理器映射器)---> DispatcherServlet調用 HandlerAdapter(處理器適配器)---> 適配調用具體的Controller ---> 返回ModelAndView ---> 傳給ViewReslover視圖解析器 ---> 解析後返回具體View ---> 根據View進行渲染視圖響應用戶
MyBatis完成2件事情
MyBatis的主要成員
MyBatis提供查詢緩存,用於減輕數據庫壓力,提升性能。MyBatis提供了一級緩存和二級緩存。
一級緩存是SqlSession級別的緩存,每一個SqlSession對象都有一個哈希表用於緩存數據,不一樣SqlSession對象之間緩存不共享。同一個SqlSession對象對象執行2遍相同的SQL查詢,在第一次查詢執行完畢後將結果緩存起來,這樣第二遍查詢就不用向數據庫查詢了,直接返回緩存結果便可。一級緩存是MyBatis內部實現的一個特性,用戶不能配置,默認狀況下自動支持的緩存,用戶沒有定製它的權利
二級緩存是Application應用級別的緩存,它的是生命週期很長,跟Application的聲明週期同樣,也就是說它的做用範圍是整個Application應用。MyBatis默認是不開啓二級緩存的,能夠在配置文件中使用以下配置來開啓二級緩存
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
複製代碼
以上對面試相關的知識點作了系統的整理,但願對你們有所幫助。想要支持樓主的話,在 Github 上點個 Star,還有就是有些知識點是從網上優秀博客摘抄下來的,若是做者不但願文章部份內容被轉載,能夠通知樓主,會及時處理,感謝。