強(qiáng)引用:Object obj = new Object(),系統(tǒng)不會回收
軟引用:描述一些有用但是非必要的對象
弱引用:非必須對象,生存到下一次垃圾回收之前
虛引用:只是為了能在這個對象被收集器回收時收到一個系統(tǒng)通知
// 懶漢模式 + synchronized 同步鎖 + double-check
public final class Singleton {
? ? private volatile static Singleton instance= null;// 不實(shí)例化
? ? public List<String> list = null;//list 屬性
? ? private Singleton(){
? ? ? list = new ArrayList<String>();
? ? }// 構(gòu)造函數(shù)
? ? public static Singleton getInstance(){// 加同步鎖,通過該函數(shù)向整個系統(tǒng)提供實(shí)例
? ? ? ? if(null == instance){// 第一次判斷,當(dāng) instance 為 null 時,則實(shí)例化對象,否則直接返回對象
? ? ? ? ? synchronized (Singleton.class){// 同步鎖
? ? ? ? ? ? if(null == instance){// 第二次判斷
? ? ? ? ? ? ? ? instance = new Singleton();// 實(shí)例化對象
? ? ? ? ? ? }
? ? ? ? ? }
? ? ? ? }
? ? ? ? return instance;// 返回已存在的對象
? ? }
}
// 懶漢模式 內(nèi)部類實(shí)現(xiàn)
public final class Singleton {
? ? public List<String> list = null;// list 屬性
? ? private Singleton() {// 構(gòu)造函數(shù)
? ? ? ? list = new ArrayList<String>();
? ? }
? ? // 內(nèi)部類實(shí)現(xiàn)
? ? public static class InnerSingleton {
? ? ? ? private static Singleton instance=new Singleton();// 自行創(chuàng)建實(shí)例
? ? }
? ? public static Singleton getInstance() {
? ? ? ? return InnerSingleton.instance;// 返回內(nèi)部類中的靜態(tài)變量
? ? }
}
1.spring的理解
spring是一個開源框架,04年發(fā)布1.0版本,到現(xiàn)在6.0版本,從完全基于配置文件到現(xiàn)在基于注解的方式。主要核心是:
控制反轉(zhuǎn)(IOC),傳統(tǒng)的 java 開發(fā)模式中,當(dāng)需要一個對象時,我們會自己使用 new 或者 getInstance 等直接或者間接調(diào)用構(gòu)造方法創(chuàng)建一個對象。而在 spring 開發(fā)模式中,spring 容器使用了工廠模式為我們創(chuàng)建了所需要的對象,不需要我們自己創(chuàng)建了,直接調(diào)用spring 提供的對象就可以了,這是控制反轉(zhuǎn)的思想。
依賴注入(DI),spring 使用 javaBean 對象的 set 方法或者帶參數(shù)的構(gòu)造方法為我們在創(chuàng)建所需對象時將其屬性自動設(shè)置所需要的值的過程,就是依賴注入的思想。
面向切面編程(AOP),在面向?qū)ο缶幊蹋╫op)思想中,我們將事物縱向抽成一個個的對象。而在面向切面編程中,我們將一個個的對象某些類似的方面橫向抽成一個切面,對這個切面進(jìn)行一些如權(quán)限控制、事物管理,記錄日志等公用操作處理的過程就是面向切面編程的思想。AOP 底層是動態(tài)代理,如果是接口采用 JDK 動態(tài)代理,如果是類采用CGLIB 方式實(shí)現(xiàn)動態(tài)代理。JDK動態(tài)代理不需要基于第三方實(shí)現(xiàn),只需要實(shí)現(xiàn) InvocationHandler 接口,使用 Proxy.newProxyInstance() 方法生成代理對象。它只能為接口創(chuàng)建動態(tài)代理。
2.spring使用的設(shè)計(jì)模式有哪些
(1)工廠模式:Spring使用工廠模式,通過BeanFactory和ApplicationContext來創(chuàng)建對象
(2)單例模式:Bean默認(rèn)為單例模式
(3)策略模式:例如Resource的實(shí)現(xiàn)類,針對不同的資源文件,實(shí)現(xiàn)了不同方式的資源獲取策略
(4)代理模式:Spring的AOP功能用到了JDK的動態(tài)代理和CGLIB字節(jié)碼生成技術(shù)
(5)模板方法:可以將相同部分的代碼放在父類中,而將不同的代碼放入不同的子類中,用來解決代碼重復(fù)的問題。比如RestTemplate, JmsTemplate, JpaTemplate
(6)適配器模式:Spring AOP的增強(qiáng)或通知(Advice)使用到了適配器模式,Spring MVC中也是用到了適配器模式適配Controller
(7)觀察者模式:Spring事件驅(qū)動模型就是觀察者模式的一個經(jīng)典應(yīng)用。
(8)橋接模式:可以根據(jù)客戶的需求能夠動態(tài)切換不同的數(shù)據(jù)源。比如我們的項(xiàng)目需要連接多個數(shù)據(jù)庫,客戶在每次訪問中根據(jù)需要會去訪問不同的數(shù)據(jù)庫
3.Autowired和Resource的區(qū)別
1).來源不同:@Autowired 來自 Spring 框架,而 @Resource 來自于(Java)JSR-250;
2).依賴查找的順序不同:@Autowired 先根據(jù)類型再根據(jù)名稱查詢,而 @Resource 先根據(jù)名稱再根據(jù)類型查詢;
3).支持的參數(shù)不同:@Autowired 只支持設(shè)置 1 個參數(shù),而 @Resource 支持設(shè)置 7 個參數(shù);
4).依賴注入的用法支持不同:@Autowired 既支持構(gòu)造方法注入,又支持屬性注入和 Setter 注入,而 @Resource 只支持屬性注入和 Setter 注入;
5).編譯器 IDEA 的提示不同:當(dāng)注入 Mapper 對象時,使用 @Autowired 注解編譯器會提示錯誤,而使用 @Resource 注解則不會提示錯誤。
4.介紹下Spring中的常用注解
@Autowired @Resource @Controller @Service
5.談?wù)勀銓ρh(huán)依賴的理解
A對象創(chuàng)建需要依賴B對象,B對象創(chuàng)建需要依賴A對象
6.Spring中如果解決循環(huán)依賴的
通過三級緩存方式解決。在 Spring 中,只有同時滿足以下兩點(diǎn)才能解決循環(huán)依賴的問題:
依賴的 Bean 必須都是單例。
依賴注入的方式,必須不全是構(gòu)造器注入,且 beanName 字母序在前的不能是構(gòu)造器注入。
7.Spring中構(gòu)造注入的循環(huán)依賴問題
在 Spring 中創(chuàng)建 Bean 分三步:
實(shí)例化,createBeanInstance,就是 new 了個對象。
屬性注入,populateBean, 就是 set 一些屬性值。
初始化,initializeBean,執(zhí)行一些 aware 接口中的方法,initMethod,AOP代理等。
明確了上面這三點(diǎn),再結(jié)合我上面說的“不完整的”,我們來理一下。
如果全是構(gòu)造器注入,比如A(B b),那表明在 new 的時候,就需要得到 B,此時需要 new B 。
但是 B 也是要在構(gòu)造的時候注入 A ,即B(A a),這時候 B 需要在一個 map 中找到不完整的 A ,發(fā)現(xiàn)找不到。
為什么找不到?因?yàn)?A 還沒 new 完呢,所以找到不完整的 A,因此如果全是構(gòu)造器注入的話,那么 Spring 無法處理循環(huán)依賴。
8.Spring循環(huán)依賴三級緩存問題
一級緩存,singletonObjects,存儲所有已創(chuàng)建完畢的單例 Bean (完整的 Bean)。
二級緩存,earlySingletonObjects,存儲所有僅完成實(shí)例化,但還未進(jìn)行屬性注入和初始化的 Bean。
三級緩存,singletonFactories,存儲能建立這個 Bean 的一個工廠,通過工廠能獲取這個 Bean,延遲化 Bean 的生成,工廠生成的 Bean 會塞入二級緩存。
這三個 map 是如何配合的呢?
首先,獲取單例 Bean 的時候會通過 BeanName 先去 singletonObjects(一級緩存) 查找完整的 Bean,如果找到則直接返回,否則進(jìn)行步驟 2。
看對應(yīng)的 Bean 是否在創(chuàng)建中,如果不在直接返回找不到,如果是,則會去 earlySingletonObjects (二級緩存)查找 Bean,如果找到則返回,否則進(jìn)行步驟 3
去 singletonFactories (三級緩存)通過 BeanName 查找到對應(yīng)的工廠,如果存著工廠則通過工廠創(chuàng)建 Bean ,并且放置到 earlySingletonObjects 中。
如果三個緩存都沒找到,則返回 null。
正常代理對象的生成是基于后置處理器,是在被代理的對象初始化后期調(diào)用生成的,所以如果你提早代理了其實(shí)是違背了 Bean 定義的生命周期。
所以 Spring 先在一個三級緩存放置一個工廠,如果產(chǎn)生循環(huán)依賴,那么就調(diào)用這個工廠提早得到代理對象。
9.Spring中bean的生命周期
實(shí)例化-》屬性注入-》初始化-》使用-》銷毀
10.Spring中支持幾種作用域
singleton,prototype,request,session,global session
11.談?wù)剆ping中的事務(wù)隔離級別
Isolation.DEFAULT:默認(rèn)的事務(wù)隔離級別,以連接的數(shù)據(jù)庫的事務(wù)隔離級別為準(zhǔn)。
Isolation.READ_UNCOMMITTED:讀未提交,可以讀取到未提交的事務(wù),存在臟讀。
Isolation.READ_COMMITTED:讀已提交,只能讀取到已經(jīng)提交的事務(wù),解決了臟讀,存在不可重復(fù)讀。
Isolation.REPEATABLE_READ:可重復(fù)讀,解決了不可重復(fù)讀,但存在幻讀(MySQL 數(shù)據(jù)庫默認(rèn)的事務(wù)隔離級別)。
Isolation.SERIALIZABLE:串行化,可以解決所有并發(fā)問題,但性能太低。
13.談?wù)剆pring中的事務(wù)實(shí)現(xiàn)方式
(1)編程式事務(wù)管理對基于 POJO 的應(yīng)用來說是唯一選擇。我們需要在代碼中調(diào)用beginTransaction()、commit()、rollback()等事務(wù)管理相關(guān)的方法,這就是編程式事務(wù)管理。
(2)基于 TransactionProxyFactoryBean的聲明式事務(wù)管理
(3)基于 @Transactional 的聲明式事務(wù)管理
(4)基于Aspectj AOP配置事務(wù)
14.談?wù)剆pring中的事務(wù)本質(zhì)
Spring 事務(wù)的本質(zhì),其實(shí)就是通過 Spring AOP 切面技術(shù),在合適的地方開啟事務(wù),接著在合適的地方提交事務(wù)或回滾事務(wù),從而實(shí)現(xiàn)了業(yè)務(wù)編程層面的事務(wù)操作。
15.BeanFactory和applicationContext的理解
BeanFactory負(fù)責(zé)讀取bean配置文檔,管理bean的加載,實(shí)例化,維護(hù)bean之間的依賴關(guān)系,負(fù)責(zé)bean的聲明周期。
ApplicationContext除了提供上述BeanFactory所能提供的功能之外,還提供了更完整的框架功能:
a. 國際化支持
b. 資源訪問:Resource rs = ctx. getResource(“classpath:config.properties”), “file:c:/config.properties”
c. 事件傳遞:通過實(shí)現(xiàn)ApplicationContextAware接口。
16.springmvc
17.springboot的自動裝配原理
18.談?wù)刬mport注解的理解
19.Spring 有哪幾種事務(wù)傳播行為?
PROPAGATION_REQUIRED(默認(rèn)) 如果當(dāng)前存在事務(wù),則用當(dāng)前事務(wù),如果沒有事務(wù)則新起一個事務(wù)
PROPAGATION_SUPPORTS 支持當(dāng)前事務(wù),如果不存在,則以非事務(wù)方式執(zhí)行
PROPAGATION_MANDATORY 支持當(dāng)前事務(wù),如果不存在,則拋出異常
PROPAGATION_REQUIRES_NEW 創(chuàng)建一個新事務(wù),如果存在當(dāng)前事務(wù),則掛起當(dāng)前事務(wù)
PROPAGATION_NOT_SUPPORTED 不支持當(dāng)前事務(wù),始終以非事務(wù)方式執(zhí)行
PROPAGATION_NEVER 不支持當(dāng)前事務(wù),如果當(dāng)前存在事務(wù),則拋出異常
PROPAGATION_NESTED 如果當(dāng)前事務(wù)存在,則在嵌套事務(wù)中執(zhí)行,內(nèi)層事務(wù)依賴外層事務(wù),如果外層失敗,則會回滾內(nèi)層,內(nèi)層失敗不影響外層。
## 19.Integer ==比較,有時候相等,有時候不相等?
在Integer類裝入內(nèi)存中時,會執(zhí)行其內(nèi)部類中靜態(tài)代碼塊進(jìn)行其初始化工作,做的主要工作就是把 [-128,127]之間的數(shù)包裝成Integer類并把其對應(yīng)的引用存入到cache數(shù)組中,這樣在方法區(qū)中開辟空間存放這些靜態(tài)Integer變量,同時靜態(tài)cache數(shù)組也存放在這里,供線程享用,這也稱靜態(tài)緩存
## 20.CUP飆高排插
1.上下文切換頻繁,為了保證線程狀態(tài),消耗資源
2.創(chuàng)建線程太多,導(dǎo)致資源消耗過快
根據(jù)top命令查看,一種是一直是一個線程,可能是死循環(huán),可以jstack dump查看,一種是多個線程,挑選幾個jstack dump查看
## 21.常見的分布式鎖
### 21.1.Redis方案
通過set key value nx px millo** 命令實(shí)現(xiàn) 但是會出現(xiàn)鎖到期了,服務(wù)還沒執(zhí)行,其他服務(wù)獲取到鎖,導(dǎo)致服務(wù)A釋放服務(wù)B鎖的情況
通過Redisson實(shí)現(xiàn),有watchdog實(shí)現(xiàn)鎖自動續(xù)期,但是會出現(xiàn)主從鎖丟失問題,獲取到鎖,還沒同步就宕機(jī)了,導(dǎo)致其他線程也獲取到鎖,操作相同資源
可以用RedLock,但是極端情況下,比如時間跳躍會導(dǎo)致鎖釋放,其他服務(wù)獲取到鎖
### 21.2.zookeeper臨時順序節(jié)點(diǎn)實(shí)現(xiàn)
1、每個獲取鎖的線程會在zk的某一個目錄下創(chuàng)建一個臨時有序的節(jié)點(diǎn)。
2、節(jié)點(diǎn)創(chuàng)建成功后,判斷當(dāng)前線程創(chuàng)建的節(jié)點(diǎn)的序號是否是最小
3、如果序號是最小的,那么獲取鎖成
4、如果序號不是最小的,則對他序號的前一個節(jié)點(diǎn)添加事件監(jiān)聽。如果前一個節(jié)點(diǎn)被刪了(鎖被釋放了),那么就會喚醒當(dāng)前節(jié)點(diǎn),則成功獲取到鎖。
zookeeper CP原則
優(yōu)點(diǎn):
1、不用設(shè)置過期時間
2、事件監(jiān)聽機(jī)制,加鎖失敗后,可以等待鎖釋放
缺點(diǎn):
1、性能不如redis
2、當(dāng)網(wǎng)絡(luò)不穩(wěn)定時,可能會有多個節(jié)點(diǎn)同時獲取鎖問題。例:node1由于網(wǎng)絡(luò)波動,導(dǎo)致zk將其刪除,剛好node2獲取到鎖,那么此時node1和node2兩者都鎖。
Redis AP原則
優(yōu)點(diǎn):性能上比較好,天然的支持高并發(fā)
缺點(diǎn):
1、獲取鎖失敗后,得輪詢的去獲2、大多數(shù)情況下redis無法保證數(shù)據(jù)強(qiáng)一致性
## 22.基于Redis和Mysql的架構(gòu),如果保證數(shù)據(jù)的一致性
1.基于rocketMQ的可靠性消息通信,實(shí)現(xiàn)最終的一致性
2.通過canal組件監(jiān)控mysql中的binlog日志,將數(shù)據(jù)同步到redis,實(shí)現(xiàn)最終一致性
## 23.ArrayList擴(kuò)容機(jī)制
oldCapacity >> 1? ? 右移運(yùn)算符? 原來長度的一半 再加上原長度也就是每次擴(kuò)容是原來的1.5倍
然后通過Arrays.copyof(elementData,newCapacity) 完成數(shù)據(jù)遷移
## 24 依賴注入的方式有幾種
1.構(gòu)造器注入,缺點(diǎn):構(gòu)造器參數(shù)列表將會很長,不夠靈活
2.setter方法注入,缺點(diǎn):依賴對象初始化完成后由于尚未注入被依賴對象,因此還不能使用
3.接口注入,缺點(diǎn):代碼入侵太強(qiáng)
## 25 Java 創(chuàng)建對象有幾種方式?
1.通過new()創(chuàng)建 2.反射創(chuàng)建 3.使用 clone 方法 4.使用反序列化創(chuàng)建對象,調(diào)用 ObjectInputStream 類的 readObject() 方法。
## 26 Consul和Eureka
Consul:強(qiáng)一致性(CP):
1?服務(wù)注冊相比Eureka會稍慢一些。因?yàn)镃onsul的Raft協(xié)議要求必須過半的節(jié)點(diǎn)都寫入成功才認(rèn)為注冊成功。
2?Leader掛掉后,重新選舉期間整個Consul不可用。保證了強(qiáng)一致性,但犧牲了可用性。
Eureka:高可用性和最終一致性(AP):
1?服務(wù)注冊相對要快,因?yàn)椴恍枰茸孕畔?fù)制到其他節(jié)點(diǎn),也不保證注冊信息是否復(fù)制成功。
2?當(dāng)數(shù)據(jù)出現(xiàn)不一致的時候,雖然A,B上的注冊信息不完全相同,但是每個Eureka節(jié)點(diǎn)依然能夠正常對外提供服務(wù),這會出現(xiàn)查詢服務(wù)信息時如果請求A查不到,但請求B就能查到。如果保證了可用性但犧牲了一致性。
## 27 Cassandra和MongoDB的區(qū)別
1.高可用性策略
Cassandra可以部署多個主節(jié)點(diǎn),如果一個或多個主節(jié)點(diǎn)過來,至少有一個主節(jié)點(diǎn)可用服務(wù)就可用。
MongoDb只有一個主節(jié)點(diǎn),通過故障轉(zhuǎn)移實(shí)現(xiàn)可用,在MongoDB集群中設(shè)置一個主節(jié)點(diǎn)。如果主站發(fā)生故障,從站節(jié)點(diǎn)將自動轉(zhuǎn)變?yōu)樾碌闹髡军c(diǎn)。這確保了連續(xù)性,但這不會立即發(fā)生,通常需要將近一分鐘
2.寫入速度
Cassandra有多個主節(jié)點(diǎn),可以并行寫入,效率更高
3.存儲結(jié)構(gòu)
Cassandra是基于列式存儲,跟傳統(tǒng)的SQL很像,MongoDb是文檔存儲結(jié)構(gòu),可以存儲對象
## 28 分布式ID生成方案
1. UUID
算法的核心思想是結(jié)合機(jī)器的網(wǎng)卡、當(dāng)?shù)貢r間、一個隨記數(shù)來生成UUID。
優(yōu)點(diǎn):本地生成,生成簡單,性能好,沒有高可用風(fēng)險
缺點(diǎn):長度過長,存儲冗余,且無序不可讀,查詢效率低
2.數(shù)據(jù)庫自增ID
使用數(shù)據(jù)庫的id自增策略,如 MySQL 的 auto_increment。并且可以使用兩臺數(shù)據(jù)庫分別設(shè)置不同步長,生成不重復(fù)ID的策略來實(shí)現(xiàn)高可用。
優(yōu)點(diǎn):數(shù)據(jù)庫生成的ID絕對有序,高可用實(shí)現(xiàn)方式簡單
缺點(diǎn):需要獨(dú)立部署數(shù)據(jù)庫實(shí)例,成本高,有性能瓶頸
3.Redis生成ID
Redis的所有命令操作都是單線程的,本身提供像 incr 和 increby 這樣的自增原子命令,所以能保證生成的 ID 肯定是唯一有序的。
優(yōu)點(diǎn):不依賴于數(shù)據(jù)庫,靈活方便,且性能優(yōu)于數(shù)據(jù)庫;數(shù)字ID天然排序,對分頁或者需要排序的結(jié)果很有幫助。
缺點(diǎn):如果系統(tǒng)中沒有Redis,還需要引入新的組件,增加系統(tǒng)復(fù)雜度;需要編碼和配置的工作量比較大。
4.Twitter的snowflake算法
優(yōu)點(diǎn):高性能,低延遲,按時間有序,一般不會造成ID碰撞
缺點(diǎn):需要獨(dú)立的開發(fā)和部署,依賴于機(jī)器的時鐘
## 29 常見的冪等性解決方案
1. 唯一索引,防止新增臟數(shù)據(jù)
2. token機(jī)制,防止頁面重復(fù)提交
處理流程:
數(shù)據(jù)提交前要向服務(wù)申請token,token放到redis或內(nèi)存,token有效時間。提交后后臺校驗(yàn)token,同時刪除token,生成新的token返回
token特點(diǎn):
要申請,一次有效性,可以限流
3. 悲觀鎖
獲取數(shù)據(jù)的時候加鎖獲取
select * from table_xxx where id=‘xxx’ for update;
注意:id字段一定是主鍵或者唯一索引,不然是鎖表,會死人的
悲觀鎖使用時一般伴隨事務(wù)一起使用,數(shù)據(jù)鎖定時間可能會很長,根據(jù)實(shí)際情況選用
4. 樂觀鎖
樂觀鎖只是在更新數(shù)據(jù)那一刻鎖表,其他時間不鎖表,所以相對于悲觀鎖,效率更高。
樂觀鎖的實(shí)現(xiàn)方式多種多樣可以通過version或者其他狀態(tài)條件:
通過版本號實(shí)現(xiàn)
update table_xxx set name=#name#,version=version+1 where version=#version#
5. 分布式鎖
6. select + insert
7. 對外提供接口的api如何保證冪等
## 30 mabatis一級緩存,二級緩存
一級緩存:
每個 SqlSession 中持有了 Executor,每個 Executor 中有一個 LocalCache。當(dāng)用戶發(fā)起查詢時,MyBatis 根據(jù)當(dāng)前執(zhí)行的語句生成 MappedStatement,在 Local Cache 進(jìn)行查詢,如果緩存命中的話,直接返回結(jié)果給用戶,如果緩存沒有命中的話,查詢數(shù)據(jù)庫,結(jié)果寫入 Local Cache,最后返回結(jié)果給用戶
二級緩存
一級緩存中,其最大的共享范圍就是一個 SqlSession 內(nèi)部,如果多個 SqlSession之間需要共享緩存,則需要使用到二級緩存。開啟二級緩存后,會使用 CachingExecutor 裝飾Executor,進(jìn)入一級緩存的查詢流程前,先在 CachingExecutor 進(jìn)行二級緩存的查詢。二級緩存開啟后,同一個 namespace 下的所有操作語句,都影響著同一個 Cache,即二級緩存被多個 SqlSession 共享,是一個全局的變量。
## 31 了解文本相似度 TF-IDF嗎
TF = Term Frequency 詞頻,一個詞在這個文檔中出現(xiàn)的頻率。值越大,說明這文檔越匹配,正向指標(biāo)。
IDF = Inverse Document Frequency 反向文檔頻率,簡單點(diǎn)說就是一個詞在所有文檔中都出現(xiàn),那么這個詞不重要。比如“的、了、我、好”這些詞所有文檔都出現(xiàn),對檢索毫無幫助。反向指標(biāo)。
## 32 Kafka消息重復(fù)解決方案
主要原因是offset的提交失敗,它本身有個機(jī)制,每5秒提交一次,然后也有balance機(jī)制,消息會均衡分不到不同partion,如果在相應(yīng)時間沒消費(fèi)完,會觸發(fā)balance,導(dǎo)致offset提交失敗
1.提高消費(fèi)端的處理性能避免觸發(fā)Balance
2.使用ConsumerRebalanceLister,再均衡監(jiān)聽器
3.使用消息冪等性 prop.put()
4.去重表,將消息生成md5然后保存到mysql或者redis,在處理消息之前先去查mysql或者redis
## 33 如何確保Kafka消息不丟失
生產(chǎn)者:ack機(jī)制 將發(fā)送消息的確認(rèn)機(jī)制設(shè)置為-1,使得所有節(jié)點(diǎn)確認(rèn)后再發(fā)送下一條數(shù)據(jù)即可
消費(fèi)者:如果消息在處理完成前就提交了offset,就有可能造成數(shù)據(jù)的丟失。解決辦法是在后臺提交位移前確保消息已經(jīng)被正常處理了,然后手動提交offset
## 35 索引失效
1.對索引使用左或者左右模糊匹配,特殊情況 name like “%a”,如果表里面沒有非索引字段,比如只有一個id主鍵和一個索引name字段,也是走索引,會是全掃描二級索引的 B+ 樹的方式查詢到數(shù)據(jù)
2.對索引使用函數(shù) where lenght(name) = 10
3.對索引使用表達(dá)式 where a+1=2
4.對索引隱式類型轉(zhuǎn)換 phone是varchar類似 where phone = 10000
5.聯(lián)合索引非最左匹配 where a = 1 and c = 2,特殊情況,如果只有id,(a,b,c)聯(lián)合索引,一共四個字段,where c = 1也會走索引
6.WHERE 子句中的 OR。在 WHERE 子句中,如果在 OR 前的條件列是索引列,而在 OR 后的條件列不是索引列,那么索引會失效。
count(*)=count(1)>count(主鍵索引)>count(字段) count(*)其實(shí)就是count(0),count(字段)會全表掃描,效率低
## 34 where a = 1 and c = 3 ,符合最左匹配嗎?(索引截?cái)?
MySQL 5.5 的話,前面 a 會走索引,在聯(lián)合索引找到主鍵值后,開始回表,到主鍵索引讀取數(shù)據(jù)行,Server 層從存儲引擎層獲取到數(shù)據(jù)行后,然后在 Server 層再比對 c 字段的值。
從 MySQL 5.6 之后,有一個索引下推功能,可以在存儲引擎層進(jìn)行索引遍歷過程中,對索引中包含的字段先做判斷,直接過濾掉不滿足條件的記錄,再返還給 Server 層,從而減少回表次數(shù)。
索引下推的大概原理是:截?cái)嗟淖侄尾粫?Server 層進(jìn)行條件判斷,而是會被下推到「存儲引擎層」進(jìn)行條件判斷(因?yàn)?c 字段的值是在 (a, b, c) 聯(lián)合索引里的),然后過濾出符合條件的數(shù)據(jù)后再返回給 Server 層。由于在引擎層就過濾掉大量的數(shù)據(jù),無需再回表讀取數(shù)據(jù)來進(jìn)行判斷,減少回表次數(shù),從而提升了性能。
## 35 count(*),count(1),count(主鍵字段),count(字段)
count(1)、 count(*)、 count(主鍵字段)在執(zhí)行的時候,如果表里存在二級索引,優(yōu)化器就會選擇二級索引進(jìn)行掃描,因?yàn)槎壦饕鎯Φ臎]有主鍵索引多。
所以,如果要執(zhí)行 count(1)、 count(*)、 count(主鍵字段) 時,盡量在數(shù)據(jù)表上建立二級索引,這樣優(yōu)化器會自動采用 key_len 最小的二級索引進(jìn)行掃描,相比于掃描主鍵索引效率會高一些。
再來,就是不要使用 count(字段) 來統(tǒng)計(jì)記錄個數(shù),因?yàn)樗男适亲畈畹?,會采用全表掃描的方式來統(tǒng)計(jì)。如果你非要統(tǒng)計(jì)表中該字段不為 NULL 的記錄個數(shù),建議給這個字段建立一個二級索引。
針對快照讀(普通 select 語句),是通過 MVCC 方式解決了幻讀。
針對當(dāng)前讀(select ... for update 等語句),是通過 next-key lock(記錄鎖+間隙鎖)方式解決了幻讀。
第一個例子:對于快照讀, MVCC 并不能完全避免幻讀現(xiàn)象。因?yàn)楫?dāng)事務(wù) A 更新了一條事務(wù) B 插入的記錄,那么事務(wù) A 前后兩次查詢的記錄條目就不一樣了,所以就發(fā)生幻讀。
第二個例子:對于當(dāng)前讀,如果事務(wù)開啟后,并沒有執(zhí)行當(dāng)前讀,而是先快照讀,然后這期間如果其他事務(wù)插入了一條記錄,那么事務(wù)后續(xù)使用當(dāng)前讀進(jìn)行查詢的時候,就會發(fā)現(xiàn)兩次查詢的記錄條目就不一樣了,所以就發(fā)生幻讀。
所以,MySQL 可重復(fù)讀隔離級別并沒有徹底解決幻讀,只是很大程度上避免了幻讀現(xiàn)象的發(fā)生。
要避免這類特殊場景下發(fā)生幻讀的現(xiàn)象的話,就是盡量在開啟事務(wù)之后,馬上執(zhí)行 select ... for update 這類當(dāng)前讀的語句,因?yàn)樗鼤τ涗浖?next-key lock,從而避免其他事務(wù)插入一條新記錄。
## 36 redis大key的影響
1.當(dāng) AOF 寫回策略配置了 Always 策略,如果寫入是一個大 Key,主線程在執(zhí)行 fsync() 函數(shù)的時候,阻塞的時間會比較久,因?yàn)楫?dāng)寫入的數(shù)據(jù)量很大的時候,數(shù)據(jù)同步到硬盤這個過程是很耗時的。
2.客戶端超時阻塞。由于 Redis 執(zhí)行命令是單線程處理,然后在操作大 key 時會比較耗時,那么就會阻塞 Redis,從客戶端這一視角看,就是很久很久都沒有響應(yīng)。
3.引發(fā)網(wǎng)絡(luò)阻塞。每次獲取大 key 產(chǎn)生的網(wǎng)絡(luò)流量較大,如果一個 key 的大小是 1 MB,每秒訪問量為 1000,那么每秒會產(chǎn)生 1000MB 的流量,這對于普通千兆網(wǎng)卡的服務(wù)器來說是災(zāi)難性的。
4.阻塞工作線程。如果使用 del 刪除大 key 時,會阻塞工作線程,這樣就沒辦法處理后續(xù)的命令。
5.內(nèi)存分布不均。集群模型在 slot 分片均勻情況下,會出現(xiàn)數(shù)據(jù)和查詢傾斜情況,部分有大 key 的 Redis 節(jié)點(diǎn)占用內(nèi)存多,QPS 也會比較大。
## 37 springcloud由以下?個核?組件構(gòu)成:
Eureka:各個服務(wù)啟動時,Eureka Client都會將服務(wù)注冊到Eureka Server,并且Eureka Client還可以反過來從Eureka Server拉取注冊表,從?知道其他服務(wù)在哪?
Ribbon:服務(wù)間發(fā)起請求的時候,基于Ribbon做負(fù)載均衡,從?個服務(wù)的多臺機(jī)器中選擇?臺
Feign:基于Feign的動態(tài)代理機(jī)制,根據(jù)注解和選擇的機(jī)器,拼接請求URL地址,發(fā)起請求
Hystrix:發(fā)起請求是通過Hystrix的線程池來?的,不同的服務(wù)?不同的線程池,實(shí)現(xiàn)了不同服務(wù)調(diào)?的隔離,避免了服務(wù)雪崩的問題
Zuul:如果前端、移動端要調(diào)?后端系統(tǒng),統(tǒng)?從Zuul?關(guān)進(jìn)?,由Zuul?關(guān)轉(zhuǎn)發(fā)請求給對應(yīng)的服務(wù)
## 38 synchronized關(guān)鍵字
synchronized關(guān)鍵字實(shí)現(xiàn)對象鎖,不管是實(shí)例方法還是靜態(tài)方法,我們都知道對象本身包括三部分吧,對象頭:Mark Word,用于存儲對象自身運(yùn)行時的數(shù)據(jù),如哈希碼(Hash Code),GC分代年齡,鎖狀態(tài)標(biāo)志,偏向線程ID、偏向時間戳等信息,它會根據(jù)對象的狀態(tài)復(fù)用自己的存儲空間。它是實(shí)現(xiàn)輕量級鎖和偏向鎖的關(guān)鍵。
實(shí)例數(shù)據(jù):存放類的屬性數(shù)據(jù)信息,包括父類的屬性信息。對齊填充,主要是內(nèi)存對齊,非必須。通過反編譯可以發(fā)現(xiàn),synchronized底層通過monitor對象實(shí)現(xiàn)的。它會關(guān)聯(lián)一個monitor對象,monitorenter,線程進(jìn)入就進(jìn)入數(shù)+1,擁有者可以重復(fù)進(jìn)入,執(zhí)行monitorexit,進(jìn)入數(shù)就減1,直到進(jìn)入數(shù)為0,放棄對monitor對象的擁有權(quán)
## 39 如何判斷一個方法區(qū)的類是無用的類
類需要同時滿足下面 3 個條件才能算是 “無用的類”,虛擬機(jī)可以對無用類進(jìn)行回收。
1.該類所有的實(shí)例都已經(jīng)被回收,也就是 Java 堆中不存在該類的任何實(shí)例。
2.加載該類的 ClassLoader 已經(jīng)被回收。
3.該類對應(yīng)的 java.lang.Class 對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。
## 40 springboot 自動裝配
springBoot在啟動的時候會調(diào)用run()方法去刷新容器,刷新容器的時候,會掃描classpath目錄下的META-INF/spring.factories文件,找到key為EnableAutoConfiguration的配置,并將其下面的配置類都加載到importselector中,隨后AutoConfigurationImportSelector會將ImportSelector中的所有標(biāo)明的類加載到IOC容器中
## 41Java OOM處理
1.一次性申請對象太多:更改申請對象數(shù)量,分頁之類的
2.內(nèi)存資源耗盡未釋放:找到未釋放的對象釋放
3.本身資源不夠:jmap -heap查看堆信息
Java visualVM
最快oom方法,new一個list,然后死循環(huán)往里面插入數(shù)據(jù)
1.線上出現(xiàn)oom
通過啟動參數(shù)-xx:+heapDumponoutofmemoryerror -xx:heapdumppath =
2.線上沒有出現(xiàn)oom
通過jmap dump:format=b,file=**.hprof 進(jìn)程id
通過visuakvm查看最多跟業(yè)務(wù)有關(guān)對象->找到GCRoot->查看線程棧
java堆用于存儲對象實(shí)例,我們只要不斷的創(chuàng)建對象,并且保證GC Roots到對象之間有可達(dá)路徑來 避免垃圾回收機(jī)制清除這些對象,就會在對象數(shù)量達(dá)到最大堆容量限制后產(chǎn)生內(nèi)存溢出異常。
出現(xiàn)這種異常,一般手段是先通過內(nèi)存映像分析工具(如Eclipse Memory Analyzer)對dump出來的 堆轉(zhuǎn)存快照進(jìn)行分析,重點(diǎn)是確認(rèn)內(nèi)存中的對象是否是必要的,先分清是因?yàn)閮?nèi)存泄漏(Memory Leak)還是內(nèi)存溢出(Memory Overflow)。
如果是內(nèi)存泄漏,可進(jìn)一步通過工具查看泄漏對象到GCRoots的引用鏈。于是就能找到泄漏對象是 通過怎樣的路徑與GC Roots相關(guān)聯(lián)并導(dǎo)致垃圾收集器無法自動回收。
如果不存在泄漏,那就應(yīng)該檢查虛擬機(jī)的參數(shù)(-Xmx與-Xms)的設(shè)置是否適當(dāng)。
##42接口優(yōu)化
1.批量處理:比如循環(huán)插入數(shù)據(jù)庫,可以改成批量插入,減少磁盤io,提升性能
2.異步處理:針對耗時很長,結(jié)果不是必須馬上返回的,可以使用異步處理,比如用消息隊(duì)列
3.空間換時間:使用緩存策略,減少數(shù)據(jù)庫的訪問
4.預(yù)處理:提前把要查詢的數(shù)據(jù)計(jì)算好,放入數(shù)據(jù)庫或者緩存,可以大大提升接口的查詢性能
5.池化思想:比如數(shù)據(jù)庫池,線程池等,避免重復(fù)創(chuàng)建對象或者建立連接
6.串行改并行:對于不一定要串行執(zhí)行的邏輯可以改并行處理
7.索引:構(gòu)建索引,加快查詢效率
8.避免大事務(wù):所謂大事務(wù)問題,就是運(yùn)行時間較長的事務(wù),由于事務(wù)一直不提交,會導(dǎo)致數(shù)據(jù)庫連接被占用,影響到別的請求訪問數(shù)據(jù)庫,影響別的接口性能。
會引起死鎖,鎖等待,回滾時間過長,高并發(fā)下數(shù)據(jù)庫連接池被占用,接口超時,數(shù)據(jù)庫主從延時。
1. RPC調(diào)用不放到事務(wù)里面
2. 查詢操作盡量放到事務(wù)之外
3. 事務(wù)中避免處理太多數(shù)據(jù)
4.盡量少用@transation
5.非事務(wù)代碼挪出來
6.異步處理
9.優(yōu)化代碼邏輯
10.優(yōu)化sql
11.控制鎖的粒度:避免鎖的粒度過粗
##43工作中redis怎么使用的?
1.可以用redis做分布式鎖和分布式id
2.可以用redis做緩存,做緩存也有兩種用法,第一種就是查詢數(shù)據(jù)的時候如果緩存里有就用緩存,如果沒有就從數(shù)據(jù)庫查,再放到緩存中去。更新的時候先更新數(shù)據(jù)庫,再刪除緩存。第二種就是redis做數(shù)據(jù)源,比如我們是做教育的,有很多課程信息變動很少,而且有很多樹形結(jié)構(gòu),如果每次都從數(shù)據(jù)庫查查會很慢。
##44MySQL索引很慢的原因
1.查詢sql有問題,查詢字段太多,或者join了很多張表??梢詢?yōu)化sql
2.索引區(qū)分度不高,導(dǎo)致命中的數(shù)據(jù)范圍很大,可以優(yōu)化查詢條件和索引
3.數(shù)據(jù)庫連接不夠,鎖等待,或者數(shù)據(jù)庫自身io或者cpu很高,或者網(wǎng)絡(luò)
不太行。可以結(jié)合日志和業(yè)務(wù)排查。
4.優(yōu)化器基于成本,選錯了索引,明明應(yīng)該走a索引,但是走了b,如果確定a索引更快,可以用force index
##45Kafka為什么這么快
1.磁盤的順序讀寫:數(shù)據(jù)寫入是順序?qū)懭氲?,避免了磁盤隨機(jī)io尋址讀取
2.稀疏索引:kafka并不是對所有的數(shù)據(jù)都建立索引,而是通過稀疏索引的方式建立索引,然后再通過二分查找算法提高查詢效率
3.批量文件壓縮:kafka是不會刪除文件的,它是通過批量文件壓縮,將多次插入的相同的key對應(yīng)的value合并成為最后一次插入的value,從而對文件進(jìn)行合理壓縮,減少網(wǎng)絡(luò)io的損耗。
4.零拷貝機(jī)制:正常情況文件的讀取是從磁盤拷貝到內(nèi)核緩沖區(qū),然后再從內(nèi)核緩沖區(qū)到用戶緩沖區(qū),再從用戶緩沖區(qū)到socket緩沖區(qū),最后到網(wǎng)卡。kafka通過使用java nio里面的transferTo方法,最終調(diào)用的是linux的sendfile函數(shù)實(shí)現(xiàn)零拷貝。
##46transaction失效原因
1.作用在非public修飾的方法上。攔截器攔截后,發(fā)現(xiàn)不是作用在public修飾的方法上,則不會獲取@Transactional 的屬性配置信息。
2.@Transactional 注解屬性 propagation 設(shè)置錯誤,比如以非事務(wù)的方式運(yùn)行,存在事務(wù)則掛起
3.@Transactional 注解屬性 rollbackFor 設(shè)置錯誤,如果在事務(wù)中拋出其他類型的異常,但卻期望 Spring 能夠回滾事務(wù),就需要指定rollbackFor屬性。
4.同一個類中方法調(diào)用,導(dǎo)致@Transactional失效
5. 異常被你的 catch“吃了”導(dǎo)致@Transactional失效
6.數(shù)據(jù)庫引擎不支持事務(wù)