java面試題核心篇(一)

LNhome發表於2018-03-28

資料儲存

1、索引使用的注意事項

(1)索引應該建在選擇性高的欄位上(鍵值唯一的記錄數/總記錄條數),選擇性越高索引的效果越好、價值越大,唯一索引的選擇性最高。
(2)組合索引中欄位的順序,選擇性越高的欄位排在最前面。
(3)where條件中包含兩個選擇性高的欄位時,可以考慮分別建立索引,引擎會同時使用兩個索引(在OR條件下,應該說必須分開建索引)。
(4)索引不會包含有null值的列,只要列中包含null,都將不會被包含到索引中,符合索引只要有一列含有null值,那麼這列對於符合索引就是無效的。
(5)不要在列上進行運算。
(6)使用短索引,對字串列進行索引,如果可以就應該指定一個字首長度,如果有一個char(255)的列,如果在前10個或20個字元內,多數值是唯一的,那麼就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁碟空間和I/O操作。
(7)like語句操作,一般情況下不鼓勵使用like操作,如果非使用不可,注意正確的使用方式。like ‘%aaa%'不會使用索引,而like ‘aaa%'可以使用索引。
(8)不使用NOT IN 、<>、!=操作,但<,<=,=,>,>=,BETWEEN,IN是可以用到索引的。


2、說說反模式設計

反模式是指在對經常面對的問題經常使用的低效,不良,或者有待優化的設計模式/方法。甚至,反模式也可以是一種錯誤的開發思想/理念。在這裡我舉一個最簡單的例子:在物件導向設計/程式設計中,有一條很重要的原則, 單一責任原則(Single responsibility principle)。其中心思想就是對於一個模組,或者一個類來說,這個模組或者這個類應該只對系統/軟體的一個功能負責,而且該責任應該被該類完全封裝起來。當開發人員需要修改系統的某個功能,這個模組/類是最主要的修改地方。相對應的一個反模式就是上帝類(God Class),通常來說,這個類裡面控制了很多其他的類,同時也依賴其他很多類。整個類不光負責自己的主要單一功能,而且還負責了其他很多功能,包括一些輔助功能。一個類裡有幾千行的程式碼,有很多功能,但是責任不明確單一。單元測試程式也變複雜無比。維護/修改這個類的時間要遠遠超出其他類的時間。


3、說說分庫與分表設計

對於訪問極為頻繁且資料量巨大的單表來說,我們首先要做的就是減少單表的記錄條數,以便減少資料查詢所需要的時間,提高資料庫的吞吐,這就是所謂的分表,分表能夠解決單表資料量過大帶來的查詢效率下降的問題,但是,卻無法給資料庫的併發處理能力帶來質的提升。面對高併發的讀寫訪問,當資料庫master伺服器無法承載寫操作壓力時,不管如何擴充套件slave伺服器,此時都沒有意義了。因此,我們必須換一種思路,對資料庫進行拆分,從而提高資料庫寫入能力,這就是所謂的分庫。一般情況下,資料庫劃分的基本思路可以歸納為:基於業務垂直劃分;基於資料水平拆分;或者複雜一點的場景就需要將兩者綜合應用。
垂直劃分:基於領域模型做資料的垂直切分是一種最佳實踐。如將訂單、產品、賬戶、財務等領域模型劃分到不同的DB庫裡面。且由於各領域資料之間join展示場景較少,在這種情況下分庫能獲得很高的價值,同時各個系統之間的擴充套件性得到很大程度的提高。
水平切分:對於一些大型的系統來說,垂直分庫之後是遠遠不夠的,還需要做資料的水平切分。


4、分庫與分錶帶來的分散式困境與應對之策

分庫分表維度問題
如果按照患者劃分維度,則每個患者維度進行分表,則每個患者的就診資訊,都儲存在同一張表中,所以可以很方便的查詢到患者就診資訊,但是如果要統計一個月內醫院就診情況,則資料可能分佈在多張表中。
表關聯問題
在單庫單表的情況下,聯合查詢是非常容易的。但是,隨著分庫與分表的演變,聯合查詢就遇到跨庫關聯和跨表關係問題。在設計之初就應該儘量避免聯合查詢,可以通過程式中進行拼裝,或者通過反正規化化設計進行規避。
分頁與排序問題
列表分頁時需要按照指定欄位進行排序。在單庫單表的情況下,分頁和排序也是非常容易的。但是,隨著分庫與分表的演變,也會遇到跨庫排序和跨表排序問題。為了最終結果的準確性,需要在不同的分表中將資料進行排序並返回,並將不同分表返回的結果集進行彙總和再次排序,最後再返回給使用者。
分散式事務問題
隨著分庫與分表的演變,一定會遇到分散式事務問題,那麼如何保證資料的一致性就成為一個必須面對的問題。目前,分散式事務並沒有很好的解決方案,難以滿足資料強一致性,一般情況下,使儲存資料儘可能達到使用者一致,保證系統經過一段較短的時間的自我恢復和修正,資料最終達到一致。
分散式全域性唯一ID
在單庫單表的情況下,直接使用資料庫自增特性來生成主鍵ID,這樣確實比較簡單。在分庫分表的環境中,資料分佈在不同的分表上,不能再借助資料庫自增長特性。


5、說說 SQL 優化之道

(1)建立必要的索引,建立索引給檢索帶來的效能提升往往是巨大的。
(2)使用預編譯查詢,引數化SQL不僅能夠避免SQL隱碼攻擊,最重要的是資料庫會對引數化SQL進行預編譯,這樣第一次執行的時候資料庫會為這個SQL語句進行查詢優化並且執行預編譯,以後再執行這個SQL時會直接使用預編譯的結果,這樣會大大提高執行速度。
(3)調整where語句中的連線順序,資料庫一般採用自下而上的順序解析SQL,根據這個原理表連線最好寫在其where條件之前,這樣可以過濾最大數量的記錄。
(4)儘量將多條SQL壓縮到一條SQL執行。
(5)用where替代having,因為HAVING只會在檢索出所有記錄之後才對結果集進行過濾,而where則是在聚合前刷選記錄,如果能通過where字句限制記錄的數目,那就能減少這方面的開銷。HAVING中的條件一般用於聚合函式的過濾,除此之外,應該將條件寫在where字句中。
(6)使用表的別名,使用表的別名並把別名字首於每個列名上。這樣就可以減少解析的時間並減少哪些列名歧義引起的語法錯誤。
(7)在in和exists中通常情況下使用EXISTS,因為in不走索引。
(8)避免在索引上使用計算,如果索引列是計算或者函式的一部分,DBMS的優化器將不會使用索引而使用全表查詢。
(9)使用unionall替換union,當SQL語句需要union兩個查詢結果集合時,即使檢索結果中不會有重複的記錄,如果使用union這兩個結果集同樣會嘗試進行合併,然後在輸出最終結果前進行排序,因此如果可以判斷檢索結果中不會有重複的記錄時候,應該用union all,這樣效率就會因此得到提高。 
(10)避免SQL中出現隱式型別轉換,當某一張表中的索引欄位在作為where條件的時候,如果進行了隱式型別轉換,則此索引欄位將會不被識別,因為隱式型別轉換也屬於計算,所以此時DBMS會使用全表掃面。
(11)防止索引檢索範圍過寬, 如果DBMS優化器認為檢索範圍過寬,那麼將放棄索引查詢而使用全表掃描,下面可能會造成索引檢索過寬:1.使用is not null或者不等於判斷,可能造成優化器假設匹配的記錄數太多;2.使用like運算子的時候,“a%”將會使用索引,而“a%c”和“%a”則會使用全表掃描。


6、MySQL 遇到的死鎖問題

死鎖是指兩個或兩個以上的程式在執行的過程中,因爭奪資源而造成的互相等待的現象,死鎖的關鍵在於:兩個(或以上)的Session加鎖的順序不一致。常見導致死鎖的場景:
1.例如兩個使用者同時投資,A使用者金額隨機分為2份,分給借款人1,2,B使用者金額隨機分為2份,分給借款人2,1,由於加鎖的順序不一樣,死鎖很快就出現了。對於這個問題的改進很簡單,直接把所有分配到的借款人直接一次鎖住就行了。Select * from xxx where id in (xx,xx,xx) for update 在in裡面的列表值mysql是會自動從小到大排序,加鎖也是一條條從小到大加的鎖
2.根據欄位值查詢(有索引),如果不存在,則插入;否則更新,當對存在的行進行鎖的時候(主鍵),mysql就只有行鎖。當對未存在的行進行鎖的時候(即使條件為主鍵),mysql是會鎖住一段範圍(有gap鎖)。鎖住的範圍為:(無窮小或小於表中鎖住id的最大值,無窮大或大於表中鎖住id的最小值),對於這種死鎖的解決辦法是:insert into t3(xx,xx) on duplicate key update `xx`='XX';用mysql特有的語法來解決此問題。因為insert語句對於主鍵來說,插入的行不管有沒有存在,都會只有行鎖。


7、儲存引擎的 InnoDB 與 MyISAM

(1)儲存結構:MyISAM每個MyISAM在磁碟上儲存成三個檔案。第一個檔案的名字以表的名字開始,副檔名指出檔案型別。.frm檔案儲存表定義。資料檔案的副檔名為.MYD(MYData)。索引檔案的副檔名是.MYI (MYIndex)。InnoDB所有的表都儲存在同一個資料檔案中(也可能是多個檔案,或者是獨立的表空間檔案),InnoDB表的大小隻受限於作業系統檔案的大小,一般為2GB。
(2)儲存空間:MyISAM可被壓縮,儲存空間較小。支援三種不同的儲存格式:靜態表(預設,但是注意資料末尾不能有空格,會被去掉)、動態表、壓縮表。InnoDB需要更多的記憶體和儲存,它會在主記憶體中建立其專用的緩衝池用於高速緩衝資料和索引。
(3)可移植性、備份及恢復:MyISAM資料是以檔案的形式儲存,所以在跨平臺的資料轉移中會很方便。在備份和恢復時可單獨針對某個表進行操作。InnoDB免費的方案可以是拷貝資料檔案、備份 binlog,或者用 mysqldump,在資料量達到幾十G的時候就相對痛苦了。
(4)事務支援:MyISAM強調的是效能,每次查詢具有原子性,其執行數度比InnoDB型別更快,但是不提供事務支援。InnoDB提供事務支援事務,外部鍵等高階資料庫功能。 具有事務(commit)、回滾(rollback)和崩潰修復能力(crash recovery capabilities)的事務安全(transaction-safe (ACID compliant))型表。
(5)自增列:MyISAM可以和其他欄位一起建立聯合索引。引擎的自動增長列必須是索引,如果是組合索引,自動增長可以不是第一列。InnoDB中必須包含只有該欄位的索引。引擎的自動增長列必須是索引,如果是組合索引也必須是組合索引的第一列。
(6)表鎖差異:MyISAM只支援表級鎖,InnoDB支援事務和行級鎖,是innodb的最大特色。行鎖大幅度提高了多使用者併發操作的新能。但是InnoDB的行鎖,只是在WHERE的主鍵是有效的,非主鍵的WHERE都會鎖全表的。
(7)全文索引:MyISAM支援 FULLTEXT型別的全文索引InnoDB:不支援FULLTEXT型別的全文索引,但是innodb可以使用sphinx外掛支援全文索引,並且效果更好。
(8)表主鍵:MyISAM允許沒有任何索引和主鍵的表存在,索引都是儲存行的地址。InnoDB如果沒有設定主鍵或者非空唯一索引,就會自動生成一個6位元組的主鍵(使用者不可見),資料是主索引的一部分,附加索引儲存的是主索引的值。
(9)表的具體行數:MyISAM儲存有表的總行數,如果select count(*) from table;會直接取出出該值。InnoDB沒有儲存表的總行數,如果使用select count(*) from table;就會遍歷整個表,消耗相當大,但是在加了wehre條件後,myisam和innodb處理的方式都一樣。
(10)CURD操作:MyISAM如果執行大量的SELECT,MyISAM是更好的選擇。InnoDB如果你的資料執行大量的INSERT或UPDATE,出於效能方面的考慮,應該使用InnoDB表。DELETE 從效能上InnoDB更優,但DELETE FROM table時,InnoDB不會重新建立表,而是一行一行的刪除,在innodb上如果要清空儲存有大量資料的表,最好使用truncate table這個命令。
(11)外來鍵:MyISAM:不支援,InnoDB:支援。


8、為什麼要用 B-tree(參考http://blog.csdn.net/v_JULY_v/article/details/6530142)

大規模的資料儲存中,樹節點儲存的元素數量是有限的(如果元素非常多,查詢就會變成節點內部的線性查詢),也就是說樹的深度過大會造成磁碟I/O讀寫過於頻繁,進而導致查詢效率低下。B樹中的每個結點根據實際情況可以包含大量的關鍵字資訊和分支(當然是不能超過磁碟塊的大小,根據磁碟驅動(disk drives)的不同,一般塊的大小在1k~4k左右);這樣樹的深度降低了,這就意味著查詢一個元素只要很少結點從外存磁碟中讀入記憶體,很快訪問到要查詢的資料。

9、資料庫索引的原理

資料庫索引的特點是避免進行資料庫全表掃描,大多數情況,只需要掃描較少的索引頁和資料頁,而不是查詢所有的資料頁,而且對於非聚合索引,有時不需要訪問資料頁就可以得到資料。資料庫通常將平衡樹作為預設的索引資料結構,建表的時候都會為表加上主鍵,這樣的話整個表就變成一個索引,也就是聚集索引,主鍵的作用就是將表資料轉換成平衡樹的格式放置。非聚集索引和聚集索引一樣,同樣是採用平衡樹作為索引的資料結構。索引樹結構中各節點的值來自於表中的索引欄位。
索引能讓資料庫查詢資料的速度上升,而使寫入資料的速度下降,原因很簡單的,因為平衡樹這個結構必須一直維持在一個正確的狀態, 增刪改資料都會改變平衡樹各節點中的索引資料內容,破壞樹結構, 因此,在每次資料改變時,DBMS必須去重新梳理樹(索引)的結構以確保它的正確,這會帶來不小的效能開銷,也就是為什麼索引會給查詢以外的操作帶來副作用的原因。

10、聚集索引與非聚集索引的區別

聚集索引一個表只能有一個,而非聚集索引一個表可以存在多個;
聚集索引儲存記錄是物理上連續存在,而非聚集索引是邏輯上的連續,物理儲存並不連續。
聚集索引和非聚集索引問題歸納:
聚集索引的約束是唯一性,是否要求欄位也是唯一的呢?
分析:如果認為是的朋友,可能是受系統預設設定的影響,一般我們指定一個表的主鍵,如果這個表之前沒有聚集索引,同時建立主鍵時候沒有強制指定使用非聚集索引,SQL會預設在此欄位上建立一個聚集索引,而主鍵都是唯一的,所以理所當然的認為建立聚集索引的欄位也需要唯一。
  結論:聚集索引可以建立在任何一列你想建立的欄位上,這是從理論上講,實際情況並不能隨便指定,否則在效能上會是惡夢。
為什麼聚集索引可以建立在任何一列上,如果此表沒有主鍵約束,即有可能存在重複行資料呢?
粗一看,這還真是和聚集索引的約束相背,但實際情況真可以建立聚集索引。分析其原因是:如果未使用 UNIQUE 屬性建立聚集索引,資料庫引擎將向表自動新增一個四位元組 uniqueifier 列。必要時,資料庫引擎 將向行自動新增一個 uniqueifier 值,使每個鍵唯一。此列和列值供內部使用,使用者不能檢視或訪問。
是不是聚集索引就一定要比非聚集索引效能優呢?
如果想查詢學分在60-90之間的學生的學分以及姓名,在學分上建立聚集索引是否是最優的呢?
  答:否。既然只輸出兩列,我們可以在學分以及學生姓名上建立聯合非聚集索引,此時的索引就形成了覆蓋索引,即索引所儲存的內容就是最終輸出的資料,這種索引在比以學分為聚集索引做查詢效能更好。
在資料庫中通過什麼描述聚集索引與非聚集索引的?
索引是通過二叉樹的形式進行描述的,我們可以這樣區分聚集與非聚集索引的區別:聚集索引的葉節點就是最終的資料節點,而非聚集索引的葉節仍然是索引節點,但它有一個指向最終資料的指標。
在主鍵是建立聚集索引的表在資料插入上為什麼比主鍵上建立非聚集索引錶速度要慢?
在有主鍵的表中插入資料行,由於有主鍵唯一性的約束,所以需要保證插入的資料沒有重複。聚集索引由於索引葉節點就是資料頁,所以如果想檢查主鍵的唯一性,需要遍歷所有資料節點才行,但非聚集索引不同,由於非聚集索引上已經包含了主鍵值,所以查詢主鍵唯一性,只需要遍歷所有的索引頁就行,這比遍歷所有資料行減少了不少IO消耗。這就是為什麼主鍵上建立非聚集索引比主鍵上建立聚集索引在插入資料時要快的真正原因。


11、limit 20000 載入很慢怎麼解決

當一個資料庫表過於龐大,LIMIT offset, length中的offset值過大,則SQL查詢語句會非常緩慢,你需增加order by,並且order by欄位需要建立索引。如果對於有where 條件,又想走索引用limit的,必須設計一個索引,將where 放第一位,limit用到的主鍵放第2位,而且只能select 主鍵!

12、選擇合適的分散式主鍵方案

最簡單的方法是MySQL主鍵自增長,若有四臺資料庫,第一臺主鍵從1開始每次加4,第二臺從2開始每次開始加4,以此類推。單獨開一個資料庫,獲取全域性唯一的自增序列號或各表的MaxId。

相關文章