Redis學習筆記(Jedis&資料型別&持久化&主從複製)

Jaybo發表於2019-03-02

一、redis介紹

1.1 什麼是NoSQL

NoSQL,泛指非關係型的資料庫,NoSQL 即 Not-Only SQL,它可以作為關係型資料庫的良好補充。隨著網際網路 web2.0 網站的興起,非關係型的資料庫現在成了一個極其熱門的新領域,非關聯式資料庫產品的發展非常迅速。而傳統的關聯式資料庫在應付 web2.0 網站,特別是超大規模和高併發的 SNS 型別的 web2.0 純動態網站已經顯得力不從心,暴露了很多難以克服的問題,例如:

  1. High performance —— 對資料庫高併發讀寫的需求

    web2.0 網站要根據使用者個性化資訊來實時生成動態頁面和提供動態資訊,所以基本上無法使用動態頁面靜態化技術,因此資料庫併發負載非常高,往往要達到每秒上萬次讀寫請求。關聯式資料庫應付上萬次 SQL 查詢還勉強頂得住,但是應付上萬次 SQL 寫資料請求,硬碟 IO 就已經無法承受了。其實對於普通的 BBS 網站,往往也存在對高併發寫請求的需求,例如網站的實時統計線上使用者狀態,記錄熱門帖子的點選次數,投票計數等,因此這是一個相當普遍的需求。

  2. Huge Storage —— 對海量資料的高效率儲存和訪問的需求

    類似 Facebook,Twitter,Friendfeed 這樣的 SNS 網站,每天使用者產生海量的使用者動態,以 Friendfeed 為例,一個月就達到了 2.5 億條使用者動態,對於關聯式資料庫來說,在一張 2.5 億條記錄的表裡面進行 SQL 查詢,效率是極其低下乃至不可忍受的。再例如大型 web 網站的使用者登入系統,例如騰訊,盛大,動輒數以億計的帳號,關聯式資料庫也很難應付。

  3. High Scalability && High Availability —— 對資料庫的高可擴充套件性和高可用性的需求

    在基於 web 的架構當中,資料庫是最難進行橫向擴充套件的,當一個應用系統的使用者量和訪問量與日俱增的時候,你的資料庫卻沒有辦法像 web server 和 app server 那樣簡單的通過新增更多的硬體和服務節點來擴充套件效能和負載能力。對於很多需要提供 24 小時不間斷服務的網站來說,對資料庫系統進行升級和擴充套件是非常痛苦的事情,往往需要停機維護和資料遷移,為什麼資料庫不能通過不斷的新增伺服器節點來實現擴充套件呢?

NoSQL 資料庫的產生就是為了解決大規模資料集合多重資料種類帶來的挑戰,尤其是大資料應用難題。

一些主流的 NoSQL 產品:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

NoSQL 資料庫的四大分類如下:

  • 1)鍵值(Key-Value)儲存資料庫

    相關產品: TokyoCabinet/Tyrant、Redis、Voldemort、Berkeley DB

    典型應用: 內容快取,主要用於處理大量資料的高訪問負載。

    資料模型: 一系列鍵值對

    優勢: 快速查詢

    劣勢: 儲存的資料缺少結構化

  • 2)列儲存資料庫

    相關產品:Cassandra、HBase、Riak

    典型應用:分散式的檔案系統

    資料模型:以列簇式儲存,將同一列資料存在一起

    優勢:查詢速度快,可擴充套件性強,更容易進行分散式擴充套件

    劣勢:功能相對侷限

  • 3)文件型資料庫

    相關產品:CouchDB、MongoDB

    典型應用:Web 應用(與 Key-Value 類似,Value 是結構化的)

    資料模型: 一系列鍵值對

    優勢:資料結構要求不嚴格

    劣勢:查詢效能不高,而且缺乏統一的查詢語法

  • 4)圖形(Graph)資料庫

    相關資料庫:Neo4J、InfoGrid、Infinite Graph

    典型應用:社交網路

    資料模型:圖結構

    優勢:利用圖結構相關演算法。

    劣勢:需要對整個圖做計算才能得出結果,不容易做分散式的叢集方案。

1.2 redis歷史發展

2008年,義大利的一家創業公司 Merzia 推出了一款基於 MySQL 的網站實時統計系統 LLOOGG,然而沒過多久該公司的創始人 SalvatoreSanfilippo 便 對 MySQL 的效能感到失望,於是他決定親自為 LLOOGG 量身定做一個資料庫,並於 2009 年開發完成,這個資料庫就是 Redis。 不過 SalvatoreSanfilippo 並不滿足只將 Redis 用於 LLOOGG 這一款產品,而是希望更多的人使用它,於是在同一年 SalvatoreSanfilippo 將 Redis 開源釋出,並開始和 Redis 的另一名主要的程式碼貢獻者 PieterNoordhuis 一起繼續著 Redis 的開發,直到今天。

SalvatoreSanfilippo 自己也沒有想到,短短的幾年時間,Redis 就擁有了龐大的使用者群體。Hacker News 在 2012 年釋出了一份資料庫的使用情況調查,結果顯示有近 12% 的公司在使用 Redis。國內如新浪微博、街旁網、知乎網,國外如 GitHub、Stack Overflow、Flickr 等都是 Redis 的使用者。

VMware 公司從 2010 年開始贊助 Redis 的開發, Salvatore Sanfilippo 和 Pieter Noordhuis 也分別在 3 月和 5 月加入 VMware,全職開發 Redis。

1.3 什麼是redis

Redis 是用 C 語言開發的一個開源的高效能鍵值對(key-value)資料庫。

Redis 是一個 nosql(not only sql不僅僅只有sql)資料庫,翻譯成中文叫做非關係型型資料庫。Redis 是將資料存放到記憶體中,由於內容存取速度快所以 Redis 被廣泛應用在網際網路專案中,

  • Redis 優點:存取速度快,官方稱讀取速度會達到 30 萬次每秒,寫速度在 10 萬次每秒最有,具體限制於硬體。
  • 缺點:對持久化支援不夠良好,所以 redis 一般不作為資料的主資料庫儲存,一般配合傳統的關係型資料庫使用。

Redis 通過提供多種鍵值資料型別來適應不同場景下的儲存需求,目前為止 Redis 支援的鍵值資料型別如下:

  • 字串型別
  • 雜湊型別
  • 列表型別
  • 集合型別
  • 有序集合型別

1.4 redis應用場景

1)快取(資料查詢、短連線、新聞內容、商品內容等等)。(最多使用)

2)分散式叢集架構中的 session 分離。

3)聊天室的線上好友列表。

4)任務佇列(秒殺、搶購、12306等等)

5)應用排行榜。

6)網站訪問統計。

7)資料過期處理(可以精確到毫秒)

8)儲存部落格或者論壇的留言回覆,等等.....

二、安裝執行redis

線上上 redis 一般都是安裝在 Linux 伺服器上執行,本教程使用 Linux 虛擬機器及 ssh 客戶端進行演示學習。

Linux 伺服器為 CentOS 6.4。

ssh 客戶端:在開發環境(windows)安裝 ssh 客戶端,本文使用 SecureCRT 作為 ssh 客戶端連線虛擬機器。

2.1 redis安裝環境

redis 是 C 語言開發,建議在 Linux 上執行,本文使用 Centos6.4 作為安裝環境。

安裝 redis 需要先將官網下載的原始碼進行編譯,編譯依賴 gcc 環境,如果沒有 gcc 環境,需要安裝 gcc:yum install gcc-c++

2.2 redis安裝

1)版本說明

本文使用 redis3.0 版本。3.0 版本主要增加了 redis 叢集功能。

2)原始碼下載

從官網下載:download.redis.io/releases/re…

將 redis-3.0.0.tar.gz 拷貝到 /usr/local 下。

3)解壓原始碼:tar -zxvf [redis-3.0.0]().tar.gz

4)進入解壓後的目錄進行編譯

cd /usr/local/redis-3.0.0
make
複製程式碼

5)安裝到指定目錄,如 /usr/local/redis

cd /usr/local/redis-3.0.0 
make PREFIX=/usr/local/redis install
複製程式碼

6)redis.conf

redis.conf 是 redis 的配置檔案,redis.conf 在 redis 原始碼目錄。

注意修改 port 作為 redis 程式的埠,port 預設 6379。

7)拷貝配置檔案到安裝目錄下

進入原始碼目錄,裡面有一份配置檔案 redis.conf,然後將其拷貝到安裝路徑下:

cd /usr/local/redis
mkdir conf
cp /usr/local/redis-3.0.0/redis.conf  /usr/local/redis/bin
複製程式碼

8)安裝目錄 bin 下的檔案列表

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

redis3.0 新增的 redis-sentinel 是 redis 叢集管理工具可實現高可用。配置檔案目錄:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

2.3 redis啟動

2.3.1 前端模式啟動

直接執行 bin/redis-server 將以前端模式啟動,前端模式啟動的缺點是 ssh 命令視窗關閉則 redis-server 程式結束,不推薦使用此方法。如下圖:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

2.3.2 後端模式啟動

修改 redis.conf 配置檔案,daemonize yes 以後端模式啟動。

執行如下命令啟動 redis:

cd /usr/local/redis
./bin/redis-server ./redis.conf
複製程式碼

redis 預設使用 6379 埠。

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

也可更改 redis.conf 檔案,修改埠號:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

2.3.3 啟動多個redis程式

1)方法一:

啟動時指定埠可在一臺伺服器啟動多個 redis 程式。

cd /usr/local/redis/bin
./redis-server ./redis.conf --port 6380
複製程式碼

2)方法二(推薦此方法):

建立多個 redis 目錄,以埠號命名,比如:建立 6379、6380 兩個目錄,將 redis 的安裝檔案 bin 和 conf 拷貝至這兩個目錄。

修改 6379 目錄下的 redis.conf 設定埠號為 6379。

修改 6380 目錄下的 redis.conf 設定埠號為 6380

啟動 6379 和 6380 目錄下的 redis-server 程式:

cd 6379
./redis-server . /redis.conf
cd 6380
./redis-server . /redis.conf
複製程式碼

查詢當前 redis 的程式:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

2.4 redis停止

強行終止 redis 程式可能會導致 redis 持久化資料丟失。正確停止 redis 的方式應該是向 redis 傳送 shutdown 命令,方法為:

cd /usr/local/redis
./bin/redis-cli shutdown
複製程式碼

2.5 redis客戶端

在 redis 的安裝目錄中有 redis 的客戶端,即 redis-cli(Redis Command Line Interface),它是 redis 自帶的基於命令列的 redis 客戶端。

2.5.1 連線redis服務端

執行 bin/redis-cli 連線 redis 服務端:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

從上圖得知 redis-cli 預設連線本機的 redis,本機的 redis 沒有啟動則報上圖中的錯誤。

指定連線 redis 服務的 ip 和埠:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

2.5.2 向redis服務端傳送命令

redis-cli 連上 redis 服務後,可以在命令列傳送命令。

  • ping:

    redis 提供了 ping 命令來測試客戶端與 redis 的連線是否正常,如果連線正常會收到回覆 PONG

    Redis學習筆記(Jedis&資料型別&持久化&主從複製)

  • set/get:

    使用 set 和 get 可以向 redis 設定資料、獲取資料。

    Redis學習筆記(Jedis&資料型別&持久化&主從複製)

  • del:

    刪除指定 key 的內容。例如:del name

  • Keys *:

    檢視當前庫中所有的 key 值。

2.6 redis多資料庫

1、redis例項

一個 redis 程式就是一個 redis 例項,一臺伺服器可以同時有多個 redis 例項,不同的 redis 例項提供不同的服務埠對外提供服務,每個 redis 例項之間互相影響。每個 redis 例項都包括自己的資料庫,資料庫中可以儲存自己的資料。

2、多資料庫測試

一個 redis 例項可以包括多個資料庫,客戶端可以指定連線某個 redis 例項的哪個資料庫,就好比一個 mysql 中建立多個資料庫,客戶端連線時指定連線哪個資料庫。

一個 redis 例項最多可提供16個資料庫,下標從0到15,客戶端預設連線第0號資料庫,也可以通過 select 選擇連線哪個資料庫,如下連線1號庫:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

在1號庫中查詢上節設定的資料,結果查詢不到:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

重新選擇第0號資料庫,查詢資料:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

如果選擇一個不存在資料庫則會報錯:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

注意:redis 不支援修改資料庫的名稱,只能通過 select 0、select 1...選擇資料庫。

3、注意問題

在0號資料庫儲存資料,在1號資料庫執行清空資料命令卻把0號資料庫的資料給清空了:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

建議:不同的應用系統要使用不同的 redis 例項而不是使用同一個 redis 例項下的不同資料庫。

三、Jedis

3.1 Jedis介紹

redis 不僅是使用命令來操作,現在基本上主流的語言都有客戶端支援,比如 Java、C、C#、C++、PHP、Node.js、Go 等。

在官方網站裡列一些 Java 的客戶端,有 Jedis、Redisson、Jredis、JDBC-Redis等,其中官方推薦使用 Jedis 和 Redisson。 在企業中用的最多的就是 Jedis,下面我們就重點學習下 Jedis。

Jedis 同樣也是託管在 GitHub 上,地址:github.com/xetorthio/j…

3.2 通過jedis連線redis單機

1、jar包

pom 座標:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.7.0</version>
</dependency>
複製程式碼

jar 包如下:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

2、單例項連線

通過建立單例項 jedis 物件連線 redis 服務,如下程式碼:

// 單例項連線redis
@Test
public void testJedisSingle() {

    Jedis jedis = new Jedis("192.168.101.3", 6379);
    jedis.set("name", "bar");
    String name = jedis.get("name");
    System.out.println(name);
    jedis.close();

}
複製程式碼

連線超時解決:

由於 Linux 防火牆預設開啟,redis 的服務埠 6379 並不在開放規則之內,所有需要將此埠開放訪問或者關閉防火牆。

  • 關閉防火牆命令:sevice iptablesstop
  • 如果是修改防火牆規則,可以修改:/etc/sysconfig/iptables 檔案

3、使用連線池連線

通過單例項連線 redis 不能對 redis 連線進行共享,可以使用連線池對 redis 連線進行共享,提高資源利用率,使用 jedisPool 連線 redis 服務,如下程式碼:

@Test
public void pool() {
    JedisPoolConfig config = new JedisPoolConfig();
    //最大連線數
    config.setMaxTotal(30);
    //最大連線空閒數
    config.setMaxIdle(2);

    JedisPool pool = new JedisPool(config, "192.168.101.3", 6379);
    Jedis jedis = null;

    try  {
        jedis = pool.getResource();

        jedis.set("name", "lisi");
        String name = jedis.get("name");
        System.out.println(name);
    }catch(Exception ex){
        ex.printStackTrace();
    }finally{
        if(jedis != null){
            //關閉連線
            jedis.close();
        }
    }
}
複製程式碼

詳細的連線池配置引數參考下節 jedis 和 spring 整合中 applicationContext.xml 的配置內容。

4、jedis 與 spring 整合

配置 spring 配置檔案 applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
                           http://www.springframework.org/schema/mvc 
                           http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 
                           http://www.springframework.org/schema/context 
                           http://www.springframework.org/schema/context/spring-context-3.2.xsd 
                           http://www.springframework.org/schema/aop 
                           http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
                           http://www.springframework.org/schema/tx 
                           http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">

    <!-- 連線池配置 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- 最大連線數 -->
        <property name="maxTotal" value="30" />
        <!-- 最大空閒連線數 -->
        <property name="maxIdle" value="10" />
        <!-- 每次釋放連線的最大數目 -->
        <property name="numTestsPerEvictionRun" value="1024" />
        <!-- 釋放連線的掃描間隔(毫秒) -->
        <property name="timeBetweenEvictionRunsMillis" value="30000" />
        <!-- 連線最小空閒時間 -->
        <property name="minEvictableIdleTimeMillis" value="1800000" />
        <!-- 連線空閒多久後釋放, 當空閒時間>該值 且 空閒連線>最大空閒連線數 時直接釋放 -->
        <property name="softMinEvictableIdleTimeMillis" value="10000" />
        <!-- 獲取連線時的最大等待毫秒數,小於零:阻塞不確定的時間,預設-1 -->
        <property name="maxWaitMillis" value="1500" />
        <!-- 在獲取連線的時候檢查有效性, 預設false -->
        <property name="testOnBorrow" value="true" />
        <!-- 在空閒時檢查有效性, 預設false -->
        <property name="testWhileIdle" value="true" />
        <!-- 連線耗盡時是否阻塞, false報異常,ture阻塞直到超時, 預設true -->
        <property name="blockWhenExhausted" value="false" />
    </bean>

    <!-- redis單機 通過連線池 -->
    <bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="close">
        <constructor-arg name="poolConfig" ref="jedisPoolConfig"/>
        <constructor-arg name="host" value="192.168.25.145"/>
        <constructor-arg name="port" value="6379"/>
    </bean>
</beans>
複製程式碼

測試程式碼:

private ApplicationContext applicationContext;

@Before
public void init() {
    applicationContext = new ClassPathXmlApplicationContext(
        "classpath:applicationContext.xml");
}

@Test
public void testJedisPool() {
    JedisPool pool = (JedisPool) applicationContext.getBean("jedisPool");
    try  {
        Jedis jedis = pool.getResource();

        jedis.set("name", "lisi");
        String name = jedis.get("name");
        System.out.println(name);
    }catch(Exception ex){
        ex.printStackTrace();
    }finally{
        if(jedis != null){
            //關閉連線
            jedis.close();
        }
    }
}
複製程式碼

四、資料型別

4.1 字串 string

1、redis string 介紹

redis 中沒有使用 C 語言的字串表示,而是自定義一個資料結構叫 SDS(simple dynamic string)即簡單動態字串。開啟下載的 redis 原始碼包,找到 src 下的 sds.h 檔案檢視 sds 原始碼:

struct sdshdr {
    //字串長度
    unsigned int len;
    //buf陣列中未使用的位元組數量
    unsigned int free;
    //用於儲存字串
    char buf[];
};
複製程式碼

C 語言對字串的儲存是使用字元陣列,遇到 '\0' 字元則認為字串結束 ,redis 的字串可以儲存任何型別的資料,因為任何型別資料都可以表示成二進位制,sds 結構中的 char buf[] 就是儲存了二進位制資料。

redis 的字串是二進位制安全的,什麼是二進位制安全?簡單理解就是存入什麼資料取出的還是什麼資料。redis 中的 sds 不像 C 語言處理字串那樣遇到 '\0' 字元則認證字串結束,它不會對儲存進去的二進位制資料進行處理,存入什麼資料取出還是什麼資料。

2、命令

1)賦值:SET key value

127.0.0.1:6379> set test 123
OK
複製程式碼

2)取值

賦值與取值:GET key

127.0.0.1:6379> get test
"123“
複製程式碼

當鍵不存在時返回空結果。

取值時同時對 key 進行賦值操作:GETSET key value

3)刪除:Del key

127.0.0.1:6379> del test
(integer) 1
複製程式碼

4)數值增減

  • 遞增數字:INCR key

    當儲存的字串是整數時,redis 提供了一個實用的命令 INCR,其作用是讓當前鍵值遞增,並返回遞增後的值。

    127.0.0.1:6379> incr num
    (integer) 1
    127.0.0.1:6379> incr num
    (integer) 2
    127.0.0.1:6379> incr num
    (integer) 3 
    複製程式碼
  • 增加指定的整數:INCRBY key increment

    127.0.0.1:6379> incrby num 2
    (integer) 5
    127.0.0.1:6379> incrby num 2
    (integer) 7
    127.0.0.1:6379> incrby num 2
    (integer) 9 
    複製程式碼
  • 遞減數值:DECR key

5)其他命令

  • 減少指定的整數:DECRBY key decrement

    127.0.0.1:6379> decr num
    (integer) 6
    127.0.0.1:6379> decr num
    (integer) 5
    127.0.0.1:6379> decrby num 3
    (integer) 2
    127.0.0.1:6379> decrby num 3
    (integer) -1 
    
    複製程式碼
  • 向尾部追加值:APPEND key value

    APPEND 的作用是向鍵值的末尾追加 value。如果鍵不存在則將該鍵的值設定為 value,即相當於 SET key value。返回值是追加後字串的總長度。

    127.0.0.1:6379> set str hello
    OK
    127.0.0.1:6379> append str " world!"
    (integer) 12
    127.0.0.1:6379> get str 
    "hello world!"
    複製程式碼
  • 獲取字串長度:STRLEN key

    STRLEN 命令返回鍵值的長度,如果鍵不存在則返回0。

    127.0.0.1:6379> strlen str 
    (integer) 0
    127.0.0.1:6379> set str hello
    OK
    127.0.0.1:6379> strlen str 
    (integer) 5
    複製程式碼
  • 同時設定/獲取多個鍵值:MSET key value [key value …]MGET key [key …]

    127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
    OK
    127.0.0.1:6379> get k1
    "v1"
    127.0.0.1:6379> mget k1 k3
    1) "v1"
    2) "v3"
    複製程式碼

3、應用

自增主鍵:商品編號、訂單號採用 string 的遞增數字特性生成。

定義商品編號 key:items:id

192.168.101.3:7003> INCR items:id
(integer) 2
192.168.101.3:7003> INCR items:id
(integer) 3
複製程式碼

4.2 雜湊 hash

1、 使用 string 的問題

假設有 Use r物件以 json 序列化的形式儲存到 redis 中,User 物件有 id,username、password、age、name 等屬性,儲存的過程如下,儲存、更新:

User 物件 --> json(string) --> redis

如果在業務上只是更新 age 屬性,其他的屬性並不做更新我應該怎麼做呢? 如果仍然採用上邊的方法在傳輸、處理時會造成資源浪費,下邊講的 hash 可以很好的解決這個問題。

2、redis hash介紹

hash 叫雜湊型別,它提供了欄位和欄位值的對映。欄位值只能是字串型別,不支援雜湊型別、集合型別等其它型別。如下:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

思考:redis hash 儲存比關聯式資料庫的好處?

3、命令

1)賦值:

  • HSET key field value 一次只能設定一個欄位值

    127.0.0.1:6379> hset user username zhangsan 
    (integer) 1
    複製程式碼
  • HMSET key field value [field value ...] 一次可以設定多個欄位值

    127.0.0.1:6379> hmset user age 20 username lisi 
    OK
    複製程式碼

2)取值:

  • HGET key field 一次只能獲取一個欄位值

    127.0.0.1:6379> hget user username
    "zhangsan“
    複製程式碼
  • HMGET key field [field ...] 一次可以獲取多個欄位值

    127.0.0.1:6379> hmget user age username
    1) "20"
    2) "lisi"
    複製程式碼
  • HGETALL key

    127.0.0.1:6379> hgetall user
    1) "age"
    2) "20"
    3) "username"
    4) "lisi"
    複製程式碼

    HSET 命令不區分插入和更新操作,當執行插入操作時 HSET 命令返回1,當執行更新操作時返回0。

3)刪除欄位:HDEL key field [field...] 可以刪除一個或多個欄位,返回值是被刪除的欄位個數

127.0.0.1:6379> hdel user age
(integer) 1
127.0.0.1:6379> hdel user age name
(integer) 0
127.0.0.1:6379> hdel user age username
(integer) 1 
複製程式碼

4)增加數字:HINCRBY key field increment

127.0.0.1:6379> hincrby user age 2	將使用者的年齡加2
(integer) 22
127.0.0.1:6379> hget user age		獲取使用者的年齡
"22“
複製程式碼

5)其他命令

  • 判斷欄位是否存在:

    • HEXISTS key field

      127.0.0.1:6379> hexists user age		檢視user中是否有age欄位
      (integer) 1
      127.0.0.1:6379> hexists user name	檢視user中是否有name欄位
      (integer) 0
      複製程式碼
    • HSETNX key field value

      當欄位不存在時賦值,類似 HSET,區別在於如果欄位已經存在,該命令不執行任何操作。

      127.0.0.1:6379> hsetnx user age 30	如果user中沒有age欄位則設定age值為30,否則不做任何操作
      (integer) 0
      複製程式碼
  • 只獲取欄位名或欄位值

    • HKEYS keyHVALS key

      127.0.0.1:6379> hmset user age 20 name lisi 
      OK
      127.0.0.1:6379> hkeys user
      1) "age"
      2) "name"
      127.0.0.1:6379> hvals user
      1) "20"
      2) "lisi"
      複製程式碼
  • 獲取欄位數量:HLEN key

    127.0.0.1:6379> hlen user
    (integer) 2
    複製程式碼

4、應用

商品資訊:商品 id、商品名稱、商品描述、商品庫存、商品好評

定義商品資訊的 key:

商品1001的資訊在 redis 中的 key 為:items:1001

儲存商品資訊:

192.168.101.3:7003> HMSET items:1001 id 3 name apple price 999.9
OK
複製程式碼

獲取商品資訊:

192.168.101.3:7003> HGET items:1001 id
"3"
192.168.101.3:7003> HGETALL items:1001
1) "id"
2) "3"
3) "name"
4) "apple"
5) "price"
6) "999.9"
複製程式碼

4.3 列表 list

1、ArrayList 與 LinkedList 的區別

ArrayList 使用陣列方式儲存資料,所以根據索引查詢資料速度快,而新增或者刪除元素時需要設計到位移操作,所以比較慢。

LinkedList 使用雙向連結方式儲存資料,每個元素都記錄前後元素的指標,所以插入、刪除資料時只是更改前後元素的指標指向即可,速度非常快,然後通過下標查詢元素時需要從頭開始索引,所以比較慢,但是如果查詢前幾個元素或後幾個元素速度比較快。

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

2、redis list介紹

列表型別(list)可以儲存一個有序的字串列表,常用的操作是向列表兩端新增元素,或者獲得列表的某一個片段。

列表型別內部是使用雙向連結串列(double linked list)實現的,所以向列表兩端新增元素的時間複雜度為0(1),獲取越接近兩端的元素速度就越快。這意味著即使是一個有幾千萬個元素的列表,獲取頭部或尾部的10條記錄也是極快的。

3、命令

1)向列表兩端增加元素

  • LPUSH key value [value ...]RPUSH key value [value ...]

    向列表左邊增加元素 
    127.0.0.1:6379> lpush list:1 1 2 3
    (integer) 3
    向列表右邊增加元素 
    127.0.0.1:6379> rpush list:1 4 5 6
    (integer) 3
    複製程式碼

2)檢視列表:LRANGE key start stop

LRANGE 命令是列表型別最常用的命令之一,獲取列表中的某一片段,將返回 start、stop 之間的所有元素(包含兩端的元素),索引從0開始。索引可以是負數,如:“-1”代表最後邊的一個元素。

127.0.0.1:6379> lrange list:1 0 2
1) "2"
2) "1"
3) "4"
複製程式碼

3)從列表兩端彈出元素:LPOP keyRPOP key

LPOP 命令從列表左邊彈出一個元素,會分兩步完成,第一步是將列表左邊的元素從列表中移除,第二步是返回被移除的元素值。

127.0.0.1:6379> lpop list:1
"3“
127.0.0.1:6379> rpop list:1
"6“
複製程式碼

4)獲取列表中元素的個數:LLEN key

127.0.0.1:6379> llen list:1
(integer) 2
複製程式碼

5)其他命令

  • 刪除列表中指定的值:LREM key count value

    LREM 命令會刪除列表中前 count 個值為 value 的元素,返回實際刪除的元素個數。根據 count 值的不同,該命令的執行方式會有所不同:

    • 當 count>0 時, LREM 會從列表左邊開始刪除。
    • 當 count<0 時, LREM 會從列表後邊開始刪除。
    • 當 count=0 時, LREM 刪除所有值為value的元素。
  • 獲得/設定指定索引的元素值:LINDEX key indexLSET key index value

    127.0.0.1:6379> lindex l:list 2
    "1"
    127.0.0.1:6379> lset l:list 2 2
    OK
    127.0.0.1:6379> lrange l:list 0 -1
    1) "6"
    2) "5"
    3) "2"
    4) "2"
    複製程式碼
  • 只保留列表指定片段,指定範圍和 LRANGE 一致:LTRIM key start stop

    127.0.0.1:6379> lrange l:list 0 -1
    1) "6"
    2) "5"
    3) "0"
    4) "2"
    127.0.0.1:6379> ltrim l:list 0 2
    OK
    127.0.0.1:6379> lrange l:list 0 -1
    1) "6"
    2) "5"
    3) "0"
    複製程式碼
  • 向列表中插入元素:LINSERT key BEFORE|AFTER pivot value

    該命令首先會在列表中從左到右查詢值為 pivot 的元素,然後根據第二個引數是 BEFORE 還是 AFTER 來決定將 value 插入到該元素的前面還是後面。

    127.0.0.1:6379> lrange list 0 -1
    1) "3"
    2) "2"
    3) "1"
    127.0.0.1:6379> linsert list after 3 4
    (integer) 4
    127.0.0.1:6379> lrange list 0 -1
    1) "3"
    2) "4"
    3) "2"
    4) "1"
    複製程式碼
  • 將元素從一個列表轉移到另一個列表中:RPOPLPUSH source destination

    127.0.0.1:6379> rpoplpush list newlist 
    "1"
    127.0.0.1:6379> lrange newlist 0 -1
    1) "1"
    127.0.0.1:6379> lrange list 0 -1
    1) "3"
    2) "4"
    3) "2" 
    複製程式碼

4、應用

商品評論列表。思路:

在 redis中建立商品評論列表。使用者釋出商品評論,將評論資訊轉成 jso n儲存到 list 中。

使用者在頁面查詢評論列表,從 redis 中取出 jso n資料展示到頁面。

定義商品評論列表key:

商品編號為1001的商品評論key:items: comment:1001

192.168.101.3:7001> LPUSH items:comment:1001 '{"id":1,"name":"商品不錯,很好!!","date":1430295077289}'
複製程式碼

4.4 集合 set

1、redis set介紹

在集合中的每個元素都是不同的,且沒有順序。集合型別和列表型別的對比:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

集合型別的常用操作是向集合中加入或刪除元素、判斷某個元素是否存在等,由於集合型別的 redis 內部是使用值為空的雜湊表實現,所有這些操作的時間複雜度都為0(1)。 redis 還提供了多個集合之間的交集、並集、差集的運算。

2、命令

1)增加/刪除元素:SADD key member [member ...]SREM key member [member ...]

127.0.0.1:6379> sadd set a b c
(integer) 3
127.0.0.1:6379> sadd set a
(integer) 0
127.0.0.1:6379> srem set c d
(integer) 1
複製程式碼

2)獲得集合中所有元素:SMEMBERS key

127.0.0.1:6379> smembers set
1) "b"
2) "a”
複製程式碼

判斷元素是否在集合中,無論集合中有多少元素都可以極速的返回結果:SISMEMBER key member

127.0.0.1:6379> sismember set a
(integer) 1
127.0.0.1:6379> sismember set h
(integer) 0
複製程式碼

3)其他命令

  • 集合的差集運算 A-B:SDIFF key [key ...]

    屬於 A 並且不屬於 B 的元素構成的集合。

    Redis學習筆記(Jedis&資料型別&持久化&主從複製)

    127.0.0.1:6379> sadd setA 1 2 3
    (integer) 3
    127.0.0.1:6379> sadd setB 2 3 4
    (integer) 3
    127.0.0.1:6379> sdiff setA setB 
    1) "1"
    127.0.0.1:6379> sdiff setB setA 
    1) "4"
    複製程式碼
  • 集合的交集運算 A ∩ B:SINTER key [key ...]

    屬於 A 且屬於 B 的元素構成的集合。

    Redis學習筆記(Jedis&資料型別&持久化&主從複製)

    127.0.0.1:6379> sinter setA setB 
    1) "2"
    2) "3"
    複製程式碼
  • 集合的並集運算 A ∪ B:SUNION key [key ...]

    屬於 A 或者屬於 B 的元素構成的集合。

    Redis學習筆記(Jedis&資料型別&持久化&主從複製)

    127.0.0.1:6379> sunion setA setB
    1) "1"
    2) "2"
    3) "3"
    4) "4"
    複製程式碼
  • 其他

    • 獲得集合中元素的個數:SCARD key

      127.0.0.1:6379> smembers setA 
      1) "1"
      2) "2"
      3) "3"
      127.0.0.1:6379> scard setA 
      複製程式碼
    • 從集合中彈出一個元素 :SPOP key

      SPOP key
      127.0.0.1:6379> spop setA 
      "1“
      複製程式碼

      注意:由於集合是無序的,所有 SPOP 命令會從集合中隨機選擇一個元素彈出。

4.5 有序集合 sorted set

1、redis sorted set介紹

在集合型別的基礎上有序集合型別為集合中的每個元素都關聯一個分數,這使得我們不僅可以完成插入、刪除和判斷元素是否存在在集合中,還能夠獲得分數最高或最低的前 N 個元素、獲取指定分數範圍內的元素等與分數有關的操作。

在某些方面有序集合和列表型別有些相似。

  1. 二者都是有序的。
  2. 二者都可以獲得某一範圍的元素。

但是,二者有著很大區別:

  1. 列表型別是通過連結串列實現的,獲取靠近兩端的資料速度極快,而當元素增多後,訪問中間資料的速度會變慢。
  2. 有序集合型別使用雜湊表實現,所有即使讀取位於中間部分的資料也很快。
  3. 列表中不能簡單的調整某個元素的位置,但是有序集合可以(通過更改分數實現)
  4. 有序集合要比列表型別更耗記憶體。

2、命令

1)增加元素

向有序集合中加入一個元素和該元素的分數,如果該元素已經存在則會用新的分數替換原有的分數。返回值是加入到集合中的元素個數,不包含之前已經存在的元素。

ZADD key score member [score member ...]

127.0.0.1:6379> zadd scoreboard 80 zhangsan 89 lisi 94 wangwu 
(integer) 3
127.0.0.1:6379> zadd scoreboard 97 lisi 
(integer) 0
複製程式碼

獲取元素的分數:ZSCORE key member

127.0.0.1:6379> zscore scoreboard lisi 
"97"
複製程式碼

2)刪除元素:ZREM key member [member ...]

移除有序集 key 中的一個或多個成員,不存在的成員將被忽略。當 key 存在但不是有序集型別時,返回一個錯誤。

127.0.0.1:6379> zrem scoreboard lisi
(integer) 1
複製程式碼

3)獲得排名在某個範圍的元素列表

獲得排名在某個範圍的元素列表:

ZRANGE key start stop [WITHSCORES] 按照元素分數從小到大的順序返回索引從 start 到 stop 之間的所有元素(包含兩端的元素)

127.0.0.1:6379> zrange scoreboard 0 2
1) "zhangsan"
2) "wangwu"
3) "lisi“
複製程式碼

ZREVRANGE key start stop [WITHSCORES] 按照元素分數從大到小的順序返回索引從 start 到 stop 之間的所有元素(包含兩端的元素)

127.0.0.1:6379> zrevrange scoreboard 0 2
1) " lisi "
2) "wangwu"
3) " zhangsan 
複製程式碼

如果需要獲得元素的分數的可以在命令尾部加上 WITHSCORES 引數:

127.0.0.1:6379> zrange scoreboard 0 1 WITHSCORES
1) "zhangsan"
2) "80"
3) "wangwu"
4) "94"
複製程式碼

3、其他命令

  • 獲得指定分數範圍的元素:ZRANGEBYSCORE key min max [WITHSCORES][LIMIT offset count]

    127.0.0.1:6379> ZRANGEBYSCORE scoreboard 90 97 WITHSCORES
    1) "wangwu"
    2) "94"
    3) "lisi"
    4) "97"
    127.0.0.1:6379> ZRANGEBYSCORE scoreboard 70 100 limit 1 2
    1) "wangwu"
    2) "lisi"
    複製程式碼
  • 增加某個元素的分數,返回值是更改後的分數:ZINCRBY key increment member

    給lisi加4分 
    127.0.0.1:6379> ZINCRBY scoreboard  4 lisi 
    "101“
    複製程式碼
  • 獲得集合中元素的數量:ZCARD key

    127.0.0.1:6379> ZCARD scoreboard
    (integer) 3
    複製程式碼
  • 獲得指定分數範圍內的元素個數:ZCOUNT key min max

    127.0.0.1:6379> ZCOUNT scoreboard 80 90
    (integer) 1
    複製程式碼
  • 按照排名範圍刪除元素:ZREMRANGEBYRANK key start stop

    127.0.0.1:6379> ZREMRANGEBYRANK scoreboard 0 1
    (integer) 2 
    127.0.0.1:6379> ZRANGE scoreboard 0 -1
    1) "lisi"
    複製程式碼
  • 按照分數範圍刪除元素:ZREMRANGEBYSCORE key min max

    127.0.0.1:6379> zadd scoreboard 84 zhangsan	
    (integer) 1
    127.0.0.1:6379> ZREMRANGEBYSCORE scoreboard 80 100
    (integer) 1
    複製程式碼
  • 獲取元素的排名:ZRANK key memberZREVRANK key member

    從小到大 
    127.0.0.1:6379> ZRANK scoreboard lisi 
    (integer) 0
    從大到小 
    127.0.0.1:6379> ZREVRANK scoreboard zhangsan 
    (integer) 1
    複製程式碼

4、應用

商品銷售排行榜:根據商品銷售量對商品進行排行顯示,定義 sorted set 集合,商品銷售量為元素的分數。

定義商品銷售排行榜 key:items:sellsort

寫入商品銷售量:

商品編號1001的銷量是9,商品編號1002的銷量是10
192.168.101.3:7007> ZADD items:sellsort 9 1001 10 1002
複製程式碼

商品編號1001的銷量加1:

192.168.101.3:7001> ZINCRBY items:sellsort 1 1001
複製程式碼

商品銷量前10名:

192.168.101.3:7001> ZRANGE items:sellsort 0 9 withscores
複製程式碼

五、key命令

5.1 設定key的生存時間

redis 在實際使用過程中更多的用作快取,然而快取的資料一般都是需要設定生存時間的,即:到期後資料銷燬。

EXPIRE key seconds			設定key的生存時間(單位:秒)key在多少秒後會自動刪除
TTL key 					檢視key生於的生存時間
PERSIST key				清除生存時間 
PEXPIRE key milliseconds	生存時間設定單位為:毫秒 
複製程式碼

例子:

192.168.101.3:7002> set test 1		設定test的值為1
OK
192.168.101.3:7002> get test			獲取test的值
"1"
192.168.101.3:7002> EXPIRE test 5	設定test的生存時間為5秒
(integer) 1
192.168.101.3:7002> TTL test			檢視test的生於生成時間還有1秒刪除
(integer) 1
192.168.101.3:7002> TTL test
(integer) -2
192.168.101.3:7002> get test			獲取test的值,已經刪除
(nil)
複製程式碼

5.2 其他命令

  • keys:返回滿足給定 pattern 的所有 key

    redis 127.0.0.1:6379> keys mylist*
    1) "mylist"
    2) "mylist5"
    3) "mylist6"
    4) "mylist7"
    5) "mylist8"
    複製程式碼
  • exists:確認一個 key 是否存在

    redis 127.0.0.1:6379> exists HongWan
    (integer) 0
    redis 127.0.0.1:6379> exists age
    (integer) 1
    redis 127.0.0.1:6379>
    複製程式碼

    從結果來資料庫中不存在 HongWan 這個 key,但是 age 這個 key 是存在的。

  • del:刪除一個 key

    redis 127.0.0.1:6379> del age
    (integer) 1
    redis 127.0.0.1:6379> exists age
    (integer) 0
    redis 127.0.0.1:6379>
    複製程式碼

    從結果來資料庫中不存在 HongWan 這個 key,但是 age 這個 key 是存在的。

  • rename:重名名 key

    redis 127.0.0.1:6379[1]> keys *
    1) "age"
    redis 127.0.0.1:6379[1]> rename age age_new
    OK
    redis 127.0.0.1:6379[1]> keys *
    1) "age_new"
    redis 127.0.0.1:6379[1]>
    複製程式碼

    age 成功的被改名為 age_new 了。

  • type:返回值的型別

    redis 127.0.0.1:6379> type addr
    string
    redis 127.0.0.1:6379> type myzset2
    zset
    redis 127.0.0.1:6379> type mylist
    list
    redis 127.0.0.1:6379>
    複製程式碼

    這個方法可以非常簡單的判斷出值的型別。

六、伺服器命令

  • ping:測試連線是否存活

    redis 127.0.0.1:6379> ping
    PONG
    //執行下面命令之前,我們停止redis 伺服器
    redis 127.0.0.1:6379> ping
    Could not connect to Redis at 127.0.0.1:6379: Connection refused
    //執行下面命令之前,我們啟動redis 伺服器
    not connected> ping
    PONG
    redis 127.0.0.1:6379>
    複製程式碼

    第一個 ping 時,說明此連線正常。

    第二個 ping 之前,我們將 redis 伺服器停止,那麼 ping 是失敗的。

    第三個 ping 之前,我們將 redis 伺服器啟動,那麼 ping 是成功的。

  • echo:在命令列列印一些內容

    redis 127.0.0.1:6379> echo HongWan
    "HongWan"
    redis 127.0.0.1:6379>
    複製程式碼
  • select:選擇資料庫。redis 資料庫編號從0~15,我們可以選擇任意一個資料庫來進行資料的存取。

    redis 127.0.0.1:6379> select 1
    OK
    redis 127.0.0.1:6379[1]> select 16
    (error) ERR invalid DB index
    redis 127.0.0.1:6379[16]>
    複製程式碼

    當選擇16 時,報錯,說明沒有編號為16 的這個資料庫。

  • quit:退出連線

    redis 127.0.0.1:6379> quit
    複製程式碼
  • dbsize:返回當前資料庫中 key 的數目

    redis 127.0.0.1:6379> dbsize
    (integer) 18
    redis 127.0.0.1:6379>
    複製程式碼

    結果說明此庫中有 18 個 key

  • info:獲取伺服器的資訊和統計

    redis 127.0.0.1:6379> info
    redis_version:2.2.12
    redis_git_sha1:00000000
    redis_git_dirty:0
    arch_bits:32
    multiplexing_api:epoll
    process_id:28480
    uptime_in_seconds:2515
    uptime_in_days:0
    複製程式碼
  • flushdb:刪除當前選擇資料庫中的所有 key、

    redis 127.0.0.1:6379> dbsize
    (integer) 18
    redis 127.0.0.1:6379> flushdb
    OK
    redis 127.0.0.1:6379> dbsize
    (integer) 0
    redis 127.0.0.1:6379>
    複製程式碼

    在本例中我們將 0 號資料庫中的 key 都清除了。

  • flushall:刪除所有資料庫中的所有 key

    redis 127.0.0.1:6379[1]> dbsize
    (integer) 1
    redis 127.0.0.1:6379[1]> select 0
    OK
    redis 127.0.0.1:6379> flushall
    OK
    redis 127.0.0.1:6379> select 1
    OK
    redis 127.0.0.1:6379[1]> dbsize
    (integer) 0
    redis 127.0.0.1:6379[1]>
    複製程式碼

    在本例中我們先檢視了一個 1 號資料庫中有一個 key,然後我切換到 0 號庫執行 flushall 命令,結果 1 號庫中的 key 也被清除了,說是此命令工作正常。

七、持久化

redis 的高效能是由於其將所有資料都儲存在了記憶體中,為了使 redis 在重啟之後仍能保證資料不丟失,需要將資料從記憶體中同步到硬碟中,這一過程就是持久化。redis 支援兩種方式的持久化,一種是 RDB 方式,一種是 AOF 方式。可以單獨使用其中一種或將二者結合使用。

7.1 RDB持久化

RDB 方式的持久化是通過快照(snapshotting)完成的,當符合一定條件時 redis 會自動將記憶體中的資料進行快照並持久化到硬碟。

RDB是 redis 預設採用的持久化方式,在 redis.conf 配置檔案中預設有此下配置:

save 900 1
save 300 10
save 60 10000
複製程式碼

save 開頭的一行就是持久化配置,可以配置多個條件(每行配置一個條件),每個條件之間是“或”的關係,“save 900 1”表示15分鐘(900秒鐘)內至少1個鍵被更改則進行快照,“save 300 10”表示5分鐘(300秒)內至少10個鍵被更改則進行快照。

redis.conf 中:

  • 配置 dir 指定 rdb 快照檔案的位置
  • 配置 dbfilenam 指定 rdb 快照檔案的名稱

redis 啟動後會讀取 RDB 快照檔案,將資料從硬碟載入到記憶體。根據資料量大小與結構和伺服器效能不同,這個時間也不同。通常將記錄一千萬個字串型別鍵、大小為1GB的快照檔案載入到記憶體中需要花費20~30秒鐘。

問題總結:

通過 RDB 方式實現持久化,一旦Redis異常退出,就會丟失最後一次快照以後更改的所有資料。這就需要開發者根據具體的應用場合,通過組合設定自動快照條件的方式來將可能發生的資料損失控制在能夠接受的範圍。如果資料很重要以至於無法承受任何損失,則可以考慮使用 AOF 方式進行持久化。

7.2 AOF持久化

預設情況下 redis 沒有開啟 AOF(append only file)方式的持久化,可以通過 appendonly 引數開啟:appendonly yes

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

八、主從複製

8.1 什麼是主從複製

持久化保證了即使 redis 服務重啟也會丟失資料,因為 redis 服務重啟後會將硬碟上持久化的資料恢復到記憶體中,但是當 redis 伺服器的硬碟損壞了可能會導致資料丟失,如果通過 redis 的主從複製機制就可以避免這種單點故障,如下圖:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

說明:

  • 主 redis 中的資料有兩個副本(replication)即從 redis1 和從 redis2,即使一臺 redis 伺服器當機其它兩臺 redis 服務也可以繼續提供服務。

  • 主 redis 中的資料和從 redis 上的資料保持實時同步,當主 redis 寫入資料時通過主從複製機制會複製到兩個從 redis 服務上。

  • 只有一個主 redis,可以有多個從 redis。

  • 主從複製不會阻塞 master,在同步資料時,master 可以繼續處理 client 請求。

  • 一個 redis 可以即是主又是從,如下圖:

    Redis學習筆記(Jedis&資料型別&持久化&主從複製)

8.2 主從配置

1、主 redis 配置

無需特殊配置。

2、從redis配置

修改從 redis 伺服器上的 redis.conf 檔案,新增 slaveof 主 redisip 主 redis 埠。

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

上邊的配置說明當前該從 redis 伺服器所對應的主 redis 是192.168.101.3,埠是6379。

8.3 主從複製過程

1、完整複製過程

在 redis2.8 版本之前主從複製過程如下圖:

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

複製過程說明:

  1. slave 服務啟動,slave 會建立和 master 的連線,傳送 sync 命令。

  2. master 啟動一個後臺程式將資料庫快照儲存到 RDB 檔案中

    注意:此時如果生成 RDB 檔案過程中存在寫資料操作會導致 RDB 檔案和當前主 redis 資料不一致,所以此時 master 主程式會開始收集寫命令並快取起來。

  3. master 就傳送 RDB 檔案給 slave

  4. slave 將檔案儲存到磁碟上,然後載入到記憶體恢復

  5. master 把快取的命令轉發給 slave

    注意:後續 master 收到的寫命令都會通過開始建立的連線傳送給 slave。

    當 master 和 slave 的連線斷開時 slave 可以自動重新建立連線。如果 master 同時收到多個 slave 發來的同步連線命令,只會啟動一個程式來寫資料庫映象,然後傳送給所有 slave。

完整複製的問題:

在 redis2.8 之前從 redis 每次同步都會從主 redis 中複製全部的資料,如果從 redis 是新建立的從主 redis 中複製全部的資料這是沒有問題的,但是,如果當從 redis 停止執行,再啟動時可能只有少部分資料和主 redis 不同步,此時啟動 redis 仍然會從主 redis 複製全部資料,這樣的效能肯定沒有隻複製那一小部分不同步的資料高。

2、部分複製

Redis學習筆記(Jedis&資料型別&持久化&主從複製)

部分複製說明:

從機連線主機後,會主動發起 PSYNC 命令,從機會提供 master 的 runid(機器標識,隨機生成的一個串) 和 offset(資料偏移量,如果offset主從不一致則說明資料不同步),主機驗證 runid 和 offset 是否有效,runid 相當於主機身份驗證碼,用來驗證從機上一次連線的主機,如果 runid 驗證未通過則,則進行全同步,如果驗證通過則說明曾經同步過,根據 offset 同步部分資料。

相關文章