大厂面试官最喜欢问的面试难点
一 平常經常使用外鍵和外鍵和級聯嗎,可以說說你對它們的理解嗎?
對于外鍵和級聯,阿里巴巴開發手冊這樣說到:
【強制】不得使用外鍵與級聯,一切外鍵概念必須在應用層解決。
說明:以學生和成績的關系為例,學生表中的 student_id 是主鍵,那么成績表中的 student_id 則為外鍵。如果更新學生表中的 student_id,同時觸發成績表中的 student_id 更新,即為級聯更新。外鍵與級聯更新適用于單機低并發,不適合分布式、高并發集群;級聯更新是強阻塞,存在數據庫更新風暴的風 險;外鍵影響數據庫的插入速度
為什么不要用外鍵呢?大部分人可能會這樣回答:
-
增加了復雜性:?a.每次做DELETE 或者UPDATE都必須考慮外鍵約束,會導致開發的時候很痛苦,測試數據極為不方便;b.外鍵的主從關系是定的,假如那天需求有變化,數據庫中的這個字段根本不需要和其他表有關聯的話就會增加很多麻煩。
-
增加了額外工作:數據庫需要增加維護外鍵的工作,比如當我們做一些涉及外鍵字段的增,刪,更新操作之后,需要觸發相關操作去檢查,保證數據的的一致性和正確性,這樣會不得不消耗資源;(個人覺得這個不是不用外鍵的原因,因為即使你不使用外鍵,你在應用層面也還是要保證的。所以,我覺得這個影響可以忽略不計。)
-
外鍵還會因為需要請求對其他表內部加鎖而容易出現死鎖情況;
-
對分不分表不友好?:因為分庫分表下外鍵是無法生效的。
-
......
我個人覺得上面這種回答不是特別的全面,只是說了外鍵存在的一個常見的問題。實際上,我們知道外鍵也是有很多好處的,比如:
保證了數據庫數據的一致性和完整性;
級聯操作方便,減輕了程序代碼量;
......
所以說,不要一股腦的就拋棄了外鍵這個概念,既然它存在就有它存在的道理,如果系統不涉及分不分表,并發量不是很高的情況還是可以考慮使用外鍵的。
我個人是不太喜歡外鍵約束,比較喜歡在應用層去進行相關操作。
二 解釋一下什么是池化設計思想。什么是數據庫連接池?為什么需要數據庫連接池?
池話設計應該不是一個新名詞。我們常見的如java線程池、jdbc連接池、redis連接池等就是這類設計的代表實現。這種設計會初始預設資源,解決的問題就是抵消每次獲取資源的消耗,如創建線程的開銷,獲取遠程連接的開銷等。就好比你去食堂打飯,打飯的大媽會先把飯盛好幾份放那里,你來了就直接拿著飯盒加菜即可,不用再臨時又盛飯又打菜,效率就高了。除了初始化資源,池化設計還包括如下這些特征:池子的初始值、池子的活躍值、池子的最大值等,這些特征可以直接映射到java線程池和數據庫連接池的成員屬性中。
數據庫連接本質就是一個 socket 的連接。數據庫服務端還要維護一些緩存和用戶權限信息之類的 所以占用了一些內存。我們可以把數據庫連接池是看做是維護的數據庫連接的緩存,以便將來需要對數據庫的請求時可以重用這些連接。為每個用戶打開和維護數據庫連接,尤其是對動態數據庫驅動的網站應用程序的請求,既昂貴又浪費資源。在連接池中,創建連接后,將其放置在池中,并再次使用它,因此不必建立新的連接。如果使用了所有連接,則會建立一個新連接并將其添加到池中。連接池還減少了用戶必須等待建立與數據庫的連接的時間。
三 分庫分表之后,id 主鍵如何處理?
因為要是分成多個表之后,每個表都是從 1 開始累加,這樣是不對的,我們需要一個全局唯一的 id 來支持。
生成全局 id 有下面這幾種方式:
-
UUID:不適合作為主鍵,因為太長了,并且無序不可讀,查詢效率低。比較適合用于生成唯一的名字的標示比如文件的名字。
-
數據庫自增 id?: 兩臺數據庫分別設置不同步長,生成不重復ID的策略來實現高可用。這種方式生成的 id 有序,但是需要獨立部署數據庫實例,成本高,還會有性能瓶頸。
-
利用 redis 生成 id :?性能比較好,靈活方便,不依賴于數據庫。但是,引入了新的組件造成系統更加復雜,可用性降低,編碼更加復雜,增加了系統成本。
-
Twitter的snowflake算法?:Github 地址:https://github.com/twitter-archive/snowflake。
-
美團的Leaf分布式ID生成系統?:Leaf 是美團開源的分布式ID生成器,能保證全局唯一性、趨勢遞增、單調遞增、信息安全,里面也提到了幾種分布式方案的對比,但也需要依賴關系數據庫、Zookeeper等中間件。感覺還不錯。美團技術團隊的一篇文章:https://tech.meituan.com/2017/04/21/mt-leaf.html 。
-
......
四 用過 BigDecimal 嗎?為啥要用它?
4.1 BigDecimal 的用處
《阿里巴巴Java開發手冊》中提到:浮點數之間的等值判斷,基本數據類型不能用==來比較,包裝數據類型不能用 equals 來判斷。具體原理和浮點數的編碼方式有關,這里就不多提了,我們下面直接上實例:
float a = 1.0f - 0.9f; float b = 0.9f - 0.8f; System.out.println(a);// 0.100000024 System.out.println(b);// 0.099999964 System.out.println(a == b);// false具有基本數學知識的我們很清楚的知道輸出并不是我們想要的結果(精度丟失),我們如何解決這個問題呢?一種很常用的方法是:使用使用 BigDecimal 來定義浮點數的值,再進行浮點數的運算操作。
BigDecimal a = new BigDecimal("1.0"); BigDecimal b = new BigDecimal("0.9"); BigDecimal c = new BigDecimal("0.8"); BigDecimal x = a.subtract(b);// 0.1 BigDecimal y = b.subtract(c);// 0.1 System.out.println(x.equals(y));// true4.2 BigDecimal 的大小比較
a.compareTo(b) : 返回 -1 表示小于,0 表示 等于, 1表示 大于。 BigDecimal a = new BigDecimal("1.0"); BigDecimal b = new BigDecimal("0.9"); System.out.println(a.compareTo(b));// 14.3 BigDecimal 保留幾位小數
通過 setScale方法設置保留幾位小數以及保留規則。保留規則有挺多種,不需要記,IDEA會提示。
BigDecimal m = new BigDecimal("1.255433"); BigDecimal n = m.setScale(3,BigDecimal.ROUND_HALF_DOWN); System.out.println(n);// 1.2554.4 BigDecimal 的使用注意事項
注意:我們在使用BigDecimal時,為了防止精度丟失,推薦使用它的?BigDecimal(String)構造方法來創建對象?!栋⒗锇桶蚃ava開發手冊》對這部分內容也有提到如下圖所示。
?
《阿里巴巴Java開發手冊》對這部分BigDecimal的描述
4.5 總結
BigDecimal 主要用來操作(大)浮點數,BigInteger 主要用來操作大整數(超過 long 類型)。
BigDecimal 的實現利用到了 BigInteger, 所不同的是 BigDecimal 加入了小數位的概念
五 Java 中 IO 流分為幾種?
-
按照流的流向分,可以分為輸入流和輸出流;
-
按照操作單元劃分,可以劃分為字節流和字符流;
-
按照流的角色劃分為節點流和處理流。
Java Io流共涉及40多個類,這些類看上去很雜亂,但實際上很有規則,而且彼此之間存在非常緊密的聯系, Java I0流的40多個類都是從如下4個抽象類基類中派生出來的。
-
InputStream/Reader: 所有的輸入流的基類,前者是字節輸入流,后者是字符輸入流。
-
OutputStream/Writer: 所有輸出流的基類,前者是字節輸出流,后者是字符輸出流。
按操作方式分類結構圖:
?
IO-操作方式分類
按操作對象分類結構圖:
?
IO-操作對象分類
六 既然有了字節流,為什么還要有字符流?
問題本質想問:不管是文件讀寫還是網絡發送接收,信息的最小存儲單元都是字節,那為什么 I/O 流操作要分為字節流操作和字符流操作呢?
回答:字符流是由 Java 虛擬機將字節轉換得到的,問題就出在這個過程還算是非常耗時,并且,如果我們不知道編碼類型就很容易出現亂碼問題。所以, I/O 流就干脆提供了一個直接操作字符的接口,方便我們平時對字符進行流操作。如果音頻文件、圖片等媒體文件用字節流比較好,如果涉及到字符的話使用字符流比較好。
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的大厂面试官最喜欢问的面试难点的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅析HDFS的副本存放策略
- 下一篇: Spark _05Standalone模