Redis 筆記:Jedis&數據類型&持久化&主從複製

1、redis介紹

1.1 什麼是NoSQL

NoSQL,泛指非關係型的數據庫,NoSQL 即 Not-Only SQL,它能夠做爲關係型數據庫的良好補充。隨着互聯網 web2.0 網站的興起,非關係型的數據庫如今成了一個極其熱門的新領域,非關係數據庫產品的發展很是迅速。而傳統的關係數據庫在應付 web2.0 網站,特別是超大規模和高併發的 SNS 類型的 web2.0 純動態網站已經顯得力不從心,暴露了不少難以克服的問題,例如:php

  1. High performance —— 對數據庫高併發讀寫的需求前端

    web2.0 網站要根據用戶個性化信息來實時生成動態頁面和提供動態信息,因此基本上沒法使用動態頁面靜態化技術,所以數據庫併發負載很是高,每每要達到每秒上萬次讀寫請求。關係數據庫應付上萬次 SQL 查詢還勉強頂得住,可是應付上萬次 SQL 寫數據請求,硬盤 IO 就已經沒法承受了。其實對於普通的 BBS 網站,每每也存在對高併發寫請求的需求,例如網站的實時統計在線用戶狀態,記錄熱門帖子的點擊次數,投票計數等,所以這是一個至關廣泛的需求。java

  2. Huge Storage —— 對海量數據的高效率存儲和訪問的需求mysql

    相似 Facebook,Twitter,Friendfeed 這樣的 SNS 網站,天天用戶產生海量的用戶動態,以 Friendfeed 爲例,一個月就達到了 2.5 億條用戶動態,對於關係數據庫來講,在一張 2.5 億條記錄的表裏面進行 SQL 查詢,效率是極其低下乃至不可忍受的。再例如大型 web 網站的用戶登陸系統,例如騰訊,盛大,動輒數以億計的賬號,關係數據庫也很難應付。c++

  3. High Scalability && High Availability —— 對數據庫的高可擴展性和高可用性的需求git

    在基於 web 的架構當中,數據庫是最難進行橫向擴展的,當一個應用系統的用戶量和訪問量與日俱增的時候,你的數據庫卻沒有辦法像 web server 和 app server 那樣簡單的經過添加更多的硬件和服務節點來擴展性能和負載能力。對於不少須要提供 24 小時不間斷服務的網站來講,對數據庫系統進行升級和擴展是很是痛苦的事情,每每須要停機維護和數據遷移,爲何數據庫不能經過不斷的添加服務器節點來實現擴展呢?github

NoSQL 數據庫的產生就是爲了解決大規模數據集合多重數據種類帶來的挑戰,尤爲是大數據應用難題。web

一些主流的 NoSQL 產品:redis

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)保存博客或者論壇的留言回覆,等等.....

2、安裝運行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 下的文件列表

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

2.3 redis啓動

2.3.1 前端模式啓動

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

2.3.2 後端模式啓動

修改 redis.conf 配置文件,daemonize yes 之後端模式啓動。

執行以下命令啓動 redis:

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

redis 默認使用 6379 端口。

也可更改 redis.conf 文件,修改端口號:

2.3.3 啓動多個redis進程

1)方法一:

啓動時指定端口可在一臺服務器啓動多個 redis 進程。

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

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

建立多個 redis 目錄,以端口號命名,好比:建立 637九、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 的進程:

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-cli 默認鏈接本機的 redis,本機的 redis 沒有啓動則報上圖中的錯誤。

指定鏈接 redis 服務的 ip 和端口:

2.5.2 向redis服務端發送命令

redis-cli 連上 redis 服務後,能夠在命令行發送命令。

  • ping:

    redis 提供了 ping 命令來測試客戶端與 redis 的鏈接是否正常,若是鏈接正常會收到回覆 PONG

  • set/get:

    使用 set 和 get 能夠向 redis 設置數據、獲取數據。

  • del:

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

  • Keys *:

    查看當前庫中全部的 key 值。

2.6 redis多數據庫

一、redis實例

一個 redis 進程就是一個 redis 實例,一臺服務器能夠同時有多個 redis 實例,不一樣的 redis 實例提供不一樣的服務端口對外提供服務,每一個 redis 實例之間互相影響。每一個 redis 實例都包括本身的數據庫,數據庫中能夠存儲本身的數據。

二、多數據庫測試

一個 redis 實例能夠包括多個數據庫,客戶端能夠指定鏈接某個 redis 實例的哪一個數據庫,就比如一個 mysql 中建立多個數據庫,客戶端鏈接時指定鏈接哪一個數據庫。

一個 redis 實例最多可提供16個數據庫,下標從0到15,客戶端默認鏈接第0號數據庫,也能夠經過 select 選擇鏈接哪一個數據庫,以下鏈接1號庫:

在1號庫中查詢上節設置的數據,結果查詢不到:

從新選擇第0號數據庫,查詢數據:

若是選擇一個不存在數據庫則會報錯:

注意:redis 不支持修改數據庫的名稱,只能經過 select 0、select 1...選擇數據庫。

三、注意問題

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

建議:不一樣的應用系統要使用不一樣的 redis 實例而不是使用同一個 redis 實例下的不一樣數據庫。

3、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單機

一、jar包

pom 座標:

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

jar 包以下:

二、單實例鏈接

經過建立單實例 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 文件

三、使用鏈接池鏈接

經過單實例鏈接 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 的配置內容。

四、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、數據類型

4.1 字符串 string

一、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' 字符則認證字符串結束,它不會對存儲進去的二進制數據進行處理,存入什麼數據取出仍是什麼數據。

二、命令

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"
    複製代碼

三、應用

自增主鍵:商品編號、訂單號採用 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

一、 使用 string 的問題

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

User 對象 --> json(string) --> redis

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

二、redis hash介紹

hash 叫散列類型,它提供了字段和字段值的映射。字段值只能是字符串類型,不支持散列類型、集合類型等其它類型。以下:

思考:redis hash 存儲比關係數據庫的好處?

三、命令

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
    複製代碼

四、應用

商品信息:商品 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

一、ArrayList 與 LinkedList 的區別

ArrayList 使用數組方式存儲數據,因此根據索引查詢數據速度快,而新增或者刪除元素時須要設計到位移操做,因此比較慢。

LinkedList 使用雙向連接方式存儲數據,每一個元素都記錄先後元素的指針,因此插入、刪除數據時只是更改先後元素的指針指向便可,速度很是快,而後經過下標查詢元素時須要從頭開始索引,因此比較慢,可是若是查詢前幾個元素或後幾個元素速度比較快。

二、redis list介紹

列表類型(list)能夠存儲一個有序的字符串列表,經常使用的操做是向列表兩端添加元素,或者得到列表的某一個片斷。

列表類型內部是使用雙向鏈表(double linked list)實現的,因此向列表兩端添加元素的時間複雜度爲0(1),獲取越接近兩端的元素速度就越快。這意味着即便是一個有幾千萬個元素的列表,獲取頭部或尾部的10條記錄也是極快的。

三、命令

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" 
    複製代碼

四、應用

商品評論列表。思路:

在 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

一、redis set介紹

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

集合類型的經常使用操做是向集合中加入或刪除元素、判斷某個元素是否存在等,因爲集合類型的 redis 內部是使用值爲空的散列表實現,全部這些操做的時間複雜度都爲0(1)。 redis 還提供了多個集合之間的交集、並集、差集的運算。

二、命令

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 的元素構成的集合。

    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 的元素構成的集合。

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

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

    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

一、redis sorted set介紹

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

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

  1. 兩者都是有序的。
  2. 兩者均可以得到某一範圍的元素。

可是,兩者有着很大區別:

  1. 列表類型是經過鏈表實現的,獲取靠近兩端的數據速度極快,而當元素增多後,訪問中間數據的速度會變慢。
  2. 有序集合類型使用散列表實現,全部即便讀取位於中間部分的數據也很快。
  3. 列表中不能簡單的調整某個元素的位置,可是有序集合能夠(經過更改分數實現)
  4. 有序集合要比列表類型更耗內存。

二、命令

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"
複製代碼

三、其餘命令

  • 得到指定分數範圍的元素: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
    複製代碼

四、應用

商品銷售排行榜:根據商品銷售量對商品進行排行顯示,定義 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
複製代碼

5、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>
    複製代碼

    這個方法能夠很是簡單的判斷出值的類型。

6、服務器命令

  • 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 也被清除了,說是此命令工做正常。

7、持久化

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、主從複製

8.1 什麼是主從複製

持久化保證了即便 redis 服務重啓也會丟失數據,由於 redis 服務重啓後會將硬盤上持久化的數據恢復到內存中,可是當 redis 服務器的硬盤損壞了可能會致使數據丟失,若是經過 redis 的主從複製機制就能夠避免這種單點故障,以下圖:

說明:

  • 主 redis 中的數據有兩個副本(replication)即從 redis1 和從 redis2,即便一臺 redis 服務器宕機其它兩臺 redis 服務也能夠繼續提供服務。

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

  • 只有一個主 redis,能夠有多個從 redis。

  • 主從複製不會阻塞 master,在同步數據時,master 能夠繼續處理 client 請求。

  • 一個 redis 能夠便是主又是從,以下圖:

8.2 主從配置

一、主 redis 配置

無需特殊配置。

二、從redis配置

修改從 redis 服務器上的 redis.conf 文件,添加 slaveof 主 redisip 主 redis 端口。

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

8.3 主從複製過程

一、完整複製過程

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

複製過程說明:

  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 複製所有數據,這樣的性能確定沒有隻複製那一小部分不一樣步的數據高。

二、部分複製

部分複製說明:

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

相關文章
相關標籤/搜索