1.可重復讀,已提交讀,這兩個隔離級別表現的現象是什么,區別是什么樣的?
可重復讀:表示整個事務看到的事務和開啟后的事務能看到的數據是一致的,既然數據是一致的,所以不存在不可重復讀。而且不會讀取其他事務修改的數據,也就是不存在臟讀。而對同一個批數據,可能會存在添加的情況,所以可能會存在幻讀的情況。
讀已提交:該隔離級別只能讀取到其他事務提交后的數據,所以不存在臟讀(是指當一個事務正在訪問數據并對數據進行了修改,而這種修改還沒有提交到數據庫中時,另一個事務也訪問了這個數據并使用了這個數據。因為這些數據是未提交的,所以另一個事務讀到的數據是“臟數據”,基于這些數據所做的操作可能是不正確的。)。但是在第一次讀取數據后,其他事務修改后數據并提交事務,此時事務讀取到數據就和第一次讀到的數據不一致了,也就存在不可重復讀。同時其他事務可以添加多條數據,也存在幻讀(是指當事務不是獨立執行時發生的現象,例如第一個事務對一個表中的數據進行修改,這種修改涉及到表中的全部數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那么,以后就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺一樣。)。
2.數據管理里,數據文件大體分成哪幾種數據文件?
例如:我們創建student數據庫,然后會在?/var/lib/mysql/ 目錄里面創建一個以 student 為名的目錄,然后保存表結構(student.frm)和表數據(student.ibd)的文件都會存放在這個目錄里。
3.日志文件分成了哪幾種?
-
redo log 重做日志,是 Innodb 存儲引擎層生成的日志,實現了事務中的持久性,主要用于掉電等故障恢復;
-
undo log 回滾日志,是 Innodb 存儲引擎層生成的日志,實現了事務中的原子性,主要用于事務回滾和 MVCC。
-
bin log 二進制日志,是 Server 層生成的日志,主要用于數據備份和主從復制;
-
relay log 中繼日志,用于主從復制場景下,slave通過io線程拷貝master的bin log后本地生成的日志
-
慢查詢日志,用于記錄執行時間過長的sql,需要設置閾值后手動開啟
log4j的日志級別?
從高到低依次是OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、ALL
ALL 最低等級的,用于打開所有日志記錄。
TRACE很低的日志級別,一般不會使用
ERROR指出雖然發生錯誤事件,但仍然不影響系統的繼續運行。打印錯誤和異常信息,如果不想輸出太多的日志,可以使用這個級別。
FATAL 指出每個嚴重的錯誤事件將會導致應用程序的退出。這個級別比較高了。重大錯誤,這種級別你可以直接停止程序了。
WARN 表明會出現潛在錯誤的情形,有些信息不是錯誤信息,但是也要給程序員的一些提示。
INFO 消息在粗粒度級別上突出強調應用程序的運行過程。打印一些你感興趣的或者重要的信息,這個可以用于生產環境中輸出程序運行的一些重要信息,但是不能濫用,避免打印過多的日志。
DEBUG 指出細粒度信息事件對調試應用程序是非常有幫助的,主要用于開發過程中打印一些運行信息。
OFF 最高等級的,用于關閉所有日志記錄。
如果將log level設置在某一個級別上,那么比此級別優先級高的log都能打印出來。例如,如果設置優先級為WARN,那么OFF、FATAL、ERROR、WARN 4個級別的log能正常輸出,而INFO、DEBUG、TRACE、 ALL級別的log則會被忽略。Log4j建議只使用四個級別,優先級從高到低分別是ERROR、WARN、INFO、DEBUG。
從我們實驗的結果可以看出,log4j默認的優先級為ERROR或者WARN(實際上是ERROR)。
4.?說下MVCC機制的原理?
?MVCC就是多版本并發控制,實現了讀寫的并發控制,解決了幻讀的問題(一個數據前后兩次讀取到的數據不一致,在RR(可重復讀)的事務隔離級別下,Innodb采用了一個MVCC的機制和LBCC去解決幻讀問題,MVCC類似于一種樂觀鎖的設計,簡單來說就是針對每個事務生成一個事務版本,然后通過undo的版本鏈進行管理,并且在MVCC里面規定了高版本能夠看到低版本的事務變更,低版本看不到高版本的事務變更,從而去實現不同版本之間的事務隔離,解決了幻讀的問題)。
5.索引的類型有哈希索引,B+樹索引,而hash索引的時間復雜度是o1,那為什么我們一般情況下不使用哈希索引,而使用b+樹索引呢?
哈希索引的key是經過hash運算得出的,即跟實際數據的值沒有關系,因此哈希索引不適用于范圍查詢和排序操作;容易導致全表掃描,因為可能存在不同的key經過hash運算后值相同;索引列上的值相同的話,易造成hash沖突,效率低下。
MySQL的索引為什么使用B+樹而不使用跳表
B+樹是多叉平衡搜索樹,扇出高,只需要3層左右就能存放2kw左右的數據,同樣情況下跳表則需要24層左右,假設層高對應磁盤IO,那么B+樹的讀性能會比跳表要好,因此mysql選了B+樹做索引。redis的讀寫全在內存里進行操作,不涉及磁盤IO,同時跳表實現簡單,相比B+樹、AVL樹、少了旋轉樹結構的開銷,因此redis使用跳表來實現ZSET,而不是樹結構。
B樹和B+樹的區別?
1.關鍵字的數量不同;B+樹中分支結點有m個關鍵字,其葉子結點也有m個,其關鍵字只是起到了一個索引的作用,但是B樹雖然也有m個子結點,但是其只擁有m-1個關鍵字。
2.存儲的位置不同;B+樹中的數據都存儲在葉子結點上,也就是其所有葉子結點的數據組合起來就是完整的數據,但是B樹的數據存儲在每一個結點中,并不僅僅存儲在葉子結點上。
3.分支結點的構造不同;B+樹的分支結點僅僅存儲著關鍵字信息和兒子的指針(這里的指針指的是磁盤塊的偏移量),也就是說內部結點僅僅包含著索引信息。
4.查詢不同;B樹在找到具體的數值以后,則結束,而B+樹則需要通過索引找到葉子結點中的數據才結束,也就是說B+樹的搜索過程中走了一條從根結點到葉子結點的路徑。
6.?對一個慢sql怎么去排查?
可通過開啟mysql的慢日志查詢,設置好時間閾值,進行捕獲慢 sql;針對慢 sql,進行 explian 去查看執行計劃。
7.索引字段是不是建的越多越好?
?不是,建的的越多會占用越多的空間,而且在寫入頻繁的場景下,對于B+樹的維護所付出的性能消耗也會越大。
8.?http協議的報文的格式有了解嗎?
分為請求報文和響應報文。
請求報文:
-
請求行:包含請求方法、請求目標(URL或URI)和HTTP協議版本。
-
請求頭部:包含關于請求的附加信息,如Host、User-Agent、Content-Type等。
-
空行:請求頭部和請求體之間用空行分隔。
-
請求體:可選,包含請求的數據,通常用于POST請求等需要傳輸數據的情況。
響應報文:?
-
狀態行:包含HTTP協議版本、狀態碼和狀態信息。
-
響應頭部:包含關于響應的附加信息,如Content-Type、Content-Length等。
-
空行:響應頭部和響應體之間用空行分隔。
-
響應體:包含響應的數據,通常是服務器返回的HTML、JSON等內容。
?9.http常用的狀態碼?
常見的具體狀態碼有:200:請求成功;301:永久重定向;302:臨時重定向;404:無法找到此頁面;405:請求的方法類型不支持;500:服務器內部出錯。
10.MyBatis運用了哪些常見的設計模式?
-
建造者模式(Builder),如:SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder等;
-
工廠模式,如:SqlSessionFactory、ObjectFactory、MapperProxyFactory;
-
單例模式,例如ErrorContext和LogFactory;
-
代理模式,Mybatis實現的核心,比如MapperProxy、ConnectionLogger,用的jdk的動態代理;
-
模板方法模式,例如BaseExecutor和SimpleExecutor,例如IntegerTypeHandler;
-
適配器模式,例如Log的Mybatis接口和它對jdbc、log4j等各種日志框架的適配實現;
11.?MyBatis中創建了一個Mapper接口,在寫一個xml文件,java的接口是要實現的,為什么這沒有實現呢?
因為執行Sql所需要的所有的JDBC操作都在Mybatis的MapperProxy中實現了,所以不需要實現類。
12.與傳統的JDBC相比,MyBatis的優點?
-
基于 SQL 語句編程,相當靈活,不會對應用程序或者數據庫的現有設計造成任何影響,SQL 寫在 XML 里,解除 sql 與程序代碼的耦合,便于統一管理;提供 XML 標簽,支持編寫動態 SQL 語句,并可重用。
-
與 JDBC 相比,減少了 50%以上的代碼量,消除了 JDBC 大量冗余的代碼,不 需要手動開關連接;
-
很好的與各種數據庫兼容,因為 MyBatis 使用 JDBC 來連接數據庫,所以只要 JDBC 支持的數據庫 MyBatis 都支持。
-
能夠與 Spring 很好的集成,開發效率高
-
提供映射標簽,支持對象與數據庫的 ORM 字段關系映射;提供對象關系映射 標簽,支持對象關系組件維護。
13.?JDBC連接數據庫的步驟?
-
加載數據庫驅動程序:在使用JDBC連接數據庫之前,需要加載相應的數據庫驅動程序。可以通過 Class.forName("com.mysql.jdbc.Driver") 來加載MySQL數據庫的驅動程序。不同數據庫的驅動類名會有所不同。
-
建立數據庫連接:使用 DriverManager 類的 getConnection(url, username, password) 方法來連接數據庫,其中url是數據庫的連接字符串(包括數據庫類型、主機、端口等)、username是數據庫用戶名,password是密碼。
-
創建 Statement 對象:通過 Connection 對象的 createStatement() 方法創建一個 Statement 對象,用于執行 SQL 查詢或更新操作。
-
執行 SQL 查詢或更新操作:使用 Statement 對象的 executeQuery(sql) 方法來執行 SELECT 查詢操作,或者使用 executeUpdate(sql) 方法來執行 INSERT、UPDATE 或 DELETE 操作。
-
處理查詢結果:如果是 SELECT 查詢操作,通過 ResultSet 對象來處理查詢結果。可以使用 ResultSet 的 next() 方法遍歷查詢結果集,然后通過 getXXX() 方法獲取各個字段的值。
-
關閉連接:在完成數據庫操作后,需要逐級關閉數據庫連接相關對象,即先關閉 ResultSet,再關閉 Statement,最后關閉 Connection。
import java.sql.*;public class Main {public static void main(String[] args) {try {// 加載數據庫驅動程序Class.forName("com.mysql.cj.jdbc.Driver");// 建立數據庫連接Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "username", "password");// 創建 Statement 對象Statement statement = connection.createStatement();// 執行 SQL 查詢ResultSet resultSet = statement.executeQuery("SELECT * FROM mytable");// 處理查詢結果while (resultSet.next()) {// 處理每一行數據}// 關閉資源resultSet.close();statement.close();connection.close();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();}}
}
?14.怎么理解SpringIoc(解耦)
IOC:控制反轉 其本身是一個容器,在之前的編碼過程中,我們需要什么對象去創建什么對象,由程序員自己來控制對象,有了IOC之后,IOC容器來控制對象,spring提供了一種方式,這種方式就是spring提供一個容器,我們在xml文件里定義各個對象的依賴關系,由容器完成對象的構建,當我們java代碼里需要使用某個實例的時候就可以從容器里獲取,那么對象的構建操作就被spring容器接管,所以它被稱為控制反轉,控制反轉的意思就是本來屬于java程序里構建對象的功能交由容器接管,依賴注入就是當程序要使用某個對象時候,容器會把它注入到程序里,這就叫做依賴注入。
IOC有三種注入方式︰構造器注入、setter方法注入、根據注解注入
?15.如果讓你設計一個SpringIoc,你覺得會從哪些方面考慮這個設計?
-
Bean的生命周期管理:需要設計Bean的創建、初始化、銷毀等生命周期管理機制,可以考慮使用工廠模式和單例模式來實現。
-
依賴注入:需要實現依賴注入的功能,包括屬性注入、構造函數注入、方法注入等,可以考慮使用反射機制和XML配置文件來實現。
-
Bean的作用域:需要支持多種Bean作用域,比如單例、原型、會話、請求等,可以考慮使用Map來存儲不同作用域的Bean實例。
-
AOP功能的支持:需要支持AOP功能,可以考慮使用動態代理機制和切面編程來實現。
-
異常處理:需要考慮異常處理機制,包括Bean創建異常、依賴注入異常等,可以考慮使用try-catch機制來處理異常。
-
配置文件加載:需要支持從不同的配置文件中加載Bean的相關信息,可以考慮使用XML、注解或者Java配置類來實現。
16.?Spring給我們提供了很多擴展點,這些有了解嗎?
-
BeanFactoryPostProcessor:允許在Spring容器實例化bean之前修改bean的定義。常用于修改bean屬性或改變bean的作用域。
-
BeanPostProcessor:可以在bean實例化、配置以及初始化之后對其進行額外處理。常用于代理bean、修改bean屬性等。
-
PropertySource:用于定義不同的屬性源,如文件、數據庫等,以便在Spring應用中使用。
-
ImportSelector和ImportBeanDefinitionRegistrar:用于根據條件動態注冊bean定義,實現配置類的模塊化。
-
Spring MVC中的HandlerInterceptor:用于攔截處理請求,可以在請求處理前、處理中和處理后執行特定邏輯。
-
Spring MVC中的ControllerAdvice:用于全局處理控制器的異常、數據綁定和數據校驗。
-
Spring Boot的自動配置:通過創建自定義的自動配置類,可以實現對框架和第三方庫的自動配置。
-
自定義注解:創建自定義注解,用于實現特定功能或約定,如權限控制、日志記錄等。
17.?大致了解SpringMVC的處理流程嗎?
-
用戶發送請求至前端控制器DispatcherServlet
-
DispatcherServlet收到請求調用處理器映射器HandlerMapping。
-
處理器映射器根據請求url找到具體的處理器,生成處理器對象及處理器攔截器(如果有則生成)一并返回給DispatcherServlet。
-
DispatcherServlet根據處理器Handler獲取處理器適配器HandlerAdapter執行HandlerAdapter處理一系列的操作,如:參數封裝,數據格式轉換,數據驗證等操作
-
Handler處理器執行相應的業務邏輯,生成ModelAndView。
-
HandlerAdapter將處理器的執行結果包裝成ModelAndView并返回到DispatcherServlet。
-
DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器
-
視圖解析器根據ModelAndView找到對應的視圖進行渲染。
-
將渲染后的視圖返回給客戶端。
18.?Handlermapping 和 handleradapter有了解嗎?
HandlerMapping負責將請求映射到處理器(Controller)。根據請求的URL、請求參數等信息,找到處理請求的 Controller。
HandlerAdapter負責調用處理器(Controller)來處理請求。處理器(Controller)可能有不同的接口類型(Controller接口、HttpRequestHandler接口等),HandlerAdapter根據處理器的類型來選擇合適的方法來調用處理器。
19.?SpringAOP主要想解決什么問題
AOP(是IOC的一個擴展功能,先有的IOC,再有的AOP,只是在IOC的整個流程中新增的一個擴展點而已),一般稱為面向切面,為解耦而生,作為面向對象的一種補充,用于將那些與業務無關,但卻對多個對象產生影響的公共行為和邏輯,抽取并封裝為一個可重用的模塊,這個模塊被命名為“切面”(Aspect) ,減少系統中的重復代碼,降低了模塊間的耦合度,同時提高了系統的可維護性。可用于權限認證、日志、事務處理。任何一個系統都是由不同的組件組成的,每個組件負責-塊特定的功能,當然會存在很多組件是跟業務無關的,例如日志事務、權限等核心服務組件,這些核心服務組件經常融入到具體的業務邏輯中,如果我們為每一個具體業務邏輯操作都添加這樣的代碼,很明顯代碼冗余太多,因此我們需要將這些公共的代碼邏輯抽象出來變成一個切面,然后注入到且標對象(具體業務)中去,AOP正是基于這樣的一個思路實現的,通過動本代理的方式,將需要注入切面的對象進行代理,在進行調用的時候,將公共的邏輯直接添加進去,而不需要修改原有業務的邏輯代碼,只需要在原來的業務邏輯基礎之上做一些增強功能即可。
SpringAOP的原理了解嗎
Spring AOP的實現依賴于動態代理技術。動態代理是在運行時動態生成代理對象,而不是在編譯時。它允許開發者在運行時指定要代理的接口和行為,從而實現在不修改源碼的情況下增強方法的功能。Spring AOP支持兩種動態代理:
-
基于JDK的動態代理:使用java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口實現。這種方式需要代理的類實現一個或多個接口。
-
基于CGLIB的動態代理:當被代理的類沒有實現接口時,Spring會使用CGLIB庫生成一個被代理類的子類作為代理。CGLIB(Code Generation Library)是一個第三方代碼生成庫,通過繼承方式實現代理。
?動態代理和靜態代理的區別
代理是一種常用的設計模式,目的是:為其他對象提供一個代理以控制對某個對象的訪問,將兩個類的關系解耦。代理類和委托類都要實現相同的接口,因為代理真正調用的是委托類的方法。區別:
-
靜態代理:由程序員創建或者是由特定工具創建,在代碼編譯時就確定了被代理的類是一個靜態代理。靜態代理通常只代理一個類;
-
動態代理:在代碼運行期間,運用反射機制動態創建生成。動態代理代理的是一個接口下的多個實現類。
代理模式和適配器模式有什么區別?
-
目的不同:代理模式主要關注控制對對象的訪問,而適配器模式則用于接口轉換,使不兼容的類能夠一起工作。
-
結構不同:代理模式一般包含抽象主題、真實主題和代理三個角色,適配器模式包含目標接口、適配器和被適配者三個角色。
-
應用場景不同:代理模式常用于添加額外功能或控制對對象的訪問,適配器模式常用于讓不兼容的接口協同工作。
?20.java線程的生命周期有了解嗎?
新建(New):當線程對象對創建后,它只是進入了新建狀態,此時它已經有了內存空間和其他資源,但線程還沒開始執行。
就緒(Runnable):當調用線程對象的start()方法,線程即進入就緒狀態。處于這個狀態的線程位于可運行線程池中,等待獲取CPU的使用權。
運行(Running):當CPU開始調度處于就緒狀態的線程,使得線程進入運行狀態。此時,它才真正開始執行run()方法里面的代碼。
阻塞(Blocked):一個正在運行的線程如果調用了某些方法,如等待輸入/輸出完成,或者等待獲取某個同步鎖,則線程會進入阻塞狀態,并且釋放CPU使用權。
等待(Waiting):一個正在運行的線程可以調用obj.wait()方法,使得當前線程進入等待狀態。進入這個狀態后,線程會釋放掉它所持有的所有資源,而且不會去爭奪鎖。
終止(Terminated):線程會在以下幾種情況下進入終止狀態:
21.使用多線程要注意哪些問題?
要保證多線程的允許是安全,不要出現數據競爭造成的數據混亂的問題。
Java的線程安全在三個方面體現:
-
原子性:提供互斥訪問,同一時刻只能有一個線程對數據進行操作,在Java中使用了atomic和synchronized這兩個關鍵字來確保原子性;
-
可見性:一個線程對主內存的修改可以及時地被其他線程看到,在Java中使用了synchronized和volatile這兩個關鍵字確保可見性;
-
有序性:一個線程觀察其他線程中的指令執行順序,由于指令重排序,該觀察結果一般雜亂無序,在Java中使用了happens-before原則來確保有序性。
22.?保證數據的一致性有哪些方案呢?
-
事務管理:使用數據庫事務來確保一組數據庫操作要么全部成功提交,要么全部失敗回滾。通過ACID(原子性、一致性、隔離性、持久性)屬性,數據庫事務可以保證數據的一致性。
-
鎖機制:使用鎖來實現對共享資源的互斥訪問。在 Java 中,可以使用 synchronized 關鍵字、ReentrantLock 或其他鎖機制來控制并發訪問,從而避免并發操作導致數據不一致。
-
版本控制:通過樂觀鎖的方式,在更新數據時記錄數據的版本信息,從而避免同時對同一數據進行修改,進而保證數據的一致性。
23.?線程池有了解嗎?線程池大概的原理?
線程池是為了減少頻繁的創建線程和銷毀線程帶來的性能損耗。
線程池分為核心線程池,線程池的最大容量,還有等待任務的隊列,提交一個任務,如果核心線程沒有滿,就創建一個線程,如果滿了,就是會加入等待隊列,如果等待隊列滿了,就會增加線程,如果達到最大線程數量,如果都達到最大線程數量,就會按照一些丟棄的策略進行處理。
-
corePoolSize:線程池核心線程數量。默認情況下,線程池中線程的數量如果 <= corePoolSize,那么即使這些線程處于空閑狀態,那也不會被銷毀。
-
maximumPoolSize:線程池中最多可容納的線程數量。當一個新任務交給線程池,如果此時線程池中有空閑的線程,就會直接執行,如果沒有空閑的線程且當前線程池的線程數量小于corePoolSize,就會創建新的線程來執行任務,否則就會將該任務加入到阻塞隊列中,如果阻塞隊列滿了,就會創建一個新線程,從阻塞隊列頭部取出一個任務來執行,并將新任務加入到阻塞隊列末尾。如果當前線程池中線程的數量等于maximumPoolSize,就不會創建新線程,就會去執行拒絕策略。
-
keepAliveTime:當線程池中線程的數量大于corePoolSize,并且某個線程的空閑時間超過了keepAliveTime,那么這個線程就會被銷毀。
-
unit:就是keepAliveTime時間的單位。
-
workQueue:工作隊列。當沒有空閑的線程執行新任務時,該任務就會被放入工作隊列中,等待執行。
-
threadFactory:線程工廠。可以用來給線程取名字等等
-
handler:拒絕策略。當一個新任務交給線程池,如果此時線程池中有空閑的線程,就會直接執行,如果沒有空閑的線程,就會將該任務加入到阻塞隊列中,如果阻塞隊列滿了,就會創建一個新線程,從阻塞隊列頭部取出一個任務來執行,并將新任務加入到阻塞隊列末尾。如果當前線程池中線程的數量等于maximumPoolSize,就不會創建新線程,就會去執行拒絕策略。
?24.ArrayList和LinkedList有什么區別
-
底層數據結構:ArrayList使用數組作為底層數據結構,而LinkedList使用雙向鏈表作為底層數據結構
-
隨機訪問性能:ArrayList支持通過索引直接訪問元素,因為底層數組的連續存儲特性,所以時間復雜度為O(1)。而LinkedList需要從頭或尾部開始遍歷鏈表,時間復雜度為O(n)。
-
插入和刪除操作:ArrayList在尾部插入和刪除元素的時間復雜度為O(1),因為它只需要調整數組的長度即可。但在中間或頭部插入和刪除元素時,需要將后續元素進行移動,時間復雜度為O(n)。而LinkedList在任意位置插入和刪除元素的時間復雜度為O(1),因為只需要調整節點的指針即可。
-
內存占用:ArrayList在每個元素中都存儲了實際的數據,而LinkedList在每個節點中存儲了數據和前后節點的指針。因此,相同數量的元素情況下,LinkedList通常比ArrayList占用更多的內存空間。
25.?ArrayList線程安全嗎?把ArrayList變成線程安全有哪些方法?
不是線程安全的,ArrayList變成線程安全的方式有:?
26.對面向對象的理解??
封裝:將對象屬性和方法的代碼封裝到一個模塊中,也就是一個類中,保證軟件內部具有優良的模塊性的基礎,實現“高內聚,低耦合”。
繼承:在已經存在的類的基礎上進行,將其定義的內容作為自己的內容,并可以加入新的內容或者修改原來的方法適合特殊的需要。
多態:同一操作作用于不同的對象,可以有不同的解釋,產生不同的執行結果,就是多態,簡單點說:就是用父類的引用指向子類的對象。目的:提高代碼復用性,解決項目中緊耦合問題,提高可擴展性。
-
讀書中遇到最難的技術是什么,怎么克服的?
-
有沒有什么強項在面試中還沒有展現的?