[TOC]
# 用戶空間以及內核空間概念
我們知道現在操作系統都是采用虛擬存儲器,那么對32位操作系統而言,它的尋址空間(虛擬存儲空間)為4G(2的32次方)。
操心系統的核心是內核,獨立于普通的應用程序,可以訪問受保護的內存空間,也有訪問底層硬件設備的所有權限。
為了保證用戶進程不能直接操作內核,保證內核的安全,操心系統將虛擬空間劃分為兩部分,一部分為內核空間,一部分為用戶空間。
針對linux操作系統而言,將最高的1G字節(從虛擬地址0xC0000000到0xFFFFFFFF),供內核使用,稱為內核空間,而將較低的3G字節(從虛擬地址0x00000000到0xBFFFFFFF),供各個進程使用,稱為用戶空間。
每個進程可以通過系統調用進入內核,因此,Linux內核由系統內的所有進程共享。于是,從具體進程的角度來看,每個進程可以擁有4G字節的虛擬空間。

有了用戶空間和內核空間,整個linux內部結構可以分為三部分,
從最底層到最上層依次是:**硬件-->內核空間-->用戶空間**

需要注意的細節問題,從上圖可以看出內核的組成:
1. 內核空間中存放的是內核代碼和數據,而進程的用戶空間中存放的是用戶程序的代碼和數據。
不管是內核空間還是用戶空間,它們都處于虛擬空間中。
2. Linux使用兩級保護機制:0級供內核使用,3級供用戶程序使用
# Linux 網絡 I/O模型
我們都知道,為了OS的安全性等的考慮,**進程是無法直接操作I/O設備的,其必須通過系統調用請求內核來協助完成I/O動作,而內核會為每個I/O設備維護一個buffer**

**整個請求過程為: 用戶進程發起請求,內核接受到請求后,從I/O設備中獲取數據到buffer中,再將buffer中的數據copy到用戶進程的地址空間,該用戶進程獲取到數據后再響應客戶端**
在整個請求過程中,數據輸入至buffer需要時間,而從buffer復制數據至進程也需要時間。因此根據在這兩段時間內等待方式的不同,I/O動作可以分為以下**五種模式**:
* 阻塞I/O (Blocking I/O)
* 非阻塞I/O (Non-Blocking I/O)
* I/O復用(I/O Multiplexing)
* 信號驅動的I/O (Signal Driven I/O)
* 異步I/O (Asynchrnous I/O)
記住這兩點很重要
1. 等待數據準備 (Waiting for the data to be ready)
2. 將數據從內核拷貝到進程中 (Copying the data from the kernel to the process)
# 同步阻塞I/O (Blocking I/O)
同步阻塞 IO 模型是最常用的一個模型,也是最簡單的模型。在linux中,**默認情況下所有的socket都是blocking**。它符合人們最常見的思考邏輯。**阻塞就是進程 "被" 休息, CPU處理其它進程去了**。
在這個IO模型中,用戶空間的應用程序執行一個系統調用(recvform),這會導致應用程序阻塞,什么也不干,直到數據準備好,并且將數據從內核復制到用戶進程,最后進程再處理數據,**在等待數據到處理數據的兩個階段**,整個進程都被阻塞。不能處理別的網絡IO。**調用應用程序處于一種不再消費 CPU 而只是簡單等待響應的狀態**,因此從處理的角度來看,這是非常有效的。在調用recv()/recvfrom()函數時,發生在內核中等待數據和復制數據的過程,大致如下圖:

當用戶進程調用了recv()/recvfrom()這個系統調用,
kernel就開始了IO的**第一個階段:準備數據**(對于網絡IO來說,很多時候數據在一開始還沒有到達。比如,還沒有收到一個完整的UDP包。這個時候kernel就要等待足夠的數據到來)。這個過程需要等待,也就是說數據被拷貝到操作系統內核的緩沖區中是需要一個過程的。而在用戶進程這邊,整個進程會被阻塞(當然,是進程自己選擇的阻塞)。
**第二個階段:當kernel一直等到數據準備好了**,它就會將數據從kernel中拷貝到用戶內存,然后kernel返回結果,用戶進程才解除block的狀態,重新運行起來
所以,blocking IO的特點就是在IO執行的兩個階段都被block了。
# 同步非阻塞I/O (Non-Blocking I/O)
## 場景描述
我女友不甘心白白在這等,又想去逛商場,又擔心飯好了。所以我們逛一會,回來詢問服務員飯好了沒有,來來回回好多次,飯都還沒吃都快累死了啦。這就是非阻塞。需要不斷的詢問,是否準備好了。
## 網絡模型
**同步非阻塞就是 “每隔一會兒瞄一眼進度條” 的輪詢(polling)方式**。在這種模型中,**設備是以非阻塞的形式打開的**。
這意味著 IO 操作不會立即完成,read 操作可能會返回一個錯誤代碼,說明這個命令不能立即滿足(EAGAIN 或 EWOULDBLOCK)。
在網絡IO時候,非阻塞IO也會進行recvform系統調用,檢查數據是否準備好,與阻塞IO不一樣,”非阻塞將大的整片時間的阻塞分成N多的小的阻塞, 所以進程不斷地有機會 ‘被’ CPU光顧”。
**也就是說非阻塞的recvform系統調用調用之后,進程并沒有被阻塞,內核馬上返回給進程,如果數據還沒準備好,此時會返回一個error。**
進程在返回之后,可以干點別的事情,然后再發起recvform系統調用。重復上面的過程,循環往復的進行recvform系統調用。**這個過程通常被稱之為輪詢**。輪詢檢查內核數據,直到數據準備好,再拷貝數據到進程,進行數據處理。**需要注意,拷貝數據整個過程,進程仍然是屬于阻塞的狀態**。
在linux下,可以通過設置socket使其變為non-blocking。當對一個non-blocking socket執行讀操作時,流程如圖所示:

當用戶進程發出read操作時,如果kernel中的數據還沒有準備好,那么它并不會block用戶進程,而是立刻返回一個error。從用戶進程角度講,它發起一個read操作后,并不需要等待,而是馬上就得到了一個結果。用戶進程判斷結果是一個error時,它就知道數據還沒有準備好,于是它可以再次發送read操作。一旦kernel中的數據準備好了,并且又再次收到了用戶進程的system call,那么它馬上就將數據拷貝到了用戶內存,然后返回。
所以,nonblocking IO的特點是用戶進程需要不斷的主動詢問kernel數據好了沒有。
任務完成的響應延遲增大了,因為每過一段時間才去輪詢一次read操作,而任務可能在兩次輪詢之間的任意時間完成。這會導致整體數據吞吐量的降低
# I/O復用(I/O Multiplexing)
IO multiplexing這個詞可能有點陌生,但是如果我說select,epoll,大概就都能明白了。有些地方也稱這種IO方式為event driven IO。我們都知道,select/epoll的好處就在于單個process就可以同時處理多個網絡連接的IO。它的基本原理就是select/epoll這個function會不斷的輪詢所負責的所有socket,當某個socket有數據到達了,就通知用戶進程。它的流程如圖:

當用戶進程調用了select,那么整個進程會被block,而同時,內核會“監視”所有select負責的socket,當任何一個socket中的數據準備好了,select就會返回。這個時候用戶進程再調用read操作,將數據從內核拷貝到用戶進程。
這個圖和blocking IO的圖其實并沒有太大的不同,事實上,還更差一些。因為這里需要使用兩個system call (select 和 recvfrom),而blocking IO只調用了一個system call (recvfrom)。但是,用select的優勢在于它可以同時處理多個connection。(多說一句。所以,如果處理的連接數不是很高的話,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延遲還更大。select/epoll的優勢**并不是對于單個連接能處理得更快,而是在于能處理更多的連接**。)
在IO multiplexing Model中,實際中,對于每一個socket,一般都設置成為non-blocking,但是,如上圖所示,整個用戶的process其實是一直被block的。只不過process是被select這個函數block,而不是被socket IO給block。
了解了前面三種IO模式,在用戶進程進行系統調用的時候,他們在等待數據到來的時候,處理的方式不一樣,直接等待,輪詢,select或poll輪詢,兩個階段過程:
第一個階段有的阻塞,有的不阻塞,有的可以阻塞又可以不阻塞。
第二個階段都是阻塞的。
從整個IO過程來看,他們都是順序執行的,因此可以歸為同步模型(synchronous)。都是進程主動等待且向內核檢查狀態。【此句很重要!!!】
## 文件描述符fd
Linux的內核將所有外部設備都可以看做一個文件來操作。那么我們對與外部設備的操作都可以看做對文件進行操作。我們對一個文件的讀寫,都通過調用內核提供的系統調用;內核給我們返回一個filede scriptor(fd,文件描述符)。而對一個socket的讀寫也會有相應的描述符,稱為socketfd(socket描述符)。描述符就是一個數字,指向內核中一個結構體(文件路徑,數據區,等一些屬性)。那么我們的應用程序對文件的讀寫就通過對描述符的讀寫完成。
## select
基本原理:select 函數監視的文件描述符分3類,分別是writefds、readfds、和exceptfds。調用后select函數會阻塞,直到有描述符就緒(有數據 可讀、可寫、或者有except),或者超時(timeout指定等待時間,如果立即返回設為null即可),函數返回。當select函數返回后,可以通過遍歷fdset,來找到就緒的描述符。
缺點:
1. select最大的缺陷就是單個進程所打開的FD是有一定限制的,它由FDSETSIZE設置,32位機默認是1024個,64位機默認是2048。
一般來說這個數目和系統內存關系很大,”具體數目可以cat /proc/sys/fs/file-max察看”。32位機默認是1024個。64位機默認是2048.
2. 對socket進行掃描時是線性掃描,即采用輪詢的方法,效率較低。
當套接字比較多的時候,每次select()都要通過遍歷FDSETSIZE個Socket來完成調度,不管哪個Socket是活躍的,都遍歷一遍。這會浪費很多CPU時間。”如果能給套接字注冊某個回調函數,當他們活躍時,自動完成相關操作,那就避免了輪詢”,這正是epoll與kqueue做的。
3. 需要維護一個用來存放大量fd的數據結構,這樣會使得用戶空間和內核空間在傳遞該結構時復制開銷大。
## poll
基本原理:poll本質上和select沒有區別,它將用戶傳入的數組拷貝到內核空間,然后查詢每個fd對應的設備狀態,如果設備就緒則在設備等待隊列中加入一項并繼續遍歷,如果遍歷完所有fd后沒有發現就緒設備,則掛起當前進程,直到設備就緒或者主動超時,被喚醒后它又要再次遍歷fd。這個過程經歷了多次無謂的遍歷。
它沒有最大連接數的限制,原因是它是基于鏈表來存儲的,但是同樣有一個缺點:1、大量的fd的數組被整體復制于用戶態和內核地址空間之間,而不管這樣的復制是不是有意義。
2 、poll還有一個特點是“水平觸發”,如果報告了fd后,沒有被處理,那么下次poll時會再次報告該fd。
注意:從上面看,select和poll都需要在返回后,通過遍歷文件描述符來獲取已經就緒的socket。事實上,同時連接的大量客戶端在一時刻可能只有很少的處于就緒狀態,因此隨著監視的描述符數量的增長,其效率也會線性下降。
## epoll
epoll是在2.6內核中提出的,是之前的select和poll的增強版本。相對于select和poll來說,epoll更加靈活,沒有描述符限制。epoll使用一個文件描述符管理多個描述符,將用戶關系的文件描述符的事件存放到內核的一個事件表中,這樣在用戶空間和內核空間的copy只需一次。
基本原理:epoll支持水平觸發和邊緣觸發,最大的特點在于邊緣觸發,它只告訴進程哪些fd剛剛變為就緒態,并且只會通知一次。還有一個特點是,epoll使用“事件”的就緒通知方式,通過epollctl注冊fd,一旦該fd就緒,內核就會采用類似callback的回調機制來激活該fd,epollwait便可以收到通知。
epoll的優點:
1. 沒有最大并發連接的限制,能打開的FD的上限遠大于1024(1G的內存上能監聽約10萬個端口)。
2. 效率提升,不是輪詢的方式,不會隨著FD數目的增加效率下降。
只有活躍可用的FD才會調用callback函數;即Epoll最大的優點就在于它只管你“活躍”的連接,而跟連接總數無關,因此在實際的網絡環境中,Epoll的效率就會遠遠高于select和poll。
3. 內存拷貝,利用mmap()文件映射內存加速與內核空間的消息傳遞;即epoll使用mmap減少復制開銷。
**JDK1.5_update10版本使用epoll替代了傳統的select/poll,極大的提升了NIO通信的性能。**
# 信號驅動的I/O (Signal Driven I/O)
由于signal driven IO在實際中并不常用,所以簡單提下

很明顯可以看出用戶進程不是阻塞的。首先用戶進程建立SIGIO信號處理程序,并通過系統調用sigaction執行一個信號處理函數,這時用戶進程便可以做其他的事了,一旦數據準備好,系統便為該進程生成一個SIGIO信號,去通知它數據已經準備好了,于是用戶進程便調用recvfrom把數據從內核拷貝出來,并返回結果
# 異步I/O
## 場景描述
女友不想逛街,又餐廳太吵了,回家好好休息一下。于是我們叫外賣,打個電話點餐,然后我和女友可以在家好好休息一下,飯好了送貨員送到家里來。這就是典型的異步,只需要打個電話說一下,然后可以做自己的事情,飯好了就送來了
## 網絡模型
一般來說,這些函數通過告訴內核啟動操作并在整個操作(包括內核的數據到緩沖區的副本)完成時通知我們。這個模型和前面的信號驅動I/O模型的主要區別是,在信號驅動的I/O中,內核告訴我們何時可以啟動I/O操作,**但是異步I/O時,內核告訴我們何時I/O操作完成**。
相對于同步IO,異步IO不是順序執行。用戶進程進行aio_read系統調用之后,無論內核數據是否準備好,都會直接返回給用戶進程,然后用戶態進程可以去做別的事情。等到socket數據準備好了,內核直接復制數據給進程,然后從內核向進程發送通知。IO兩個階段,進程都是非阻塞的
Linux提供了AIO庫函數實現異步,但是用的很少。目前有很多開源的異步IO庫,例如libevent、libev、libuv。異步過程如下圖所示

當用戶進程向內核發起某個操作后,會立刻得到返回,并把所有的任務都交給內核去完成(包括將數據從內核拷貝到用戶自己的緩沖區),內核完成之后,只需返回一個信號告訴用戶進程已經完成就可以了。
## 流程描述
用戶進程發起aio_read操作之后,立刻就可以開始去做其它的事。而另一方面,從kernel的角度,當它受到一個asynchronous read之后,**首先它會立刻返回,所以不會對用戶進程產生任何block**。然后,kernel會等待數據準備完成,然后將數據拷貝到用戶內存,**當這一切都完成之后,kernel會給用戶進程發送一個signal或執行一個基于線程的回調函數來完成這次 IO 處理過程**,告訴它read操作完成了。
在 Linux 中,通知的方式是 “信號”:
~~~
如果這個進程正在用戶態忙著做別的事(例如在計算兩個矩陣的乘積),那就強行打斷之,調用事先注冊的信號處理函數,
這個函數可以決定何時以及如何處理這個異步任務。
由于信號處理函數是突然闖進來的,因此跟中斷處理程序一樣,有很多事情是不能做的,
因此保險起見,一般是把事件 “登記” 一下放進隊列, 然后返回該進程原來在做的事。
如果這個進程正在內核態忙著做別的事,例如以同步阻塞方式讀寫磁盤,那就只好把這個通知掛起來了,等到內核態的事情忙完了,快要回到用戶態的時候,再觸發信號通知。
如果這個進程現在被掛起了,例如無事可做 sleep 了,那就把這個進程喚醒,下次有 CPU 空閑的時候,就會調度到這個進程,觸發信號通知。
~~~
異步 API 說來輕巧,做來難,這主要是對 API 的實現者而言的。Linux 的異步 IO(AIO)支持是 2.6.22 才引入的,還有很多系統調用不支持異步 IO。Linux 的異步 IO 最初是為數據庫設計的,**因此通過異步 IO 的讀寫操作不會被緩存或緩沖,這就無法利用操作系統的緩存與緩沖機制**。
**很多人把 Linux 的 O_NONBLOCK 認為是異步方式,但事實上這是前面講的同步非阻塞方式**。需要指出的是,雖然 Linux 上的 IO API 略顯粗糙,但每種編程框架都有封裝好的異步 IO 實現。操作系統少做事,把更多的自由留給用戶,正是 UNIX 的設計哲學,也是 Linux 上編程框架百花齊放的一個原因。
提下
IO多路復用除了需要阻塞之外,select 函數所提供的功能(異步阻塞 IO)與 AIO 類似。不過,它是對通知事件進行阻塞,而不是對 IO 調用進行阻塞
# 5中I/O模型的對比
從同步、異步,以及阻塞、非阻塞兩個維度來劃分來看:

# 零拷貝
CPU不執行拷貝數據從一個存儲區域到另一個存儲區域的任務,這通常用于在網絡上傳輸文件時節省CPU周期和內存帶寬
# 緩存 IO
緩存 IO 又被稱作標準 IO,大多數文件系統的默認 IO 操作都是緩存 IO。在 Linux 的緩存 IO 機制中,操作系統會將 IO 的數據緩存在文件系統的頁緩存( page cache )中,也就是說,數據會先被拷貝到操作系統內核的緩沖區中,然后才會從操作系統內核的緩沖區拷貝到應用程序的地址空間。
緩存 IO 的缺點:數據在傳輸過程中需要在應用程序地址空間和內核進行多次數據拷貝操作,這些數據拷貝操作所帶來的 CPU 以及內存開銷是非常大的
- 基礎
- 編譯和安裝
- classpath到底是什么?
- 編譯運行
- 安裝
- sdkman多版本
- jabba多版本
- java字節碼查看
- 數據類型
- 簡介
- 整形
- char和int
- 變量和常量
- 大數值運算
- 基本類型包裝類
- Math類
- 內存劃分
- 位運算符
- 方法相關
- 方法重載
- 可變參數
- 方法引用
- 面向對象
- 定義
- 繼承和覆蓋
- 接口和抽象類
- 接口定義增強
- 內建函數式接口
- 多態
- 泛型
- final和static
- 內部類
- 包
- 修飾符
- 異常
- 枚舉類
- 代碼塊
- 對象克隆
- BeanUtils
- java基礎類
- scanner類
- Random類
- System類
- Runtime類
- Comparable接口
- Comparator接口
- MessageFormat類
- NumberFormat
- 數組相關
- 數組
- Arrays
- string相關
- String
- StringBuffer
- StringBuilder
- 正則
- 日期類
- Locale類
- Date
- DateFormat
- SimpleDateFormat
- Calendar
- 新時間日期API
- 簡介
- LocalDate,LocalTime,LocalDateTime
- Instant時間點
- 帶時區的日期,時間處理
- 時間間隔
- 日期時間校正器
- TimeUnit
- 用yyyy
- 集合
- 集合和迭代器
- ArrayList集合
- List
- Set
- 判斷集合唯一
- Map和Entry
- stack類
- Collections集合工具類
- Stream數據流
- foreach不能修改內部元素
- of方法
- IO
- File類
- 字節流stream
- 字符流Reader
- IO流分類
- 轉換流
- 緩沖流
- 流的操作規律
- properties
- 序列化流與反序列化流
- 打印流
- System類對IO支持
- commons-IO
- IO流總結
- NIO
- 異步與非阻塞
- IO通信
- Unix的IO模型
- epoll對于文件描述符操作模式
- 用戶空間和內核空間
- NIO與普通IO的主要區別
- Paths,Path,Files
- Buffer
- Channel
- Selector
- Pipe
- Charset
- NIO代碼
- 多線程
- 創建線程
- 線程常用方法
- 線程池相關
- 線程池概念
- ThreadPoolExecutor
- Runnable和Callable
- 常用的幾種線程池
- 線程安全
- 線程同步的幾種方法
- synchronized
- 死鎖
- lock接口
- ThreadLoad
- ReentrantLock
- 讀寫鎖
- 鎖的相關概念
- volatile
- 釋放鎖和不釋放鎖的操作
- 等待喚醒機制
- 線程狀態
- 守護線程和普通線程
- Lamda表達式
- 反射相關
- 類加載器
- 反射
- 注解
- junit注解
- 動態代理
- 網絡編程相關
- 簡介
- UDP
- TCP
- 多線程socket上傳圖片
- NIO
- JDBC相關
- JDBC
- 預處理
- 批處理
- 事務
- properties配置文件
- DBUtils
- DBCP連接池
- C3P0連接池
- 獲得MySQL自動生成的主鍵
- Optional類
- Jigsaw模塊化
- 日志相關
- JDK日志
- log4j
- logback
- xml
- tomcat
- maven
- 簡介
- 倉庫
- 目錄結構
- 常用命令
- 生命周期
- idea配置
- jar包沖突
- 依賴范圍
- 私服
- 插件
- git-commit-id-plugin
- maven-assembly-plugin
- maven-resources-plugin
- maven-compiler-plugin
- versions-maven-plugin
- maven-source-plugin
- tomcat-maven-plugin
- 多環境
- 自定義插件
- stream
- swing
- json
- jackson
- optional
- junit
- gradle
- servlet
- 配置
- ServletContext
- 生命周期
- HttpServlet
- request
- response
- 亂碼
- session和cookie
- cookie
- session
- jsp
- 簡介
- 注釋
- 方法,成員變量
- 指令
- 動作標簽
- 隱式對象
- EL
- JSTL
- javaBean
- listener監聽器
- Filter過濾器
- 圖片驗證碼
- HttpUrlConnection
- 國際化
- 文件上傳
- 文件下載
- spring
- 簡介
- Bean
- 獲取和實例化
- 屬性注入
- 自動裝配
- 繼承和依賴
- 作用域
- 使用外部屬性文件
- spel
- 前后置處理器
- 生命周期
- 掃描規則
- 整合多個配置文件
- 注解
- 簡介
- 注解分層
- 類注入
- 分層和作用域
- 初始化方法和銷毀方法
- 屬性
- 泛型注入
- Configuration配置文件
- aop
- aop的實現
- 動態代理實現
- cglib代理實現
- aop名詞
- 簡介
- aop-xml
- aop-注解
- 代理方式選擇
- jdbc
- 簡介
- JDBCTemplate
- 事務
- 整合
- junit整合
- hibernate
- 簡介
- hibernate.properties
- 實體對象三種狀態
- 檢索方式
- 簡介
- 導航對象圖檢索
- OID檢索
- HQL
- Criteria(QBC)
- Query
- 緩存
- 事務管理
- 關系映射
- 注解
- 優化
- MyBatis
- 簡介
- 入門程序
- Mapper動態代理開發
- 原始Dao開發
- Mapper接口開發
- SqlMapConfig.xml
- map映射文件
- 輸出返回map
- 輸入參數
- pojo包裝類
- 多個輸入參數
- resultMap
- 動態sql
- 關聯
- 一對一
- 一對多
- 多對多
- 整合spring
- CURD
- 占位符和sql拼接以及參數處理
- 緩存
- 延遲加載
- 注解開發
- springMVC
- 簡介
- RequestMapping
- 參數綁定
- 常用注解
- 響應
- 文件上傳
- 異常處理
- 攔截器
- springBoot
- 配置
- 熱更新
- java配置
- springboot配置
- yaml語法
- 運行
- Actuator 監控
- 多環境配置切換
- 日志
- 日志簡介
- logback和access
- 日志文件配置屬性
- 開機自啟
- aop
- 整合
- 整合Redis
- 整合Spring Data JPA
- 基本查詢
- 復雜查詢
- 多數據源的支持
- Repository分析
- JpaSpeci?cationExecutor
- 整合Junit
- 整合mybatis
- 常用注解
- 基本操作
- 通用mapper
- 動態sql
- 關聯映射
- 使用xml
- spring容器
- 整合druid
- 整合郵件
- 整合fastjson
- 整合swagger
- 整合JDBC
- 整合spingboot-cache
- 請求
- restful
- 攔截器
- 常用注解
- 參數校驗
- 自定義filter
- websocket
- 響應
- 異常錯誤處理
- 文件下載
- 常用注解
- 頁面
- Thymeleaf組件
- 基本對象
- 內嵌對象
- 上傳文件
- 單元測試
- 模擬請求測試
- 集成測試
- 源碼解析
- 自動配置原理
- 啟動流程分析
- 源碼相關鏈接
- Servlet,Filter,Listener
- springcloud
- 配置
- 父pom
- 創建子工程
- Eureka
- Hystrix
- Ribbon
- Feign
- Zuul
- kotlin
- 基本數據類型
- 函數
- 區間
- 區塊鏈
- 簡介
- linux
- ulimit修改
- 防止syn攻擊
- centos7部署bbr
- debain9開啟bbr
- mysql
- 隔離性
- sql執行加載順序
- 7種join
- explain
- 索引失效和優化
- 表連接優化
- orderby的filesort問題
- 慢查詢
- show profile
- 全局查詢日志
- 死鎖解決
- sql
- 主從
- IDEA
- mac快捷鍵
- 美化界面
- 斷點調試
- 重構
- springboot-devtools熱部署
- IDEA進行JAR打包
- 導入jar包
- ProjectStructure
- toString添加json模板
- 配置maven
- Lombok插件
- rest client
- 文檔顯示
- sftp文件同步
- 書簽
- 代碼查看和搜索
- postfix
- live template
- git
- 文件頭注釋
- JRebel
- 離線模式
- xRebel
- github
- 連接mysql
- 選項沒有Java class的解決方法
- 擴展
- 項目配置和web部署
- 前端開發
- json和Inject language
- idea內存和cpu變高
- 相關設置
- 設計模式
- 單例模式
- 簡介
- 責任鏈
- JUC
- 原子類
- 原子類簡介
- 基本類型原子類
- 數組類型原子類
- 引用類型原子類
- JVM
- JVM規范內存解析
- 對象的創建和結構
- 垃圾回收
- 內存分配策略
- 備注
- 虛擬機工具
- 內存模型
- 同步八種操作
- 內存區域大小參數設置
- happens-before
- web service
- tomcat
- HTTPS
- nginx
- 變量
- 運算符
- 模塊
- Rewrite規則
- Netty
- netty為什么沒用AIO
- 基本組件
- 源碼解讀
- 簡單的socket例子
- 準備netty
- netty服務端啟動
- 案例一:發送字符串
- 案例二:發送對象
- websocket
- ActiveMQ
- JMS
- 安裝
- 生產者-消費者代碼
- 整合springboot
- kafka
- 簡介
- 安裝
- 圖形化界面
- 生產過程分析
- 保存消息分析
- 消費過程分析
- 命令行
- 生產者
- 消費者
- 攔截器interceptor
- partition
- kafka為什么快
- kafka streams
- kafka與flume整合
- RabbitMQ
- AMQP
- 整體架構
- RabbitMQ安裝
- rpm方式安裝
- 命令行和管控頁面
- 消息生產與消費
- 整合springboot
- 依賴和配置
- 簡單測試
- 多方測試
- 對象支持
- Topic Exchange模式
- Fanout Exchange訂閱
- 消息確認
- java client
- RabbitAdmin和RabbitTemplate
- 兩者簡介
- RabbitmqAdmin
- RabbitTemplate
- SimpleMessageListenerContainer
- MessageListenerAdapter
- MessageConverter
- 詳解
- Jackson2JsonMessageConverter
- ContentTypeDelegatingMessageConverter
- lucene
- 簡介
- 入門程序
- luke查看索引
- 分析器
- 索引庫維護
- elasticsearch
- 配置
- 插件
- head插件
- ik分詞插件
- 常用術語
- Mapping映射
- 數據類型
- 屬性方法
- Dynamic Mapping
- Index Template 索引模板
- 管理映射
- 建立映射
- 索引操作
- 單模式下CURD
- mget多個文檔
- 批量操作
- 版本控制
- 基本查詢
- Filter過濾
- 組合查詢
- 分析器
- redis
- String
- list
- hash
- set
- sortedset
- 發布訂閱
- 事務
- 連接池
- 管道
- 分布式可重入鎖
- 配置文件翻譯
- 持久化
- RDB
- AOF
- 總結
- Lettuce
- zookeeper
- zookeeper簡介
- 集群部署
- Observer模式
- 核心工作機制
- zk命令行操作
- zk客戶端API
- 感知服務動態上下線
- 分布式共享鎖
- 原理
- zab協議
- 兩階段提交協議
- 三階段提交協議
- Paxos協議
- ZAB協議
- hadoop
- 簡介
- hadoop安裝
- 集群安裝
- 單機安裝
- linux編譯hadoop
- 添加新節點
- 退役舊節點
- 集群間數據拷貝
- 歸檔
- 快照管理
- 回收站
- 檢查hdfs健康狀態
- 安全模式
- hdfs簡介
- hdfs命令行操作
- 常見問題匯總
- hdfs客戶端操作
- mapreduce工作機制
- 案例-單詞統計
- 局部聚合Combiner
- combiner流程
- combiner案例
- 自定義排序
- 自定義Bean對象
- 排序的分類
- 案例-按總量排序需求
- 一次性完成統計和排序
- 分區
- 分區簡介
- 案例-結果分區
- 多表合并
- reducer端合并
- map端合并(分布式緩存)
- 分組
- groupingComparator
- 案例-求topN
- 全局計數器
- 合并小文件
- 小文件的弊端
- CombineTextInputFormat機制
- 自定義InputFormat
- 自定義outputFormat
- 多job串聯
- 倒排索引
- 共同好友
- 串聯
- 數據壓縮
- InputFormat接口實現類
- yarn簡介
- 推測執行算法
- 本地提交到yarn
- 框架運算全流程
- 數據傾斜問題
- mapreduce的優化方案
- HA機制
- 優化
- Hive
- 安裝
- shell參數
- 數據類型
- 集合類型
- 數據庫
- DDL操作
- 創建表
- 修改表
- 分區表
- 分桶表
- DML操作
- load
- insert
- select
- export,import
- Truncate
- 注意
- 嚴格模式
- 函數
- 內置運算符
- 內置函數
- 自定義函數
- Transfrom實現
- having和where不同
- 壓縮
- 存儲
- 存儲和壓縮結合使用
- explain詳解
- 調優
- Fetch抓取
- 本地模式
- 表的優化
- GroupBy
- count(Distinct)去重統計
- 行列過濾
- 動態分區調整
- 數據傾斜
- 并行執行
- JVM重用
- 推測執行
- reduce內存和個數
- sql查詢結果作為變量(shell)
- youtube
- flume
- 簡介
- 安裝
- 常用組件
- 攔截器
- 案例
- 監聽端口到控制臺
- 采集目錄到HDFS
- 采集文件到HDFS
- 多個agent串聯
- 日志采集和匯總
- 單flume多channel,sink
- 自定義攔截器
- 高可用配置
- 使用注意
- 監控Ganglia
- sqoop
- 安裝
- 常用命令
- 數據導入
- 準備數據
- 導入數據到HDFS
- 導入關系表到HIVE
- 導入表數據子集
- 增量導入
- 數據導出
- 打包腳本
- 作業
- 原理
- azkaban
- 簡介
- 安裝
- 案例
- 簡介
- command類型單一job
- command類型多job工作流flow
- HDFS操作任務
- mapreduce任務
- hive腳本任務
- oozie
- 安裝
- hbase
- 簡介
- 系統架構
- 物理存儲
- 尋址機制
- 讀寫過程
- 安裝
- 命令行
- 基本CURD
- java api
- CURD
- CAS
- 過濾器查詢
- 建表高級屬性
- 與mapreduce結合
- 與sqoop結合
- 協處理器
- 參數配置優化
- 數據備份和恢復
- 節點管理
- 案例-點擊流
- 簡介
- HUE
- 安裝
- storm
- 簡介
- 安裝
- 集群啟動及任務過程分析
- 單詞統計
- 單詞統計(接入kafka)
- 并行度和分組
- 啟動流程分析
- ACK容錯機制
- ACK簡介
- BaseRichBolt簡單使用
- BaseBasicBolt簡單使用
- Ack工作機制
- 本地目錄樹
- zookeeper目錄樹
- 通信機制
- 案例
- 日志告警
- 工具
- YAPI
- chrome無法手動拖動安裝插件
- 時間和空間復雜度
- jenkins
- 定位cpu 100%
- 常用腳本工具
- OOM問題定位
- scala
- 編譯
- 基本語法
- 函數
- 數組常用方法
- 集合
- 并行集合
- 類
- 模式匹配
- 異常
- tuple元祖
- actor并發編程
- 柯里化
- 隱式轉換
- 泛型
- 迭代器
- 流stream
- 視圖view
- 控制抽象
- 注解
- spark
- 企業架構
- 安裝
- api開發
- mycat
- Groovy
- 基礎