TME Java面試題

1 微服務(wù)Feign[/fe?n/]的底層調(diào)用原理

Feign是?個(gè)輕量級(jí)RESTful的HTTP服務(wù)客戶端(?它來發(fā)起請(qǐng)求,
遠(yuǎn)程調(diào)?的) ,底層依賴于Java的動(dòng)態(tài)代理機(jī)制,對(duì)原生Java Socket或者Apache HttpClient進(jìn)行封裝,實(shí)現(xiàn)了基于Http協(xié)議的遠(yuǎn)程過程調(diào)用。

@EnableFeignClients注解是開啟Fiegn功能的關(guān)鍵,我們通常會(huì)在該注解中添加FeignClient的所在包,以便Spring容器能夠掃描到所有的FeignClient,并進(jìn)行托管。后面可以使用@Autowired注解自動(dòng)注入,但是注入的是一個(gè)代理對(duì)象,然后通過代理對(duì)象調(diào)用方法,方法執(zhí)行前后可以加入一些增強(qiáng)邏輯,比如調(diào)用負(fù)載均衡策略完成 server 的選擇,發(fā)起http請(qǐng)求。


image.png

2 Mybatis 二級(jí)緩存有了解嗎

image.png

一級(jí)緩存默認(rèn)開始,二級(jí)緩存手動(dòng)開啟。


image.png

?級(jí)緩存的原理和?級(jí)緩存原理?樣,第?次查詢,會(huì)將數(shù)據(jù)放?緩存中,然后第?次查詢則會(huì)直接去 緩存中取。但是?級(jí)緩存是基于sqlSession的,??級(jí)緩存是基于mapper?件的namespace的,也 就是說多個(gè)sqlSession可以共享?個(gè)mapper中的?級(jí)緩存區(qū)域,并且如果兩個(gè)mapper的namespace 相同,即使是兩個(gè)mapper,那么這兩個(gè)mapper中執(zhí)?sql查詢到的數(shù)據(jù)也將存在相同的?級(jí)緩存區(qū)域中。

3 rabbitMQ,Kafka,Rocket MQ的區(qū)別,你們的項(xiàng)目為什么選了 Kafka

我覺得 Kafka 相比其他消息隊(duì)列主要的優(yōu)勢(shì)如下:

  • 極致的性能 :基于 Scala 和 Java 語言開發(fā),設(shè)計(jì)中大量使用了批量處理和異步的思想,
    最高可以每秒處理千萬級(jí)別的消息。
  • 生態(tài)系統(tǒng)兼容性無可匹敵 : Kafka 與周邊生態(tài)系統(tǒng)的兼容性是最好的沒有之一,尤其在大
    數(shù)據(jù)和流計(jì)算領(lǐng)域

4 怎么優(yōu)化慢查詢,各種優(yōu)化手段發(fā)現(xiàn)sql還是很慢,怎么處理

在 MySQL 中,會(huì)引發(fā)性能問題的慢查詢,大體有以下三種可能:

  • 索引沒有設(shè)計(jì)好,需要通過緊急創(chuàng)建索引來解決
  • SQL 語句沒寫好導(dǎo)致沒有使用上索引,可以通過改寫索引來處理
  • MySQL 選錯(cuò)了索引,應(yīng)急方案就是給這個(gè)語句加上 force index

依舊很慢,會(huì)不會(huì)是表數(shù)據(jù)已經(jīng)達(dá)到極限了,需要分表分庫(kù)?

5 Mysql 的索引,索引失效的場(chǎng)景,主鍵為什么要設(shè)置自動(dòng)遞增?

性能層面
插入新記錄的時(shí)候可以不指定 ID 的值,系統(tǒng)會(huì)獲取當(dāng)前 ID 最大值加 1 作為下一條記錄的 ID 值。即每次插入一條新記錄,都是追加操作,都不涉及到挪動(dòng)其他記錄,也不會(huì)觸發(fā)葉子節(jié)點(diǎn)的分裂。

而有業(yè)務(wù)邏輯的字段做主鍵,則往往不容易保證有序插入,這樣寫數(shù)據(jù)成本相對(duì)較高。在分布式系統(tǒng)里,不采用 UUID 作為分布式 ID,也是由于 UUID 是無序的,會(huì)引起索引節(jié)點(diǎn)的頻繁變動(dòng),影響性能。

存儲(chǔ)層面
假設(shè)你的表中確實(shí)有一個(gè)唯一字段,比如字符串類型的身份證號(hào),那應(yīng)該用身份證號(hào)做主鍵,還是用自增字段做主鍵呢?

由于每個(gè)非主鍵索引的葉子節(jié)點(diǎn)上都是主鍵的值。如果用身份證號(hào)做主鍵,那么每個(gè)二級(jí)索引的葉子節(jié)點(diǎn)占用約 20 個(gè)字節(jié),而如果用整型做主鍵,則只要 4 個(gè)字節(jié),如果是長(zhǎng)整型(bigint)則是 8 個(gè)字節(jié)。顯然,主鍵長(zhǎng)度越小,輔助索引的葉子節(jié)點(diǎn)就越小,輔助索引占用的空間也就越小。

6 高并發(fā)除了加鎖還有什么解決方案

  • 分布式鎖
  • 采用MQ異步處理,達(dá)到削峰效果
  • 配置限流,用的是阿里的 sentinel[/?sent?nl/] (哨兵)
  • 頁(yè)面靜態(tài)化


    image.png

7 Kafka持久化內(nèi)存滿了除了遷移還能怎么辦?Kafka具體業(yè)務(wù)的使用場(chǎng)景。

每個(gè)分區(qū)各?存在?個(gè)記錄消息數(shù)據(jù)的?志?件。

數(shù)據(jù)清理

  • 日志刪除
  • 日志壓縮

8 Redis 為什么這么快?Redis 過期key的刪除策略?Redis 的淘汰策略?I/O多路復(fù)用的epoll機(jī)制原理?Redis具體業(yè)務(wù)的使用場(chǎng)景。

Redis為什么用單線程
Redis 單線程是指它對(duì)網(wǎng)絡(luò) IO 和數(shù)據(jù)讀寫的操作采用了一個(gè)線程,而采用單線程的一個(gè)核心原因是避免多線程開發(fā)的并發(fā)控制問題。
但 Redis 的其他功能,比如持久化、異步刪除、集群數(shù)據(jù)同步等,其實(shí)是由額外的線程執(zhí)行的。
Redis 為什么這么快

  • Redis 的大部分操作在內(nèi)存上完成,再加上它采用了高效的數(shù)據(jù)結(jié)構(gòu),例如哈希表和跳表
  • Redis 采用了多路復(fù)用機(jī)制,使其在網(wǎng)絡(luò) IO 操作中能并發(fā)處理大量的客戶端請(qǐng)求,實(shí)現(xiàn)高吞吐率

Redis 過期key的刪除策略

  • 定時(shí)刪除:在設(shè)置鍵的過期時(shí)間的同時(shí),創(chuàng)建一個(gè)定時(shí)器 timer). 讓定時(shí) 器在鍵的過期時(shí)
    間來臨時(shí),立即執(zhí)行對(duì)鍵的刪除操作。
  • 惰性刪除:但是每次查到key都檢查是否過期,過期,就刪除該鍵;沒有過期,就返回該鍵。
  • 定期刪除:每隔一段時(shí)間程序就對(duì)數(shù)據(jù)庫(kù)進(jìn)行一次檢查,刪除里面的過期鍵。至于要?jiǎng)h除多少過期鍵,以及要檢查多少個(gè)數(shù)據(jù)庫(kù),則由算法決定。

Redis 的淘汰策略

image.png

I/O多路復(fù)用的epoll機(jī)制原理
在 Redis 只運(yùn)行單線程的情況下,Linux的多路復(fù)用機(jī)制允許內(nèi)核中可以同時(shí)存在多個(gè)監(jiān)聽 Socket 和已連接 Socket,一旦有請(qǐng)求到達(dá),就會(huì)觸發(fā)相應(yīng)的事件,這些事件會(huì)被放進(jìn)一個(gè)事件隊(duì)列,然后通知 Redis 單線程對(duì)事件隊(duì)列的事件進(jìn)行處理。

Redis具體業(yè)務(wù)的使用場(chǎng)景

  • String:存儲(chǔ)圖片id
  • List:適用于展示最新評(píng)論列表、排行榜等場(chǎng)景
  • Set:統(tǒng)計(jì)手機(jī) App 每天的新增用戶數(shù)和第二天的留存用戶數(shù),交集,并集計(jì)算
  • Sorted Set:可以按分值排序,適用于各種排行榜,如:點(diǎn)擊排行榜、銷量排行榜、關(guān)注排行榜等
  • Hash:適用于對(duì)象的存儲(chǔ),常用于購(gòu)物車
  • bitmap:點(diǎn)贊,簽到,打卡
  • 分布式鎖
  • 計(jì)算器,統(tǒng)計(jì)驗(yàn)證碼操作次數(shù)


    image.png

9 多線程的使用場(chǎng)景,具體的實(shí)戰(zhàn)經(jīng)驗(yàn)

image.png

image.png

10 es的倒排索引原理,具體使用場(chǎng)景,用的分詞器是什么,數(shù)據(jù)怎么從db同步到es

具體使用場(chǎng)景
存儲(chǔ)商品數(shù)據(jù),用戶數(shù)據(jù),訂單數(shù)據(jù),用于商家端搜索,對(duì)實(shí)時(shí)性要求不那么高的場(chǎng)景。

es的倒排索引原理

image.png

當(dāng)我們?cè)谒阉骺蜉斎肷唐访Q,點(diǎn)擊查詢。es的分詞器會(huì)對(duì)商品名稱進(jìn)行分詞,形成一個(gè)或多個(gè)term。然后根據(jù) term 去查詢內(nèi)存中的 term index(一棵前綴樹),通過 term index 可以定位到 term 在 term dictionary 中的位置,找到對(duì)應(yīng)的倒排列表(存的文檔id,地址偏移量等),拿到相關(guān)的文檔id,根據(jù)文檔ID找到對(duì)應(yīng)的文檔數(shù)據(jù)返回。

用的分詞器是什么
IK 分詞器

數(shù)據(jù)怎么從db同步到es

image.png

11 Spring Aop 的具體使用場(chǎng)景,底層原理,Spring用到了哪些設(shè)計(jì)模式,Spring怎么解決循環(huán)依賴

Spring Aop
AOP(Aspect Oriented Programming )本質(zhì):在不改變?cè)袠I(yè)務(wù)邏輯的情況下,運(yùn)用動(dòng)態(tài)代理技術(shù),在運(yùn)行期間對(duì)需要使用業(yè)務(wù)邏輯的方法進(jìn)行增強(qiáng)。增強(qiáng)邏輯代碼往往是權(quán)限校驗(yàn)代碼、?志代碼、事務(wù)控制代碼、性能監(jiān)控代碼。

image.png

Spring 實(shí)現(xiàn) AOP 思想使?的是動(dòng)態(tài)代理技術(shù)。Spring 會(huì)根據(jù)被代理對(duì)象是否實(shí)現(xiàn)接?來選擇使? JDK 還是 CGLIB,默認(rèn)使用 JDK 動(dòng)態(tài)代理。
JDK:被代理對(duì)象需要實(shí)現(xiàn)接?
CGLIB:被代理對(duì)象沒有實(shí)現(xiàn)接?
Spring用到了哪些設(shè)計(jì)模式

  • 動(dòng)態(tài)代理:AOP,聲明式事務(wù)
  • 工廠模式:通過BeanFactory和ApplicationContext來創(chuàng)建對(duì)象
  • 單例子模式:Bean默認(rèn)為單例模式
  • 適配器模式:
    image.png

    Spring怎么解決循環(huán)依賴
    循環(huán)依賴其實(shí)就是循環(huán)引?,也就是兩個(gè)或者兩個(gè)以上的 Bean 互相持有對(duì)?,最終形成閉環(huán)。?如A依賴于B, B依賴于C, C?依賴于A。
    image.png

    通過三級(jí)緩存解決set注入導(dǎo)致的循環(huán)依賴問題
    image.png

12 平時(shí)開發(fā)用到的設(shè)計(jì)模式,具體的業(yè)務(wù)場(chǎng)景

策略模式:價(jià)格計(jì)算,地址組裝

13 java的鎖有哪些,什么是鎖升級(jí),兩個(gè)版本的 synchronized

java的鎖有哪些

  • ReentrantLock 可重入鎖
  • Lock
  • synchronized
    synchronized 關(guān)鍵字可以加在方法上,不需要指定鎖對(duì)象(此時(shí)的鎖對(duì)象為 this),也可以新建一個(gè)同步代碼塊并且自定義 monitor 鎖對(duì)象;而 Lock 接口必須顯示用 Lock 鎖對(duì)象開始加鎖 lock() 和解鎖 unlock(),并且一般會(huì)在 finally 塊中確保用 unlock() 來解鎖,以防發(fā)生死鎖。

與 Lock 顯式的加鎖和解鎖不同的是 synchronized 的加解鎖是隱式的,尤其是拋異常的時(shí)候也能保證釋放鎖。

// Lock 可以不完全按照加鎖的反序解鎖,比如可以先獲取 Lock1 鎖,再獲取 Lock2 鎖
// 解鎖時(shí)則先解鎖 Lock1,再解鎖 Lock2,加解鎖有一定的靈活度
lock1.lock();
lock2.lock();
...
lock1.unlock();
lock2.unlock();
 
// synchronized 解鎖的順序和加鎖的順序必須完全相反
 synchronized(obj1){
    synchronized(obj2){
        ...
    }

鎖升級(jí)

image.png

針對(duì) synchronized 獲取鎖的方式,JVM 使用了鎖升級(jí)的優(yōu)化方式,就是先使用偏向鎖(做標(biāo)記)優(yōu)先同一線程然后再次獲取鎖,如果失敗,就升級(jí)為 CAS 輕量級(jí)鎖,如果失敗就會(huì)短暫自旋,防止線程被系統(tǒng)掛起。最后如果以上都失敗就升級(jí)為重量級(jí)鎖。

兩個(gè)版本的 synchronized
在 Java 5 以及之前,synchronized 的性能比較低,但是到了 Java 6 以后,發(fā)生了變化,因?yàn)?JDK 對(duì) synchronized 進(jìn)行了很多優(yōu)化,比如自適應(yīng)自旋、鎖消除、鎖粗化、輕量級(jí)鎖、偏向鎖等,所以后期的 Java 版本里的 synchronized 的性能并不比 Lock 差。

14 內(nèi)存泄漏怎么排查出來的,JVM的垃圾回收器有哪些,項(xiàng)目用的是哪個(gè)

分代垃圾回收
java 的堆進(jìn)行分代管理是為了方便垃圾回收。
年輕代

image.png

新創(chuàng)建的對(duì)象分配在 Eden 區(qū),Eden區(qū)發(fā)生一次GC后,存活的對(duì)象移到S0區(qū),滿了之后移到S1區(qū)。
老年代
image.png

沒發(fā)生一次Minor GC,年齡就加1,達(dá)到默認(rèn)的15之后,晉升到老年代,進(jìn)入Old區(qū)。
垃圾回收算法

  • 標(biāo)記-清除,容易產(chǎn)生內(nèi)存碎片
  • 標(biāo)記-整理,對(duì)存活的對(duì)象和垃圾對(duì)象進(jìn)行標(biāo)記,然后將所有存活對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存,不會(huì)產(chǎn)生內(nèi)存碎片
  • 復(fù)制:算法將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了,就將還存活的對(duì)象復(fù)制到另外一塊上,再把已使用過的內(nèi)存空間一次清理掉。
  • 分代收集:新生代中,每次垃圾收集都發(fā)現(xiàn)有大批對(duì)象死去,只有少量存活,選用復(fù)制算法。
    老年代中,對(duì)象存活率高,沒有額外空間進(jìn)行分配擔(dān)保,使用“標(biāo)記 - 清除”或“標(biāo)記 - 整理”算法來進(jìn)行回收。

垃圾回收器

  • 串行收集器
  • 并行收集器:Parallel Old收集器,公司用的也是并行收集器
  • 并發(fā)收集器:CMS,G1
    CMS:標(biāo)記清除,用戶線程與GC線程并發(fā)執(zhí)行,會(huì)產(chǎn)生內(nèi)存碎片
    G1:標(biāo)記-整理,標(biāo)記復(fù)制的算法,不會(huì)產(chǎn)生內(nèi)存碎片

15 有Spring boot starter 的具體實(shí)戰(zhàn)例子嗎,項(xiàng)目基于Spring boot做了哪些整合

16 Dubbo的SPI擴(kuò)展點(diǎn)有了解嗎

17 數(shù)據(jù)庫(kù)的事務(wù)隔離級(jí)別?重復(fù)讀是怎么實(shí)現(xiàn)的?怎么解決幻讀?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容