索引的實現方式
1、B+樹
我們經常聽到B+樹就是這個概念,用這個樹的目的和紅黑樹差不多,也是為了儘量保持樹的平衡,當然紅黑樹是二叉樹,但B+樹就不是二叉樹了,節點下面可以有多個子節點,資料庫開發商會設定子節點數的一個最大值,這個值不會太小,所以B+樹一般來說比較矮胖,而紅黑樹就比較瘦高了。
關於B+樹的插入,刪除,會涉及到一些演算法以保持樹的平衡,這裡就不詳述了。ORACLE的預設索引就是這種結構的。
如果經常需要同時對兩個欄位進行AND查詢,那麼使用兩個單獨索引不如建立一個複合索引,因為兩個單獨索引通常資料庫只能使用其中一個,而使用複合索引因為索引本身就對應到兩個欄位上的,效率會有很大提高。
2、雜湊索引
第二種索引叫做雜湊索引,就是通過雜湊函式來定位的一種索引,不過很少有單獨使用雜湊索引的,反而是雜湊檔案組織用的比較多。
雜湊檔案組織就是根據一個鍵通過雜湊計算把對應的記錄都放到同一個槽中,這樣的話相同的鍵值對應的記錄就一定是放在同一個檔案裡了,也就減少了檔案讀取的次數,提高了效率。
雜湊索引呢就是根據對應鍵的雜湊碼來找到最終的索引項的技術,其實和B樹就差不多了,也就是一種索引之上的二級輔助索引,我理解雜湊索引都是二級或更高階的稀疏索引,否則桶就太多了,效率也不會很高。
3、點陣圖索引
點陣圖索引是一種針對多個欄位的簡單查詢設計一種特殊的索引,適用範圍比較小,只適用於欄位值固定並且值的種類很少的情況,比如性別,只能有男和女,或者級別,狀態等等,並且只有在同時對多個這樣的欄位查詢時才能體現出點陣圖的優勢。
點陣圖的基本思想就是對每一個條件都用0或者1來表示,如有5條記錄,性別分別是男,女,男,男,女,那麼如果使用點陣圖索引就會建立兩個點陣圖,對應男的10110和對應女的01001,這樣做有什麼好處呢,就是如果同時對多個這種型別的欄位進行and或or查詢時,可以使用按位與和按位或來直接得到結果了。
為了進一步榨取MySQL的效率,就要考慮建立組合索引。就是將 name, city, age建到一個索引裡:
ALTER TABLE mytable ADD INDEX name_city_age (name(10),city,age);複製程式碼
建表時,usernname長度為 16,這裡用 10。這是因為一般情況下名字的長度不會超過10,這樣會加速索引查詢速度,還會減少索引檔案的大小,提高INSERT的更新速度。
如果分別在 usernname,city,age上建立單列索引,讓該表有3個單列索引,查詢時和上述的組合索引效率也會大不一樣,遠遠低於我們的組合索引。雖然此時有了三個索引,但MySQL只能用到其中的那個它認為似乎是最有效率的單列索引。
建立這樣的組合索引,其實是相當於分別建立了下面三組組合MySQL資料庫索引:
CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, city VARCHAR(50) NOT NULL, age INT NOT NULL );複製程式碼
usernname,city,age usernname,city usernname 為什麼沒有 city,age這樣的組合索引呢?這是因為MySQL組合索引“最左字首”的結果。簡單的理解就是隻從最左面的開始組合。並不是只要包含這三列的查詢都會用到該組合索引,下面的幾個SQL就會用到這個組合MySQL資料庫索引:
SELECT * FROM mytable WHREE username="admin" AND city="鄭州" SELECT * FROM mytable WHREE username="admin"複製程式碼
而下面幾個則不會用到:
SELECT * FROM mytable WHREE age=20 AND city="鄭州" SELECT * FROM mytable WHREE city="鄭州"複製程式碼
使用索引的代價
1、索引需要佔用資料表以外的物理儲存空間
2、建立索引和維護索引要花費一定的時間
3、當對錶進行更新操作時,索引需要被重建,這樣降低了資料的維護速度
什麼情況下不適合建立索引?
1.對於在查詢過程中很少使用或參考的列,不應該建立索引。
2.對於那些只有很少資料值的列,不應該建立索引。
3.對於那些定義為image,text和bit資料型別的列,不應該建立索引。
4.當修改效能遠大於檢索效能,不應該建立索引。
執行緒池中的corenum和maxnum有什麼不同?
(1) 直接提交的佇列:該功能由SynchronizedQueue物件提供。SynchronizedQueue是一個特殊的阻塞佇列。SynchronizedQueue沒有容量,每一個插入操作都要等待一個相應的刪除操作,反之每一個刪除操作都需要等待對應的插入操作。使用SynchronizedQueue時提交的任務不會被真實的儲存,而總是將新任務提交給執行緒執行,如果沒有空閒的執行緒則嘗試建立新的執行緒,如果執行緒數量達到最大值就執行決絕策略。使用SynchronizedQueue佇列通常要設定很大的maxnumPoolSize,否則很容易執行拒絕策略。可以當做大小為0的佇列來理解。
(2) 有界的任務佇列:有界的任務佇列可以使用ArrayBlockingQueue實現。當使用有界的任務佇列時,若有新的任務需要執行,如果執行緒池的實際執行緒數小於核心執行緒數,則有優先建立新的執行緒,若大於核心執行緒數,則會將新任務加入等待佇列。若佇列已滿,無法加入則在匯流排程數不大於最大執行緒數的前提下,建立新的執行緒。若大於最大執行緒數,則執行拒絕策略。也就是說,有界佇列僅當任務佇列滿時才可能將執行緒數提升到核心執行緒數只上,否則確保執行緒數維持在核心執行緒數大小。
(3) 無界任務佇列:無界任務佇列可以通過LinkedBlockingQueue類來實現。與有界佇列相比,除非系統資源耗盡,否則無界佇列不存在任務入隊失敗的情況。當有新的任務到來,系統的執行緒數小於核心執行緒數時執行緒池會生成新的執行緒執行任務,但當系統執行緒數大於核心執行緒數後,就不會繼續增加。若後續有新的任務,則將任務放入無界佇列中等待。
(4) 優先任務佇列:優先任務佇列是帶有執行優先順序的佇列,通過PriorityBlockingQueue實現,可以控制任務的執行先後順序,是一個特殊的無界佇列。無論是有界佇列還ArrayBlockingQueue還是未指定大小的無界佇列LinkedBlockingQueue,都是按照先進先出演算法處理任務的,而有限佇列則可以根據任務自身的優先順序順序執行,在確保系統效能的同時,也能有很好的質量保證。
回過頭看Executor框架提供了幾種執行緒池,newFixedThreadPool()返回的固定大小執行緒池中核心執行緒數和最大執行緒數一樣,並且使用了無界佇列。因為對於固定大小的執行緒池來說,不存線上程數量的動態變化,所以最大執行緒數等於核心執行緒數。同時,使用無界佇列存放無法立即執行的任務,當任務提交非常頻繁時,佇列可能迅速膨脹,從而耗盡系統資源。
newSingleThreadExecutor()返回的單執行緒執行緒池,是固定大小執行緒池的一種退化,只是簡單的將執行緒池數量設定為1。
newCachedThreadExecutor()返回核心執行緒數為0,最大執行緒數為無窮大的執行緒池。使用直接提交佇列SynchronizedQueue。
當Executor提供的執行緒池不滿足使用場景時,則需要使用自定義執行緒池,選擇合適的任務佇列來作為緩衝。不同的併發佇列對系統和效能的影響均不同。
如何找出單連結串列中的倒數第k個元素?
思路一:
初看題目,最容易想到的方法就是遍歷。首先遍歷一遍單連結串列,得出整個連結串列的長度n(元素個數從1到n),然後找到倒數第k個元素的位置n-k+1,接著從頭遍歷到第n-k+1元素,就是倒數第k個元素。但是該方法需要對連結串列進行兩次遍歷,遍歷的元素個數為n+n-k+1=2n+1-k個。
思路二:
有了思路一的提示,是不是可以想到用兩個指標,讓它們之間的距離保持為k-1,同時對連結串列進行遍歷,當第一個指標到達連結串列的最後一個元素(即倒數第一個元素時),第二個指標剛好停留在倒數第k個元素上。此方法看似對連結串列進行了一次遍歷,其實是用兩個指標對連結串列進行了同時遍歷,對連結串列本身而言,它被遍歷的元素個數仍是n+n-k+1=2n+1-k個。
思路三:
思路一和思路二是兩種不同思路,但就本質而言,都是兩次對連結串列進行2次遍歷,一次遍歷n個元素,另一次遍歷n-k+1個,總共遍歷2n+1-k個元素。此時,想想能否再減少遍歷的元素個數而找到倒數第k個元素呢?我們注意到思路二,是用兩個指標,保持k-1個元素的距離同時進行遍歷的,可否按著每次k個元素這樣的遍歷下去呢。這樣遍歷的結果就是,每次遍歷k個元素,遍歷m次(m=n/k),最後一次遍歷的個數為i個(i=n%k),我們只需記錄最後一次遍歷k個元素的起始位置,然後再遍歷i個元素,此時的位置即為倒數第k個元素。此時,對連結串列遍歷的元素個數為n+i(i為n除以k的餘數)。
多執行緒缺點?
1、將給定的工作量劃分給過多的執行緒會造成每個執行緒的工作量過少,因此可能導致執行緒啟動和終止時的開銷比程式實際工作的開銷還要多;
2、過多併發執行緒的存在將導致共享有限硬體資源的開銷增大。
執行緒相對於程式的優點:
1、開銷小
2、資源共享性好。
執行緒相對於程式的缺點:
1、共享資源需要耗費一定的鎖資源,同步相對複雜。
2、一個執行緒崩潰可能導致整個程式崩潰,這個當然是自己的應用程式有問題
迭代和遞迴的最大區別是?
遞迴與迭代都是基於控制結構:迭代用重複結構,而遞迴用選擇結構。
遞迴與迭代都涉及重複:迭代顯式使用重複結構,而遞迴通過重複函式呼叫實現重複。
遞迴與迭代都涉及終止測試:迭代在迴圈條件失敗時終止,遞迴在遇到基本情況時終止。
使用計數器控制重複的迭代和遞迴都逐漸到達終止點:迭代一直修改計數器,直到計數器值使迴圈條件失敗;遞迴不斷產生最初問題的簡化副本,直到達到基本情況。迭代和遞迴過程都可以無限進行:如果迴圈條件測試永遠不變成false,則迭代發生無限迴圈;如果遞迴永遠無法回推到基本情況,則發生無窮遞迴。
遞迴函式是通過呼叫函式自身來完成任務,而且在每次呼叫自身時減少任務量。而迭代是迴圈的一種形式,這種迴圈不是由使用者輸入而控制,每次迭代步驟都必須將剩餘的任務減少;也就是說,迴圈的每一步都必須執行一個有限的過程,並留下較少的步驟。
SQL truncate 、delete與drop區別?
相同點:
1.truncate和不帶where子句的delete、以及drop都會刪除表內的資料。
2.drop、truncate都是DDL語句(資料定義語言),執行後會自動提交。
不同點:
- truncate 和 delete 只刪除資料不刪除表的結構(定義)
drop 語句將刪除表的結構被依賴的約束(constrain)、觸發器(trigger)、索引(index);依賴於該表的儲存過程/函式將保留,但是變為 invalid 狀態。
- delete 語句是資料庫操作語言(dml),這個操作會放到 rollback segement 中,事務提交之後才生效;如果有相應的 trigger,執行的時候將被觸發。
truncate、drop 是資料庫定義語言(ddl),操作立即生效,原資料不放到 rollback segment 中,不能回滾,操作不觸發 trigger。
3.delete 語句不影響表所佔用的 extent,高水線(high watermark)保持原位置不動
drop 語句將表所佔用的空間全部釋放。
truncate 語句預設情況下見空間釋放到 minextents個 extent,除非使用reuse storage;truncate 會將高水線復位(回到最開始)。
4.速度,一般來說: drop> truncate > delete
5.安全性:小心使用 drop 和 truncate,尤其沒有備份的時候.否則哭都來不及
使用上,想刪除部分資料行用 delete,注意帶上where子句. 回滾段要足夠大.
想刪除表,當然用 drop;
想保留表而將所有資料刪除,如果和事務無關,用truncate即可。如果和事務有關,或者想觸發trigger,還是用delete。
如果是整理表內部的碎片,可以用truncate跟上reuse stroage,再重新匯入/插入資料。
6.delete是DML語句,不會自動提交。drop/truncate都是DDL語句,執行後會自動提交。
7、TRUNCATE TABLE 在功能上與不帶 WHERE 子句的 DELETE 語句相同:二者均刪除表中的全部行。但 TRUNCATE TABLE 比 DELETE 速度快,且使用的系統和事務日誌資源少。DELETE 語句每次刪除一行,並在事務日誌中為所刪除的每行記錄一項。TRUNCATE TABLE 通過釋放儲存表資料所用的資料頁來刪除資料,並且只在事務日誌中記錄頁的釋放。
8、TRUNCATE TABLE 刪除表中的所有行,但表結構及其列、約束、索引等保持不變。新行標識所用的計數值重置為該列的種子。如果想保留標識計數值,請改用 DELETE。如果要刪除表定義及其資料,請使用 DROP TABLE 語句。
9、對於由 FOREIGN KEY 約束引用的表,不能使用 TRUNCATE TABLE,而應使用不帶 WHERE 子句的 DELETE 語句。由於 TRUNCATE TABLE 不記錄在日誌中,所以它不能啟用觸發器。
10、TRUNCATE TABLE 不能用於參與了索引檢視的表。
總結常見的mysql資料庫優化操作?
1、Index索引
2、少用SELECT *
可能有的人查詢資料庫時,遇到要查詢的都會select,這是不恰當的行為。我們應該取我們要用的資料,而不是全取,因為當我們select時,會增加web伺服器的負擔,增加網路傳輸的負載,查詢速度自然就下降 。
3、開啟查詢快取
大多數的MySQL伺服器都開啟了查詢快取。這是提高性最有效的方法之一,而且這是被MySQL的資料庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個快取中,這樣,後續的相同的查詢就不用操作表而直接訪問快取結果了。
4、使用NOT NULL
很多表都包含可為NULL(空值)的列,即使應用程式並不需要儲存 NULL 也是如此 ,這是因為可為NULL是列的預設屬性。通常情況下最好指定列為 NOT NULL,除非真的需要儲存NULL值。如果查詢中包含可為NULL的列,對 MySQL 來說更難優化 ,因為可為 NULL 的列使 得索引、索引統計和值比較都更復雜 。可為NULL 的列會使用更多的儲存空間 ,在MySQL裡也需要特殊處理 。當可為NULL 的列被索引肘,每個索引記錄需要一個額 外的位元組,在 MyISAM 裡甚至還可能導致固定大小 的索引 (例如只有一個整數列的 索引) 變成可變大小的索引。
通常把可為 NULL 的列改為 NOT NULL 帶來的效能提升比較小 ,所以 (調優時) 沒有 必要首先在現有schema中查詢井修改掉這種情況 ,除非確定這會導致問題。但是, 如果計劃在列上建索引 ,就應該儘量避免設計成可為 NULL 的列。當然也有例外 ,例如值得一提的是,InnoDB 使用單獨的位 (bit ) 儲存 NULL 值 ,所 以對於稀疏資料由有很好的空間效率 。但這一點不適用於MyISAM 。
5、避免在 where 子句中使用 or 來連線
如果一個欄位有索引,一個欄位沒有索引,將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where num=10 or Name = 'admin'複製程式碼
可以這樣查詢:
select id from t where num = 10
union all
select id from t where Name = 'admin'複製程式碼
6、多使用varchar/nvarchar
使用varchar/nvarchar代替 char/nchar ,因為首先變長欄位儲存空間小,可以節省儲存空間,其次對於查詢來說,在一個相對較小的欄位內搜尋效率顯然要高些。
7、避免大資料量返回
這裡要考慮使用limit,來限制返回的資料量,如果每次返回大量自己不需要的資料,也會降低查詢速度。
8、where子句優化
where 子句中使用引數,會導致全表掃描,因為SQL只有在執行時才會解析區域性變數,但優化程式不能將訪問計劃的選擇推遲到執行時;它必須在編譯時進行選擇。然 而,如果在編譯時建立訪問計劃,變數的值還是未知的,因而無法作為索引選擇的輸入項。
應儘量避免在 where 子句中對欄位進行表示式操作,避免在where子句中對欄位進行函式操作這將導致引擎放棄使用索引而進行全表掃描。不要在 where 子句中的“=”左邊進行函式、算術運算或其他表示式運算,否則系統將可能無法正確使用索引。
SQL語句中executeQuery、executeUpdate、execute的區別?
- ResultSet executeQuery(String sql); 執行SQL查詢,並返回ResultSet 物件。
- int executeUpdate(String sql); 可執行增,刪,改,返回執行受到影響的行數。
- boolean execute(String sql); 可執行任何SQL語句,返回一個布林值,表示是否返回ResultSet 。
execute是executeQuery和executeUpdate的綜合.
executeUpdate() 這是 PreparedStatement 介面中的方法
executeUpdate(String sql) 這是 PreparedStatement 從父介面 Statement 中繼承過來的方法
executeUpdate() 中執行 SQL 語句需要在建立 PerparedStatement 時通過 Connection 的 prepareStatement(String sql) 方法中寫出,因為 PerparedStatement 中的 SQL 語句資料庫需要進行預編譯和快取,因此要在建立 PerparedStatement 物件時給出 SQL 語句。
而 executeUpdate(String sql) 是 Statement 中的方法,引數中的 SQL 語句只是提交給資料庫去執行,並不需要預編譯。
如果 SQL 語句中有 ? 佔位符,那麼在設定好佔位符中的值後,必須使用 executeUpdate() 執行。而 executeUpdate(String sql) 只是提交一個 SQL 語句,且這個語句中不能帶有 ? 佔位符。1、在Java中如何使用execute()、executeQuery()、executeUpdate()三個方法?
execute(String sql)
執行給定的 SQL 語句,該語句可能返回多個結果。
executeQuery(String sql)
執行給定的 SQL 語句,該語句返回單個 ResultSet 物件
executeUpdate(String sql)
執行給定 SQL 語句,該語句可能為 INSERT、UPDATE 或 DELETE 語句,或者不返回任何內容的 SQL 語句(如 SQL DDL 語句)
頭2種一般在查詢中使用
最後一個在插入、更新、刪除時使用
2、executeQuery()是幹什麼用的?實現什麼功能啊?
使用JDBC連線資料庫需要四步,第一步載入驅動程式;第二步,連線資料庫;第三步,訪問資料庫;第四步,執行查詢;其中在第四步執行查詢時,要用statement類的executeQuery()方法來下達select指令以查詢資料庫,executeQuery()方法會把資料庫響應的查詢結果存放在ResultSet類物件中供我們使用。即語句:String sql="select * from"+tableName; ResultSet rs=s.executeQuery(sql);
3、executeQuery、executeUpdate或execute方法區別?
在用純JSP做一個頁面報警功能的時候習慣性的用executeQuery來執行SQL語句,結果執行update時就遇到問題,語句能執行,但返回結果出現問題,另外還忽略了executeUpdate的返回值不是結果集ResultSet,而是數值!特收藏如下一篇文章(感謝網友們對各種資訊的貢獻):
JDBCTM中Statement介面提供的execute、executeQuery和executeUpdate之間的區別
Statement 介面提供了三種執行 SQL 語句的方法:executeQuery、executeUpdate 和 execute。使用哪一個方法由 SQL 語句所產生的內容決定。
方法executeQuery
用於產生單個結果集的語句,例如 SELECT 語句。 被使用最多的執行 SQL 語句的方法是 executeQuery。這個方法被用來執行 SELECT 語句,它幾乎是使用最多的 SQL 語句。
方法executeUpdate
用於執行 INSERT、UPDATE 或 DELETE 語句以及 SQL DDL(資料定義語言)語句,例如 CREATE TABLE 和 DROP TABLE。INSERT、UPDATE 或 DELETE 語句的效果是修改表中零行或多行中的一列或多列。executeUpdate 的返回值是一個整數,指示受影響的行數(即更新計數)。對於 CREATE TABLE 或 DROP TABLE 等不操作行的語句,executeUpdate 的返回值總為零。
使用executeUpdate方法是因為在 createTableCoffees 中的 SQL 語句是 DDL (資料定義語言)語句。建立表,改變表,刪除表都是 DDL 語句的例子,要用 executeUpdate 方法來執行。你也可以從它的名字裡看出,方法 executeUpdate 也被用於執行更新表 SQL 語句。實際上,相對於建立表來說,executeUpdate 用於更新表的時間更多,因為表只需要建立一次,但經常被更新。
方法execute:
用於執行返回多個結果集、多個更新計數或二者組合的語句。因為多數程式設計師不會需要該高階功能
execute方法應該僅在語句能返回多個ResultSet物件、多個更新計數或ResultSet物件與更新計數的組合時使用。當執行某個已儲存過程 或動態執行未知 SQL 字串(即應用程式程式設計師在編譯時未知)時,有可能出現多個結果的情況,儘管這種情況很少見。
因為方法 execute 處理非常規情況,所以獲取其結果需要一些特殊處理並不足為怪。例如,假定已知某個過程返回兩個結果集,則在使用方法 execute 執行該過程後,必須呼叫方法 getResultSet 獲得第一個結果集,然後呼叫適當的 getXXX 方法獲取其中的值。要獲得第二個結果集,需要先呼叫 getMoreResults 方法,然後再呼叫 getResultSet 方法。如果已知某個過程返回兩個更新計數,則首先呼叫方法 getUpdateCount,然後呼叫 getMoreResults,並再次呼叫 getUpdateCount。
對於不知道返回內容,則情況更為複雜。如果結果是 ResultSet 物件,則方法 execute 返回 true;如果結果是 Java int,則返回 false。如果返回 int,則意味著結果是更新計數或執行的語句是 DDL 命令。在呼叫方法 execute 之後要做的第一件事情是呼叫 getResultSet 或 getUpdateCount。呼叫方法 getResultSet 可以獲得兩個或多個 ResultSet 物件中第一個物件;或呼叫方法 getUpdateCount 可以獲得兩個或多個更新計數中第一個更新計數的內容。
當 SQL 語句的結果不是結果集時,則方法 getResultSet 將返回 null。這可能意味著結果是一個更新計數或沒有其它結果。在這種情況下,判斷 null 真正含義的唯一方法是呼叫方法 getUpdateCount,它將返回一個整數。這個整數為呼叫語句所影響的行數;如果為 -1 則表示結果是結果集或沒有結果。如果方法 getResultSet 已返回 null(表示結果不是 ResultSet 物件),則返回值 -1 表示沒有其它結果。也就是說,當下列條件為真時表示沒有結果(或沒有其它結果):
((stmt.getResultSet() == null) && (stmt.getUpdateCount() == -1))
如果已經呼叫方法 getResultSet 並處理了它返回的 ResultSet 物件,則有必要呼叫方法 getMoreResults 以確定是否有其它結果集或更新計數。如果 getMoreResults 返回 true,則需要再次呼叫 getResultSet 來檢索下一個結果集。如上所述,如果 getResultSet 返回 null,則需要呼叫 getUpdateCount 來檢查 null 是表示結果為更新計數還是表示沒有其它結果。
當 getMoreResults 返回 false 時,它表示該 SQL 語句返回一個更新計數或沒有其它結果。因此需要呼叫方法 getUpdateCount 來檢查它是哪一種情況。在這種情況下,當下列條件為真時表示沒有其它結果:
((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))
下面的程式碼演示了一種方法用來確認已訪問呼叫方法 execute 所產生的全部結果集和更新計數:
stmt.execute(queryStringWithUnknownResults);
while (true) {
int rowCount = stmt.getUpdateCount();
if (rowCount > 0) { // 它是更新計數
System.out.println("Rows changed = " + count);
stmt.getMoreResults();
Spring初始化過程?
在傳統的Java應用中,Bean的生命週期非常簡單。Java的關鍵詞new用來例項化Bean(或許他是非序列化的)。這樣就夠用了。相反,Bean 的生命週期在spring容器中更加細緻。理解Spring Bean的生命週期非常重要,因為你或許要利用Spring提供的機會來訂製Bean的建立過程。
1.容器尋找Bean的定義資訊並且將其例項化。
2.使用依賴注入,Spring按照Bean定義資訊配置Bean的所有屬性。
3.如果Bean實現了BeanNameAware介面,工廠呼叫Bean的setBeanName()方法傳遞Bean的ID。
4.如果Bean實現了BeanFactoryAware介面,工廠呼叫setBeanFactory()方法傳入工廠自身。
5.如果BeanPostProcessor和Bean關聯,那麼它們的postProcessBeforeInitialzation()方法將被呼叫。
6.如果Bean指定了init-method方法,它將被呼叫。
7.最後,如果有BeanPsotProcessor和Bean關聯,那麼它們的postProcessAfterInitialization()方法將被呼叫。
到這個時候,Bean已經可以被應用系統使用了,並且將被保留在Bean Factory中知道它不再需要。有兩種方法可以把它從Bean Factory中刪除掉。
1.如果Bean實現了DisposableBean介面,destory()方法被呼叫。
2.如果指定了訂製的銷燬方法,就呼叫這個方法。
Bean在Spring應用上下文的生命週期與在Bean工廠中的生命週期只有一點不同,唯一不同的是,如果Bean實現了ApplicationContextAwre介面,setApplicationContext()方法被呼叫。
MySQL Hash索引和B-Tree索引的區別?
MySQL Hash索引結構的特殊性,其檢索效率非常高,索引的檢索可以一次定位,不像B-Tree 索引需要從根節點到枝節點,最後才能訪問到頁節點這樣多次的IO訪問,所以 Hash 索引的查詢效率要遠高於 B-Tree 索引。
可能很多人又有疑問了,既然 Hash 索引的效率要比 B-Tree 高很多,為什麼大家不都用 Hash 索引而還要使用 B-Tree 索引呢?任何事物都是有兩面性的,Hash 索引也一樣,雖然 Hash 索引效率高,但是 Hash 索引本身由於其特殊性也帶來了很多限制和弊端,主要有以下這些。
(1)MySQL Hash索引僅僅能滿足"=","IN"和"< >"查詢,不能使用範圍查詢。
由於 MySQL Hash索引比較的是進行 Hash 運算之後的 Hash 值,所以它只能用於等值的過濾,不能用於基於範圍的過濾,因為經過相應的 Hash 演算法處理之後的 Hash 值的大小關係,並不能保證和Hash運算前完全一樣。
(2)MySQL Hash索引無法被用來避免資料的排序操作。
由於 MySQL Hash索引中存放的是經過 Hash 計算之後的 Hash 值,而且Hash值的大小關係並不一定和 Hash 運算前的鍵值完全一樣,所以資料庫無法利用索引的資料來避免任何排序運算;
(3)MySQL Hash索引不能利用部分索引鍵查詢。
對於組合索引,Hash 索引在計算 Hash 值的時候是組合索引鍵合併後再一起計算 Hash 值,而不是單獨計算 Hash 值,所以通過組合索引的前面一個或幾個索引鍵進行查詢的時候,Hash 索引也無法被利用。
(4)MySQL Hash索引在任何時候都不能避免表掃描。
前面已經知道,Hash 索引是將索引鍵通過 Hash 運算之後,將 Hash運算結果的 Hash 值和所對應的行指標資訊存放於一個 Hash 表中,由於不同索引鍵存在相同 Hash 值,所以即使取滿足某個 Hash 鍵值的資料的記錄條數,也無法從 Hash 索引中直接完成查詢,還是要通過訪問表中的實際資料進行相應的比較,並得到相應的結果。
(5)MySQL Hash索引遇到大量Hash值相等的情況後效能並不一定就會比B-Tree索引高。
對於選擇性比較低的索引鍵,如果建立 Hash 索引,那麼將會存在大量記錄指標資訊存於同一個 Hash 值相關聯。這樣要定位某一條記錄時就會非常麻煩,會浪費多次表資料的訪問,而造成整體效能低下。
單例與靜態變數的區別
單例的特點:
1、保證某類只存在唯一例項。
2、該類本身完成自身的初始化。
3、獲取該唯一例項的方式非常明確,可以通過該類本身定義的靜態方法getInstance()獲取該類的唯一例項引用。
靜態變數定義某類的例項引用特點:
1、該類的例項引用的靜態變數可定義在任何文件類當中。
2、獲取該類的例項引用的靜態變數,可以通過定義該靜態變數的類名通過點語法進行訪問該引用。
3、任何位置可以對該靜態變數進行重新賦值。
通過這兩者方式的特點,我們可以很明顯的看出兩者之間的區別。(這一切都是基於某類只需要存在一個例項物件的前提來討論)
首先靜態變數方式不能確保某類的例項的唯一性,這樣在專案中,可能因為在某個文件類中對該靜態變數進行再次賦值,存不可意料的風險(這種風險可以規避)。同樣的,因為靜態變數的定義的位置不確定,所以需要協議商定,這些靜態變數分類別進行定義在一個固定的位置(比如說某個專門存放靜態變數方式的某類的物件的引用的文件類當中)。
而單例模式也就是靜態變數方式建立一個類的例項引用所帶來的缺陷的改善。首先解決引用的唯一例項可能被重新賦值的問題,單例模式中的getInstance()靜態方法實現時,採用懶漢式建立一個物件(當然這只是建立方式的一種),規避了這一風險,無則建立,有則跳過建立。其次,getInstance()靜態方法定義在該類的內部,獲取該類物件的引用位置非常明確,無需額外的溝通商定,團隊成員拿起即用。最後一個區別並不是很明顯,宣告一個靜態變數,實際上,我們會直接對其進行初始化賦值,這樣,在記憶體佔用上,所佔用的記憶體為該初始化賦值物件實際的記憶體。而單例模式可以通過懶漢建立法延遲該記憶體的佔用,要知道,當一個靜態變數只進行宣告,而不進行初始化時,實際的記憶體佔用只有4個位元組(筆者個人推測,這四個位元組只是一個指標地址所佔用的記憶體空間)。
關於HashMap與HashTable
HashMap/HashTable初始容量大小和每次擴充容量大小的不同可以看到HashTable預設的初始大小為11,之後每次擴充為原來的2n+1。HashMap預設的初始化大小為16,之後每次擴充為原來的2倍。還有我沒列出程式碼的一點,就是如果在建立時給定了初始化大小,那麼HashTable會直接使用你給定的大小,而HashMap會將其擴充為2的冪次方大小。
也就是說HashTable會盡量使用素數、奇數。而HashMap則總是使用2的冪作為雜湊表的大小。我們知道當雜湊表的大小為素數時,簡單的取模雜湊的結果會更加均勻(具體證明,見這篇文章),所以單從這一點上看,HashTable的雜湊表大小選擇,似乎更高明些。但另一方面我們又知道,在取模計算時,如果模數是2的冪,那麼我們可以直接使用位運算來得到結果,效率要大大高於做除法。所以從hash計算的效率上,又是HashMap更勝一籌。
所以,事實就是HashMap為了加快hash的速度,將雜湊表的大小固定為了2的冪。當然這引入了雜湊分佈不均勻的問題,所以HashMap為解決這問題,又對hash演算法做了一些改動。具體我們來看看,在獲取了key物件的hashCode之後,HashTable和HashMap分別是怎樣將他們hash到確定的雜湊桶(Entry陣列位置)中的。
正如我們所言,HashMap由於使用了2的冪次方,所以在取模運算時不需要做除法,只需要位的與運算就可以了。但是由於引入的hash衝突加劇問題,HashMap在呼叫了物件的hashCode方法之後,又做了一些位運算在打散資料。關於這些位計算為什麼可以打散資料的問題,本文不再展開了。感興趣的可以看這裡。
HashMap是支援null鍵和null值的,而HashTable在遇到null時,會丟擲NullPointerException異常。這並不是因為HashTable有什麼特殊的實現層面的原因導致不能支援null鍵和null值,這僅僅是因為HashMap在實現時對null做了特殊處理,將null的hashCode值定為了0,從而將其存放在雜湊表的第0個bucket中。
JSP指令元素與動作元素的區別
1:先將top.jsp中的java指令碼和jsp指令都執行完畢以後再將top.jsp頁面加入到引用頁面中。
2:<%@ include file="top.jsp"%>靜態讀取:則是將top.jsp的整個頁面不加解析(無論是指令碼還是指令)統統讀入到引用頁面中,然後和引用頁面一起進行解析(即開始執行指令碼和指令)。
3:區別:其實上邊的兩條就是區別,但是需要注意的是用<%@ include file=""%>的時候被引用頁面中不能再出現其他網頁標籤和page指令了,否則會衝突的
(jsp:include page="")
父頁面和包含進來的頁面單獨編譯,單獨翻譯成servlet後,在前臺拼成一個HTML頁面。
(%@include file=""%)
父頁面和包含進來的頁面,程式碼合併後,才一起翻譯成servlet,反饋到前臺,形成一個HTML頁面。
由此我們知道:jsp頁面是把include指令元素(<%@ include file=""%>)所指定的頁面的實際內容(也就是程式碼段)加入到引入它的jsp頁面中,合成一個檔案後被jsp容器將它轉化成servlet。可以看到這時會產生一個臨時class檔案和一個servlet原始檔。而動作元素()是在請求處理階段引入的,會被JSP容器生成兩個臨時class檔案和兩個servlet原檔案。而引入的只是servlet的輸出結果,即JspWriter物件的輸出結果,而不是jsp的原始碼。
java是在伺服器端執行的程式碼,jsp在伺服器的servlet裡執行,而javascript和html都是在瀏覽器端執行的程式碼。所以載入執行順序是是java>jsp>js。
所有的JSP都會在客戶端發出請求後被容器轉譯成servlet的原始碼(java),然後再將原始碼(java)編譯成servlet的類(class),放入到記憶體裡面。
可重入鎖 公平鎖 讀寫鎖
1.可重入鎖
如果鎖具備可重入性,則稱作為可重入鎖。
像Synchronized和ReentrantLock都是可重入鎖,可重入性在我看來實際上表明瞭鎖的分配機制:
基於執行緒的分配,而不是基於方法呼叫的分配。
舉個簡單的例子,當一個執行緒執行到某個Synchronized方法時,比如說method1,而在method1中會呼叫另外一個Synchronized方法method2,此時執行緒不必重新去申請鎖,而是可以直接執行方法method2。
class MyClass {
public synchronized void method1() {
method2();
}
public synchronized void method2() {
}
}複製程式碼
上述程式碼中的兩個方法method1和method2都用Synchronized修飾了。
假如某一時刻,執行緒A執行到了method1,此時執行緒A獲取了這個物件的鎖,而由於method2也是Synchronized方法,假如Synchronized不具備可重入性,此時執行緒A需要重新申請鎖。
但是這就會造成一個問題,因為執行緒A已經持有了該物件的鎖,而又在申請獲取該物件的鎖,這樣就會執行緒A一直等待永遠不會獲取到的鎖。
而由於Synchronized和Lock都具備可重入性,所以不會發生上述現象。
2.可中斷鎖
可中斷鎖:顧名思義,就是可以響應中斷的鎖。
在Java中,Synchronized就不是可中斷鎖,而Lock是可中斷鎖。
如果某一執行緒A正在執行鎖中的程式碼,另一執行緒B正在等待獲取該鎖,可能由於等待時間過長,執行緒B不想等待了,想先處理其他事情,我們可以讓它中斷自己或者在別的執行緒中中斷它,這種就是可中斷鎖。
在前面演示LockInterruptibly()的用法時已經體現了Lock的可中斷性。
3.公平鎖
公平鎖即儘量以請求鎖的順序來獲取鎖。比如同是有多個執行緒在等待一個鎖,當這個鎖被釋放時,等待時間最久的執行緒(最先請求的執行緒)會獲得該所,這種就是公平鎖。
非公平鎖即無法保證鎖的獲取是按照請求鎖的順序進行的。這樣就可能導致某個或者一些執行緒永遠獲取不到鎖。
在Java中,Synchronized就是非公平鎖,它無法保證等待的執行緒獲取鎖的順序。
而對於ReentrantLock和ReentrantReadWriteLock,它預設情況下是非公平鎖,但是可以設定為公平鎖。這一點由建構函式可知:
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = (fair)? new FairSync() : new NonfairSync();
}複製程式碼
在ReentrantLock中定義了2個靜態內部類,一個是NotFairSync,一個是FairSync,分別用來實現非公平鎖和公平鎖。
我們可以在建立ReentrantLock物件時,通過知道布林引數來決定使用非公平鎖還是公平鎖。
如果引數為true表示為公平鎖,為fasle為非公平鎖。預設情況下,如果使用無參構造器,則是非公平鎖。
另外在ReentrantLock類中定義了很多方法,比如:
isFair() //判斷鎖是否是公平鎖
isLocked() //判斷鎖是否被任何執行緒獲取了
isHeldByCurrentThread() //判斷鎖是否被當前執行緒獲取了
hasQueuedThreads() //判斷是否有執行緒在等待該鎖
在ReentrantReadWriteLock中也有類似的方法,同樣也可以設定為公平鎖和非公平鎖。
不過要記住,ReentrantReadWriteLock並未實現Lock介面,它實現的是ReadWriteLock介面。
4.讀寫鎖
讀寫鎖將對一個資源(比如檔案)的訪問分成了2個鎖,一個讀鎖和一個寫鎖。
正因為有了讀寫鎖,才使得多個執行緒之間的讀操作不會發生衝突。
ReadWriteLock就是讀寫鎖,它是一個介面,ReentrantReadWriteLock實現了這個介面。
可以通過readLock()獲取讀鎖,通過writeLock()獲取寫鎖。
資料庫的樂觀鎖與悲觀鎖
樂觀鎖是假定讀取的資料,在寫之前不會被更新。適用於資料更新不頻繁的場景。
相反,當資料更新頻繁的時候,樂觀鎖的效率很低,因為基本上每次寫的時候都要重複讀寫兩次以上。
對於資料更新頻繁的場合,悲觀鎖效率更高 ;
對於資料更新不頻繁的場合,樂觀鎖效率更高;
一般來說如果併發量很高的話,建議使用悲觀鎖,否則的話就使用樂觀鎖。
如果併發量很高時使用樂觀鎖的話,會導致很多的併發事務回滾、操作失敗。
工作記憶體與主記憶體
執行緒工作記憶體是cpu暫存器和快取記憶體的抽象描述,使用頻率高的資料從主存拷貝到快取記憶體中,每個執行緒在cpu快取記憶體中對拷貝的資料進行讀取、計算、賦值,再在合適的時候同步更新到主存的該資料,如i=1,i+1=2,若2在更新到主存前,其他執行緒是不知道該值被改變了,其他執行緒快取記憶體中該值依然為1。
解決方法:需要各執行緒間可見的變數前加上volatile修飾,在一個執行緒的快取記憶體中改變該值時,其他執行緒會獲得該值的更新值。
垃圾回收器
垃圾收集器:
序列:Serial、Parallel、CMS
CMS:每個垃圾收集週期只有兩次短的停頓,開始時有一個短的停頓,稱為初始標記;標記從外部直接可達的老年代物件;然後在併發標記階段,標記所有從這些物件可達的存活物件;由於在標記期間應用可能正在執行並更新引用,所以到併發標記階段結束時,未必所有存活的物件都能確保被標記;所以必須再次停頓,稱為重新標記;最後一個階段是併發清除。
c3p0與dbcp區別
dbcp沒有自動地去回收空閒連線的功能 c3p0有自動回收空閒連線功能 。
兩者主要是對資料連線的處理方式不同!c3p0提供最大空閒時間,dbcp提供最大連線數。
前者當連線超過最大空閒連線時間時,當前連線就會被斷掉。dbcp當連線數超過最大連線數時,所有連線都會被斷開。
dbcp和c3p0都是單執行緒的,在高併發的環境下效能會非常低下;tomcat jdbc pool 近乎相容 dbcp ,效能更高,非同步方式獲取連線。