[TOC]
## **Redis 是什么**
Redis是一個使用 C編寫的開源、支持網絡、基于內存、分布式、可選持久性的鍵值對存儲數據庫。與傳統數據庫不同的是,Redis 的數據是存在內存中的,所以讀寫速度非常快,被廣泛應用于緩存方向。Redis 可以將數據寫入磁盤中,保證了數據的安全不丟失,而且 Redis 的操作是原子性的。
## **Redis 的優點**
1. **基于內存操作**,內存讀寫速度快。
2. Redis 是**單線程**的,避免線程切換開銷及多線程的競爭問題。單線程是指網絡請求使用一個線程來處理,即一個線程處理所有網絡請求,Redis 運行時不止有一個線程,比如數據持久化的過程會另起線程。
3. **支持多種數據類型**,包括 String、Hash、List、Set、ZSet 等
4. **支持持久化**。Redis 支持 `RDB` 和 `AOF` 兩種持久化機制,持久化功能可以有效地避免數據丟失問題。
5. **支持事務**。Redis 的所有操作都是原子性的,同時 Redis 還支持對幾個操作合并后的原子性執行。
6. **支持主從復制**。主節點會自動將數據同步到從節點,可以進行讀寫分離。
## **Redis 為什么這么快**
* **基于內存**:Redis 是使用內存存儲,沒有磁盤IO上的開銷。數據在內存中,讀寫速度快。
* **單線程實現(Redis 6.0 以前)**:Redis 使用單個線程處理請求,避免了多個線程之間線程切換和鎖資源爭用的開銷。
* **IO多路復用模型**:Redis 采用I/O多路復用技術,使用單線程來輪詢描述符,將數據庫的操作都轉換成了事件,不在網絡I/O 上浪費過多時間。
* **高效的數據結構**:數據結構簡單,對數據操作也簡單,Redis中的數據結構是專門進行設計的;
## **Redis 為何選擇單線程**
* **避免過多的上下文切換開銷**。程序始終運行在進程的單個線程中,沒有多線程切換的場景
* **避免同步機制的開銷**:如果 Redis 選擇多線程模型,需要考慮數據同步的問題,則必然會引入某些同步機制,會導致在操作數據過程中帶來更多的開銷,增加程序復雜度的同時還會降低性能。
* ****實現簡單,方便維護****:如果 Redis 使用多線程模式,那么所有的底層數據結構的設計都必須考慮線程安全問題,那么 Redis 的實現將會變得更加復雜。
> 單線程指的是網絡請求模塊使用了一個線程(所以不需考慮并發安全性),即一個線程處理所有網絡請求,其他模塊仍用了多個線程。
## **Memcached 和 Redis 的區別**
1. Redis 只使用**單核**,而 Memcached 可以使用多核。
2. MemCached 數據結構單一,僅用來緩存數據,而 **Redis 支持多種數據類型**
3. MemCached 不支持數據持久化,重啟后數據會消失。**Redis 支持數據持久化**
4. **Redis 提供主從同步機制和 cluster 集群部署能力**,能夠提供高可用服務。Memcached 沒有提供原生的集群模式,需要依靠客戶端實現往集群中分片寫入數據。
5. **Redis 使用單線程的多路 IO 復用模型**,Memcached 使用多線程的非阻塞 IO 模型
## **Redis 數據類型**
### 基本數據類型
1. **Sting**:可以是字符串、數字或者二進制,但值最大不能超過 512MB。
2. ** Hash** :一個鍵值對集合。
3. **Set**:無序去重的集合。Set 提供了交集、并集等方法,對于實現共同好友、共同關注等功能特別方便。
4. **List**:有序可重復的集合,底層是依賴雙向鏈表實現的
5. **Sorted Set(ZSet)**:有序 Set。內部維護了一個 `score` 的參數來實現。適用于排行榜和帶權重的消息隊列等場景。
### 特殊的數據類型
1. **Bitmap**:位圖,可以認為是一個以位為單位數組,數組中的每個單元只能存 0 或者 1,數組的下標在 Bitmap 中叫做偏移量。Bitmap 的長度與集合中元素個數無關,而是與基數的上限有關。
2. **Hyperloglog**:用來做基數統計的算法,其優點是,在輸入元素的數量或者體積非常非常大時,計算基數所需的空間總是固定的、并且是很小的。典型的使用場景是統計獨立訪客
3. **Geospatial **:主要用于存儲地理位置信息,并對存儲的信息進行操作,適用場景如定位、附近的人等。
## **Redis 事務**
事務的原理是將一個事務范圍內的若干命令發送給 Redis,然后再讓 Redis 依次執行這些命令。
一組命令的集合! 一個事務中的所有命令都會被序列化,在事務執行過程的中,會按 照順序執行! 一次性、順序性、排他性!執行一些列的命令!
事務的生命周期:
1. 使用`MULTI`開啟一個事務
2. 開啟事務的時候,每次操作的命令將會被插入到一個隊列中,同時這個命令并不會被真正執行
3. `EXEC`命令進行提交事務
一個事務范圍內某個命令出錯不會影響其他命令的執行,不保證原子性
~~~
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set a 1
QUEUED
127.0.0.1:6379(TX)> set b 1 2
QUEUED
127.0.0.1:6379(TX)> set c 3
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (error) ERR syntax error
3) OK
127.0.0.1:6379> keys *
1) "c"
2) "a"
~~~
### WATCH 命令
`WATCH`命令可以監控一個或多個鍵,一旦其中有一個鍵被修改,之后的事務就不會執行(類似于樂觀鎖)。執行`EXEC`命令之后,就會自動取消監控。
~~~
127.0.0.1:6379> watch name
OK
127.0.0.1:6379> set name zhaosi
OK
127.0.0.1:6379> get name
"zhaosi"
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set name liuneng
QUEUED
127.0.0.1:6379(TX)> exec
(nil)
127.0.0.1:6379> get name
"zhaosi"
~~~
## **redis 內存淘汰機制**
* **被動刪除(惰性)**:在訪問 key 時,如果發現 key 已經過期,那么會將 key 刪除。
* **主動刪除(定期)**:定時清理 key,每次清理會依次遍歷所有 DB,從 db 隨機取出 20 個 key,如果過期就刪除,如果其中有 5 個 key 過期,那么就繼續對這個 db 進行清理,否則開始清理下一個 db
* **內存不夠時清理**:Redis 有最大內存的限制,通過 maxmemory 參數可以設置最大內存,當使用的內存超過了設置的最大內存,就要進行內存釋放, 在進行內存釋放的時候,會按照配置的淘汰策略清理內存。
## 內存淘汰策略
~~~
# The default is:
#
# maxmemory-policy noeviction
~~~
當 Redis 的內存超過最大允許的內存之后,Redis 會觸發內存淘汰策略,刪除一些不常用的數據,以保證Redis 服務器正常運行。
Redis v4.0 之前的 6 中數據淘汰策略:
* `volatile-lru`:LRU (Least Recently Used)最近使用。利用 LRU 算法移除設置了過期時間的 key。
* `allkeys-lru`:當內存不足以容納新寫入數據時,從數據集中移除最少使用的 key
* `volatile-ttl`:從已設置過期時間的數據集中挑選將要過期的數據淘汰
*` volatile-random`:從已設置過期時間的數據集中任意選擇數據淘汰
* `allkeys-random`:從數據集中任意選擇數據淘汰
* `no-eviction`:禁止刪除數據,當內訓不足以容納新寫入數據時,新寫入操作會報錯
Redis v4.0之后增加:
* `volatile-lfu`:LFU(Least Frequently Used)最少使用。從已設置過期時間的數據集中挑選最不經常使用的數據淘汰
* `allkeys-lfu`:從數據集中移除最不經常使用的 key
## **Redis 的持久化**
* **RDB** 做全量持久化(Redis 默認的持久化方式)。按照一定的時間周期策略把內存的數據以快照的形式保存到硬盤的二進制文件。對應產生的數據文件為 dump.rdb,通過配置文件中的 save 參數來定義快照的周期。( 快照可以是其所表示的數據的一個副本,也可以是數據的一個復制品。)
* **AOP** 增量持久化:Redis 會將每一個收到的寫命令都通過 Write 函數追加到文件最后,類似于 MySQL 的 `binlog`。必要時,可以通過重新執行文件中保存的寫命令來在內存中重建Redis 數據庫。
Redis 本身的機制是:當 AOF 持久化開啟且存在 AOF 文件時,優先加載 AOF 文件。當 AOF 持久化關閉或不存在 AOF 文件時,加載 RDB 文件。加載 AOF/RDB 文件成功之后,Redis 啟動成功。如果 AOF/RDB 文件存在錯誤,則 Redis 啟動失敗并打印錯誤信息
## **簡單介紹 Pipeline**
可以將多條命令一起打包發送至 redis 處理,處理完成后,將處理結果按照順序打包,一起返回,好處是可以減少 I/O 次數,提升 redis 吞吐量。
**注意**:一個 pipeline 中的命令互相之間不能有因果關系。另外,一個 pipeline 中的命令不宜過多,不然數據量過大,增加客戶端的等待時間,還可能造成網絡阻塞。可以將大量命令的拆分多個小的 pipeline 命令完成。
## **使用過 redis 做異步隊列實現方式**
一般使用 list 結構做隊列, `lpush` 生產消息, `rpop` 消費消息,當 `rpop` 沒有消息的時候,適當的 `sleep` 一會兒再重試
### 不用 sleep
使用 list 結構中的阻塞指令,`brpop`、`blpop`。在列表沒有元素時,會阻塞列表直接等待超時或發現可彈出元素為止
### 如果我想生產一次,消費多次呢?
可以使用 pub/sub 發布訂閱模式 ,實現 1:N 的消息隊列
## **pub /sub 模式缺點**
在消費者下線的情況下,生產的消息會丟失。可以使用專業的消息隊列保證消息不丟失,如 rocketMQ、rabbitMQ 等
## **Redis 如何實現延時隊列**
使用 `sortedset`,用時間戳作為 `score`,消息內容作為 key,生產者調用 zadd 生產消息,消費者用 zrangebyscore 指令獲取 N 秒之前的消息,輪詢進行處理
## **如果事務中有某條 / 某些命令執行失敗了會怎么樣呢?**
事務中的某條命令執行失敗了,事務中的其他指令依然會繼續執行。另外,Redis 事務不支持回滾
## **假如 Redis 里面有 1 億個 key,其中有 10w 個 key 是以某個固定的已知的前綴開頭的,如何將它們全部找出來?**
使用 keys 指令可以掃出指定模式的 key 列表。
### 如果這個 redis 正在給線上的業務提供服務,那使用 keys 指令會有什么問題
這時回答一個 Redis 關鍵性特性:redis 是**單線程**的。roolback keys 指令會導致線程阻塞一段時間,線上服務會停頓,知道指令執行完成,服務才能恢復。這時候可以使用 `scan` 命令,`scan` 命令可以無阻塞的提取出指定模式的 key列表,但是 key 可能會有一點重復,在客戶端做一次去重就可以了。 scan 整體所花費的時間會比用 keys 指令長。
## **MySQL 里有 2000w 數據,redis 中只能存 20w 的數據,如何保證 redis 中的數據都是熱點數據?**
當客戶端往 Redis 添加了新的數據后。Redis 會檢查內存使用情況,如果大于 maxmemory 的限制,則根據設定好的**淘汰策略**對舊數據進行回收。
## **怎么保證集群的高可用**
Redis Sentinal(哨兵)在 master 宕機時會自動將 slave 提升為 master,繼續提供服務。
Redis 集群沒有使用一致性 hash, 而是引入了 哈希槽的概念。Redis 集群有 16384 個哈希槽,每個 key 通過 CRC16 校驗后對 16384 取模來決定放置哪個槽,集群的每個節點負責一部分 hash 槽。
## 單線程可以處理高并發請求嗎?
可以。
并發并不是并行。并發性I/O流,意味著能夠讓一個計算單元來處理來自多個客戶端的流請求。并行性是服務器能夠同時執行幾個事情,具有多個計算單元。
Redis 是用“單線程-多路復用I/O模型”來實現高性能的內存數據服務的,這種機制避免了使用鎖,但同時這種機制在進行耗時命令時會使 redis 并發下降,因為是單一線程,所以同一時刻只能有一個操作進行,耗時的命令會導致并發下降,不只是讀并發,寫并發也會下降。 單一線程也只能用到一個 CPU 核心,所以可以在同一個多核服務器中,啟動多個實例,組成`master-master`或者 `master-slave` 的形式,耗時的讀命令可以完全在slave進行。
*
- PHP
- PHP 核心架構
- PHP 生命周期
- PHP-FPM 詳解
- PHP-FPM 配置優化
- PHP 命名空間和自動加載
- PHP 運行模式
- PHP 的 Buffer(緩沖區)
- php.ini 配置文件參數優化
- 常見面試題
- 常用函數
- 幾種排序算法
- PHP - 框架
- Laravel
- Laravel 生命周期
- ThinkPHP
- MySQL
- 常見問題
- MySQL 索引
- 事務
- 鎖機制
- Explain 使用分析
- MySQL 高性能優化規范
- UNION 與 UNION ALL
- MySQL報錯:sql_mode=only_full_group_by
- MySQL 默認的 sql_mode 詳解
- 正則表達式
- Redis
- Redis 知識
- 持久化
- 主從復制、哨兵、集群
- Redis 緩存擊穿、穿透、雪崩
- Redis 分布式鎖
- RedisBloom
- 網絡
- 計算機網絡模型
- TCP
- UDP
- HTTP
- HTTPS
- WebSocket
- 常見幾種網絡攻擊方式
- Nginx
- 狀態碼
- 配置文件
- Nginx 代理+負載均衡
- Nginx 緩存
- Nginx 優化
- Nginx 配置 SSL 證書
- Linux
- 常用命令
- Vim 常用操作命令
- Supervisor 進程管理
- CentOS與Ubuntu系統區別
- Java
- 消息隊列
- 運維
- RAID 磁盤陣列
- 邏輯分區管理 LVM
- 業務
- 標準通信接口設計
- 業務邏輯開發套路的三板斧
- 微信小程序登錄流程
- 7種Web實時消息推送方案
- 用戶簽到
- 用戶注冊-短信驗證碼
- SQLServer 刪除同一天用戶重復簽到
- 軟件研發完整流程
- 前端
- Redux
- 其他
- 百度云盤大文件下載
- 日常報錯記錄
- GIT
- SSL certificate problem: unable to get local issuer certificate
- NPM
- reason: connect ECONNREFUSED 127.0.0.1:31181
- SVN
- SVN客戶端無法連接SVN服務器,主機積極拒絕
- Python
- 基礎
- pyecharts圖表
- 對象
- 數據庫
- PySpark
- 多線程
- 正則
- Hadoop
- 概述
- HDFS