Redis真的又小又快又持久嗎

龍騰萬里sky發表於2022-01-07

一本正經

面試官:小夥子,談談對Redis的看法。
:啊,看法呀,坐著看還是躺著看。Redis很小?很快?但很持久?

面試官:一本正經的說,我懷疑你在開車,不僅開開車還搞顏色。
:。。。

面試官:去去去,我時間有限,別瞎扯淡。回到正題,你對Redis瞭解有多少。
:輕量體積小、基於記憶體非常快、RDB配合AOF持久化讓其一樣堅挺持久。

面試官:說點具體的。
:請看正文。

正文

簡介
Redis是一個開源的、高效能的、基於鍵值對的快取與儲存系統,通過提供多種鍵值資料型別來適應不同場景下的快取與儲存需求。與此同時,Redis的諸多高層級功能讓其可以勝任訊息佇列、任務佇列等不同的角色。除此之外,Redis還支援外部模組擴充套件,在某些特定的場景下可以作為主資料庫使用。

由於記憶體的讀寫速度遠快於硬碟,就算現在的固態盤思維估計也是朝著記憶體那個思維模式發展的,大概也許我是個外行,但是長久儲存還是使用機械盤。所以Redis資料庫中的所有資料都儲存在記憶體中那是相當快的。也有一定的風險,會導致丟失資料,但配合RDB以及AOF持久化會減少風險。

一、初識Redis

1、linux下安裝(Redhat7系列)

1.1、安裝

此處準備的是原始碼包,版本不在於最新,在於穩定適用。

其餘版本在官網獲取,或者在其託管的平臺github上獲取,如下為Redis的官網下載地址。

https://redis.io/download

redis-6.0.8.tar.gz
#安裝
tar -zxvf redis-6.0.8.tar.gz
#編譯
make && make install

1.2、排查錯誤

make[1]: *** [server.o] 錯誤 1

1.3、解決方案

1.3.1、安裝依賴環境

yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils

1.3.2、加環境變數並生效

scl enable devtoolset-9 bash
echo "/opt/rh/devtoolset-9/enable" >> /etc/profile 

重新讀取環境變數配置檔案

source /etc/profile

重新編譯解決問題

#切換到Redis的安裝目錄,一般原始碼包安裝會放在/usr/local/下面,看個人使用習慣
cd /opt/redis-6.0.8/
#編譯
make && make install

常用基本命令練習可以參考菜鳥教程

https://www.runoob.com/redis/redis-commands.html

1.4、啟動與登入

啟動redis-server服務端

#啟動redis服務
nohup /opt/redis-6.0.8/src/redis-server & 

登入redis-cli客戶端

#登入redis-cli
/opt/redis-6.0.8/src/redis-cli

測試驗證,此時linux下的redis正式啟動成功,下面會帶來基本用法介紹。

ping
pong

1.5、設定密碼

預設是沒有開放密碼設定的,需要手動開啟註釋掉的引數配置。

#編輯配置檔案
vim /opt/redis-6.0.8/redis.conf

#原本的被註釋掉,複製一行改成你設定的密碼即可
#requirepass foobared
requirepass 123456

2、Windows下安裝

2.1、安裝

Redis-x64-3.2.100.zip

2.1.1、Windows下解壓或者msi直接安裝即可。

2.1.2、設定服務命令(註冊為服務形式,自啟)
安裝服務

redis-server --service-install redis.windows-service.conf --loglevel verbose

解除安裝服務

redis-server --service-uninstall

2.2、啟動與關閉

redis-server redis.windows.conf

2.2.1、開啟服務

redis-server --service-start

2.2.2、停止服務

redis-server --service-stop

2.3、啟動redis服務

#同樣在redis解壓的或者安裝的目錄以管理員身份執行cmd
redis-server --service-start

2.4、cmd下執行測試登入

#在redis解壓的或者安裝的目錄以管理員身份執行cmd
redis-cli.exe -h 127.0.0.1 -p 6379
#或者直接執行
redis-cli
#執行
redis-cli
#登入測試
ping

5、Windows下的管理工具rdm,是視覺化介面
https://redisdesktop.com/download

二、基礎知識

1、面試常問到

面試官:redis中的資料型別有哪些,能聊聊嗎?

:string(字串型別)、hash(雜湊型別)、list(列表型別)、set(集合型別)、zset(有序集合型別)、stream(流型別)
stream是redis5.0新增的特性支援。

面試官:嚯,小夥子有點東西啊,知道的還不少嘛,連stream流型別都知道。

:一臉懵逼...

三、進階

1、持久化

面試官:Redis的一些高階特性瞭解嗎?

:略有了解。

面試官:能具體談談嗎?

:飛速在大腦搜尋者以前看書總結的。快取、持久化迎面而來。

將Redis作為快取伺服器,但快取被穿透後會對效能照成較大影響,所有快取同時失效快取雪崩,從而使服務無法響應。

我們希望Redis能將資料從記憶體中以某種形式同步到磁碟中,使之重啟以後根據磁碟中的記錄恢復資料。這一過程就是持久化。

面試官:知道Redis有哪幾種常見的持久化方式嗎?

:Redis預設開啟的RDB持久化,AOF持久化方式需要手動開啟。

Redis支援兩種持久化。一種是RDB方式,一種是AOF方式。前者會根據指定的規則“定時”將記憶體中的資料儲存到硬碟上,而後者在每次執行命令後將命令本書記錄下來。對於這兩種持久化方式,你可以單獨使用其中一種,但大多數情況下是將二者緊密結合起來。

此時的面試官一臉期待,炯炯有神的看向了我,請繼續。

2、RDB方式

繼續介紹,RDB採取的是快照方式,預設設定自定義快照【自動同步】,預設配置如下。

同樣可以手動同步

#不推薦在生產環境中使用
SAVE
#非同步形式
BGSAVE
#基於自定義快照
FLASHALL

3、AOF方式

當使用Redis儲存非臨時資料時,一般需要開啟AOF持久化來降低程式終止導致資料的丟失。AOF可以將Redis執行的每一條命令追加到硬碟檔案中,著這個過程中顯然會讓Redis的效能打折扣,但大部分情況下這種情況可以接受。這裡強調一點,使用讀寫較快的硬碟可以提高AOF的效能。

預設沒有開啟,需要手動開啟AOF,當你檢視redis.conf檔案時也會發現appendonly配置的是no

appendonly yes

開啟AOF持久化後,每次執行一條命令會會更改Redis中的資料的目錄,Redis會將該命令寫入磁碟中的AOF檔案。AOF檔案的儲存位置和RDB檔案的位置相同,都是通過dir引數設定,預設的檔名是appendonly.aof,可以通過appendfilename引數修改。

appendfilename "appendonly.aof

實際上Redis也正是這樣做的,每當達到一定的條件時Redis就會自動重寫AOF檔案,這個條件可以通過redis.conf配置檔案中設定:

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

在啟動時Redis會逐行執行AOF檔案中的命令將硬碟中的資料載入到記憶體中,載入的速度相比RDB會慢一些。

雖然每次執行更改資料庫內容的操作時,AOF都將命令記錄在AOF檔案中。但事實上,由於作業系統的快取機制,資料並沒與真正寫入硬碟,而是進入了作業系統的硬碟快取。在預設情況下,作業系統每30秒會執行一次同步操作,以便將硬碟快取中的內容寫入硬碟。

在Redis中可以通過appendfsync設定同步的時機

# appendfsync always
#預設設定為everysec
appendfsync everysec
# appendfsync no

Redis允許同時開啟AOF和RDB。這樣既保證了資料的安全,又對進行備份等操作比較友好。此時重新啟動Redis後,會使用AOF檔案來恢復資料。因為AOF方式的持久化,將會丟失資料的概率降至最小化。

4、Redis複製

通過持久化功能,Redis保證了即使伺服器重啟的情況下也不會丟失(少部分遺失)資料。但是資料庫是儲存在單臺伺服器上的,難免不會發生各種突發情況,比如硬碟故障,伺服器突然當機等等,也會導致資料遺失。

為了儘可能的避免故障,通常做法是將資料庫複製多個副本以部署在不同的伺服器上。這樣即使有一臺出現故障,其它的伺服器依舊可以提供服務。為此,Redis提供了複製(replication)功能。即實現一個資料庫中的資料更新後,自動將更新的資料同步到其它資料庫上。

此時熟悉MySQL的同學,是不是覺得與MySQL的主從複製很像,以開啟二進位制日誌binlog實現同步複製。

而Redis中使用複製功能更為容易,相比MySQL而言。只需要在從庫中啟動時加入slaveof 從資料庫地址。

#在從庫中配置
slaveof master_database_ip_addr
#測試,加了nohup與&是放入後臺,並且輸出日誌到/root/目錄下的nohup.out
nohup /opt/redis-6.0.8/src/redis-server --6380 --slaveof 192.168.245.147 6379 &

4.1、原理

複製初始化。這裡主要原理是從庫啟動,會向主庫傳送SYNC命令。同時主庫接收到SYNC命令後會開始在後臺儲存快照,即RDB持久化的過程,並將快照期間接收的命令快取起來。當快照完成後,Redis會將快照檔案和所有快取的命令傳送給從資料庫。從資料庫收到後,會載入快照檔案並執行收到的快取命令。

複製同步階段會貫穿整個主從同步過程,直到主從關係終止為止。在複製的過程中快照起到了至關重要的作用,只要執行復制就會進行快照,即使關閉了RDB方式的持久化,通過刪除所有save引數。

4.2、樂觀複製

Redis採用了樂觀複製(optimistic replication)的複製策略。容忍在一定時間內主從資料庫的內容是不同的,但是兩者的資料最終是會同步的。具體來講,Redis在主從資料庫之間複製資料的過程本身是非同步的,這就意味著,主資料庫執行完客戶端請求的命令會立即將命令在主資料庫的執行結果反饋給客戶端,並非同步的將資料同步給從庫,不會等待從資料庫接收到該命令在返回給客戶端。

當資料至少同步給指定數量的從庫時,才是可寫,通過引數指定:

#設定最少限制3
min-slaves-to-write 3
#設定允許從資料最長失去連線時間
min-slaves-max-lag 10

4.3、增量複製

基於以下三點實現

  • 從庫會儲存主庫的執行ID(run id)。每個Redis執行例項均會擁有一個唯一執行ID,每當例項重啟後,就會自動生成一個新的執行ID。類似於MySQL的從節點配置的唯一ID去識別。
  • 在複製同步階段,主庫一條命令被傳送到從庫時,會同時把該命令存放到一個積壓佇列(backlog)中,記錄當前積壓佇列中存放的命令的偏移量範圍
  • 從庫接收到主庫傳來的命令時,會記錄該命令的偏移量

4.4、注意

當主資料庫崩潰時,情況略微複雜。手動通過從資料庫資料庫恢復主庫資料時,需要嚴格遵循以下原則:

  • 在從資料庫中使用SLAVEOF NO ONE命令將從庫提升為主庫繼續服務。
  • 啟動之前崩潰的主庫,然後使用SLAVEOF命令將其設定為新的主庫的從庫。

注意:當開啟複製且資料庫關閉持久化功能時,一定不要使用supervisor以及類似的程式管理工具令主庫崩潰後重啟。同樣當主庫所在的伺服器因故障關閉時,也要避免直接重新啟動。因為當主庫重啟後,沒有開啟持久化功能,資料庫中所有資料都被清空。此時從庫依然會從主庫中接收資料,從而導致所有從庫也被清空,導致資料庫的持久化開了個寂寞

手動維護確實很麻煩,好在Redis提供了一種自動化方案:哨兵去實現這一過程,避免手動維護易出錯的問題。

5、哨兵(sentinel)

從Redis的複製歷中,我們瞭解到在一個典型的一主多從的Redis系統中,從庫在整個系統中起到了冗餘備份以及讀寫分離的作用。當主庫遇到異常中斷服務後,開發人員手動將從升主時,使系統繼續服務。過程相對複雜,不好實現自動化。此時可藉助哨兵工具。

哨兵的作用

  • 監控Redis系統執行情況
  • 監控主庫和從庫是否正常執行
  • 主庫gg思密達,自動將從庫升為主庫,美滋滋

當然也有多個哨兵監控主從資料庫模式,哨兵之間也會互相監控,如下圖:

首先需要建立起一主多從的模型,然後開啟配置哨兵。

#主庫
sentinel monitor master 127.0.0.1 6379 1
#建立配置檔案,例如sentinel.conf
redis-sentinel /opt/path/to/sentinel.conf

關於哨兵就介紹這麼多,現在大腦中有印象。至少知道有那麼回事,可以和美女面試官多掰扯掰扯。

6、叢集(cluster)

從Redis3.0開始加入了叢集這一特性。

即使使用哨兵,此時的Redis叢集的每個資料庫依然存有叢集中的所有資料,從而導致叢集的總資料儲存量受限於可用記憶體最小的資料庫節點,繼而出現木桶效應。正因為Redis所有資料都是基於記憶體儲存,問題已經很突出,尤其是當Redis作為持久化儲存服務時。

有這樣一種場景。就擴容來說,在客戶端分片後,如果像增加更多的節點,需要對資料庫進行手動遷移。遷移的過程中,為了保證資料的一致性,需要將進群暫時下線,相對比較複雜。

此時考慮到Redis很小,啊不口誤,是輕量的特點。可以採用預分片(presharding)在一定程度上避免問題的出現。換句話說,就是在部署的初期,提前考慮日後的儲存規模,建立足夠多的例項。

從上面的理論知識來看,哨兵和叢集類似,但哨兵和叢集是兩個獨立的功能。如果要進行水平擴容,叢集是不錯的選擇。

配置叢集,開啟配置檔案redis.conf中的cluster-enabled

cluster-enabled yes

配置叢集每個節點配置不同工作目錄,或者修改持久化檔案

cluster-config-file nodes-6379.conf

叢集測試大家可以執行配置,參考其他書籍亦可,實現並不難。只要是知其原理。

四、Redis for Java

示例

package com.jedis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class Test {

	@org.junit.Test
	public void demo() {
		Jedis jedis = new Jedis("127.0.0.1", 6379);
		jedis.set("name", "sky");
		String params = jedis.get("jedis");
		System.out.println(params);
		jedis.close();
	}
	
	@org.junit.Test
	public void config() {
		// 獲取連線池的配置物件
		JedisPoolConfig config = new JedisPoolConfig();
		// 設定最大連線數
		config.setMaxTotal(30);
		// 設定最大空閒連線數
		config.setMaxIdle(10);
		// 獲取連線池
		JedisPool pool = new JedisPool(config, "127.0.0.1", 6379);
		// 獲得核心物件
		Jedis jedis = null;
		try {
			//通過連線池獲取連線
			jedis = pool.getResource();
			//設定物件
			jedis.set("poolname", "pool");
			//獲取物件
			String pools = jedis.get("poolname");
			System.out.println("values:"+pools);
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			//釋放資源
			if(jedis != null){
				jedis.close();
			}
			if(pool != null){
				pool.close();
			}
		}	
	}
}

最後放一個製作很粗超的思維導圖,一時興起,居然寫了這麼多。

by 龍騰萬里sky 原創不易,白嫖有癮

相關文章