[TOC]
# 一、概述
Redis 是速度非常快的非關系型(NoSQL)內存鍵值數據庫,可以存儲鍵和五種不同類型的值之間的映射。
鍵的類型只能為字符串,值支持五種數據類型:字符串、列表、集合、散列表、有序集合。
Redis 支持很多特性,例如將內存中的數據持久化到硬盤中,使用復制來擴展讀性能,使用分片來擴展寫性能。
# 二、數據類型
| 數據類型 | 可以存儲的值 | 操作 |
| :-: | :-: | :-: |
| STRING | 字符串、整數或者浮點數 | 對整個字符串或者字符串的其中一部分執行操作
對整數和浮點數執行自增或者自減操作 |
| LIST | 列表 | 從兩端壓入或者彈出元素
對單個或者多個元素
進行修剪,只保留一個范圍內的元素 |
| SET | 無序集合 | 添加、獲取、移除單個元素
檢查一個元素是否存在于集合中
計算交集、并集、差集
從集合里面隨機獲取元素 |
| HASH | 包含鍵值對的無序散列表 | 添加、獲取、移除單個鍵值對
獲取所有鍵值對
檢查某個鍵是否存在 |
| ZSET | 有序集合 | 添加、獲取、刪除元素
根據分值范圍或者成員來獲取元素
計算一個鍵的排名 |
> [What Redis data structures look like](https://redislabs.com/ebook/part-1-getting-started/chapter-1-getting-to-know-redis/1-2-what-redis-data-structures-look-like/)
## STRING
[](https://camo.githubusercontent.com/ffeeb439ae35f77594d0ad0bbb2a1e0adf7b781c/68747470733a2f2f67697465652e636f6d2f437943323031382f43532d4e6f7465732f7261772f6d61737465722f646f63732f706963732f36303139623264622d626333652d343430382d623664382d3936303235663434383164362e706e67)
~~~html
> set hello world
OK
> get hello
"world"
> del hello
(integer) 1
> get hello
(nil)
~~~
## LIST
[](https://camo.githubusercontent.com/40e497537a0c6e46eb52ecc5e0ba788dd677407c/68747470733a2f2f67697465652e636f6d2f437943323031382f43532d4e6f7465732f7261772f6d61737465722f646f63732f706963732f66623332373631312d376532622d346632662d396635622d3338353932643430386630372e706e67)
~~~html
> rpush list-key item
(integer) 1
> rpush list-key item2
(integer) 2
> rpush list-key item
(integer) 3
> lrange list-key 0 -1
1) "item"
2) "item2"
3) "item"
> lindex list-key 1
"item2"
> lpop list-key
"item"
> lrange list-key 0 -1
1) "item2"
2) "item"
~~~
## SET
[](https://camo.githubusercontent.com/9b087a285336cafc79e70f13f9840da9ebd7b2f8/68747470733a2f2f67697465652e636f6d2f437943323031382f43532d4e6f7465732f7261772f6d61737465722f646f63732f706963732f63643566626366662d336633352d343361362d386666612d3038326139336365306630652e706e67)
~~~html
> sadd set-key item
(integer) 1
> sadd set-key item2
(integer) 1
> sadd set-key item3
(integer) 1
> sadd set-key item
(integer) 0
> smembers set-key
1) "item"
2) "item2"
3) "item3"
> sismember set-key item4
(integer) 0
> sismember set-key item
(integer) 1
> srem set-key item2
(integer) 1
> srem set-key item2
(integer) 0
> smembers set-key
1) "item"
2) "item3"
~~~
## HASH
[](https://camo.githubusercontent.com/5ae0fdd9fa8b442c7d4595a2a17f1caf057fffa5/68747470733a2f2f67697465652e636f6d2f437943323031382f43532d4e6f7465732f7261772f6d61737465722f646f63732f706963732f37626432303261372d393364342d346633612d613837382d6166363861653235353339612e706e67)
~~~html
> hset hash-key sub-key1 value1
(integer) 1
> hset hash-key sub-key2 value2
(integer) 1
> hset hash-key sub-key1 value1
(integer) 0
> hgetall hash-key
1) "sub-key1"
2) "value1"
3) "sub-key2"
4) "value2"
> hdel hash-key sub-key2
(integer) 1
> hdel hash-key sub-key2
(integer) 0
> hget hash-key sub-key1
"value1"
> hgetall hash-key
1) "sub-key1"
2) "value1"
~~~
## ZSET
[](https://camo.githubusercontent.com/ef4742c8f7261bd279d70aa7686e1a9e41f21860/68747470733a2f2f67697465652e636f6d2f437943323031382f43532d4e6f7465732f7261772f6d61737465722f646f63732f706963732f31323032623264362d393436392d343235312d626434372d6361363033346662363131362e706e67)
~~~html
> zadd zset-key 728 member1
(integer) 1
> zadd zset-key 982 member0
(integer) 1
> zadd zset-key 982 member0
(integer) 0
> zrange zset-key 0 -1 withscores
1) "member1"
2) "728"
3) "member0"
4) "982"
> zrangebyscore zset-key 0 800 withscores
1) "member1"
2) "728"
> zrem zset-key member1
(integer) 1
> zrem zset-key member1
(integer) 0
> zrange zset-key 0 -1 withscores
1) "member0"
2) "982"
~~~
# 三、數據結構
## 字典
dictht 是一個散列表結構,使用拉鏈法保存哈希沖突。
~~~c
/* This is our hash table structure. Every dictionary has two of this as we
* implement incremental rehashing, for the old to the new table. */
typedef struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
} dictht;
~~~
~~~c
typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;
} dictEntry;
~~~
Redis 的字典 dict 中包含兩個哈希表 dictht,這是為了方便進行 rehash 操作。在擴容時,將其中一個 dictht 上的鍵值對 rehash 到另一個 dictht 上面,完成之后釋放空間并交換兩個 dictht 的角色。
~~~c
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
unsigned long iterators; /* number of iterators currently running */
} dict;
~~~
rehash 操作不是一次性完成,而是采用漸進方式,這是為了避免一次性執行過多的 rehash 操作給服務器帶來過大的負擔。
漸進式 rehash 通過記錄 dict 的 rehashidx 完成,它從 0 開始,然后每執行一次 rehash 都會遞增。例如在一次 rehash 中,要把 dict\[0\] rehash 到 dict\[1\],這一次會把 dict\[0\] 上 table\[rehashidx\] 的鍵值對 rehash 到 dict\[1\] 上,dict\[0\] 的 table\[rehashidx\] 指向 null,并令 rehashidx++。
在 rehash 期間,每次對字典執行添加、刪除、查找或者更新操作時,都會執行一次漸進式 rehash。
采用漸進式 rehash 會導致字典中的數據分散在兩個 dictht 上,因此對字典的查找操作也需要到對應的 dictht 去執行。
~~~c
/* Performs N steps of incremental rehashing. Returns 1 if there are still
* keys to move from the old to the new hash table, otherwise 0 is returned.
*
* Note that a rehashing step consists in moving a bucket (that may have more
* than one key as we use chaining) from the old to the new hash table, however
* since part of the hash table may be composed of empty spaces, it is not
* guaranteed that this function will rehash even a single bucket, since it
* will visit at max N*10 empty buckets in total, otherwise the amount of
* work it does would be unbound and the function may block for a long time. */
int dictRehash(dict *d, int n) {
int empty_visits = n * 10; /* Max number of empty buckets to visit. */
if (!dictIsRehashing(d)) return 0;
while (n-- && d->ht[0].used != 0) {
dictEntry *de, *nextde;
/* Note that rehashidx can't overflow as we are sure there are more
* elements because ht[0].used != 0 */
assert(d->ht[0].size > (unsigned long) d->rehashidx);
while (d->ht[0].table[d->rehashidx] == NULL) {
d->rehashidx++;
if (--empty_visits == 0) return 1;
}
de = d->ht[0].table[d->rehashidx];
/* Move all the keys in this bucket from the old to the new hash HT */
while (de) {
uint64_t h;
nextde = de->next;
/* Get the index in the new hash table */
h = dictHashKey(d, de->key) & d->ht[1].sizemask;
de->next = d->ht[1].table[h];
d->ht[1].table[h] = de;
d->ht[0].used--;
d->ht[1].used++;
de = nextde;
}
d->ht[0].table[d->rehashidx] = NULL;
d->rehashidx++;
}
/* Check if we already rehashed the whole table... */
if (d->ht[0].used == 0) {
zfree(d->ht[0].table);
d->ht[0] = d->ht[1];
_dictReset(&d->ht[1]);
d->rehashidx = -1;
return 0;
}
/* More to rehash... */
return 1;
}
~~~
## 跳躍表
是有序集合的底層實現之一。
跳躍表是基于多指針有序鏈表實現的,可以看成多個有序鏈表。
[](https://camo.githubusercontent.com/1c48c2de3ce1e18918bbeec16fc97d515d962c9a/68747470733a2f2f67697465652e636f6d2f437943323031382f43532d4e6f7465732f7261772f6d61737465722f646f63732f706963732f62656261363132652d646335622d346663322d383639642d3062323334303861633930612e706e67)
在查找時,從上層指針開始查找,找到對應的區間之后再到下一層去查找。下圖演示了查找 22 的過程。
[](https://camo.githubusercontent.com/62f44a87add83063ba7b757aa3ab4292bef0756d/68747470733a2f2f67697465652e636f6d2f437943323031382f43532d4e6f7465732f7261772f6d61737465722f646f63732f706963732f30656133376565322d633232342d346337392d623839352d6531333163363830356334302e706e67)
與紅黑樹等平衡樹相比,跳躍表具有以下優點:
* 插入速度非常快速,因為不需要進行旋轉等操作來維護平衡性;
* 更容易實現;
* 支持無鎖操作。
# 四、使用場景
## 計數器
可以對 String 進行自增自減運算,從而實現計數器功能。
Redis 這種內存型數據庫的讀寫性能非常高,很適合存儲頻繁讀寫的計數量。
## 緩存
將熱點數據放到內存中,設置內存的最大使用量以及淘汰策略來保證緩存的命中率。
## 查找表
例如 DNS 記錄就很適合使用 Redis 進行存儲。
查找表和緩存類似,也是利用了 Redis 快速的查找特性。但是查找表的內容不能失效,而緩存的內容可以失效,因為緩存不作為可靠的數據來源。
## 消息隊列
List 是一個雙向鏈表,可以通過 lpush 和 rpop 寫入和讀取消息。
不過最好使用 Kafka、RabbitMQ 等消息中間件。
## 會話緩存
可以使用 Redis 來統一存儲多臺應用服務器的會話信息。
當應用服務器不再存儲用戶的會話信息,也就不再具有狀態,一個用戶可以請求任意一個應用服務器,從而更容易實現高可用性以及可伸縮性。
## 分布式鎖實現
在分布式場景下,無法使用單機環境下的鎖來對多個節點上的進程進行同步。
可以使用 Redis 自帶的 SETNX 命令實現分布式鎖,除此之外,還可以使用官方提供的 RedLock 分布式鎖實現。
## 其它
Set 可以實現交集、并集等操作,從而實現共同好友等功能。
ZSet 可以實現有序性操作,從而實現排行榜等功能。
# 五、Redis 與 Memcached
兩者都是非關系型內存鍵值數據庫,主要有以下不同:
## 數據類型
Memcached 僅支持字符串類型,而 Redis 支持五種不同的數據類型,可以更靈活地解決問題。
## 數據持久化
Redis 支持兩種持久化策略:RDB 快照和 AOF 日志,而 Memcached 不支持持久化。
## 分布式
Memcached 不支持分布式,只能通過在客戶端使用一致性哈希來實現分布式存儲,這種方式在存儲和查詢時都需要先在客戶端計算一次數據所在的節點。
Redis Cluster 實現了分布式的支持。
## 內存管理機制
* 在 Redis 中,并不是所有數據都一直存儲在內存中,可以將一些很久沒用的 value 交換到磁盤,而 Memcached 的數據則會一直在內存中。
* Memcached 將內存分割成特定長度的塊來存儲數據,以完全解決內存碎片的問題。但是這種方式會使得內存的利用率不高,例如塊的大小為 128 bytes,只存儲 100 bytes 的數據,那么剩下的 28 bytes 就浪費掉了。
# 六、鍵的過期時間
Redis 可以為每個鍵設置過期時間,當鍵過期時,會自動刪除該鍵。
對于散列表這種容器,只能為整個鍵設置過期時間(整個散列表),而不能為鍵里面的單個元素設置過期時間。
# 七、數據淘汰策略
可以設置內存最大使用量,當內存使用量超出時,會施行數據淘汰策略。
Redis 具體有 6 種淘汰策略:
| 策略 | 描述 |
| :-: | :-: |
| volatile-lru | 從已設置過期時間的數據集中挑選最近最少使用的數據淘汰 |
| volatile-ttl | 從已設置過期時間的數據集中挑選將要過期的數據淘汰 |
| volatile-random | 從已設置過期時間的數據集中任意選擇數據淘汰 |
| allkeys-lru | 從所有數據集中挑選最近最少使用的數據淘汰 |
| allkeys-random | 從所有數據集中任意選擇數據進行淘汰 |
| noeviction | 禁止驅逐數據 |
作為內存數據庫,出于對性能和內存消耗的考慮,Redis 的淘汰算法實際實現上并非針對所有 key,而是抽樣一小部分并且從中選出被淘汰的 key。
使用 Redis 緩存數據時,為了提高緩存命中率,需要保證緩存數據都是熱點數據。可以將內存最大使用量設置為熱點數據占用的內存量,然后啟用 allkeys-lru 淘汰策略,將最近最少使用的數據淘汰。
Redis 4.0 引入了 volatile-lfu 和 allkeys-lfu 淘汰策略,LFU 策略通過統計訪問頻率,將訪問頻率最少的鍵值對淘汰。
# 八、持久化
Redis 是內存型數據庫,為了保證數據在斷電后不會丟失,需要將內存中的數據持久化到硬盤上。
## RDB 持久化
將某個時間點的所有數據都存放到硬盤上。
可以將快照復制到其它服務器從而創建具有相同數據的服務器副本。
如果系統發生故障,將會丟失最后一次創建快照之后的數據。
如果數據量很大,保存快照的時間會很長。
## AOF 持久化
將寫命令添加到 AOF 文件(Append Only File)的末尾。
使用 AOF 持久化需要設置同步選項,從而確保寫命令什么時候會同步到磁盤文件上。這是因為對文件進行寫入并不會馬上將內容同步到磁盤上,而是先存儲到緩沖區,然后由操作系統決定什么時候同步到磁盤。有以下同步選項:
| 選項 | 同步頻率 |
| :-: | :-: |
| always | 每個寫命令都同步 |
| everysec | 每秒同步一次 |
| no | 讓操作系統來決定何時同步 |
* always 選項會嚴重減低服務器的性能;
* everysec 選項比較合適,可以保證系統崩潰時只會丟失一秒左右的數據,并且 Redis 每秒執行一次同步對服務器性能幾乎沒有任何影響;
* no 選項并不能給服務器性能帶來多大的提升,而且也會增加系統崩潰時數據丟失的數量。
隨著服務器寫請求的增多,AOF 文件會越來越大。Redis 提供了一種將 AOF 重寫的特性,能夠去除 AOF 文件中的冗余寫命令。
# 九、事務
一個事務包含了多個命令,服務器在執行事務期間,不會改去執行其它客戶端的命令請求。
事務中的多個命令被一次性發送給服務器,而不是一條一條發送,這種方式被稱為流水線,它可以減少客戶端與服務器之間的網絡通信次數從而提升性能。
Redis 最簡單的事務實現方式是使用 MULTI 和 EXEC 命令將事務操作包圍起來。
# 十、事件
Redis 服務器是一個事件驅動程序。
## 文件事件
服務器通過套接字與客戶端或者其它服務器進行通信,文件事件就是對套接字操作的抽象。
Redis 基于 Reactor 模式開發了自己的網絡事件處理器,使用 I/O 多路復用程序來同時監聽多個套接字,并將到達的事件傳送給文件事件分派器,分派器會根據套接字產生的事件類型調用相應的事件處理器。
[](https://camo.githubusercontent.com/3832a6170decbae3f3664272599ea0efe3aaf660/68747470733a2f2f67697465652e636f6d2f437943323031382f43532d4e6f7465732f7261772f6d61737465722f646f63732f706963732f39656138366562352d303030612d343238312d623934382d3762353637626436663164382e706e67)
## 時間事件
服務器有一些操作需要在給定的時間點執行,時間事件是對這類定時操作的抽象。
時間事件又分為:
* 定時事件:是讓一段程序在指定的時間之內執行一次;
* 周期性事件:是讓一段程序每隔指定時間就執行一次。
Redis 將所有時間事件都放在一個無序鏈表中,通過遍歷整個鏈表查找出已到達的時間事件,并調用相應的事件處理器。
## 事件的調度與執行
服務器需要不斷監聽文件事件的套接字才能得到待處理的文件事件,但是不能一直監聽,否則時間事件無法在規定的時間內執行,因此監聽時間應該根據距離現在最近的時間事件來決定。
事件調度與執行由 aeProcessEvents 函數負責,偽代碼如下:
~~~python
def aeProcessEvents():
# 獲取到達時間離當前時間最接近的時間事件
time_event = aeSearchNearestTimer()
# 計算最接近的時間事件距離到達還有多少毫秒
remaind_ms = time_event.when - unix_ts_now()
# 如果事件已到達,那么 remaind_ms 的值可能為負數,將它設為 0
if remaind_ms < 0:
remaind_ms = 0
# 根據 remaind_ms 的值,創建 timeval
timeval = create_timeval_with_ms(remaind_ms)
# 阻塞并等待文件事件產生,最大阻塞時間由傳入的 timeval 決定
aeApiPoll(timeval)
# 處理所有已產生的文件事件
procesFileEvents()
# 處理所有已到達的時間事件
processTimeEvents()
~~~
將 aeProcessEvents 函數置于一個循環里面,加上初始化和清理函數,就構成了 Redis 服務器的主函數,偽代碼如下:
~~~python
def main():
# 初始化服務器
init_server()
# 一直處理事件,直到服務器關閉為止
while server_is_not_shutdown():
aeProcessEvents()
# 服務器關閉,執行清理操作
clean_server()
~~~
從事件處理的角度來看,服務器運行流程如下:
[](https://camo.githubusercontent.com/05e06331475c5d9056ac7986401c9be9bbda2f85/68747470733a2f2f67697465652e636f6d2f437943323031382f43532d4e6f7465732f7261772f6d61737465722f646f63732f706963732f63306139666139312d646132652d343839322d386339662d3830323036613666373034372e706e67)
# 十一、復制
通過使用 slaveof host port 命令來讓一個服務器成為另一個服務器的從服務器。
一個從服務器只能有一個主服務器,并且不支持主主復制。
## 連接過程
1. 主服務器創建快照文件,發送給從服務器,并在發送期間使用緩沖區記錄執行的寫命令。快照文件發送完畢之后,開始向從服務器發送存儲在緩沖區中的寫命令;
2. 從服務器丟棄所有舊數據,載入主服務器發來的快照文件,之后從服務器開始接受主服務器發來的寫命令;
3. 主服務器每執行一次寫命令,就向從服務器發送相同的寫命令。
## 主從鏈
隨著負載不斷上升,主服務器可能無法很快地更新所有從服務器,或者重新連接和重新同步從服務器將導致系統超載。為了解決這個問題,可以創建一個中間層來分擔主服務器的復制工作。中間層的服務器是最上層服務器的從服務器,又是最下層服務器的主服務器。
[](https://camo.githubusercontent.com/eaaa1326b162942d874dc87c76e086ef5ff8b233/68747470733a2f2f67697465652e636f6d2f437943323031382f43532d4e6f7465732f7261772f6d61737465722f646f63732f706963732f33393561396538332d623161312d346131642d623137302d6430383165376262356261622e706e67)
# 十二、Sentinel
Sentinel(哨兵)可以監聽集群中的服務器,并在主服務器進入下線狀態時,自動從從服務器中選舉出新的主服務器。
# 十三、分片
分片是將數據劃分為多個部分的方法,可以將數據存儲到多臺機器里面,這種方法在解決某些問題時可以獲得線性級別的性能提升。
假設有 4 個 Redis 實例 R0,R1,R2,R3,還有很多表示用戶的鍵 user:1,user:2,... ,有不同的方式來選擇一個指定的鍵存儲在哪個實例中。
* 最簡單的方式是范圍分片,例如用戶 id 從 0~1000 的存儲到實例 R0 中,用戶 id 從 1001~2000 的存儲到實例 R1 中,等等。但是這樣需要維護一張映射范圍表,維護操作代價很高。
* 還有一種方式是哈希分片,使用 CRC32 哈希函數將鍵轉換為一個數字,再對實例數量求模就能知道應該存儲的實例。
根據執行分片的位置,可以分為三種分片方式:
* 客戶端分片:客戶端使用一致性哈希等算法決定鍵應當分布到哪個節點。
* 代理分片:將客戶端請求發送到代理上,由代理轉發請求到正確的節點上。
* 服務器分片:Redis Cluster。
# 十四、一個簡單的論壇系統分析
該論壇系統功能如下:
* 可以發布文章;
* 可以對文章進行點贊;
* 在首頁可以按文章的發布時間或者文章的點贊數進行排序顯示。
## 文章信息
文章包括標題、作者、贊數等信息,在關系型數據庫中很容易構建一張表來存儲這些信息,在 Redis 中可以使用 HASH 來存儲每種信息以及其對應的值的映射。
Redis 沒有關系型數據庫中的表這一概念來將同種類型的數據存放在一起,而是使用命名空間的方式來實現這一功能。鍵名的前面部分存儲命名空間,后面部分的內容存儲 ID,通常使用 : 來進行分隔。例如下面的 HASH 的鍵名為 article:92617,其中 article 為命名空間,ID 為 92617。
[](https://camo.githubusercontent.com/25aa17ff07a14ee7b3e09573383962ab6650c1c4/68747470733a2f2f67697465652e636f6d2f437943323031382f43532d4e6f7465732f7261772f6d61737465722f646f63732f706963732f37633534646532312d653266662d343032652d626334322d3430333764653163313539322e706e67)
## 點贊功能
當有用戶為一篇文章點贊時,除了要對該文章的 votes 字段進行加 1 操作,還必須記錄該用戶已經對該文章進行了點贊,防止用戶點贊次數超過 1。可以建立文章的已投票用戶集合來進行記錄。
為了節約內存,規定一篇文章發布滿一周之后,就不能再對它進行投票,而文章的已投票集合也會被刪除,可以為文章的已投票集合設置一個一周的過期時間就能實現這個規定。
[](https://camo.githubusercontent.com/36fd73840bdbb6d95ccec28aa2ef66871b4ff2cc/68747470733a2f2f67697465652e636f6d2f437943323031382f43532d4e6f7465732f7261772f6d61737465722f646f63732f706963732f34383566646633342d636366382d343138352d393763362d3137333734656537313961302e706e67)
## 對文章進行排序
為了按發布時間和點贊數進行排序,可以建立一個文章發布時間的有序集合和一個文章點贊數的有序集合。(下圖中的 score 就是這里所說的點贊數;下面所示的有序集合分值并不直接是時間和點贊數,而是根據時間和點贊數間接計算出來的)
[](https://camo.githubusercontent.com/f4edd48505a7bbcd99a6188648f415e6cff83323/68747470733a2f2f67697465652e636f6d2f437943323031382f43532d4e6f7465732f7261772f6d61737465722f646f63732f706963732f66376431373061332d653434362d346136342d616332642d6362393530323866383161382e706e67)
# 參考資料
* Carlson J L. Redis in Action\[J\]. Media.johnwiley.com.au, 2013.
* [黃健宏. Redis 設計與實現 \[M\]. 機械工業出版社, 2014.](http://redisbook.com/index.html)
* [REDIS IN ACTION](https://redislabs.com/ebook/foreword/)
* [Skip Lists: Done Right](http://ticki.github.io/blog/skip-lists-done-right/)
* [論述 Redis 和 Memcached 的差異](http://www.cnblogs.com/loveincode/p/7411911.html)
* [Redis 3.0 中文版- 分片](http://wiki.jikexueyuan.com/project/redis-guide)
* [Redis 應用場景](http://www.scienjus.com/redis-use-case/)
* [Using Redis as an LRU cache](https://redis.io/topics/lru-cache)
- 一.JVM
- 1.1 java代碼是怎么運行的
- 1.2 JVM的內存區域
- 1.3 JVM運行時內存
- 1.4 JVM內存分配策略
- 1.5 JVM類加載機制與對象的生命周期
- 1.6 常用的垃圾回收算法
- 1.7 JVM垃圾收集器
- 1.8 CMS垃圾收集器
- 1.9 G1垃圾收集器
- 2.面試相關文章
- 2.1 可能是把Java內存區域講得最清楚的一篇文章
- 2.0 GC調優參數
- 2.1GC排查系列
- 2.2 內存泄漏和內存溢出
- 2.2.3 深入理解JVM-hotspot虛擬機對象探秘
- 1.10 并發的可達性分析相關問題
- 二.Java集合架構
- 1.ArrayList深入源碼分析
- 2.Vector深入源碼分析
- 3.LinkedList深入源碼分析
- 4.HashMap深入源碼分析
- 5.ConcurrentHashMap深入源碼分析
- 6.HashSet,LinkedHashSet 和 LinkedHashMap
- 7.容器中的設計模式
- 8.集合架構之面試指南
- 9.TreeSet和TreeMap
- 三.Java基礎
- 1.基礎概念
- 1.1 Java程序初始化的順序是怎么樣的
- 1.2 Java和C++的區別
- 1.3 反射
- 1.4 注解
- 1.5 泛型
- 1.6 字節與字符的區別以及訪問修飾符
- 1.7 深拷貝與淺拷貝
- 1.8 字符串常量池
- 2.面向對象
- 3.關鍵字
- 4.基本數據類型與運算
- 5.字符串與數組
- 6.異常處理
- 7.Object 通用方法
- 8.Java8
- 8.1 Java 8 Tutorial
- 8.2 Java 8 數據流(Stream)
- 8.3 Java 8 并發教程:線程和執行器
- 8.4 Java 8 并發教程:同步和鎖
- 8.5 Java 8 并發教程:原子變量和 ConcurrentMap
- 8.6 Java 8 API 示例:字符串、數值、算術和文件
- 8.7 在 Java 8 中避免 Null 檢查
- 8.8 使用 Intellij IDEA 解決 Java 8 的數據流問題
- 四.Java 并發編程
- 1.線程的實現/創建
- 2.線程生命周期/狀態轉換
- 3.線程池
- 4.線程中的協作、中斷
- 5.Java鎖
- 5.1 樂觀鎖、悲觀鎖和自旋鎖
- 5.2 Synchronized
- 5.3 ReentrantLock
- 5.4 公平鎖和非公平鎖
- 5.3.1 說說ReentrantLock的實現原理,以及ReentrantLock的核心源碼是如何實現的?
- 5.5 鎖優化和升級
- 6.多線程的上下文切換
- 7.死鎖的產生和解決
- 8.J.U.C(java.util.concurrent)
- 0.簡化版(快速復習用)
- 9.鎖優化
- 10.Java 內存模型(JMM)
- 11.ThreadLocal詳解
- 12 CAS
- 13.AQS
- 0.ArrayBlockingQueue和LinkedBlockingQueue的實現原理
- 1.DelayQueue的實現原理
- 14.Thread.join()實現原理
- 15.PriorityQueue 的特性和原理
- 16.CyclicBarrier的實際使用場景
- 五.Java I/O NIO
- 1.I/O模型簡述
- 2.Java NIO之緩沖區
- 3.JAVA NIO之文件通道
- 4.Java NIO之套接字通道
- 5.Java NIO之選擇器
- 6.基于 Java NIO 實現簡單的 HTTP 服務器
- 7.BIO-NIO-AIO
- 8.netty(一)
- 9.NIO面試題
- 六.Java設計模式
- 1.單例模式
- 2.策略模式
- 3.模板方法
- 4.適配器模式
- 5.簡單工廠
- 6.門面模式
- 7.代理模式
- 七.數據結構和算法
- 1.什么是紅黑樹
- 2.二叉樹
- 2.1 二叉樹的前序、中序、后序遍歷
- 3.排序算法匯總
- 4.java實現鏈表及鏈表的重用操作
- 4.1算法題-鏈表反轉
- 5.圖的概述
- 6.常見的幾道字符串算法題
- 7.幾道常見的鏈表算法題
- 8.leetcode常見算法題1
- 9.LRU緩存策略
- 10.二進制及位運算
- 10.1.二進制和十進制轉換
- 10.2.位運算
- 11.常見鏈表算法題
- 12.算法好文推薦
- 13.跳表
- 八.Spring 全家桶
- 1.Spring IOC
- 2.Spring AOP
- 3.Spring 事務管理
- 4.SpringMVC 運行流程和手動實現
- 0.Spring 核心技術
- 5.spring如何解決循環依賴問題
- 6.springboot自動裝配原理
- 7.Spring中的循環依賴解決機制中,為什么要三級緩存,用二級緩存不夠嗎
- 8.beanFactory和factoryBean有什么區別
- 九.數據庫
- 1.mybatis
- 1.1 MyBatis-# 與 $ 區別以及 sql 預編譯
- Mybatis系列1-Configuration
- Mybatis系列2-SQL執行過程
- Mybatis系列3-之SqlSession
- Mybatis系列4-之Executor
- Mybatis系列5-StatementHandler
- Mybatis系列6-MappedStatement
- Mybatis系列7-參數設置揭秘(ParameterHandler)
- Mybatis系列8-緩存機制
- 2.淺談聚簇索引和非聚簇索引的區別
- 3.mysql 證明為什么用limit時,offset很大會影響性能
- 4.MySQL中的索引
- 5.數據庫索引2
- 6.面試題收集
- 7.MySQL行鎖、表鎖、間隙鎖詳解
- 8.數據庫MVCC詳解
- 9.一條SQL查詢語句是如何執行的
- 10.MySQL 的 crash-safe 原理解析
- 11.MySQL 性能優化神器 Explain 使用分析
- 12.mysql中,一條update語句執行的過程是怎么樣的?期間用到了mysql的哪些log,分別有什么作用
- 十.Redis
- 0.快速復習回顧Redis
- 1.通俗易懂的Redis數據結構基礎教程
- 2.分布式鎖(一)
- 3.分布式鎖(二)
- 4.延時隊列
- 5.位圖Bitmaps
- 6.Bitmaps(位圖)的使用
- 7.Scan
- 8.redis緩存雪崩、緩存擊穿、緩存穿透
- 9.Redis為什么是單線程、及高并發快的3大原因詳解
- 10.布隆過濾器你值得擁有的開發利器
- 11.Redis哨兵、復制、集群的設計原理與區別
- 12.redis的IO多路復用
- 13.相關redis面試題
- 14.redis集群
- 十一.中間件
- 1.RabbitMQ
- 1.1 RabbitMQ實戰,hello world
- 1.2 RabbitMQ 實戰,工作隊列
- 1.3 RabbitMQ 實戰, 發布訂閱
- 1.4 RabbitMQ 實戰,路由
- 1.5 RabbitMQ 實戰,主題
- 1.6 Spring AMQP 的 AMQP 抽象
- 1.7 Spring AMQP 實戰 – 整合 RabbitMQ 發送郵件
- 1.8 RabbitMQ 的消息持久化與 Spring AMQP 的實現剖析
- 1.9 RabbitMQ必備核心知識
- 2.RocketMQ 的幾個簡單問題與答案
- 2.Kafka
- 2.1 kafka 基礎概念和術語
- 2.2 Kafka的重平衡(Rebalance)
- 2.3.kafka日志機制
- 2.4 kafka是pull還是push的方式傳遞消息的?
- 2.5 Kafka的數據處理流程
- 2.6 Kafka的腦裂預防和處理機制
- 2.7 Kafka中partition副本的Leader選舉機制
- 2.8 如果Leader掛了的時候,follower沒來得及同步,是否會出現數據不一致
- 2.9 kafka的partition副本是否會出現腦裂情況
- 十二.Zookeeper
- 0.什么是Zookeeper(漫畫)
- 1.使用docker安裝Zookeeper偽集群
- 3.ZooKeeper-Plus
- 4.zk實現分布式鎖
- 5.ZooKeeper之Watcher機制
- 6.Zookeeper之選舉及數據一致性
- 十三.計算機網絡
- 1.進制轉換:二進制、八進制、十六進制、十進制之間的轉換
- 2.位運算
- 3.計算機網絡面試題匯總1
- 十四.Docker
- 100.面試題收集合集
- 1.美團面試常見問題總結
- 2.b站部分面試題
- 3.比心面試題
- 4.騰訊面試題
- 5.哈羅部分面試
- 6.筆記
- 十五.Storm
- 1.Storm和流處理簡介
- 2.Storm 核心概念詳解
- 3.Storm 單機版本環境搭建
- 4.Storm 集群環境搭建
- 5.Storm 編程模型詳解
- 6.Storm 項目三種打包方式對比分析
- 7.Storm 集成 Redis 詳解
- 8.Storm 集成 HDFS 和 HBase
- 9.Storm 集成 Kafka
- 十六.Elasticsearch
- 1.初識ElasticSearch
- 2.文檔基本CRUD、集群健康檢查
- 3.shard&replica
- 4.document核心元數據解析及ES的并發控制
- 5.document的批量操作及數據路由原理
- 6.倒排索引
- 十七.分布式相關
- 1.分布式事務解決方案一網打盡
- 2.關于xxx怎么保證高可用的問題
- 3.一致性hash原理與實現
- 4.微服務注冊中心 Nacos 比 Eureka的優勢
- 5.Raft 協議算法
- 6.為什么微服務架構中需要網關
- 0.CAP與BASE理論
- 十八.Dubbo
- 1.快速掌握Dubbo常規應用
- 2.Dubbo應用進階
- 3.Dubbo調用模塊詳解
- 4.Dubbo調用模塊源碼分析
- 6.Dubbo協議模塊