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)求。

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

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

?級(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 的淘汰策略

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)


10 es的倒排索引原理,具體使用場(chǎng)景,用的分詞器是什么,數(shù)據(jù)怎么從db同步到es
具體使用場(chǎng)景
存儲(chǔ)商品數(shù)據(jù),用戶數(shù)據(jù),訂單數(shù)據(jù),用于商家端搜索,對(duì)實(shí)時(shí)性要求不那么高的場(chǎng)景。
es的倒排索引原理

當(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

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)控代碼。

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í)

針對(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)行分代管理是為了方便垃圾回收。
年輕代

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

沒發(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)存碎片




