BAT 大厂Java 面试题集锦之核心篇附参考答案
核心篇
數據結構與算法
網路:TCP/IP,?HTTP
操作系統, 文件, shell, CPU, IO, epoll, 非阻塞IO,
進程/線程/協程,鎖
HashMap, ConcurrentHashMap實現原理, 鏈表, 紅黑樹
git
maven
緩存:各種緩存, redis zset與跳躍表
高并發,高可用,降級,限流,容災,弱依賴
分布式框架
架構設計, clean code, DDD, API 設計
數據庫, 索引與B+樹,SQL調優, WAL, 2階段提交, CAP與BASE
class字節碼,序列化, JVM, 內存模型, GC
多線程與并發編程
Java, Kotlin, JavaScript, 編程語言
Spring, SpringBoot,?Spring MVC, MyBatis
OOP?,??FP, 響應式
大數據, 分布式文件存儲,MapReduce, Hadoop, Spark, Storm,?Flink, AI
.....
首先我們需要明白一個事實,招聘的一個很關鍵的因素是在給自己找未來的同事,同級別下要找比自己優秀的人,面試是一個雙向選擇的過程,也是一個將心比心去溝通的過程。
就像我們有的人感覺自己很牛逼,但是拿不到offer,而其他的人菜的一筆,卻可以拿到offer,我們稱之為玄學offer,遇到這種情況大家也不要感覺到有什么不可描述的心情,一切隨緣即可!
必備Java 面試題
小編這里可是有Java面試題參考答案的喲,需要各位小伙伴下來逐一學習!
一、開場白
簡單的介紹一下自己的工作經歷與職責,在校或者工作中主要的工作內容,主要負責的內容;(你的信息一清二白的寫在簡歷上,能答出來的最好寫在上面,模棱兩可不是很清楚的最好不要寫,否則會被問的很尷尬)
介紹下自己最滿意的,有技術亮點的項目或平臺,重點介紹下自己負責那部分的技術細節;(主要對自己做過的事情是否有清晰的描述)
二、Java基礎
什么是字符串常量池?
String為什么是不可變的?
String s = new String("xyz");究竟產生了幾個對象,從JVM角度談談?
String拼接字符串效率低,你知道原因嗎?
你真的了解String的常見API嗎?
Java中的subString()真的會引起內存泄露么?
淺析Java中的final關鍵字?
淺析Java中的static關鍵字?
你對Java中的volatile關鍵字了解多少?
i++是線程安全的嗎?如何解決線程安全性?
從字節碼角度深度解析 i++ 和 ++i 線程安全性原理?
請談談什么是CAS?
從源碼角度看看ArrayList的實現原理?
手寫LinkedList的實現,徹底搞清楚什么是鏈表?
Java中方法參數的傳遞規則?
Java中throw和throws的區別是什么?
重載和重寫的區別?
手寫ArrayList的實現,在筆試中如何過關斬將?
finally語句塊你踩過哪些坑?
為什么重寫equals方法需同時重寫hashCode方法?
equals() 與 == 的區別?
StringBuffer和StringBuilder的區別,從源碼角度分析?
你知道HashMap的數據結構嗎?
為何HashMap的數組長度一定是2的次冪?
HashMap何時擴容以及它的擴容機制?
HashMap的key一般用字符串,能用其他對象嗎?
HashMap的key和value都能為null么?如果key能為null,那么它是怎么樣查找值的?
HashMap是線程安全的嗎?如何實現線程安全?
從源碼角度分析HashSet實現原理?
HashTable與HashMap的實現原理有什么不同?
String方法intern() 你真的會用嗎?
什么是自動拆裝箱?
String.valueOf和Integer.toString的區別?
三、Java多線程
線程的生命周期包括哪幾個階段?
多線程有幾種實現方式?
請談談什么是進程,什么是線程?
啟動線程是用start()方法還是run()方法?
說說線程安全問題,什么實現線程安全,如何實現線程安全?
sychronized和Lock的區別?
sleep()和wait()的區別?
深入分析ThreadLocal的實現原理?
你看過AbstractQueuedSynchronizer源碼閱讀嗎,請說說實現原理?
談談對synchronized的偏向鎖、輕量級鎖、重量級鎖的理解?
通過三種方式實現生產者消費者模式?
JVM層面分析sychronized如何保證線程安全的?
JDK層面分析sychronized如何保證線程安全的?
如何寫一個線程安全的單例?
通過AQS實現一個自定義的Lock?
ThreadLocal什么時候會出現OOM的情況?為什么?
為什么wait, notify 和 notifyAll這些方法不在thread類里面?
你真的理解CountDownLatch與CyclicBarrier使用場景嗎?
出現死鎖,如何排查定位問題?
notify和notifyAll的區別?
線程池啟動線程submit和execute有什么不同?
SimpleDateFormat是線程安全的嗎?如何解決?
請談談ConcurrentHashmap底層實現原理?
使用synchronized修飾靜態方法和非靜態方法有什么區別?
當一個線程進入一個對象的一個synchronized方法后,其它線程是否可進入此對象的其方法?
線程池的原理,為什么要創建線程池?創建線程池的方式?
創建線程池有哪幾個核心參數? 如何合理配置線程池的大小?
synchronized修飾的靜態方法和非靜態方法有什么區別?
四、Java Web
什么是Servlet,Servlet生命周期方法?
什么Session和Cookie,它們之間有什么聯系?
JSP的八個隱含對象有哪些?
JSP的四個域對象的作用范圍?
Post和Get請求的區別?
轉發和重定向有什么區別?
JSP自定義標簽,如何實現循環打印功能?
Http1.0和Http1.1的區別是什么?
攔截器與過濾器的區別?
五、JVM面試題
JVM內存區域如何劃分?
JVM堆中對象是如何創建的?
JVM對象的結構?
JVM垃圾回收-如何判斷對象是否是垃圾對象?
JVM垃圾回收算法有哪些?
JVM垃圾收集器有哪些?
JVM內存是如何分配的?
從一道面試題分析類的加載過程?
JVM雙親委派機制?
JVM可以作為GC Root的對象有哪些?
請寫出幾段可以導致內存溢出、內存泄漏、棧溢出的代碼?
哪些情況會導致Full GC?
頻繁GC問題或內存溢出問題,如何定位?
六、SQL性能優化
數據庫三范式是什么?
數據庫的事務、ACID及隔離級別?
不考慮事務的隔離性,容易產生哪三種情況?
數據庫連接池原理?
什么是B-Tree?
什么是B+Tree?
MySQL數據庫索引結構?
什么是索引?什么條件適合建立索引?什么條件不適合建立索引?
索引失效的原因有哪些?如何優化避免索引失效?
MySQL如何啟動慢查詢日志?
MySQL如何使用show Profile進行SQL分析?
一條執行慢的SQL如何進行優化,如何通過Explain+SQL分析性能?
什么是行鎖、表鎖、讀鎖、寫鎖,說說它們各自的特性?
什么情況下行鎖變表鎖?
什么情況下會出現間隙鎖?
談談你對MySQL的in和exists用法的理解?
MySQL的數據庫引擎有哪些,如何確定在項目中要是用的存儲引擎?
count(*)、count(列名)和count(1)的區別?
union和union all的區別?
七、Spring框架
Spring的IOC和AOP機制?
Spring中Autowired和Resource關鍵字的區別?
依賴注入的方式有幾種,各是什么?
Spring容器對Bean組件是如何管理的?
Spring容器如何創建?
Spring事務分類?
Spring事務的傳播特性?
Spring事務的隔離級別?
Spring的通知類型有哪些?
八、SpringMVC框架
SpringMVC完整工作流程,熟讀源碼流程?
SpringMVC如何處理JSON數據?
SpringMVC攔截器原理,如何自定義攔截器?
SpringMVC如何將請求映射定位到方法上面?結合源碼闡述?
SpringMVC常見注解有哪些?
SpringMVC容器和Spring容器的區別?
SpringMVC的控制器是不是單例模式,如果是,有什么問題,怎么解決?
九、MyBatis框架
MyBatis中#和$的區別?
MyBatis一級緩存原理以及失效情況?
MyBatis二級緩存的使用?
MyBatis攔截器原理?
看過MyBatis源碼嗎,請說說它的工作流程?
十、Java高級部分
Dubbo負載均衡策略?
Dubbo中Zookeeper做注冊中心,如果注冊中心集群都掛掉,發布者和訂閱者之間還能通信么?
Dubbo完整的一次調用鏈路介紹?
請說說SpringBoot自動裝配原理?
有用過SpringCloud嗎,請說說SpringCloud和Dubbo有什么不一樣?
什么是WebService,如何基于WebService開發接口?
談談項目中分布式事務應用場景?
使用Redis如何實現分布式鎖?
請談談單點登錄原理?
Tomcat如何優化?
后臺系統怎么防止請求重復提交?
Linux常見命令有哪些?
請說說什么是Maven的依賴、繼承以及聚合?
Git暫存區和工作區的區別?
Git如何創建、回退以及撤銷版本?
常見的設計模式有哪些?
十一、其他
看過哪些源代碼?然后會根據你說的源碼問一些細節的問題?(這里主要考察面試者是否對技術有鉆研的精神,還是只停留在表面,還是背了幾道面經,這個對于很多有強迫癥的面試官,如果你連源碼都沒看過,基本上是會pass掉的!)
項目中遇到了哪些比較有挑戰性的問題,是如何解決的;(這個很有爭議,一方面是你連一個復雜的問題都解決不了,要你過來干什么,還有就是,我的能力牛逼啊,但是公司沒有業務場景讓我展示啊!這個就看你遇到的面試官了,祝你好運!)
java基礎以及多個“比較”
1.Collections.sort排序內部原理
在Java 6中Arrays.sort()和Collections.sort()使用的是MergeSort,而在Java 7中,內部實現換成了TimSort,其對對象間比較的實現要求更加嚴格
2.hashMap原理,java8做的改變
從結構實現來講,HashMap是數組+鏈表+紅黑樹(JDK1.8增加了紅黑樹部分)實現的。HashMap最多只允許一條記錄的鍵為null,允許多條記錄的值為null。HashMap非線程安全。ConcurrentHashMap線程安全。解決碰撞:當出現沖突時,運用拉鏈法,將關鍵詞為同義詞的結點鏈接在一個單鏈表中,散列表長m,則定義一個由m個頭指針組成的指針數組T,地址為i的結點插入以T(i)為頭指針的單鏈表中。Java8中,沖突的元素超過限制(8),用紅黑樹替換鏈表。
3.String 和 StringBuilder 的區別
1)可變與不可變:String不可變,每一次執行“+”都會新生成一個新對象,所以頻繁改變字符串的情況中不用String,以節省內存。
2)是否多線程安全:StringBuilder并沒有對方法進行加同步鎖,所以是非線程安全的。StringBuffer和String均線程安全。
4.Vector 與 Array 的區別
1)ArrayList在內存不夠時默認是擴展50% + 1個,Vector是默認擴展1倍。
2)Vector屬于線程安全級別的,但是大多數情況下不使用Vector,因為線程安全需要更大的系統開銷。
5.HashMap 與 Hashtable 的區別
1) 歷史原因: Hashtable繼承Dictonary類, HashMap繼承自abstractMap
2) HashMap允許空的鍵值對, 但最多只有一個空對象,而HashTable不允許。
3) HashTable同步,而HashMap非同步,效率上比HashTable要高
6.ConncurrentHashMap和hashtable比較(兩個線程并發訪問map中同一條鏈,一個線程在尾部刪除,一個線程在前面遍歷查找,問為什么前面的線程還能正確的查找到后面被另一個線程刪除的節點)
ConcurrentHashMap融合了hashtable和hashmap二者的優勢。hashtable是做了同步的,即線程安全,hashmap未考慮同步。所以hashmap在單線程情況下效率較高。hashtable在的多線程情況下,同步操作能保證程序執行的正確性。但是hashtable是阻塞的,每次同步執行的時候都要鎖住整個結構,ConcurrentHashMap正是為了解決這個問題而誕生的,
ConcurrentHashMap允許多個修改操作并發進行,其關鍵在于使用了鎖分離技術(一個Array保存多個Object,使用這些對象的鎖作為分離鎖,get/put時隨機使用任意一個)。它使用了多個鎖來控制對hash表的不同部分進行的修改。在JDK 1.6中,有HashEntry結構存在,每次插入將新添加節點作為鏈的頭節點(同HashMap實現),而且每次刪除一個節點時,會將刪除節點之前的所有節點拷貝一份組成一個新的鏈,而將當前節點的上一個節點的next指向當前節點的下一個節點,從而在刪除以后有兩條鏈存 在,因而可以保證即使在同一條鏈中,有一個線程在刪除,而另一個線程在遍歷,它們都能工作良好,因為遍歷的線程能繼續使用原有的鏈。
Java8中,采用volatile HashEntry保存數據,table元素作為鎖;從table數組+單向鏈表加上了紅黑樹。紅黑樹是一種特別的二叉查找樹,特性為:1.節點為紅或者黑 2.根節點為黑 3.葉節點為黑 4.一節點為紅,則葉節點為黑 5.一節點到其子孫節點所有路徑上的黑節點數目相同。
7.ArrayList與 LinkedList 的區別?
最明顯的區別是
ArrrayList 底層的數據結構是數組,支持隨機訪問,而 LinkedList 的底層數據結構書鏈表,不支持隨機訪問。使用下標訪問一個元素,ArrayList 的時間復雜度是 O(1),而 LinkedList 是 O(n)。LinkedList是雙向鏈表
8.Java 中,Comparator 與Comparable 有什么不同?
Comparable 接口用于定義對象的自然順序,是排序接口,而 comparator 通常用于定義用戶定制的順序,是比較接口。我們如果需要控制某個類的次序,而該類本身不支持排序(即沒有實現Comparable接口),那么我們就可以建立一個“該類的比較器”來進行排序。Comparable 總是只有一個,但是可以有多個 comparator 來定義對象的順序。
9.抽象類是什么?它與接口有什么區別?你為什么要使用過抽象類?
抽象類是指不允許被實例化的類;一個類只能使用一次繼承關系。但是,一個類卻可以實現多個interface。
abstract class和interface所反映出的設計理念不同。其實abstract class表示的是"is-a"關系,interface表示的是"like-a"關系
實現抽象類和接口的類必須實現其中的所有方法。抽象類中可以有非抽象方法。接口中則不能有實現方法。但在Java8中允許接口中有靜態默認的方法。
接口中定義的變量默認是public static final 型,且必須給其初值,所以實現類中不能重新定義,也不能改變其值。抽象類中的變量默認是 friendly 型,其值可以在子類中重新定義,也可以重新賦值。
子類中實現父類中的抽象方法時,可見性可以大于等于父類中的;而接口實現類中的接口 方法的可見性只能與接口中相同(public)。
用抽象類是為了重用。減少編碼量,降低耦合性。
10.描述 Java 中的重載和重寫?
重載和重寫都允許你用相同的名稱來實現不同的功能,但是重載是編譯時活動,而重寫是運行時活動。你可以在同一個類中重載方法,但是只能在子類中重寫方法。重寫必須要有繼承
重寫:1、在子類中可以根據需要對從基類中繼承來的方法進行重寫。2、重寫的方法和被重寫的方法必須具有相同方法名稱、參數列表和返回類型。3、重寫方法不能使用比被重寫的方法更嚴格的訪問權限。
重載的時候,方法名要一樣,但是參數類型和個數不一樣,返回值類型可以相同也可以不相同。無法以返回型別作為重載函數的區分標準。
11.Collection與Collections的區別是什么?
Collection是Java集合框架中的基本接口;
Collections是Java集合框架提供的一個工具類,其中包含了大量用于操作或返回集合的靜態方法。
12.Java中多態的實現原理
所謂多態,指的就是父類引用指向子類對象,調用方法時會調用子類的實現而不是父類的實現。多態的實現的關鍵在于“動態綁定”。
13.object中定義了哪些方法?
clone(), equals(), hashCode(), toString(), notify(), notifyAll(),
wait(), finalize(), getClass()
14.Java泛型和類型擦除?
泛型即參數化類型,在創建集合時,指定集合元素的類型,此集合只能傳入該類型的參數。類型擦除:java編譯器生成的字節碼不包含泛型信息,所以在編譯時擦除:1.泛型用最頂級父類替換;2.移除。
15.說出 5 個 JDK 1.8 引入的新特性?
Java 8 在 Java 歷史上是一個開創新的版本,下面 JDK 8 中 5 個主要的特性:
Lambda 表達式;允許像對象一樣傳遞匿名函數 Stream API,充分利用現代多核 CPU,可以寫出很簡潔的代碼 ;Date 與 Time API,最終,有一個穩定、簡單的日期和時間庫可供你使用 擴展方法,現在,接口中可以有靜態、默認方法; 重復注解,現在你可以將相同的注解在同一類型上使用多次。
16.java中public,private,protected以及默認關鍵字的訪問范圍:
Protected可在包內及包外子類訪問,default只能同一包內訪問,prvate只能同一類
17. 常用數據結構:
集合,線性結構(數組,隊列,鏈表和棧),樹形結構,圖狀結構
18.Java 中的 TreeMap 是采用什么樹實現的?(答案)
Java 中的 TreeMap 是使用紅黑樹實現的。
19. 匿名內部類是什么?如何訪問在其外面定義的變量?
匿名內部類也就是沒有名字的內部類,匿名內部類只能使用一次,它通常用來簡化代碼編寫。
匿名內部類只能訪問外部類的Final變量. Java 8更加智能:如果局部變量被匿名內部類訪問,那么該局部變量相當于自動使用了final修飾。
20. 如何創建單例模式?說了雙重檢查,他說不是線程安全的。如何高效的創建一個線程安全的單例?
一種是通過枚舉,一種是通過靜態內部類。
21.poll() 方法和 remove() 方法的區別?
poll() 和
remove() 都是從隊列中取出一個元素,但是 poll() 在獲取元素失敗的時候會返回空,但是 remove() 失敗的時候會拋出異常。
22.寫一段代碼在遍歷 ArrayList 時移除一個元素
使用迭代器。
Iterator itr = list.iterator();
while(itr.hasNext()) {if(…) { itr.remove();} }
JVM
1.JVM如何加載一個類的過程,雙親委派模型中有哪些方法
類加載過程:加載、驗證(驗證階段作用是保證Class文件的字節流包含的信息符合JVM規范,不會給JVM造成危害)、準備(準備階段為變量分配內存并設置類變量的初始化)、解析(解析過程是將常量池內的符號引用替換成直接引用)、初始化。
雙親委派模型中方法:雙親委派是指如果一個類收到了類加載的請求,不會自己先嘗試加載,先找父類加載器去完成。當頂層啟動類加載器表示無法加載這個類的時候,子類才會嘗試自己去加載。當回到最開的發起者加載器還無法加載時,并不會向下找,而是拋出ClassNotFound異常。
方法:啟動(Bootstrap)類加載器,標準擴展(Extension)類加載器,應用程序類加載器(Application ),上下文(Custom)類加載器。意義是防止內存中出現多份同樣的字節碼 。
2.GC算法(什么樣的對象算是可回收對象,可達性分析),CMS收集器
jvm是如何判斷一個對象已經變成了可回收的“垃圾”,一般是兩個方法:引用記數法和根搜索算法。引用記數法沒辦法解決循環引用的問題,所以用根搜索。從一系列的”GC Roots“對象開始向下搜索,搜索走過的路徑稱為引用鏈。當一個對象到”GC Roots“之間沒有引用鏈時,被稱為引用不可達。引用不可到的對象被認為是可回收的對象。
幾種垃圾收集器:1,Serial New/Serial Old(串行),2,Parrallel New (并行),3,Parrallel Scavenge,4,Parrallel Old,5,CMS(CMS收集器是一個以獲得最短回收停頓時間為目標的收集器,它是一種并發收集器,采用的是Mark-sweep算法。),6,G1(是一款并行與并發收集器,并且可建立可預測的停頓時間模型,整體上是基于標記清理,局部采用復制)
3.JVM分為哪些區,每一個區干嗎的?
1)方法區(method):被所有的線程共享。方法區包含所有的類信息和靜態變量。
2)堆(heap):被所有的線程共享,存放對象實例以及數組,Java堆是GC的主要區域。
3)棧(stack):每個線程包含一個棧區,棧中保存一些局部變量等。
4)程序計數器:是當前線程執行的字節碼的行指示器。
4.JVM新生代,老年代,持久代,都存儲哪些東西?
持久代主要存放的是Java類的類信息,與垃圾收集要收集的Java對象關系不大。所有新生成的對象首先都是放在年輕代的,年老代中存放的都是一些生命周期較長的對象。
5.內存溢出和內存泄漏:
內存溢出:程序申請內存時,沒有足夠的內存,out of memory;內存泄漏值垃圾對象無法回收,可以使用memory analyzer工具查看泄漏。
6.進程與線程:
進程值運行中的程序(獨立性,動態性,并發性),線程指進程中的順序執行流。區別是:1.進程間不共享內存 2.創建進程進行資源分配的代價要大得多,所以多線程在高并發環境中效率高。
7.序列化與反序列化:
序列化指將java對象轉化為字節序列,反序列化相反。主要是為了java線程間通訊,實現對象傳遞。只有實現了Serializable或Externalizable接口類對象才可被序列化。
8.64 位 JVM 中,int 的長度是多數?
Java 中,int 類型變量的長度是一個固定值,與平臺無關,都是 32 位。意思就是說,在 32 位 和 64 位 的Java 虛擬機中,int 類型的長度是相同的。
9.Java 中 WeakReference 與 SoftReference的區別?
Java中一共有四種類型的引用。StrongReference、 SoftReference、 WeakReference 以及 PhantomReference。
StrongReference 是 Java 的默認引用實現, 它會盡可能長時間的存活于 JVM 內,當沒有任何對象指向它時將會被GC回收
WeakReference,顧名思義, 是一個弱引用, 當所引用的對象在
JVM 內不再有強引用時, 將被GC回收
雖然 WeakReference 與 SoftReference 都有利于提高 GC 和 內存的效率,但是 WeakReference ,一旦失去最后一個強引用,就會被 GC 回收,而 SoftReference 會盡可能長的保留引用直到 JVM 內存不足時才會被回收(虛擬機保證), 這一特性使得
SoftReference 非常適合緩存應用
10.解釋 Java 堆空間及 GC?
當通過 Java 命令啟動
Java 進程的時候,會為它分配內存。內存的一部分用于創建堆空間,當程序中創建對象的時候,就從對空間中分配內存。GC 是 JVM 內部的一個進程,回收無效對象的內存用于將來的分配。
11.Java 中堆和棧有什么區別?
JVM 中堆和棧屬于不同的內存區域,使用目的也不同。棧常用于保存方法幀和局部變量,而對象總是在堆上分配。棧通常都比堆小,也不會在多個線程之間共享,而堆被整個 JVM 的所有線程共享。
并發,鎖
1.volatile關鍵字, Lock
并發編程中:原子性問題,可見性問題,有序性問題。
volatile關鍵字能保證可見性,字能禁止指令重排序,但是不能保證原子性。可見性只能保證每次讀取的是最新的值,但是volatile沒辦法保證對變量的操作的原子性。在生成的會變語句中加入Lock關鍵字和內存屏障。
Lock 實現提供了比使用synchronized 方法和語句可獲得的更廣泛的鎖定操作,它能以更優雅的方式處理線程同步問題。用sychronized修飾的方法或者語句塊在代碼執行完之后鎖自動釋放,而用Lock需要我們手動釋放鎖
2.MYSQL常用優化(sql優化,表結構優化等)
SQL優化、表機構優化、索引優化、緩存參數優化
3.java每改一點都需要重新編譯打包部署,有沒有更好的方法
可以使用熱加載
4.進程間通信有哪幾種方式?
1)管道(Pipe),2)命名管道(named pipe),3)信號(Signal),4)消息(Message)隊列,5)共享內存,6)內存映射(mapped memory),7)信號量(semaphore),8)套接口(Socket)
5.Sychronized修飾靜態方法,鎖定類本身而不是實例,非靜態方法鎖定實例。
6. 操作系統什么情況下會死鎖?
所謂死鎖:是指多個進程在運行過程中因爭奪資源而造成的一種僵局。產生的原因:競爭資源:當系統中多個進程使用共享資源,并且資源不足以滿足需要,會引起進程對資源的競爭而產生死鎖。進程間推進的順序非法:請求和釋放資源的順序不當,也同樣會導致產生進程死鎖
7.產生死鎖的四個條件:
1.互斥條件(進程獨占資源)2.請求與保持(進程因請求資源而阻塞時,對已獲得的資源保持不放) 3.不剝奪條件(進程已獲得的資源,在末使用完之前,不能強行剝奪) 4.循環等待(若干進程之間形成一種頭尾相接的循環等待資源關系)
8. 如何理解分布式鎖?
由于在平時的工作中,線上服務器是分布式多臺部署的,經常會面臨解決分布式場景下數據一致性的問題,那么就要利用分布式鎖來解決這些問題。
9. 線程同步與阻塞的關系?同步一定阻塞嗎?阻塞一定同步嗎?
線程同步與否 跟 阻塞非阻塞沒關系,同步是個過程,阻塞是線程的一種狀態。多個線程操作共享變量時可能會出現競爭。這時需要同步來防止兩個以上的線程同時進入臨界區內,在這個過程中后進入臨界區的線程將阻塞,等待先進入的線程走出臨界區。
10. 同步和異步有什么區別?
同步和異步最大的區別就在于。一個需要等待,一個不需要等待。同步可以避免出現死鎖,讀臟數據的發生,一般共享某一資源的時候用,如果每個人都有修改權限,同時修改一個文件,有可能使一個人讀取另一個人已經刪除的內容,就會出錯,同步就會按順序來修改。
11. 線程池
根據系統自身的環境情況,有效的限制執行線程的數量,使得運行效果達到最佳。線程主要是通過控制執行的線程的數量,超出數量的線程排隊等候,等待有任務執行完畢,再從隊列最前面取出任務執行
12. 如何調用 wait()方法?使用 if 塊還是循環?為什么?
wait() 方法應該在循環調用,因為當線程獲取到 CPU 開始執行的時候,其他條件可能還沒有滿足,所以在處理前,循環檢測條件是否滿足會更好。
wait(),notify()和notifyall()方法是java.lang.Object類為線程提供的用于實現線程間通信的同步控制方法。等待或者喚醒
13. 實現線程的幾種方法
(1)繼承Thread類,重寫run函數
(2)實現Runnable接口,重寫run函數
(3)實現Callable接口,重寫call函數
14. 什么是多線程環境下的偽共享(false sharing)?
偽共享是多線程系統(每個處理器有自己的局部緩存)中一個眾所周知的性能問題。緩存系統中是以緩存行(cache line)為單位存儲的。緩存行是2的整數冪個連續字節,一般為32-256個字節。最常見的緩存行大小是64個字節。當多線程修改互相獨立的變量時,如果這些變量共享同一個緩存行,就會無意中影響彼此的性能,這就是偽共享。
網絡、數據庫
1.TCP如何保證可靠傳輸?三次握手過程?
在TCP的連接中,數據流必須以正確的順序送達對方。TCP的可靠性是通過順序編號和確認(ACK)來實現的。TCP 連接是通過三次握手進行初始化的。三次握手的目的是同步連接雙方的序列號和確認號并交換 TCP 窗口大小信息。第一次是客戶端發起連接;第二次表示服務器收到了客戶端的請求;第三次表示客戶端收到了服務器的反饋。
2. Linux下你常用的命令有哪些?
1. cd命令用來改變所在目錄。cd / 轉到根目錄中cd ~ 轉到用戶目錄下
2. ls命令用來查看目錄的內容。
3. cp命令用來拷貝文件cp
4.mv命令 mv t.txt Document 把文件t.txt 移動到目錄Document中。
3. 常用的hash算法有哪些?
1.加法hash:所謂的加法Hash就是把輸入元素一個一個的加起來構成最后的結果。
2.位運算hash:這類型Hash函數通過利用各種位運算(常見的是移位和異或)來充分的混合輸入元素
3.乘法hash:33*hash + key.charAt(i)
4. 什么是一致性哈希?
設計目標是為了解決因特網中的熱點(Hot spot)問題,一致性hash算法提出了在動態變化的Cache環境中,判定哈希算法好壞的四個定義:1、平衡性(Balance) 2、單調性(Monotonicity) 3、分散性(Spread) 4、負載(Load)
5. 數據庫中的范式有哪些?
第一范式----數據庫中的表(所有字段值)都是不可分割的原子數據項。
第二范式----數據庫表中的每一列都和主鍵相關,而不能只和主鍵的某一部分相關。
第三范式----數據庫表中每一列數據都和主鍵直接相關,不能間接相關。范式是為了減小數據冗余。
6. 數據庫中的索引的結構?什么情況下適合建索引?
數據庫中索引的結構是一種排序的數據結構,數據庫索引是通過B樹和變形的B+樹實現的。什么情況下不適合建立索引:1.對于在查詢過程中很少使用或參考的列;對于那些只有很少數據值的列;對于那些定義為image,text和bit數據類型的列;當修改性能遠大于檢索性能。
根據系統自身的環境情況,有效的限制執行線程的數量,使得運行效果達到最佳。線程主要是通過控制執行的線程的數量,超出數量的線程排隊等候,等待有任務執行完畢,再從隊列最前面取出任務執行
7. concurrent包下面,都用過什么?
java.util.concurrent、java.util.concurrent.atomic和java.util.concurrent.lock
8. 常用的數據庫有哪些?redis用過嗎?
…
9. 你知道的開源協議有哪些?
GPL (GNU General Public License) :GNU通用公共許可協議
LGPL (GNU Lesser General Public License) :GNU寬通用公共許可協議
BSD
(Berkeley Software Distribution) :伯克利軟件分發許可協議
MIT (Massachusetts Institute of Technology):MIT之名源自麻省理工學院
Apache (Apache License) :Apache許可協議
MPL (Mozilla Public License) :Mozilla公共許可協議
10.表單提交中,get和post區別
1.get從服務器獲取信息,post向服務器傳信息
2.get傳送數據量比較小,post可以比較大
3.get安全性比較低
11. TCP 協議與 UDP 協議有什么區別?(answer答案)
TCP(Tranfer Control Protocol)的縮寫,是一種面向連接的保證傳輸的協議,在傳輸數據流前,雙方會先建立一條虛擬的通信道。可以很少差錯傳輸數據。
UDP(User DataGram Protocol)的縮寫,是一種無連接的協議,使用UDP傳輸數據時,每個數據段都是一個獨立的信息,包括完整的源地址和目的地,在網絡上以任何可能的 路徑傳到目的地,因此,能否到達目的地,以及到達目的地的時間和內容的完整性都不能保證。
所以TCP必UDP多了建立連接的時間。相對UDP而言,TCP具有更高的安全性和可靠性。
TCP協議傳輸的大小不限制,一旦連接被建立,雙方可以按照一定的格式傳輸大量的數據,而UDP是一個不可靠的協議,大小有限制,每次不能超過64K。
參考:https://www.jianshu.com/p/ce1fb8497883
來看 208 道面試題,具體的內容。
一、Java 基礎
1.JDK 和 JRE 有什么區別?
2.== 和 equals 的區別是什么?
3.兩個對象的 hashCode()相同,則 equals()也一定為 true,對嗎?
4.final 在 java 中有什么作用?
5.java 中的 Math.round(-1.5) 等于多少?
6.String 屬于基礎的數據類型嗎?
7.java 中操作字符串都有哪些類?它們之間有什么區別?
8.String str="i"與 String str=new String(“i”)一樣嗎?
9.如何將字符串反轉?
10.String 類的常用方法都有那些?
11.抽象類必須要有抽象方法嗎?
12.普通類和抽象類有哪些區別?
13.抽象類能使用 final 修飾嗎?
14.接口和抽象類有什么區別?
15.java 中 IO 流分為幾種?
16.BIO、NIO、AIO 有什么區別?
17.Files的常用方法都有哪些?
二、容器
18.java 容器都有哪些?
19.Collection 和 Collections 有什么區別?
20.List、Set、Map 之間的區別是什么?
21.HashMap 和 Hashtable 有什么區別?
22.如何決定使用 HashMap 還是 TreeMap?
23.說一下 HashMap 的實現原理?
24.說一下 HashSet 的實現原理?
25.ArrayList 和 LinkedList 的區別是什么?
26.如何實現數組和 List 之間的轉換?
27.ArrayList 和 Vector 的區別是什么?
28.Array 和 ArrayList 有何區別?
29.在 Queue 中 poll()和 remove()有什么區別?
30.哪些集合類是線程安全的?
31.迭代器 Iterator 是什么?
32.Iterator 怎么使用?有什么特點?
33.Iterator 和 ListIterator 有什么區別?
34.怎么確保一個集合不能被修改?
三、多線程
35.并行和并發有什么區別?
36.線程和進程的區別?
37.守護線程是什么?
38.創建線程有哪幾種方式?
39.說一下 runnable 和 callable 有什么區別?
40.線程有哪些狀態?
41.sleep() 和 wait() 有什么區別?
42.notify()和 notifyAll()有什么區別?
43.線程的 run()和 start()有什么區別?
44.創建線程池有哪幾種方式?
45.線程池都有哪些狀態?
46.線程池中 submit()和 execute()方法有什么區別?
47.在 java 程序中怎么保證多線程的運行安全?
48.多線程鎖的升級原理是什么?
49.什么是死鎖?
50.怎么防止死鎖?
51.ThreadLocal 是什么?有哪些使用場景?
52.說一下 synchronized 底層實現原理?
53.synchronized 和 volatile 的區別是什么?
54.synchronized 和 Lock 有什么區別?
55.synchronized 和 ReentrantLock 區別是什么?
56.說一下 atomic 的原理?
四、反射
57.什么是反射?
58.什么是 java 序列化?什么情況下需要序列化?
59.動態代理是什么?有哪些應用?
60.怎么實現動態代理?
五、對象拷貝
61.為什么要使用克隆?
62.如何實現對象克隆?
63.深拷貝和淺拷貝區別是什么?
六、Java Web
64.jsp 和 servlet 有什么區別?
65.jsp 有哪些內置對象?作用分別是什么?
66.說一下 jsp 的 4 種作用域?
67.session 和 cookie 有什么區別?
68.說一下 session 的工作原理?
69.如果客戶端禁止 cookie 能實現 session 還能用嗎?
70.spring mvc 和 struts 的區別是什么?
71.如何避免 sql 注入?
72.什么是 XSS 攻擊,如何避免?
73.什么是 CSRF 攻擊,如何避免?
七、異常
74.throw 和 throws 的區別?
75.final、finally、finalize 有什么區別?
76.try-catch-finally 中哪個部分可以省略?
77.try-catch-finally 中,如果 catch 中 return 了,finally 還會執行嗎?
78.常見的異常類有哪些?
八、網絡
79.http 響應碼 301 和 302 代表的是什么?有什么區別?
80.forward 和 redirect 的區別?
81.簡述 tcp 和 udp的區別?
82.tcp 為什么要三次握手,兩次不行嗎?為什么?
83.說一下 tcp 粘包是怎么產生的?
84.OSI 的七層模型都有哪些?
85.get 和 post 請求有哪些區別?
86.如何實現跨域?
87.說一下 JSONP 實現原理?
九、設計模式
88.說一下你熟悉的設計模式?
89.簡單工廠和抽象工廠有什么區別?
十、Spring/Spring MVC
90.為什么要使用 spring?
91.解釋一下什么是 aop?
92.解釋一下什么是 ioc?
93.spring 有哪些主要模塊?
94.spring 常用的注入方式有哪些?
95.spring 中的 bean 是線程安全的嗎?
96.spring 支持幾種 bean 的作用域?
97.spring 自動裝配 bean 有哪些方式?
98.spring 事務實現方式有哪些?
99.說一下 spring 的事務隔離?
100.說一下 spring mvc 運行流程?
101.spring mvc 有哪些組件?
102.@RequestMapping 的作用是什么?
103.@Autowired 的作用是什么?
十一、Spring Boot/Spring Cloud
104.什么是 spring boot?
105.為什么要用 spring boot?
106.spring boot 核心配置文件是什么?
107.spring boot 配置文件有哪幾種類型?它們有什么區別?
108.spring boot 有哪些方式可以實現熱部署?
109.jpa 和 hibernate 有什么區別?
110.什么是 spring cloud?
111.spring cloud 斷路器的作用是什么?
112.spring cloud 的核心組件有哪些?
十二、Hibernate
113.為什么要使用 hibernate?
114.什么是 ORM 框架?
115.hibernate 中如何在控制臺查看打印的 sql 語句?
116.hibernate 有幾種查詢方式?
117.hibernate 實體類可以被定義為 final 嗎?
118.在 hibernate 中使用 Integer 和 int 做映射有什么區別?
119.hibernate 是如何工作的?
120.get()和 load()的區別?
121.說一下 hibernate 的緩存機制?
122.hibernate 對象有哪些狀態?
123.在 hibernate 中 getCurrentSession 和 openSession 的區別是什么?
124.hibernate 實體類必須要有無參構造函數嗎?為什么?
十三、Mybatis
125.mybatis 中 #{}和 ${}的區別是什么?
126.mybatis 有幾種分頁方式?
127.RowBounds 是一次性查詢全部結果嗎?為什么?
128.mybatis 邏輯分頁和物理分頁的區別是什么?
129.mybatis 是否支持延遲加載?延遲加載的原理是什么?
130.說一下 mybatis 的一級緩存和二級緩存?
131.mybatis 和 hibernate 的區別有哪些?
132.mybatis 有哪些執行器(Executor)?
133.mybatis 分頁插件的實現原理是什么?
134.mybatis 如何編寫一個自定義插件?
十四、RabbitMQ
135.rabbitmq 的使用場景有哪些?
136.rabbitmq 有哪些重要的角色?
137.rabbitmq 有哪些重要的組件?
138.rabbitmq 中 vhost 的作用是什么?
139.rabbitmq 的消息是怎么發送的?
140.rabbitmq 怎么保證消息的穩定性?
141.rabbitmq 怎么避免消息丟失?
142.要保證消息持久化成功的條件有哪些?
143.rabbitmq 持久化有什么缺點?
144.rabbitmq 有幾種廣播類型?
145.rabbitmq 怎么實現延遲消息隊列?
146.rabbitmq 集群有什么用?
147.rabbitmq 節點的類型有哪些?
148.rabbitmq 集群搭建需要注意哪些問題?
149.rabbitmq 每個節點是其他節點的完整拷貝嗎?為什么?
150.rabbitmq 集群中唯一一個磁盤節點崩潰了會發生什么情況?
151.rabbitmq 對集群節點停止順序有要求嗎?
十五、Kafka
152.kafka 可以脫離 zookeeper 單獨使用嗎?為什么?
153.kafka 有幾種數據保留的策略?
154.kafka 同時設置了 7 天和 10G 清除數據,到第五天的時候消息達到了 10G,這個時候 kafka 將如何處理?
155.什么情況會導致 kafka 運行變慢?
156.使用 kafka 集群需要注意什么?
十六、Zookeeper
157.zookeeper 是什么?
158.zookeeper 都有哪些功能?
159.zookeeper 有幾種部署模式?
160.zookeeper 怎么保證主從節點的狀態同步?
161.集群中為什么要有主節點?
162.集群中有 3 臺服務器,其中一個節點宕機,這個時候 zookeeper 還可以使用嗎?
163.說一下 zookeeper 的通知機制?
十七、MySql
164.數據庫的三范式是什么?
165.一張自增表里面總共有 7 條數據,刪除了最后 2 條數據,重啟 mysql 數據庫,又插入了一條數據,此時 id 是幾?
166.如何獲取當前數據庫版本?
167.說一下 ACID 是什么?
168.char 和 varchar 的區別是什么?
169.float 和 double 的區別是什么?
170.mysql 的內連接、左連接、右連接有什么區別?
171.mysql 索引是怎么實現的?
172.怎么驗證 mysql 的索引是否滿足需求?
173.說一下數據庫的事務隔離?
174.說一下 mysql 常用的引擎?
175.說一下 mysql 的行鎖和表鎖?
176.說一下樂觀鎖和悲觀鎖?
177.mysql 問題排查都有哪些手段?
178.如何做 mysql 的性能優化?
十八、Redis
179.redis 是什么?都有哪些使用場景?
180.redis 有哪些功能?
181.redis 和 memecache 有什么區別?
182.redis 為什么是單線程的?
183.什么是緩存穿透?怎么解決?
184.redis 支持的數據類型有哪些?
185.redis 支持的 java 客戶端都有哪些?
186.jedis 和 redisson 有哪些區別?
187.怎么保證緩存和數據庫數據的一致性?
188.redis 持久化有幾種方式?
189.redis 怎么實現分布式鎖?
190.redis 分布式鎖有什么缺陷?
191.redis 如何做內存優化?
192.redis 淘汰策略有哪些?
193.redis 常見的性能問題有哪些?該如何解決?
十九、JVM
194.說一下 jvm 的主要組成部分?及其作用?
195.說一下 jvm 運行時數據區?
196.說一下堆棧的區別?
197.隊列和棧是什么?有什么區別?
198.什么是雙親委派模型?
199.說一下類加載的執行過程?
200.怎么判斷對象是否可以被回收?
201.java 中都有哪些引用類型?
202.說一下 jvm 有哪些垃圾回收算法?
203.說一下 jvm 有哪些垃圾回收器?
204.詳細介紹一下 CMS 垃圾回收器?
205.新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么區別?
206.簡述分代垃圾回收器是怎么工作的?
207.說一下 jvm 調優的工具?
208.常用的 jvm 調優的參數都有哪些?
原文:https://blog.csdn.net/sufu1065/article/details/88051083
必備Spring Boot高級面試題
自Spring Boot誕生以來,就引起了業界轟動,目前越來越多的公司技術選型選擇擁抱Spring Boot。所以Spring Boot也成為面試必問的問題之一。下面的問題是小胖哥面試了很多候選人后總結出來的,希望對你有所幫助
問
? ? ? ? ? ?Spring和Spring Boot有什么區別?
答
Spring Framework提供了多種功能,使Web應用程序的開發更加容易。這些功能包括依賴注入,數據綁定,面向方面的編程,數據訪問等等。
隨著Spring社區的壯大,Spring慢慢變得越來越復雜,不再像開始宣稱的那么輕量級。開發應用程序的配置量越來越大令開發者頭疼。這時Spring Boot就派上用場了 - 它采用“約定大于配置”的思想簡化了配置,對Spring提供的功能和配置而且將一些功能抽象成為“Starter”開箱即用、按需引用。極大地簡化了開發。
問
? ? ? ? ? ? ??我們如何使用Maven設置Spring Boot應用程序?
答
我們可以像在任何其他庫中一樣在Maven項目中包含Spring Boot。但是,最好的方法是從spring-boot-starter-parent項目繼承并聲明依賴于Spring Boot啟動器。這樣做可以讓我們的項目重用Spring Boot的默認設置。
繼承spring-boot-starter-parent項目非常簡單 - 我們只需要在pom.xml中指定一個parent元素:
我們可以在Maven 中央倉庫找到最新版本的?spring-boot-starter-parent。
上面的方式很方便但是并不一定符合實際需要。例如公司要求所有項目依賴構建從一個標準BOM開始,我們就不能按上面的方式進行。
在這種情況下,我們可以進行如下引用:
然后在 dependencies 標簽下引用Spring Boot 的starters 就行了。
問
? ? ? ? ? ? ? ??Spring boot 中的starter是什么?
答
依賴管理對于項目至關重要。當項目足夠復雜時,管理依賴項可能會變成一場噩夢,因為涉及的組件太多了。
這就是Spring Boot 的starter就派上用場了。每個starter都可以為我們提供所需要的Spring技術的一站式服務。并且以一致的方式傳遞和管理其他所需的依賴關系。
所有官方starter都在org.springframework.boot組下,其名稱以spring-boot-starter-開頭 。非官方的starter的名稱在前,如mybatis-spring-boot-starter。這種命名模式使得查找啟動器變得很容易,尤其是在使用支持按名稱搜索依賴關系的IDE時。但是這個不是絕對的,有些開發者可能不遵從這種契約。
目前大概有超過50種官方starter。最常用的是:
spring-boot-starter:?核心啟動器,包括自動配置支持,日志記錄和YAML
spring-boot-starter-aop:?使用Spring AOP和AspectJ進行面向方面編程的初學者
spring-boot-starter-data-jpa:?使用Spring Data JPA和Hibernate的啟動器
spring-boot-starter-jdbc:?用于將JDBC與HikariCP連接池一起使用的啟動器
spring-boot-starter-security:?使用Spring Security的啟動器
spring-boot-starter-test:?用于測試Spring Boot應用程序的啟動器
spring-boot-starter-web:?使用Spring MVC構建Web的啟動器,包括RESTful應用程序
其他starter 可去spring.io查詢
問
? ? ? ? ? ? ? ??Spring Boot 如何禁用特定的自動配置?
答
如果我們需要禁用特定Spring Boot的自動配置,我們可以使用@EnableAutoConfiguration注解的exclude屬性來指示它。如下禁用了
DataSourceAutoConfiguration:
如果我們使用@SpringBootApplication注解。?它具有@EnableAutoConfiguration作為元注解 - 我們同樣可以配置exclude屬性來禁用自動配置:
我們還可以使用spring.autoconfigure.exclude環境屬性禁用自動配置。在application.properties配置文件設置如下也可以達到同樣的目的:
問
? ? ? ? ? ? ? ??Spring Boot 如何注冊自定義自動配置?
答
要注冊自動配置類,我們必須在META-INF /?
spring.factories文件的EnableAutoConfiguration?鍵
下列出其完全限定名稱,如果是多個按照以下風格配置:
如果我們使用Maven構建一個項目,那么該文件應放在resources / META-INF目錄中。
問
? ? ? ? ? ? ? ??Spring Boot如何根據不同的條件來加載bean?
答
?
你可在配置中使用@Conditional 系列注解。例如@ConditionalOnMissingBean。此注釋的最顯著屬性是:
value:要檢查的bean類型
name:要檢查的bean的名稱
放置在使用@Bean裝飾的方法上時,目標類型默認為方法的返回類型:
表示的意思是如果不存在CustomService類型的bean則初始化并注入該bean。
問
? ? ? ? ? ? ? ???如何將Spring Boot Web應用程序部署為JAR和 WAR文件?
答
傳統上,我們將Web應用程序打包為WAR文件,然后將其部署到外部服務器中。這樣做可以讓我們在同一臺服務器上安排多個應用程序。在CPU和內存稀缺的時候,這是節省資源的好方法。
但事情發生了變化。現在計算機硬件相當便宜,并且注意力轉向服務器配置。在部署期間配置服務器的一個小錯誤可能會導致災難性后果。
Spring通過提供一個插件即spring-boot-maven-plugin來解決這個問題,將Web應用程序打包為可執行的JAR。要包含此插件,只需向pom.xml添加一個插件元素:
有了這個插件,我們將在執行包階段后得到一個fat JAR 。此JAR包含所有必需的依賴項,包括嵌入式服務器。因此,我們不再需要擔心配置外部服務器。
然后我們可以像運行普通的可執行JAR一樣運行應用程序。
請注意,必須將pom.xml文件中的packaging元素設置為?jar?才能構建JAR文件:
如果我們不包含這個元素,它也默認為jar。
如果我們想要構建WAR文件,請將包裝?元素更改為war:
并將容器依賴關系從打包文件中刪除:
執行Maven?包階段后,我們將擁有一個可部署的WAR文件。
問
? ? ? ? ? ? ? ???如何在Spring Boot啟動的時候運行一些邏輯?
答
可以實現Spring Boot 提供的接口 ApplicationRunner ?也可以實現接口CommandLineRunner,這兩個接口實現方式一樣,它們都只提供了一個 run 方法。
問
? ? ? ? ? ? ? ? ???Spring boot支持哪些外部配置?
答
Spring Boot支持外部配置,允許我們在各種環境中運行相同的應用程序。我們可以使用properties文件,YAML文件,環境變量,系統屬性和命令行選項參數來指定配置屬性。
然后,我們可以訪問使用這些屬性@Value注釋,經由綁定對象 的@ConfigurationProperties注釋,或Environment 環境抽象類注入。
以下是最常見的外部配置來源:
命令行屬性:命令行選項參數是以雙連字符開頭的程序參數,例如-server.port = 8080。Spring Boot將所有參數轉換為屬性,并將它們添加到環境屬性集中。
應用程序屬性:應用程序屬性是從application.properties文件或其YAML對應文件加載的屬性。默認情況下,Spring Boot會在當前目錄,類路徑根或其config子目錄中搜索此文件。
特定于配置文件的屬性:特定于配置文件的屬性從application- {profile} .properties文件或其YAML對應文件加載。{profile}占位符是指活性輪廓。這些文件與非特定屬性文件位于相同位置,并且優先于非特定屬性文件。
問
? ? ? ? ? ? ? ? ??Spring和Spring Boot有什么區別?
答
在為Spring應用程序運行集成測試時,我們必須有一個ApplicationContext。
為了簡化測試,Spring Boot為測試提供了一個特殊的注釋?@SpringBootTest。此批注從其classes屬性指示的配置類創建ApplicationContext。
如果未設置classes屬性,Spring Boot將搜索主配置類。搜索從包含測試的包開始,直到找到使用@SpringBootApplication或@SpringBootConfiguration注釋的類。
請注意,如果我們使用JUnit 4,我們必須用@RunWith(SpringRunner.class)裝飾測試類。可以查閱我前面的關于Spring Boot Mock測試的文章來學習更多的測試方式。
問
? ? ? ? ? ?Spring Boot Actuator有什么用?
答
Spring Boot Actuator可以幫助你監控和管理Spring Boot應用,比如健康檢查、審計、統計和HTTP追蹤等。所有的這些特性可以通過JMX或者HTTP endpoints來獲得。
Actuator同時還可以與外部應用監控系統整合,比如 Prometheus, Graphite, DataDog, Influx, Wavefront, New Relic等。這些系統提供了非常好的儀表盤、圖標、分析和告警等功能,使得你可以通過統一的接口輕松的監控和管理你的應用。
Actuator使用Micrometer來整合上面提到的外部應用監控系統。這使得只要通過非常小的配置就可以集成任何應用監控系統。
將Spring Boot Actuator集成到一個項目中非常簡單。我們需要做的就是在pom.xml文件中包含?spring-boot-starter-actuator啟動器:
Spring Boot Actuator可以使用HTTP或JMX端點公開操作信息。但是,大多數應用程序都使用HTTP,其中端點的標識和/執行器前綴形成URL路徑。
以下是Actuator提供的一些最常見的內置端點:
auditevents:?公開審計事件信息
env:?公開環境屬性
health:?顯示應用程序運行狀況信息
httptrace:?顯示HTTP跟蹤信息
info:?顯示任意應用程序信息
metric:?顯示指標信息
mapping:?顯示所有@RequestMapping路徑的列表
scheduledtasks:?顯示應用程序中的計劃任務
threaddump:?執行線程轉儲
beans :所有加載的spring bean
生產使用Actuator務必保護好這些端點,避免未授權的訪問請求。
Java 面試 Spring MVC 必問題
Spring MVC是Spring構建在Servlet API上的Web框架。目前大部分的Java Web 開發已經使用Spring MVC 來做。它提供了模型 - 視圖 - 控制器架構,可用于開發靈活的Web應用程序。在本教程中,我們將重點關注與之相關的問題,因為它通常是Spring開發人員面試的熱點問題。強烈建議收藏!
Q
為什么選擇Spring MVC ?
A
Spring MVC 實現了一些明確而且相對低耦合的概念,可以讓開發者很容易開發和測試他們的Web應用。這些概念有:
Dispatcher Servlet ——核心Servlet前置控制器,配置在web.xml文件中的。
攔截匹配的請求,Servlet攔截匹配規則要自己定義,把攔截下來的請求,依據相應的規則分發到目標Controller來處理
Controllers ——具體的業務控制器,處理具體請求的業務并響應
View Resolvers ——視圖解析器,用于將響應的邏輯視圖解析為真正的視圖View對象
Views, Models ——Views的主要作用是用于處理響應視圖,然后返回給客戶端,Models主要用于傳遞控制方法處理數據到響應視圖頁面
ModelAndView ——Model 和 View 的復合體
Model and Session Attributes ——對模型屬性和會話屬性的處理
這些概念都是完全獨立而且職責單一。因此Spring MVC給了我們很大的靈活性。它基于接口(提供的實現類),我們可以使用自定義接口配置框架的每個部分。另一個重要的事情是我們不再依賴于特定的視圖技術(例如,JSP),可以選擇我們最復合業務的視圖技術。此外,我們不僅僅在Web應用程序開發中使用Spring MVC,也可以用它創建RESTful Web服務。?
Q
SpringMVC的流程是什么?
A
1. 用戶發送請求至前端控制器DispatcherServlet;
2. DispatcherServlet收到請求后,調用HandlerMapping處理器映射器,請求獲取Handle;
3. 處理器映射器根據請求url找到具體的處理器,生成處理器對象及處理器攔截器(如果有)一并返回給DispatcherServlet;
4. DispatcherServlet 調用 HandlerAdapter處理器適配器;
5. HandlerAdapter 經過適配調用 具體處理器(Handler,也叫后端控制器);
6. Handler執行完成返回ModelAndView;
7. HandlerAdapter將Handler執行結果ModelAndView返回給DispatcherServlet;
8. DispatcherServlet將ModelAndView傳給ViewResolver視圖解析器進行解析;
9. ViewResolver解析后返回具體View;
10. DispatcherServlet對View進行渲染視圖(即將模型數據填充至視圖中)
11. DispatcherServlet響應用戶。
流程圖:
Q
@Autowired 注解的規則是什么?
A
@Autowired注解可以使用在成員屬性上或方法上,按類型注入Spring bean。這個注解允許Spring解析協作的bean并注入到你業務需要的bean中。?
Q
簡述一下注解@ModelAttribute 。
A
@ModelAttribute注解是Spring MVC中最重要的注解之一。它將方法參數或方法返回值綁定到命名中的Model屬性中,然后將其公開給Web視圖。如果我們在方法級別使用它,則表明該方法的目的是添加一個或多個模型屬性。另一方面,當用作方法參數時,它表示應從模型中檢索參數。如果不存在,我們應該首先實例化它,然后將其添加到Model中。一旦出現在模型中,我們應該填充所有具有匹配名稱的請求參數的參數字段。?
Q
@Controller和@RestController之間有什么區別?
A
@Controller和@RestController注釋之間的主要區別在于@ResponseBody注解功能已經自動包含在@RestController中。這意味著我們不需要使用@ResponseBody來注釋我們的處理程序方法。?
Q
描述一下 @PathVariable注解?。
A
我們可以使用@PathVariable注解來從請求URI中提取一個特定模版變量的值來作為我們的請求參數。例如 從/user/123提取值123給/user/{id}控制器來獲取一個id=123的數據映射關系。需要特別指出的是通過此注解獲取的值不會被編碼。具體可通過我的文章來獲取原因。?
Q
如何在Spring MVC中校驗參數??
A
Spring MVC 默認支持JSR-303校驗規范 。并在Spring-Boot-starter-web中提供了JSR-303規范實現Hibernate Validator。我們可以使用它來進行參數校驗。詳情可查看我的相關文章。?
Q
@RequestBody 和 @ResponseBody是干嘛用的?
A
1. @RequestBody注解用于控制器方法參數上,目的是將Http 請求體轉換為領域對象(請求參數)。Spring 通過`HttpMessageConverter`將請求體反序列化到Java對象中,默認使用jackson類庫反序列化。
2.@ResponseBody注解使用于Spring MVC控制器中的處理程序方法上,它表明我們將把方法的返回類型直接寫入HTTP響應主體而不會將它放在Model中,同樣不會將其解釋為視圖名稱。?
Q
Spring MVC 攔截器有什么用怎么用?
A
Spring MVC攔截器允許我們攔截客戶端請求并在三個地方處理它 - 在處理之前,處理之后或完成之后(在呈現視圖時)。攔截器切面處理一些公共邏輯而避免重復處理程序代碼(如日志記錄),也可以用來更改Spring模型中全局使用的參數。通過以下方式:
org.springframework.web.servlet.handler.HandlerInterceptorAdapter —繼承該類
org.springframework.web.servlet.HandlerInterceptor—實現該接口?
Q
如何全局處理控制器異常??
A
通過@ControllerAdvice 或者@RestControllerAdvice 和@ExceptionHandler注解組合,通過在方法入參中捕獲異常進行處理,舉例如下:
?
@Slf4j@RestControllerAdvice("cn.felord.manage.api")
public?class?GlobalExceptionControllerAdvice?{
????@ExceptionHandler(NullPointerException.class)
????public?Rest?nullPointHandler(HttpServletRequest request, NullPointerException e)?{
????????log.error("空指針啦,趕緊關注公眾號:Felordcn", e);
????????return?RestBody.failure(-1,?"null point exception");
????}
}
Q
如何處理Spring MVC 中的跨域問題?
A
Spring MVC 解決跨域問題主要有以下幾種辦法:
通過Spring MVC 攔截器來處理,同理servlet中的filter也可以處理。
通過在控制層方法使用@CrossOrigin注解。?請注意該方案需要在Spring MVC 4.x 以上。
通過在Spring MVC xml配置文件中的<mvc:cors>標簽中配置。
通過`WebMvcConfigurer#addCorsMappings(CorsRegistry)`來配置。
如果想具體深入可通過公眾號:Felordcn?來獲取具體的教程。?
Q
如何格式化Spring MVC如參參數?
A
一般可通過兩種方式:
實現org.springframework.core.convert.
converter.Converter<S,T>?,并將實現注入Spring容器中。
實現org.springframework.format.
Formatter<T>?,并將實現注入Spring 容器中。?
透徹理解并發編程的三個核心知識點
遠看并發,并發編程可以抽象成三個核心問題: 分工、同步/協作、互斥
如果你已經工作了,那么你一定聽說過或者正在應用敏捷開發模式來交付日常的工作任務,我們就用你熟悉的流程來解釋這三個核心問題
分工
將當前 Sprint 的 Story 拆分成「合適」大小的 Task,并且安排給「合適」的 Team Member 去完成
這里面用了兩個「合適」,將 Story 拆分成大小適中,可完成的 Task 是非常重要的。拆分的粒度太粗,導致這個任務完成難度變高,耗時長,不易與其他人配合;拆分的粒度太細,又導致任務太多,不好管理與追蹤,浪費精力和資源。(合適的線程才能更好的完成整塊工作,當然一個線程可以輕松搞定的就沒必要多線程);安排給合適的人員去完成同樣重要,UX-UE 問題交給后端人員處理,很顯然是有問題的 (主線程應該做的事交給子線程顯然是解決不了問題的,每個線程做正確的事才能發揮作用)
關于分工,常見的 Executor,生產者-消費者模式,Fork/Join 等,這都是分工思想的體現
同步/協作
任務拆分完畢,我要等張三的任務,張三要等李四的任務,也就是說任務之間存在依賴關系,前面的任務執行完畢,后面的任務才可以執行,人高級在可以通過溝通反復確認,確保自己的任務可以開始執行。但面對程序,我們需要了解程序的溝通方式,一個線程執行完任務,如何通知后續線程執行
所有的同步/協作關系我們都可以用你最熟悉的 If-then-else 來表示:
if(前序任務完成){execute();
}else{
wait();
}
上面的代碼就是說:當某個條件不滿足時,線程需要等待;當某個條件滿足時,線程需要被喚醒執行,線程之間的協作可能是主線程與子線程的協作,可能是子線程與子線程的合作, Java SDK 中 CountDownLatch 和 CyclicBarrier 就是用來解決線程協作問題的
互斥
分工和同步強調的是性能,但是互斥是強調正確性,就是我們常常提到的「線程安全」,當多個線程同時訪問一個共享變量/成員變量時,就可能發生不確定性,造成不確定性主要是有可見性、原子性、有序性這三大問題,而解決這些問題的核心就是互斥
互斥
同一時刻,只允許一個線程訪問共享變量
來看下圖,主干路就是共享變量,進入主干路一次只能有一輛車,這樣你是否理解了呢?「天下大事,分久必合」
同樣 Java SDK 也有很多互斥的解決方案,比如你馬上就能想到 synchronized 關鍵字,Lock,ThreadLocal 等就是互斥的解決方案
總結
資本家瘋狂榨取勞動工人的剩余價值,獲得最大收益。當你面對 CPU,內存,IO 這些勞動工人時,你就是那個資本家,你要思考如何充分榨取它們的價值
當一個工人能干的活,絕不讓兩個人來干(單線程能滿足就沒必要為了多線程)當多個工人干活時,就要讓他們分工明確,合作順暢,沒矛盾
當任務很大時,由于 IO 干活慢,CPU 干活快,就沒必要讓 CPU 死等當前的 IO,轉而去執行其他指令,這就是榨取剩余價值,如何最大限度的榨取其價值,這就涉及到后續的調優問題,比如多少線程合適等
分工是設計,同步和互斥是實現,沒有好的設計也就沒有好的實現,所以在分工階段,強烈建議大家勾劃草圖,了解瓶頸所在,這樣才會有更好的實現,后續章節的內容,我也會帶領大家畫草圖,分析問題,逐步養成這個習慣
本章內容可以用下面的圖來簡單概括,葉子結點的內容我們會逐步點亮,現階段不用過分關注(如果你上來就啃 JDK 源碼,也許你會痛苦的迷失,并最終放棄你的進階之路的)
理解三大核心問題,你要充分結合生活中的實際,程序中的并發問題,基本上都能在實際生活中找得到原型
下一篇文章的內容,我們就要聊聊,引起線程安全的三個問題:「可見性,原子性,有序性」,這涉及到 JMM 的一點內容,可以提前了解一下的,這樣我們才能更好的碰撞
靈魂追問
工作中多線程編程的場景多嗎?
想到多線程,只會想到 synchronized 嗎?
Java 并發包各個類,你有了解底層實現和設計理念嗎?
緩存使用
Redis 有哪些類型
在Redis中有五種數據類型
String----------字符串
Hash------------字典
List-------------列表
Set--------------集合
Sorted Set------有序集合
Redis 內部結構
Redis 內部使用一個 redisObject 對象來表示所有的 key 和 value。type :代表一個 value 對象具體是何種數據類型。
encoding :是不同數據類型在 redis 內部的存儲方式,比如:type=string 代表 value 存儲的是一個普通字符串,那么對應的 encoding 可以是 raw 或者是 int,如果是 int 則代表實際 redis 內部是按數值型類存儲和表示這個字符串的,當然前提是這個字符串本身可以用數值表示,比如:"123" "456"這樣的字符串。
vm 字段:只有打開了 Redis 的虛擬內存功能,此字段才會真正的分配內存,該功能默認是關閉狀態的。Redis 使用 redisObject 來表示所有的 key/value 數據是比較浪費內存的,當然這些內存管理成本的付出主要也是為了給 Redis 不同數據類型提供一個統一的管理接口,實際作者也提供了多種方法幫助我們盡量節省內存使用。
作者:zhanglbjames
鏈接:https://www.jianshu.com/p/f09480c05e42
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
聊聊 Redis 使用場景
緩存
會話緩存
時效性
訪問頻率
計數器
社交列表
記錄用戶判定信息
交集、并集和差集
熱門列表與排行榜
最新動態
消息隊列
摘抄自:http://blog.720ui.com/2017/redis_core_use/
Redis 持久化機制
redis有兩種持久化機制RDB與AOF。
摘抄自:http://shanks.leanote.com/post/Untitled-55ca439338f41148cd000759-22
Redis 如何實現持久化
RDB持久化方式會在一個特定的間隔保存那個時間點的一個數據快照。
AOF持久化方式則會記錄每一個服務器收到的寫操作。在服務啟動時,這些記錄的操作會逐條執行從而重建出原來的數據。寫操作命令記錄的格式跟Redis協議一致,以追加的方式進行保存。
Redis的持久化是可以禁用的,就是說你可以讓數據的生命周期只存在于服務器的運行時間里。
兩種方式的持久化是可以同時存在的,但是當Redis重啟時,AOF文件會被優先用于重建數據。
Redis 集群方案與實現
客戶端分片
基于代理的分片
路由查詢
客戶端分片
由客戶端決定key寫入或者讀取的節點。
包括jedis在內的一些客戶端,實現了客戶端分片機制。
路由查詢
將請求發送到任意節點,接收到請求的節點會將查詢請求發送到正確的節點上執行。
開源方案
Redis 為什么是單線程的
因為CPU不是Redis的瓶頸。Redis的瓶頸最有可能是機器內存或者網絡帶寬。(以上主要來自官方FAQ)既然單線程容易實現,而且CPU不會成為瓶頸,那就順理成章地采用單線程的方案了。
緩存奔潰
碰到這種情況,一般并發量不是特別多的時候,使用最多的解決方案是加鎖排隊。
加鎖排隊只是為了減輕數據庫的壓力,并沒有提高系統吞吐量。假設在高并發下,緩存重建期間key是鎖著的,這是過來1000個請求999個都在阻塞的。同樣會導致用戶等待超時,這是個治標不治本的方法。
緩存降級
頁面降級:在大促或者某些特殊情況下,某些頁面占用了一些稀缺服務資源,在緊急情況下可以對其整個降級,以達到丟卒保帥;
頁面片段降級:比如商品詳情頁中的商家部分因為數據錯誤了,此時需要對其進行降級;
頁面異步請求降級:比如商品詳情頁上有推薦信息/配送至等異步加載的請求,如果這些信息響應慢或者后端服務有問題,可以進行降級;
服務功能降級:比如渲染商品詳情頁時需要調用一些不太重要的服務:相關分類、熱銷榜等,而這些服務在異常情況下直接不獲取,即降級即可;
讀降級:比如多級緩存模式,如果后端服務有問題,可以降級為只讀緩存,這種方式適用于對讀一致性要求不高的場景;
寫降級:比如秒殺搶購,我們可以只進行Cache的更新,然后異步同步扣減庫存到DB,保證最終一致性即可,此時可以將DB降級為Cache。
爬蟲降級:在大促活動時,可以將爬蟲流量導向靜態頁或者返回空數據,從而保護后端稀缺資源。
自動開關降級
自動降級是根據系統負載、資源使用情況、SLA等指標進行降級。
超時降級
當訪問的數據庫/http服務/遠程調用響應慢或者長時間響應慢,且該服務不是核心服務的話可以在超時后自動降級;比如商品詳情頁上有推薦內容/評價,但是推薦內容/評價暫時不展示對用戶購物流程不會產生很大的影響;對于這種服務是可以超時降級的。如果是調用別人的遠程服務,和對方定義一個服務響應最大時間,如果超時了則自動降級。
摘抄自:http://jinnianshilongnian.iteye.com/blog/2306477
使用緩存的合理性問題
熱點數據,緩存才有價值
頻繁修改的數據,看情況考慮使用緩存
數據不一致性
緩存更新機制
緩存可用性
緩存服務降級
緩存預熱
緩存穿透
摘抄自:http://blog.720ui.com/2016/redis_action_01_use_core/
消息隊列
消息隊列的使用場景
校驗用戶名等信息,如果沒問題會在數據庫中添加一個用戶記錄
如果是用郵箱注冊會給你發送一封注冊成功的郵件,手機注冊則會發送一條短信
分析用戶的個人信息,以便將來向他推薦一些志同道合的人,或向那些人推薦他
發送給用戶一個包含操作指南的系統通知
消息的重發補償解決思路
可靠消息服務定時查詢狀態為已發送并超時的消息
可靠消息將消息重新投遞到 MQ 組件中
下游應用監聽消息,在滿足冪等性的條件下,重新執行業務。
下游應用通知可靠消息服務該消息已經成功消費。
通過消息狀態確認和消息重發兩個功能,可以確保上游應用、可靠消息服務和下游應用數據的最終一致性。
消息的冪等性解決思路
查詢操作
查詢一次和查詢多次,在數據不變的情況下,查詢結果是一樣的。select是天然的冪等操作
刪除操作
刪除操作也是冪等的,刪除一次和多次刪除都是把數據刪除。(注意可能返回結果不一樣,刪除的數據不存在,返回0,刪除的數據多條,返回結果多個)
3.唯一索引,防止新增臟數據
比如:支付寶的資金賬戶,支付寶也有用戶賬戶,每個用戶只能有一個資金賬戶,怎么防止給用戶創建資金賬戶多個,那么給資金賬戶表中的用戶ID加唯一索引,所以一個用戶新增成功一個資金賬戶記錄
token機制,防止頁面重復提交
悲觀鎖
獲取數據的時候加鎖獲取
select * from table_xxx where id='xxx' for update;
注意:id字段一定是主鍵或者唯一索引,不然是鎖表,會死人的
悲觀鎖使用時一般伴隨事務一起使用,數據鎖定時間可能會很長,根據實際情況選用
樂觀鎖
樂觀鎖只是在更新數據那一刻鎖表,其他時間不鎖表,所以相對于悲觀鎖,效率更高。
分布式鎖
還是拿插入數據的例子,如果是分布是系統,構建全局唯一索引比較困難,例如唯一性的字段沒法確定,這時候可以引入分布式鎖,通過第三方的系統(redis或zookeeper),在業務系統插入數據或者更新數據,獲取分布式鎖,然后做操作,之后釋放鎖,這樣其實是把多線程并發的鎖的思路,引入多多個系統,也就是分布式系統中得解決思路。
select + insert
并發不高的后臺系統,或者一些任務JOB,為了支持冪等,支持重復執行,簡單的處理方法是,先查詢下一些關鍵數據,判斷是否已經執行過,在進行業務處理,就可以了
注意:核心高并發流程不要用這種方法
狀態機冪等
在設計單據相關的業務,或者是任務相關的業務,肯定會涉及到狀態機(狀態變更圖),就是業務單據上面有個狀態,狀態在不同的情況下會發生變更,一般情況下存在有限狀態機,這時候,如果狀態機已經處于下一個狀態,這時候來了一個上一個狀態的變更,理論上是不能夠變更的,這樣的話,保證了有限狀態機的冪等。
對外提供接口的api如何保證冪等
如銀聯提供的付款接口:需要接入商戶提交付款請求時附帶:source來源,seq序列號
source+seq在數據庫里面做唯一索引,防止多次付款,(并發時,只能處理一個請求)
摘抄自:http://825635381.iteye.com/blog/2276077
消息的堆積解決思路
如果還沒開始投入使用kafka,那應該在設計分區數的時候,盡量設置的多點(當然也不要太大,太大影響延遲,具體可以參考我前面提到的文章),從而提升生產和消費的并行度,避免消費太慢導致消費堆積。
增大批次
瓶頸在消費吞吐量的時候,增加批次也可以改善性能
增加線程數
如果一些消費者組中的消費者線程還是有1個消費者線程消費多個分區的情況,建議增加消費者線程。盡量1個消費者線程對應1個分區,從而發揮現有分區數下的最大并行度。
摘抄自:https://kaimingwan.com/post/framworks/kafka/kafkaxiao-xi-dui-ji-chu-li
自己如何實現消息隊列
大體上的設計是由一條線程1執行從等待列表中獲取任務插入任務隊列再由線程池中的線程從任務隊列中取出任務去執行.
添加一條線程1主要是防止在執行耗時的任務時阻塞主線程.當執行耗時任務時,添加的任務的操作快于取出任務的操作,
當任務隊列長度達到最大值時,線程1將被阻塞,等待線程2,3…從任務隊列取出任務執行。
作者:DrJasonZhang
鏈接:https://www.jianshu.com/p/2d2271ecc64d
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
如何保證消息的有序性
通過輪詢所有隊列的方式來確定消息被發送到哪一個隊列(負載均衡策略)。訂單號相同的消息會被先后發送到同一個隊列中,
在獲取到路由信息以后,會根據算法來選擇一個隊列,同一個OrderId獲取到的肯定是同一個隊列。
框架篇
Spring
BeanFactory 和 ApplicationContext 有什么區別
BeanFactory 可以理解為含有bean集合的工廠類。BeanFactory 包含了種bean的定義,以便在接收到客戶端請求時將對應的bean實例化。
BeanFactory還能在實例化對象的時生成協作類之間的關系。此舉將bean自身與bean客戶端的配置中解放出來。BeanFactory還包含了bean生命周期的控制,調用客戶端的初始化方法(initialization methods)和銷毀方法(destruction methods)。
從表面上看,application context如同bean factory一樣具有bean定義、bean關聯關系的設置,根據請求分發bean的功能。但application context在此基礎上還提供了其他的功能。
提供了支持國際化的文本消息
統一的資源文件讀取方式
已在監聽器中注冊的bean的事件
摘抄自:http://www.importnew.com/15851.html
Spring Bean 的生命周期
Spring Bean的生命周期簡單易懂。在一個bean實例被初始化時,需要執行一系列的初始化操作以達到可用的狀態。同樣的,當一個bean不在被調用時需要進行相關的析構操作,并從bean容器中移除。
Spring bean factory 負責管理在spring容器中被創建的bean的生命周期。Bean的生命周期由兩組回調(call back)方法組成。
初始化之后調用的回調方法。
銷毀之前調用的回調方法。
Spring框架提供了以下四種方式來管理bean的生命周期事件:
InitializingBean和DisposableBean回調接口
針對特殊行為的其他Aware接口
Bean配置文件中的Custom init()方法和destroy()方法
@PostConstruct和@PreDestroy注解方式
摘抄自:http://www.importnew.com/15851.html
Spring IOC 如何實現
Spring中的 org.springframework.beans 包和 org.springframework.context包構成了Spring框架IoC容器的基礎。
BeanFactory 接口提供了一個先進的配置機制,使得任何類型的對象的配置成為可能。ApplicationContex接口對BeanFactory(是一個子接口)進行了擴展,在BeanFactory的基礎上添加了其他功能,比如與Spring的AOP更容易集成,也提供了處理message resource的機制(用于國際化)、事件傳播以及應用層的特別配置,比如針對Web應用的WebApplicationContext。
org.springframework.beans.factory.BeanFactory 是Spring IoC容器的具體實現,用來包裝和管理前面提到的各種bean。BeanFactory接口是Spring IoC 容器的核心接口。
說說 Spring AOP
面向切面編程,在我們的應用中,經常需要做一些事情,但是這些事情與核心業務無關,比如,要記錄所有update方法的執行時間時間,操作人等等信息,記錄到日志,通過spring的AOP技術,就可以在不修改update的代碼的情況下完成該需求。
Spring AOP 實現原理
Spring AOP中的動態代理主要有兩種方式,JDK動態代理和CGLIB動態代理。JDK動態代理通過反射來接收被代理的類,并且要求被代理的類必須實現一個接口。JDK動態代理的核心是InvocationHandler接口和Proxy類。
如果目標類沒有實現接口,那么Spring AOP會選擇使用CGLIB來動態代理目標類。CGLIB(Code Generation Library),是一個代碼生成的類庫,可以在運行時動態的生成某個類的子類,注意,CGLIB是通過繼承的方式做的動態代理,因此如果某個類被標記為final,那么它是無法使用CGLIB做動態代理的。
動態代理(cglib 與 JDK)
JDK 動態代理類和委托類需要都實現同一個接口。也就是說只有實現了某個接口的類可以使用Java動態代理機制。但是,事實上使用中并不是遇到的所有類都會給你實現一個接口。因此,對于沒有實現接口的類,就不能使用該機制。而CGLIB則可以實現對類的動態代理。
摘抄自:http://www.importnew.com/22015.html
Spring 事務實現方式
1、編碼方式
所謂編程式事務指的是通過編碼方式實現事務,即類似于JDBC編程實現事務管理。
2、聲明式事務管理方式
聲明式事務管理又有兩種實現方式:基于xml配置文件的方式;另一個是在業務方法上進行@Transaction注解,將事務規則應用到業務邏輯中
Spring 事務底層原理
a、劃分處理單元——IOC
由于spring解決的問題是對單個數據庫進行局部事務處理的,具體的實現首先用spring中的IOC劃分了事務處理單元。并且將對事務的各種配置放到了ioc容器中(設置事務管理器,設置事務的傳播特性及隔離機制)。
b、AOP攔截需要進行事務處理的類
Spring事務處理模塊是通過AOP功能來實現聲明式事務處理的,具體操作(比如事務實行的配置和讀取,事務對象的抽象),用TransactionProxyFactoryBean接口來使用AOP功能,生成proxy代理對象,通過TransactionInterceptor完成對代理方法的攔截,將事務處理的功能編織到攔截的方法中。讀取ioc容器事務配置屬性,轉化為spring事務處理需要的內部數據結構(TransactionAttributeSourceAdvisor),轉化為TransactionAttribute表示的數據對象。
c、對事物處理實現(事務的生成、提交、回滾、掛起)
spring委托給具體的事務處理器實現。實現了一個抽象和適配。適配的具體事務處理器:DataSource數據源支持、hibernate數據源事務處理支持、JDO數據源事務處理支持,JPA、JTA數據源事務處理支持。這些支持都是通過設計PlatformTransactionManager、AbstractPlatforTransaction一系列事務處理的支持。為常用數據源支持提供了一系列的TransactionManager。
d、結合
PlatformTransactionManager實現了TransactionInterception接口,讓其與TransactionProxyFactoryBean結合起來,形成一個Spring聲明式事務處理的設計體系。
如何自定義注解實現功能
創建自定義注解和創建一個接口相似,但是注解的interface關鍵字需要以@符號開頭。
注解方法不能帶有參數;
注解方法返回值類型限定為:基本類型、String、Enums、Annotation或者是這些類型的數組;
注解方法可以有默認值;
注解本身能夠包含元注解,元注解被用來注解其它注解。
摘抄自:http://www.importnew.com/20286.html
Spring MVC 運行流程
1.spring mvc將所有的請求都提交給DispatcherServlet,它會委托應用系統的其他模塊負責對請求 進行真正的處理工作。
2.DispatcherServlet查詢一個或多個HandlerMapping,找到處理請求的Controller.
3.DispatcherServlet請請求提交到目標Controller
4.Controller進行業務邏輯處理后,會返回一個ModelAndView
5.Dispathcher查詢一個或多個ViewResolver視圖解析器,找到ModelAndView對象指定的視圖對象
6.視圖對象負責渲染返回給客戶端。
摘抄自:http://blog.csdn.net/liangzi_lucky/article/details/52459378
Spring MVC 啟動流程
在 web.xml 文件中給 Spring MVC 的 Servlet 配置了 load-on-startup,所以程序啟動的
時候會初始化 Spring MVC,在 HttpServletBean 中將配置的 contextConfigLocation
屬性設置到 Servlet 中,然后在 FrameworkServlet 中創建了 WebApplicationContext,
DispatcherServlet 根據 contextConfigLocation 配置的 classpath 下的 xml 文件初始化了
Spring MVC 總的組件。
摘自:《SpringMVC 源代碼分析與實踐》
Spring 的單例實現原理
Spring 對 Bean 實例的創建是采用單例注冊表的方式進行實現的,而這個注冊表的緩存是 ConcurrentHashMap 對象。
摘抄自:http://blog.720ui.com/2017/design_pattern_singleton_reg/
Spring 框架中用到了哪些設計模式
代理模式—在AOP和remoting中被用的比較多。
單例模式—在spring配置文件中定義的bean默認為單例模式。
模板方法—用來解決代碼重復的問題。比如. RestTemplate, JmsTemplate, JpaTemplate。
前端控制器—Spring提供了DispatcherServlet來對請求進行分發。
視圖幫助(View Helper )—Spring提供了一系列的JSP標簽,高效宏來輔助將分散的代碼整合在視圖里。
依賴注入—貫穿于BeanFactory / ApplicationContext接口的核心理念。
工廠模式—BeanFactory用來創建對象的實例。
摘抄自:http://www.importnew.com/15851.html#design_patterns_used_in_spring
Spring 其他產品(Srping Boot、Spring Cloud、Spring Secuirity、Spring Data、Spring AMQP 等)
Netty
為什么選擇 Netty
1) API使用簡單,開發門檻低;
2) 功能強大,預置了多種編解碼功能,支持多種主流協議;
3) 定制能力強,可以通過 ChannelHandler 對通信框架進行靈活的擴展;
4) 性能高,通過與其它業界主流的NIO框架對比,Netty的綜合性能最優;
5) 成熟、穩定,Netty修復了已經發現的所有JDK NIO BUG,業務開發人員不需要再為NIO的BUG而煩惱;
6) 社區活躍,版本迭代周期短,發現的BUG可以被及時修復,同時,更多的新功能會被加入;
7) 經歷了大規模的商業應用考驗,質量已經得到驗證。在互聯網、大數據、網絡游戲、企業應用、電信軟件等眾多行業得到成功商用,證明了它可以完全滿足不同行業的商業應用。
正是因為這些優點,Netty逐漸成為Java NIO編程的首選框架。
摘抄自:http://ifeve.com/netty-2-6/
說說業務中,Netty 的使用場景
構建高性能、低時延的各種Java中間件,例如MQ、分布式服務框架、ESB消息總線等,Netty主要作為基礎通信框架提供高性能、低時延的通信服務;
公有或者私有協議棧的基礎通信框架,例如可以基于Netty構建異步、高性能的WebSocket協議棧;
各領域應用,例如大數據、游戲等,Netty作為高性能的通信框架用于內部各模塊的數據分發、傳輸和匯總等,實現模塊之間高性能通信。
摘抄自:http://www.voidcn.com/article/p-xydqhgxk-uk.html
原生的 NIO 在 JDK 1.7 版本存在 epoll bug
它會導致Selector空輪詢,最終導致CPU 100%。官方聲稱在JDK 1.6版本的update18修復了該問題,但是直到JDK 1.7版本該問題仍舊存在,只不過該BUG發生概率降低了一些而已,它并沒有得到根本性解決。
摘抄自:http://blog.csdn.net/broadview2006/article/details/46041995
什么是TCP 粘包/拆包
1、要發送的數據大于TCP發送緩沖區剩余空間大小,將會發生拆包。
2、待發送數據大于MSS(最大報文長度),TCP在傳輸前將進行拆包。
3、要發送的數據小于TCP發送緩沖區的大小,TCP將多次寫入緩沖區的數據一次發送出去,將會發生粘包。
4、接收數據端的應用層沒有及時讀取接收緩沖區中的數據,將發生粘包。
摘抄自:https://blog.insanecoder.top/tcp-packet-splice-and-split-issue/
TCP粘包/拆包的解決辦法
1、發送端給每個數據包添加包首部,首部中應該至少包含數據包的長度,這樣接收端在接收到數據后,通過讀取包首部的長度字段,便知道每一個數據包的實際長度了。
2、發送端將每個數據包封裝為固定長度(不夠的可以通過補0填充),這樣接收端每次從接收緩沖區中讀取固定長度的數據就自然而然的把每個數據包拆分開來。
3、可以在數據包之間設置邊界,如添加特殊符號,這樣,接收端通過這個邊界就可以將不同的數據包拆分開。
摘抄自:https://blog.insanecoder.top/tcp-packet-splice-and-split-issue/
Netty 線程模型
首先,Netty使用EventLoop來處理連接上的讀寫事件,而一個連接上的所有請求都保證在一個EventLoop中被處理,一個EventLoop中只有一個Thread,所以也就實現了一個連接上的所有事件只會在一個線程中被執行。一個EventLoopGroup包含多個EventLoop,可以把一個EventLoop當做是Reactor線程模型中的一個線程,而一個EventLoopGroup類似于一個ExecutorService
作者:一字馬胡
鏈接:https://www.jianshu.com/p/128ddc36e713
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
說說 Netty 的零拷貝
“零拷貝”是指計算機操作的過程中,CPU不需要為數據在內存之間的拷貝消耗資源。而它通常是指計算機在網絡上發送文件時,不需要將文件內容拷貝到用戶空間(User Space)而直接在內核空間(Kernel Space)中傳輸到網絡的方式。
Netty 內部執行流程
Netty的接收和發送ByteBuffer采用DIRECT BUFFERS,使用堆外直接內存進行Socket讀寫,不需要進行字節緩沖區的二次拷貝。如果使用傳統的堆內存(HEAP BUFFERS)進行Socket讀寫,JVM會將堆內存Buffer拷貝一份到直接內存中,然后才寫入Socket中。相比于堆外直接內存,消息在發送過程中多了一次緩沖區的內存拷貝。
Netty提供了組合Buffer對象,可以聚合多個ByteBuffer對象,用戶可以像操作一個Buffer那樣方便的對組合Buffer進行操作,避免了傳統通過內存拷貝的方式將幾個小Buffer合并成一個大的Buffer。
Netty的文件傳輸采用了transferTo方法,它可以直接將文件緩沖區的數據發送到目標Channel,避免了傳統通過循環write方式導致的內存拷貝問題。
摘抄自:http://blog.onlycatch.com/post/Netty%E4%B8%AD%E7%9A%84%E9%9B%B6%E6%8B%B7%E8%B4%9D
Netty 重連實現
1.心跳機制檢測連接存活
2.啟動時連接重試
3.運行中連接斷開時重試
摘抄自:http://www.spring4all.com/article/889
簡述設計模式原則
1、為什么需要設計模式
其實沒有設計模式我們也能完成開發工作。但是為什么需要設計模式呢?讓你看起來很牛,沒錯這個算一個。讓你的代碼層次感分明,可讀性強而且容易維護。讓你像我一樣有更多的摸魚劃水時間。
可能有人說我一個類或者方法就干完的東西,你搞了七八個。當然使用設計模式也是要斟酌的。一些簡單穩定的業務也不推薦使用設計模式。設計模式多用于復雜多變的業務或者要求適配性、擴展性更強的場景中。不要為了設計模式而設計模式。
接下來我們結合實際開探討一下設計模式的一些原則。
2、開閉原則
public class Seller {????public?BigDecimal?sellCar(Car?car)?{return car.getPrice();????}}
上面模擬4S店一個銷售在賣車。突然老板搞了一個促銷:在雙十一要開展打折活動。在sellCar方法內增加一個計算可行嗎?這勢必影響整個業務,導致所有車都打折。不行不行!那么在Car里面操作?然后你改啊改!結果各種邏輯流程判斷。才實現了業務要求。如果后續打折活動結束了或者升級了,你還要再進行各種改動。你發現一個打折讓你的代碼面目全非、臃腫不堪。上面說了對于復雜而多變的業務使用設計模式就可以解決。
那么設計模式最重要的一個原則就是開閉原則。也就是說一個軟件模型實體如類、模塊和函數應該對擴展開放,對修改關閉。也就是需要我們將業務行為抽象出來,使用抽象來構建。具體的業務通過抽象的實現來解決。那么我們就搞一個DiscountCar來extends Car.這樣sellCar是什么具體的實現就執行什么具體的邏輯。不會影響以前的邏輯,而且不會因為改動原來的代碼影響其他邏輯。保證接口可靠性和穩定性。如下:
??
public class DiscountCar extends Car{private BigDecimal price;private BigDecimal discount;
@Overridepublic BigDecimal getPrice() {return price.multiply(discount); }
}
3、依賴倒置原則
還拿上面的例子來說。經過一系列的打折活動4S店的生意蒸蒸日上。老板突然想擴展一下周邊,同時壓榨一下銷售。讓他們賣車的同時賣點玻璃水、防凍液之類的。這個需求當然又拋給了苦逼的程序員。sellCar太具體了不能滿足需要了。很多情況下你會增加一個賣玻璃水、賣防凍液的方法。如果以后增加了賣大米,甚至買起了雞蛋餅呢?總不能一直增加方法吧。我們需要考慮這種問題。我們可以抽象所有賣東西的場景。然后我們把賣的物品抽象成了一個抽象化的概念(java對應的是接口,把賣的行為抽象成了sell方法:
?
public interface Any {String getName();
BigDecimal getPrice();
}
public class Seller {
public BigDecimal sell(Any any) {return any.getPrice(); }}
這樣隨便老板以后賣什么你都可以通過該方法進行處理了,只需要關注于Any的實現。
4、職責單一原則
4S店銷售賣了一段東西后,發現對客戶的吸引力度不大。突然腦子比較靈活的老板又想起了電影中的一句臺詞:少林功夫加唱歌跳舞有沒有搞頭?對啊你們銷售能不能搞搞什么唱、跳、Rap,當然打籃球就不要了別砸壞了車玻璃。但是人與人是不一樣的,有的人只會唱,有的人只會跳,有的人可能唱跳Rap都會甚至籃球都很溜。所以為了適配這么多情況,我們必須把每種技能獨立出來,根據不同的人來組合這些技能。
?
public class Seller implements Sing, Jump, Rap {public BigDecimal sell(Any any) {return any.doSell(); }
@Overridepublic void sing() { System.out.println("seller sing "); }
@Overridepublic void jump() { System.out.println("seller jumping "); }
@Overridepublic void rap() { System.out.println("seller raping "); }}
但是注意一定要適度,根據業務來細分。否則會導致接口過多反而增大開發難度以及代碼的復雜度。
5、迪米特法則
新的銷售方法搞了一段時間后,老板想看看檢驗一下他這個餿主意的效果。于是就叫了一個銷售讓他提供一份報表出來看看。那么程序員該如何實現老板查看報表功能呢,很可能有人會這么寫:
public class Boss {private Seller seller;private Report report;public void read() { seller.apply(report); }}
?
乍看功能實現了,細看會發現邏輯不對。哪里不對呢?老板已經持有了報表,如果老板已經知道了你的業績還叫你干什么?這種邏輯肯定是不對的!也就是說Boss直接依賴了Report;而這個Report不應該直接由Boss處理,而應由Seller控制。
?
public class Boss {private Seller seller;public void read(){ seller.apply();????}???}
public class Seller {private Report report;
public void apply(){ report.show();??????}}
這種最大化隔離了類與類之間的關系。降低了類之間的耦合。迪米特法則因此又得名最少知道原則。
6、接口隔離原則
用多個專門的接口,而不使用單一的總接口,客戶端不應該依賴它不需要的接口。一個類對一個類的依賴應該建立在最小的接口上。
建立單一接口,不要建立龐大臃腫的接口盡量細化接口,接口中的方法盡量少,盡量細化接口。注意適度原則,一定要適度。不能濫用
就像上面的唱跳 rap,分離是最好的。
7、里氏代換原則
這里主要針對類的繼承關系而言。比較正式的定義:如果對每一個類型為S的對象o1,都有類型為T的對象o2,使得以T定義的所有程序P在所有的對象o1都代換成o2 時,程序P的行為沒有發生變化,那么類型 S 是類型 T 的子類型。
在4S店老板眼里,只要新來的能在銷售崗位上像銷售老手一樣賣出汽車,他就是一名合格的銷售。感覺這種定義就像一句名言:不管你黑貓白貓,能抓老鼠的都是好貓。
從某種含義上里氏代換有著以下的契約:
1. 子類必須完全實現父類的方法。父類出現的地方子類都可以代替父類。
2. ?子類可以有自己的個性定義。里氏替換原則 可以正著用,但是不能反過來用。在子類出現的地方,父類未必就可以勝任。子類一般比父類有個性。
3. 覆蓋或實現父類的方法時輸入參數可以被放大。如果4S店老板規定基礎車談價的折扣最多九折,銷售打個九五折沒有問題,打八折老板肯定要跟你說道說道了。
4. 覆寫或實現父類的方法時輸出結果可以被縮小。同樣是15W本來只能賣出給客戶一個乞丐版,結果換了個銷售結果給出了一輛旗艦版。怕不是過不了試用期哦。
8、合成/復用原則
它要求在軟件復用時,要盡量先使用組合或者聚合等關聯關系來實現,其次才考慮使用繼承關系來實現。
如果要使用繼承關系,則必須嚴格遵循里氏替換原則。合成復用原則同里氏替換原則相輔相成的,兩者都是開閉原則的具體實現規范。
9、總結
這七種設計原則是軟件設計模式必須盡量遵循的原則,各種原則要求的側重點不同。其中,開閉原則是總綱,它告訴我們要對擴展開放,對修改關閉;里氏替換原則告訴我們不要破壞繼承體系;依賴倒置原則告訴我們要面向接口編程;單一職責原則告訴我們實現類要職責單一;接口隔離原則告訴我們在設計接口的時候要精簡單一;迪米特法則告訴我們要降低耦合度;合成復用原則告訴我們要優先使用組合或者聚合關系復用,少用繼承關系復用。在實際開發中我們可以根據業務來進行設計模式的使用,但是很重要的一點千萬不要被這些條條框框束縛了你的手腳。
數據存儲
MySQL 索引使用的注意事項
1.索引不會包含有NULL的列
只要列中包含有NULL值,都將不會被包含在索引中,復合索引中只要有一列含有NULL值,那么這一列對于此符合索引就是無效的。
2.使用短索引
對串列進行索引,如果可以就應該指定一個前綴長度。例如,如果有一個char(255)的列,如果在前10個或20個字符內,多數值是唯一的,那么就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁盤空間和I/O操作。
3.索引列排序
mysql查詢只使用一個索引,因此如果where子句中已經使用了索引的話,那么order by中的列是不會使用索引的。因此數據庫默認排序可以符合要求的情況下不要使用排序操作,盡量不要包含多個列的排序,如果需要最好給這些列建復合索引。
4.like語句操作
一般情況下不鼓勵使用like操作,如果非使用不可,注意正確的使用方式。like ‘%aaa%’不會使用索引,而like ‘aaa%’可以使用索引。
5.不要在列上進行運算
6.不使用NOT IN 、<>、!=操作,但<,<=,=,>,>=,BETWEEN,IN是可以用到索引的
7.索引要建立在經常進行select操作的字段上。
這是因為,如果這些列很少用到,那么有無索引并不能明顯改變查詢速度。相反,由于增加了索引,反而降低了系統的維護速度和增大了空間需求。
8.索引要建立在值比較唯一的字段上。
9.對于那些定義為text、image和bit數據類型的列不應該增加索引。因為這些列的數據量要么相當大,要么取值很少。
10.在where和join中出現的列需要建立索引。
11.where的查詢條件里有不等號(where column != …),mysql將無法使用索引。
12.如果where字句的查詢條件里使用了函數(如:where DAY(column)=…),mysql將無法使用索引。
13.在join操作中(需要從多個數據表提取數據時),mysql只有在主鍵和外鍵的數據類型相同時才能使用索引,否則及時建立了索引也不會使用。
說說分庫與分表設計
垂直分表在日常開發和設計中比較常見,通俗的說法叫做“大表拆小表”,拆分是基于關系型數據庫中的“列”(字段)進行的。通常情況,某個表中的字段比較多,可以新建立一張“擴展表”,將不經常使用或者長度較大的字段拆分出去放到“擴展表”中。在字段很多的情況下,拆分開確實更便于開發和維護(筆者曾見過某個遺留系統中,一個大表中包含100多列的)。某種意義上也能避免“跨頁”的問題(MySQL、MSSQL底層都是通過“數據頁”來存儲的,“跨頁”問題可能會造成額外的性能開銷,拆分字段的操作建議在數據庫設計階段就做好。如果是在發展過程中拆分,則需要改寫以前的查詢語句,會額外帶來一定的成本和風險,建議謹慎。
垂直分庫在“微服務”盛行的今天已經非常普及了。基本的思路就是按照業務模塊來劃分出不同的數據庫,而不是像早期一樣將所有的數據表都放到同一個數據庫中。系統層面的“服務化”拆分操作,能夠解決業務系統層面的耦合和性能瓶頸,有利于系統的擴展維護。而數據庫層面的拆分,道理也是相通的。與服務的“治理”和“降級”機制類似,我們也能對不同業務類型的數據進行“分級”管理、維護、監控、擴展等。
眾所周知,數據庫往往最容易成為應用系統的瓶頸,而數據庫本身屬于“有狀態”的,相對于Web和應用服務器來講,是比較難實現“橫向擴展”的。數據庫的連接資源比較寶貴且單機處理能力也有限,在高并發場景下,垂直分庫一定程度上能夠突破IO、連接數及單機硬件資源的瓶頸,是大型分布式系統中優化數據庫架構的重要手段。
然后,很多人并沒有從根本上搞清楚為什么要拆分,也沒有掌握拆分的原則和技巧,只是一味的模仿大廠的做法。導致拆分后遇到很多問題(例如:跨庫join,分布式事務等)。
水平分表也稱為橫向分表,比較容易理解,就是將表中不同的數據行按照一定規律分布到不同的數據庫表中(這些表保存在同一個數據庫中),這樣來降低單表數據量,優化查詢性能。最常見的方式就是通過主鍵或者時間等字段進行Hash和取模后拆分。水平分表,能夠降低單表的數據量,一定程度上可以緩解查詢性能瓶頸。但本質上這些表還保存在同一個庫中,所以庫級別還是會有IO瓶頸。所以,一般不建議采用這種做法。
水平分庫分表與上面講到的水平分表的思想相同,唯一不同的就是將這些拆分出來的表保存在不同的數據中。這也是很多大型互聯網公司所選擇的做法。某種意義上來講,有些系統中使用的“冷熱數據分離”(將一些使用較少的歷史數據遷移到其他的數據庫中。而在業務功能上,通常默認只提供熱點數據的查詢),也是類似的實踐。在高并發和海量數據的場景下,分庫分表能夠有效緩解單機和單庫的性能瓶頸和壓力,突破IO、連接數、硬件資源的瓶頸。當然,投入的硬件成本也會更高。同時,這也會帶來一些復雜的技術問題和挑戰(例如:跨分片的復雜查詢,跨分片事務等)。
以上摘抄自:http://www.infoq.com/cn/articles/key-steps-and-likely-problems-of-split-table
分庫與分表帶來的分布式困境與應對之策
數據遷移與擴容問題
前面介紹到水平分表策略歸納總結為隨機分表和連續分表兩種情況。連續分表有可能存在數據熱點的問題,有些表可能會被頻繁地查詢從而造成較大壓力,熱數據的表就成為了整個庫的瓶頸,而有些表可能存的是歷史數據,很少需要被查詢到。連續分表的另外一個好處在于比較容易,不需要考慮遷移舊的數據,只需要添加分表就可以自動擴容。隨機分表的數據相對比較均勻,不容易出現熱點和并發訪問的瓶頸。但是,分表擴展需要遷移舊的數據。
針對于水平分表的設計至關重要,需要評估中短期內業務的增長速度,對當前的數據量進行容量規劃,綜合成本因素,推算出大概需要多少分片。對于數據遷移的問題,一般做法是通過程序先讀出數據,然后按照指定的分表策略再將數據寫入到各個分表中。
表關聯問題
在單庫單表的情況下,聯合查詢是非常容易的。但是,隨著分庫與分表的演變,聯合查詢就遇到跨庫關聯和跨表關系問題。在設計之初就應該盡量避免聯合查詢,可以通過程序中進行拼裝,或者通過反范式化設計進行規避。
分頁與排序問題
一般情況下,列表分頁時需要按照指定字段進行排序。在單庫單表的情況下,分頁和排序也是非常容易的。但是,隨著分庫與分表的演變,也會遇到跨庫排序和跨表排序問題。為了最終結果的準確性,需要在不同的分表中將數據進行排序并返回,并將不同分表返回的結果集進行匯總和再次排序,最后再返回給用戶。
分布式事務問題
隨著分庫與分表的演變,一定會遇到分布式事務問題,那么如何保證數據的一致性就成為一個必須面對的問題。目前,分布式事務并沒有很好的解決方案,難以滿足數據強一致性,一般情況下,使存儲數據盡可能達到用戶一致,保證系統經過一段較短的時間的自我恢復和修正,數據最終達到一致。
分布式全局唯一ID
在單庫單表的情況下,直接使用數據庫自增特性來生成主鍵ID,這樣確實比較簡單。在分庫分表的環境中,數據分布在不同的分表上,不能再借助數據庫自增長特性。需要使用全局唯一 ID,例如 UUID、GUID等。關于如何選擇合適的全局唯一 ID,我會在后面的章節中進行介紹。
摘抄自:http://blog.csdn.net/jiangpingjiangping/article/details/78069480
說說 SQL 優化之道
一、一些常見的SQL實踐
(1)負向條件查詢不能使用索引
select * from order where status!=0 and stauts!=1
not in/not exists都不是好習慣
可以優化為in查詢:
select * from order where status in(2,3)
(2)前導模糊查詢不能使用索引
select * from order where desc like '%XX'
而非前導模糊查詢則可以:
select * from order where desc like 'XX%'
(3)數據區分度不大的字段不宜使用索引
select * from user where sex=1
原因:性別只有男,女,每次過濾掉的數據很少,不宜使用索引。
經驗上,能過濾80%數據時就可以使用索引。對于訂單狀態,如果狀態值很少,不宜使用索引,如果狀態值很多,能夠過濾大量數據,則應該建立索引。
(4)在屬性上進行計算不能命中索引
select * from order where YEAR(date) < = '2017' 即使date上建立了索引,也會全表掃描,可優化為值計算:select * from order where date < = CURDATE() 或者:select * from order where date < = '2017-01-01' 二、并非周知的SQL實踐 (5)如果業務大部分是單條查詢,使用Hash索引性能更好,例如用戶中心 select * from user where uid=? select * from user where login_name=? 原因:B-Tree索引的時間復雜度是O(log(n));Hash索引的時間復雜度是O(1) (6)允許為null的列,查詢有潛在大坑 單列索引不存null值,復合索引不存全為null的值,如果列允許為null,可能會得到“不符合預期”的結果集 select * from user where name != 'shenjian' 如果name允許為null,索引不存儲null值,結果集中不會包含這些記錄。所以,請使用not null約束以及默認值。(7)復合索引最左前綴,并不是值SQL語句的where順序要和復合索引一致 用戶中心建立了(login_name, passwd)的復合索引 select * from user where login_name=? and passwd=? select * from user where passwd=? and login_name=? 都能夠命中索引 select * from user where login_name=? 也能命中索引,滿足復合索引最左前綴 select * from user where passwd=? 不能命中索引,不滿足復合索引最左前綴 (8)使用ENUM而不是字符串 ENUM保存的是TINYINT,別在枚舉中搞一些“中國”“北京”“技術部”這樣的字符串,字符串空間又大,效率又低。三、小眾但有用的SQL實踐 (9)如果明確知道只有一條結果返回,limit 1能夠提高效率 select * from user where login_name=? 可以優化為:select * from user where login_name=? limit 1 原因:你知道只有一條結果,但數據庫并不知道,明確告訴它,讓它主動停止游標移動 (10)把計算放到業務層而不是數據庫層,除了節省數據的CPU,還有意想不到的查詢緩存優化效果 select * from order where date < = CURDATE() 這不是一個好的SQL實踐,應該優化為:?res = mysql_query(
'select * from order where date < = $curDate');
原因:
釋放了數據庫的CPU
多次調用,傳入的SQL相同,才可以利用查詢緩存
(11)強制類型轉換會全表掃描
select * from user where phone=13800001234
你以為會命中phone索引么?大錯特錯了,這個語句究竟要怎么改?
末了,再加一條,不要使用select *(潛臺詞,文章的SQL都不合格 =_=),只返回需要的列,能夠大大的節省數據傳輸量,與數據庫的內存使用量喲。
整理自:https://cloud.tencent.com/developer/article/1054203
MySQL 遇到的死鎖問題
產生死鎖的四個必要條件:
(1) 互斥條件:一個資源每次只能被一個進程使用。
(2) 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
(3) 不剝奪條件:進程已獲得的資源,在末使用完之前,不能強行剝奪。
(4) 循環等待條件:若干進程之間形成一種頭尾相接的循環等待資源關系。
這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之一不滿足,就不會發生死鎖。
下列方法有助于最大限度地降低死鎖:
(1)按同一順序訪問對象。
(2)避免事務中的用戶交互。
(3)保持事務簡短并在一個批處理中。
(4)使用低隔離級別。
(5)使用綁定連接。
整理自:http://onwise.xyz/2017/04/20/mysql-%E6%AD%BB%E9%94%81%E9%97%AE%E9%A2%98%E5%8F%8A%E8%A7%A3%E5%86%B3/
存儲引擎的 InnoDB 與 MyISAM
◆1.InnoDB不支持FULLTEXT類型的索引。
◆2.InnoDB 中不保存表的具體行數,也就是說,執行select count() from table時,InnoDB要掃描一遍整個表來計算有多少行,但是MyISAM只要簡單的讀出保存好的行數即可。注意的是,當count()語句包含 where條件時,兩種表的操作是一樣的。
◆3.對于AUTO_INCREMENT類型的字段,InnoDB中必須包含只有該字段的索引,但是在MyISAM表中,可以和其他字段一起建立聯合索引。
◆4.DELETE FROM table時,InnoDB不會重新建立表,而是一行一行的刪除。
◆5.LOAD TABLE FROM MASTER操作對InnoDB是不起作用的,解決方法是首先把InnoDB表改成MyISAM表,導入數據后再改成InnoDB表,但是對于使用的額外的InnoDB特性(例如外鍵)的表不適用。
另外,InnoDB表的行鎖也不是絕對的,假如在執行一個SQL語句時MySQL不能確定要掃描的范圍,InnoDB表同樣會鎖全表,例如update table set num=1 where name like “%aaa%”
數據庫索引的原理
數據庫索引,是數據庫管理系統中一個排序的數據結構,以協助快速查詢、更新數據庫表中數據。索引的實現通常使用B樹及其變種B+樹。
為什么要用 B-tree
一般來說,索引本身也很大,不可能全部存儲在內存中,因此索引往往以索引文件的形式存儲的磁盤上。這樣的話,索引查找過程中就要產生磁盤I/O消耗,相對于內存存取,I/O存取的消耗要高幾個數量級,所以評價一個數據結構作為索引的優劣最重要的指標就是在查找過程中磁盤I/O操作次數的漸進復雜度。換句話說,索引的結構組織要盡量減少查找過程中磁盤I/O的存取次數。
聚集索引與非聚集索引的區別
1).聚集索引一個表只能有一個,而非聚集索引一個表可以存在多個
2).聚集索引存儲記錄是物理上連續存在,而非聚集索引是邏輯上的連續,物理存儲并不連續
3).聚集索引:物理存儲按照索引排序;聚集索引是一種索引組織形式,索引的鍵值邏輯順序決定了表數據行的物理存儲順序
非聚集索引:物理存儲不按照索引排序;非聚集索引則就是普通索引了,僅僅只是對數據列創建相應的索引,不影響整個表的物理存儲順序.
4).索引是通過二叉樹的數據結構來描述的,我們可以這么理解聚簇索引:索引的葉節點就是數據節點。而非聚簇索引的葉節點仍然是索引節點,只不過有一個指針指向對應的數據塊。
limit 20000 加載很慢怎么解決
mysql的性能低是因為數據庫要去掃描N+M條記錄,然后又要放棄之前N條記錄,開銷很大
解決思略:
1、前端加緩存,或者其他方式,減少落到庫的查詢操作,例如某些系統中數據在搜索引擎中有備份的,可以用es等進行搜索
2、使用延遲關聯,即先通用limit得到需要數據的索引字段,然后再通過原表和索引字段關聯獲得需要數據
select a.* from a,(select id from table_1 where is_deleted='N' limit 100000,20) b where a.id = b.id
3、從業務上實現,不分頁如此多,例如只能分頁前100頁,后面的不允許再查了
4、不使用limit N,M,而是使用limit N,即將offset轉化為where條件。
選擇合適的分布式主鍵方案
數據庫自增長序列或字段
UUID
使用UUID to Int64的方法
Redis生成ID
Twitter的snowflake算法
利用zookeeper生成唯一ID
MongoDB的ObjectId
選擇合適的數據存儲方案
關系型數據庫 MySQL
MySQL 是一個最流行的關系型數據庫,在互聯網產品中應用比較廣泛。一般情況下,MySQL 數據庫是選擇的第一方案,基本上有 80% ~ 90% 的場景都是基于 MySQL 數據庫的。因為,需要關系型數據庫進行管理,此外,業務存在許多事務性的操作,需要保證事務的強一致性。同時,可能還存在一些復雜的 SQL 的查詢。值得注意的是,前期盡量減少表的聯合查詢,便于后期數據量增大的情況下,做數據庫的分庫分表。
內存數據庫 Redis
隨著數據量的增長,MySQL 已經滿足不了大型互聯網類應用的需求。因此,Redis 基于內存存儲數據,可以極大的提高查詢性能,對產品在架構上很好的補充。例如,為了提高服務端接口的訪問速度,盡可能將讀頻率高的熱點數據存放在 Redis 中。這個是非常典型的以空間換時間的策略,使用更多的內存換取 CPU 資源,通過增加系統的內存消耗,來加快程序的運行速度。
在某些場景下,可以充分的利用 Redis 的特性,大大提高效率。這些場景包括緩存,會話緩存,時效性,訪問頻率,計數器,社交列表,記錄用戶判定信息,交集、并集和差集,熱門列表與排行榜,最新動態等。
使用 Redis 做緩存的時候,需要考慮數據不一致與臟讀、緩存更新機制、緩存可用性、緩存服務降級、緩存穿透、緩存預熱等緩存使用問題。
文檔數據庫 MongoDB
MongoDB 是對傳統關系型數據庫的補充,它非常適合高伸縮性的場景,它是可擴展性的表結構。基于這點,可以將預期范圍內,表結構可能會不斷擴展的 MySQL 表結構,通過 MongoDB 來存儲,這就可以保證表結構的擴展性。
此外,日志系統數據量特別大,如果用 MongoDB 數據庫存儲這些數據,利用分片集群支持海量數據,同時使用聚集分析和 MapReduce 的能力,是個很好的選擇。
MongoDB 還適合存儲大尺寸的數據,GridFS 存儲方案就是基于 MongoDB 的分布式文件存儲系統。
列族數據庫 HBase
HBase 適合海量數據的存儲與高性能實時查詢,它是運行于 HDFS 文件系統之上,并且作為 MapReduce 分布式處理的目標數據庫,以支撐離線分析型應用。在數據倉庫、數據集市、商業智能等領域發揮了越來越多的作用,在數以千計的企業中支撐著大量的大數據分析場景的應用。
全文搜索引擎 ElasticSearch
在一般情況下,關系型數據庫的模糊查詢,都是通過 like 的方式進行查詢。其中,like “value%” 可以使用索引,但是對于 like “%value%” 這樣的方式,執行全表查詢,這在數據量小的表,不存在性能問題,但是對于海量數據,全表掃描是非常可怕的事情。ElasticSearch 作為一個建立在全文搜索引擎 Apache Lucene 基礎上的實時的分布式搜索和分析引擎,適用于處理實時搜索應用場景。此外,使用 ElasticSearch 全文搜索引擎,還可以支持多詞條查詢、匹配度與權重、自動聯想、拼寫糾錯等高級功能。因此,可以使用 ElasticSearch 作為關系型數據庫全文搜索的功能補充,將要進行全文搜索的數據緩存一份到 ElasticSearch 上,達到處理復雜的業務與提高查詢速度的目的。
ElasticSearch 不僅僅適用于搜索場景,還非常適合日志處理與分析的場景。著名的 ELK 日志處理方案,由 ElasticSearch、Logstash 和 Kibana 三個組件組成,包括了日志收集、聚合、多維度查詢、可視化顯示等。
摘抄自:http://blog.720ui.com/2017/db_better_db_use/
ObjectId 規則
[0,1,2,3] [4,5,6] [7,8] [9,10,11]
時間戳 |機器碼 |PID |計數器
前四位是時間戳,可以提供秒級別的唯一性。
接下來三位是所在主機的唯一標識符,通常是機器主機名的散列值。
接下來兩位是產生ObjectId的PID,確保同一臺機器上并發產生的ObjectId是唯一的。
前九位保證了同一秒鐘不同機器的不同進程產生的ObjectId時唯一的。
最后三位是自增計數器,確保相同進程同一秒鐘產生的ObjectId是唯一的。
https://github.com/qianjiahao/MongoDB/wiki/MongoDB%E4%B9%8B_id%E7%94%9F%E6%88%90%E8%A7%84%E5%88%99
聊聊 MongoDB 使用場景
高伸縮性的場景
MongoDB 非常適合高伸縮性的場景,它是可擴展性的表結構。基于這點,可以將預期范圍內,表結構可能會不斷擴展的 MySQL 表結構,通過 MongoDB 來存儲,這就可以保證表結構的擴展性。
日志系統的場景
日志系統數據量特別大,如果用 MongoDB 數據庫存儲這些數據,利用分片集群支持海量數據,同時使用聚集分析和 MapReduce 的能力,是個很好的選擇。
分布式文件存儲
MongoDB 還適合存儲大尺寸的數據,之前介紹的 GridFS 存儲方案,就是基于 MongoDB 的分布式文件存儲系統。
摘抄自:http://blog.720ui.com/2017/mongodb_core_use/
倒排索引
倒排索引(英語:Inverted index),也常被稱為反向索引、置入檔案或反向檔案,是一種索引方法,被用來存儲在全文搜索下某個單詞在一個文檔或者一組文檔中的存儲位置的映射。它是文檔檢索系統中最常用的數據結構。
有兩種不同的反向索引形式:
一條記錄的水平反向索引(或者反向檔案索引)包含每個引用單詞的文檔的列表。
一個單詞的水平反向索引(或者完全反向索引)又包含每個單詞在一個文檔中的位置。
聊聊 ElasticSearch 使用場景
全文搜索,這個是用的最多的。加上分詞插件、拼音插件什么的可以做成強大的全文搜索引擎。
數據庫,挺奇葩的用法,因為ES存數相同數據,更費空間,不過確實不錯,因為他的強大統計分析匯總能力,再加上分布式P2P擴展能力,現在硬件又那么便宜,所以就有人拿來當數據庫了。
在線統計分析引擎,日志系統。logstash,不用解釋了吧。可以實時動態分析數據,很是爽。
CAP與BASE理論
分布式事務
分布式事務是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位于分布式系統的不同節點之上。通常一個分布式事務中會涉及對多個數據源或業務系統的操作。
例如在下單場景下,庫存和訂單如果不在同一個節點上,就涉及分布式事務。
一個分布式事務可以看作是由多個分布式的操作序列組成的,通常可以把這一系列分布式的操作序列稱為子事務。因此分布式事務也可以被定義為一種嵌套型的事務,同時也就具有了 ACID 事務特性。但由于在分布式事務中,各個子事務的執行是分布式的,因此要實現一種能夠保證 ACID 特性的分布式事務處理系統就顯得格外復雜。
CAP和BASE理論的引出
對于本地事務處理或者是集中式的事務處理系統,依照傳統的ACID特性即可保證數據的嚴格一致性。
可是對于分布式事務,傳統的單機事務模型已經無法勝任。如果我們期望實現一套嚴格滿足ACID特性的分布式事務,很可能出現的情況就是在系統的可用性和嚴格一致性之間出現沖突。
因此,在可用性和一致性之間永遠無法存在一個兩全其美的方案,于是如何構建一個兼顧可用性和一致性的分布式系統成為了無數工程師探討的難題,出現了諸如CAP和BASE這樣的分布式系統經典理論。
CAP定理
CAP定理告訴我們,分布式系統不可能同時滿足一致性(C:Consistency)、可用性(A:Availability)和分區容錯性(P:Partition Tolerance),最多只能同時滿足其中兩項。
一、一致性(C:Consistency)
一致性指的是多個數據副本是否能保持一致的特性,在一致性的條件下,系統在執行數據更新操作之后能夠從一致性狀態轉移到另一個一致性狀態。
對系統的一個數據更新成功之后,如果所有用戶都能夠讀取到最新的值,該系統就被認為具有強一致性。
二、可用性(A:Availability)
可用性指分布式系統在面對各種異常時可以提供正常服務的能力,可以用系統可用時間占總時間的比值來衡量,4 個 9 的可用性表示系統 99.99% 的時間是可用的。
在可用性條件下,要求系統提供的服務一直處于可用的狀態,對于用戶的每一個操作請求總是能夠在有限的時間內返回結果。
三、分區容錯性(P:Partition Tolerance)
網絡分區指分布式系統中的節點被劃分為多個區域,每個區域內部可以通信,但是區域之間無法通信。
在分區容錯性條件下,分布式系統在遇到任何網絡分區故障的時候,仍然需要能對外提供一致性和可用性的服務,除非是整個網絡環境都發生了故障。
四、權衡
由上面我們可以看出,在分布式系統中,分區容錯性必不可少,因為需要總是假設網絡是不可靠的。因此,CAP 理論實際上是要在可用性和一致性之間做權衡。
可用性和一致性往往是沖突的,很難使它們同時滿足。在多個節點之間進行數據同步時:
為了保證一致性(CP),不能訪問未同步完成的節點,也就失去了部分可用性;
為了保證可用性(AP),允許讀取所有節點的數據,但是數據可能不一致。
BASE理論
BASE 是基本可用(Basically Available)、軟狀態(Soft State)和最終一致性(Eventually Consistent)三個短語的縮寫。
BASE 理論是對 CAP 中一致性和可用性權衡的結果,它的核心思想是:即使無法做到強一致性,但每個應用都可以根據自身業務特點,采用適當的方式來使系統達到最終一致性。
基本可用
指分布式系統在出現故障的時候,保證核心可用,允許損失部分可用性。
例如,電商在做促銷時,為了保證購物系統的穩定性,部分消費者可能會被引導到一個降級的頁面。
軟狀態
指允許系統中的數據存在中間狀態,并認為該中間狀態不會影響系統整體可用性,即允許系統不同節點的數據副本之間進行同步的過程存在時延。
最終一致性
最終一致性強調的是系統中所有的數據副本,在經過一段時間的同步后,最終能達到一致的狀態。
ACID 要求強一致性,通常運用在傳統的數據庫系統上。而 BASE 要求最終一致性,通過犧牲強一致性來達到可用性,通常運用在大型分布式系統中。
在實際的分布式場景中,不同業務單元和組件對一致性的要求是不同的,因此 ACID 和 BASE 往往會結合在一起使用。
參考內容:《從Paxos到ZooKeeper 分布式一致性原理與實踐》
說說反模式設計
簡單的來說,反模式是指在對經常面對的問題經常使用的低效,不良,或者有待優化的設計模式/方法。甚至,反模式也可以是一種錯誤的開發思想/理念。在這里我舉一個最簡單的例子:在面向對象設計/編程中,有一條很重要的原則, 單一責任原則(Single responsibility principle)。其中心思想就是對于一個模塊,或者一個類來說,這個模塊或者這個類應該只對系統/軟件的一個功能負責,而且該責任應該被該類完全封裝起來。當開發人員需要修改系統的某個功能,這個模塊/類是最主要的修改地方。相對應的一個反模式就是上帝類(God Class),通常來說,這個類里面控制了很多其他的類,同時也依賴其他很多類。整個類不光負責自己的主要單一功能,而且還負責了其他很多功能,包括一些輔助功能。很多維護老程序的開發人員們可能都遇過這種類,一個類里有幾千行的代碼,有很多功能,但是責任不明確單一。單元測試程序也變復雜無比。維護/修改這個類的時間要遠遠超出其他類的時間。很多時候,形成這種情況并不是開發人員故意的。很多情況下主要是由于隨著系統的年限,需求的變化,項目的資源壓力,項目組人員流動,系統結構的變化而導致某些原先小型的,符合單一原則類慢慢的變的臃腫起來。最后當這個類變成了維護的噩夢(特別是原先熟悉的開發人員離職后),重構該類就變成了一個不容易的工程。
總結
以上是生活随笔為你收集整理的BAT 大厂Java 面试题集锦之核心篇附参考答案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NEON 常用函数讲解
- 下一篇: 赛事启动 |香港科大-杰瑞集团 2022