Java高級篇

1 新技術

1.1 Java 8

lambda 表達式、Stream API、時間 API

1.2 Java 9

Jigsaw、Jshell、Reactive Streams

1.3 Java 10

局部變量類型推斷、G1 的並行 Full GC、ThreadLocal 握手機制

1.4 Java 11

ZGC、Epsilon、增強 var

1.5 Spring 5

響應式編程

1.6 Spring Boot 2.0

1.7 HTTP/2

1.8 HTTP/3

2 性能優化

2.1Java程序優化

2.1.1 使用單例

2.1.2 使用 Future 模式

Future模式是多線程中非常常見的一種設計模式,它的核心思想是異步調用
當我們需要調用一個函數方式是,如果這個函數執行很慢,那麼我們就要進行等待。但有時候,我們可能並不急着需要結果。,因此,我們可以讓被調者立即返回,讓他在後臺慢慢處理這個請求。對於調用者來說,則可以先處理一些其他任務,在真正需要數據的場合再去嘗試獲得需要的數據。
Future模式流程圖

2.1.3 使用線程池

什麼是線程池
線程池中,總有那麼幾個活躍線程。當需要使用線程時,可以從池子中隨便拿一個空閒線程,當完成工作時,並不急着關閉線程,而是將這個線程退回到池子,方便其他人使用。簡而言之,在使用線程池後,創建線程變成了從線程池中獲取空閒線程,關閉線程變成了向池子歸還線程。
JDK對線程池的支持
爲了能夠更好地控制多線程,JDK提供了一套Executor框架,幫助開發人員有效地進行線程控制,其本質就是一個線程池。
線程池框架結構圖

以上成員均在java.util.concurrent包中,是JDK併發包的核心類。其中ThreadPoolExecutor表示一個線程池。Executors類則扮演着線程池工廠的角色,通過Executors可以取得一個擁有特定功能的線程池。

主要工廠方法:

  • newFixedThreadPool()
    返回一個固定線程池數量的線程池。該線程池中的線程數量始終不變。當有一個新的任務提交時,線程池中若有空閒線程,則立即執行。若沒有,則新的任務會被暫存在一個任務隊列中,待有空閒線程時,便處理在任務隊列中的任務。
  • newSingleThreadExecutor()
    該方法返回一個只有一個線程的線程池。若多餘的一個任務被提交到該線程池,任務會被保存在一個任務隊列中,待線程空閒,按先入先出的順序執行隊列中的任務。
  • newCachedThreadPool()
    該方法返回一個可根據實際情況調整線程數量的線程池。線程池的數量不確定,但若有空閒線程可以複用,則會優先使用可複用的線程。若所有線程均在工作,又有新的任務提交,則會創建新的線程處理任務。所有線程在當前任務執行完畢後,將返回線程池進行復用。
  • newSingleThreadScheduledExecutor()
    該方法返回一個ScheduledExecutorService對象,線程池大小爲1。ScheduledExecutorService接口在ExecutorService接口之上擴展了在給定時間執行某任務的功能,如在某個固定的延時之後執行,或者週期性執行某個任務。
  • newScheduledThreadPool()
    該方法也返回一個ScheduledExecutorService對象,但該線程池可以指定線程數量。
線程池關鍵點
儘量減少線程切換和管理的開支;
最大化利用CPU;

2.1.4 選擇就緒、減少上下文切換

  • 上下文切換
    上下文切換是指CPU的控制權由運行任務轉移到另外一個就緒任務時所發生的事件;
    讓步式上下文切換:指執行線程主動釋放CPU,與鎖競爭眼中程度成正比,可通過減少鎖競爭來避免。
    搶佔式上下文切換:指線程因分配的時間片用盡而被迫泛起CPU或者被其他優先級更高的線程所搶佔,一般由於線程數大於CPU可用核心數引起,可通過調整線程數,適當減少線程數來避免。

  • 減少上下文切換
    無鎖併發編程:多線程競爭鎖時,會引起上下文切換,所以多線程處理數據時,可以用一些辦法來避免使用鎖,如將數據的 ID 按照 Hash 算法取模分段,不同的線程處理不同段的數據;
    CAS:避免加鎖和線程阻塞;
    使用最少線程:避免創建不需要的線程,比如任務很少,但是創建了很多線程來處理,這樣會造成大量線程都處於等待狀態;
    協程:比線程更輕量級的併發機制;在單線程實現多任務的調度,並在單線程裏維持多個任務間的切換。

2.1.5 鎖的優化

  • 減小鎖持有時間
    只在必要的時候進行同步,synchronized的作用對象優先級:變量>方法>類;
  • 減小鎖粒度
    一種削弱多線程鎖競爭的有效手段。
    如ConcurrentHashMap中的應用,它內部進一步細分了若干個小的HashMap,稱之爲段(SEGMENT)。默認情況下,一個ConcurrentHashMap被進一步細分爲16個段。
    在ConcurrentHashMap中增加一個新的表項,並不是將整個HashMap加鎖,而是首先根據hashcode得到該表項應該被存放到哪個段中,然後對該段加鎖,並完成put()操作。在多線程環境中,乳溝多個線程同時進行put()操作,只要被加入的表項不存放在同一個段中,則線程間便可以做到真正的並行。
  • 讀寫分離鎖來替換獨佔鎖
    減少鎖粒度是通過分割數據結構實現的,讀寫鎖則是對系統功能點的分割。
    在讀多寫少的場合,使用讀寫鎖可以有效提升系統的併發能力。
  • 鎖分離
    讀寫鎖根據讀寫操作功能上 的不同,進行了有效的鎖分離。依據應用程序的功能特點,使用類似的分離思想,也可以對獨佔鎖進行分離。一個經典案例:java.util.concurrent.LinkedBlockingQueue的實現。
    LinkedBlockingQueue是基於鏈表的,從隊列中取數take() 和增加數據put() 分別作用於隊列的前端和尾端,理論上說,兩者並不衝突。
    使用獨佔鎖,則要求兩個操作進行時獲取當前隊列的獨佔鎖,則take() 和put() 並不能真正的併發,在運行時,會彼此等待對方釋放鎖資源。
    因此,在JDK的實現中,取而代之的是兩把不同的鎖,分離了take() 和 put() 操作。
  • 鎖粗化
    虛擬機在遇到一連串地對同一鎖不斷進行請求和釋放的操作時,便會把所有的鎖操作整合成對鎖的一次請求,從而減少對鎖的請求同步次數。

詳細參考:實戰Java高併發程序設計第四章

2.2 數據壓縮

對於行存儲(相比列存儲)的表和索引,啓用數據壓縮最直接效果是能夠減小數據佔用的存儲空間的大小;除了節省空間之外,數據壓縮還能提高 I/O 密集型查詢的性能,因爲數據存儲在更少的數據頁(Data Page)中,SQL Server需要從磁盤讀取的數據頁更少,數據從Disk加載到內存的速度更快,查詢的性能更好。但是,壓縮和解壓縮的過程都需要消耗額外的CPU資源,開發者必須均衡CPU資源,數據存儲和硬盤IO的開銷。
詳細介紹:數據壓縮

2.3 結果緩存

3 線上問題分析

此章節待整理

3.1 dump 獲取

線程 Dump、內存 Dump、gc 情況

3.2 dump 分析

分析死鎖、分析內存泄露

3.3 dump 分析及獲取工具

jstack、jstat、jmap、jhat、Arthas

3.4 自己編寫各種 outofmemory,stackoverflow 程序

HeapOutOfMemory、 Young OutOfMemory、
MethodArea OutOfMemory、ConstantPool OutOfMemory、
DirectMemory OutOfMemory、Stack OutOfMemory Stack OverFlow

3.5 Arthas

jvm 相關、class/classloader 相關、monitor/watch/trace 相關、
options、管道、後臺異步任務
文檔:https://alibaba.github.io/arthas/advanced-use.html

3.6 常見問題解決思路

內存溢出、線程死鎖、類加載衝突

3.7 使用工具嘗試解決以下問題,並寫下總結

當一個 Java 程序響應很慢時如何查找問題
當一個 Java 程序頻繁 FullGC 時如何解決問題
如何查看垃圾回收日誌
當一個 Java 應用發生 OutOfMemory 時該如何解決
如何判斷是否出現死鎖
如何判斷是否存在內存泄露
使用 Arthas 快速排查 Spring Boot 應用404/401問題
使用 Arthas 排查線上應用日誌打滿問題
利用 Arthas 排查 Spring Boot 應用 NoSuchMethodError

4 編譯原理知識

待整理

5 操作系統

待整理

6 數據庫

6.1 MySQL 存儲引擎

  • MyISAM
    訪問速度快,但不支持事務,也不支持外鍵;對事務完整性沒有要求或者以SELECT、INSERT爲主的應用基本都可以使用這個引擎。
    曾經有位阿里面試官告訴我,MyISAM基本不會使用了,其功能都可以使用Memcached完成。

  • InnoDB
    事務型存儲引擎,MySQL默認的存儲引擎。支持行級鎖定和外鍵約束。

適合場景:
更新密集的表;InnoDB存儲引擎特別適合處理多重併發的更新請求。
事務;InnoDB存儲引擎是支持事務的標準MySQL存儲引擎。
自動災難恢復;與其它存儲引擎不同,InnoDB表能夠自動從災難中恢復。
外鍵約束;MySQL支持外鍵的存儲引擎只有InnoDB。
支持自動增加列AUTO_INCREMENT屬性。
  • MEMORY
    使用MySQL Memory存儲引擎的出發點是速度。爲得到最快的響應時間,採用的邏輯存儲介質是系統內存。雖然在內存中存儲表數據確實會提供很高的性能,但當mysqld守護進程崩潰時,所有的Memory數據都會丟失。獲得速度的同時也帶來了一些缺陷。它要求存儲在Memory數據表裏的數據使用的是長度不變的格式,這意味着不能使用BLOB和TEXT這樣的長度可變的數據類型,VARCHAR是一種長度可變的類型,但因爲它在MySQL內部當做長度固定不變的CHAR類型,所以可以使用。

6.2 MySQL邏輯架構

MySQL邏輯架構分爲三層
MySQL邏輯架構

  • 客戶端
    如,連接處理、授權認證、安全等功能
  • 核心服務
    MySQL大多數核心服務均在這一層,包括查詢解析、分析、優化、緩存、內置函數(如,時間、數學、加密等),所有的跨存儲引擎的功能也在這一層,如,存儲過程、觸發器、視圖等
  • 存儲引擎
    負責MySQL中的數據存儲和讀取
    中間的服務層通過API與存儲引擎通信,這些API屏蔽了不同存儲引擎間的差異
查詢緩存
對於select語句,在解析查詢之前,服務器會先檢查查詢緩存(Query Cache)。如果命中,服務器便不再執行查詢解析、優化和執行的過程,而是直接返回緩存中的結果集。

6.3 MySQL查詢過程

在這裏插入圖片描述
MySQL查詢過程:

  • 客戶端將查詢發送到MySQL服務器;
  • 服務器先檢查查詢緩存,如果命中,立即返回緩存中的結果;否則進入下一階段;
  • 服務器對SQL進行解析、預處理,再由優化器生成對象的執行計劃;
  • MySQL根據優化器生成的執行計劃,調用存儲引擎API來執行查詢;
  • 服務器將結果返回給客戶端,同時緩存查詢結果。

參考鏈接:MySQL執行計劃解析

6.4 執行計劃

什麼是執行計劃
簡單的來說,就是SQL在數據庫中執行時的表現情況,通常用於SQL性能分析、優化場景。
可以通過explain關鍵字查看優化器優化過程中的各個因素,使用戶知道數據庫是如何進行優化決策的,並提供一個參考基準,便於用戶重構查詢和數據庫表的schema、修改數據庫配置等,使查詢儘可能高效。
可通過**關鍵字提示(hint)**優化器,從而影響優化器的決策過程。

MySQL會解析查詢,並創建內部數據結構(解析樹),並對其進行各種優化,包括重寫查詢、決定表的讀取順序、選擇合適的索引等。

6.4.1 EXPLAIN 輸出列信息

Column JSON Name Meaning
id select_id 查詢序列號
select_type None 查詢類型
table table_name 輸出行信息所屬表
partitions partitions 匹配的分區
type access_type 連接使用類型
possible_keys possible_keys 可能加速查詢的索引
key key 真正使用的索引
key_len key_length 使用的索引長度,在不損失精確性的情況下,長度越短越好
ref ref 索引所在的列
rows rows 引擎認爲必須檢查的用來返回請求數據的行數
filtered filtered 按表條件過濾的行百分比
Extra None 附加信息

參考:MySQL官方文檔
亦可參考鏈接:MySQL執行計劃解析

6.5 索引

6.5.1 Hash索引

哈希索引就是採用一定的哈希算法,把鍵值換算成新的哈希值,檢索時不需要類似B+樹那樣從根節點到葉子節點逐級查找,只需一次哈希算法即可立刻定位到相應的位置,速度非常快。

6.5.2 B樹索引

B樹
類似二叉搜索樹,實質爲一棵m階B樹是一棵平衡的m路搜索樹;
最重要的性質是每個非根節點所包含的關鍵字個數 j 滿足:┌m/2┐ - 1 <= j <= m - 1;一個節點的子節點數量會比關鍵字個數多1,這樣關鍵字就變成了子節點的分割標誌。一般會在圖示中把關鍵字畫到子節點中間,非常形象,也容易和後面的B+樹區分。由於數據同時存在於葉子節點和非葉子結點中,無法簡單完成按順序遍歷B樹中的關鍵字,必須用中序遍歷的方法。

B樹

B+樹
一棵m階B樹是一棵平衡的m路搜索樹;
最重要的性質是每個非根節點所包含的關鍵字個數 j 滿足:┌m/2┐ - 1 <= j <= m;子樹的個數最多可以與關鍵字一樣多。非葉節點存儲的是子樹裏最小的關鍵字。同時數據節點只存在於葉子節點中,且葉子節點間增加了橫向的指針,這樣順序遍歷所有數據將變得非常容易。

B+樹

B*樹
一棵m階B樹是一棵平衡的m路搜索樹;
最重要的兩個性質是:(1)每個非根節點所包含的關鍵字個數 j 滿足:┌m2/3┐ - 1 <= j <= m;(2)非葉節點間添加了橫向指針。

B*樹

B+樹適合作爲數據庫的基礎結構的原因

完全是因爲計算機的內存-機械硬盤兩層存儲結構內存可以完成快速的隨機訪問(隨機訪問即給出任意一個地址,要求返回這個地址存儲的數據)但是容量較小。而硬盤的隨機訪問要經過機械動作(1磁頭移動 2盤片轉動),訪問效率比內存低幾個數量級,但是硬盤容量較大。典型的數據庫容量大大超過可用內存大小,這就決定了在B+樹中檢索一條數據很可能要藉助幾次磁盤IO操作來完成。

6.5.3 聚簇索引與非聚簇索引

當數據庫一條記錄裏包含多個字段時,一棵B+樹就只能存儲主鍵,如果檢索的是非主鍵字段,則主鍵索引失去作用,又變成順序查找了。這時應該在第二個要檢索的列上建立第二套索引。 這個索引由獨立的B+樹來組織。
有兩種常見的方法可以解決多個B+樹訪問同一套表數據的問題,一種叫做聚簇索引(clustered index ),一種叫做非聚簇索引(secondary index)
聚簇索引與非聚簇索引

  • InnoDB使用的是聚簇索引
    將主鍵組織到一棵B+樹中,而行數據就儲存在葉子節點上,若使用"where id = 14"這樣的條件查找主鍵,則按照B+樹的檢索算法即可查找到對應的葉節點,之後獲得行數據。若對Name列進行條件搜索,則需要兩個步驟:第一步在輔助索引B+樹中檢索Name,到達其葉子節點獲取對應的主鍵。第二步使用主鍵在主索引B+樹種再執行一次B+樹檢索操作,最終到達葉子節點即可獲取整行數據。

  • MyISM使用的是非聚簇索引
    非聚簇索引的兩棵B+樹看上去沒什麼不同,節點的結構完全一致只是存儲的內容不同而已,主鍵索引B+樹的節點存儲了主鍵,輔助鍵索引B+樹存儲了輔助鍵。表數據存儲在獨立的地方,這兩顆B+樹的葉子節點都使用一個地址指向真正的表數據,對於表數據來說,這兩個鍵沒有任何差別。由於索引樹是獨立的,通過輔助鍵檢索無需訪問主鍵的索引樹。

以上參考鏈接:MySQL的InnoDB索引原理詳解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章