【學習】020 Redis

 

 

Java緩存機制

Java中要用到緩存的地方不少,首當其衝的就是持久層緩存,針對持久層談一下: 
要實現java緩存有不少種方式,最簡單的無非就是static HashMap,這個顯然是基於內存緩存,一個map就能夠搞定引用對象的緩存,最簡單也最不實用,首要的問題就是保存對象的有效性以及週期沒法控制,這樣很容易就致使內存急劇上升,週期沒法控制能夠採用SoftReference,WeakReference,PhantomReference這三種對象來執行(看了Ibatis的緩存機制才發現JDK竟然還提供了PhantomReference這玩意兒,得惡補基礎啊),這三種都是弱引用,區別在於強度不一樣,至於弱引用概念我的理解就是對象的生命週期與JVM掛鉤,JVM內存不夠了就回收,這樣能很好的控制OutOfMemoryError 異常。 html

經常使用的有Oscache,Ehcache,Jcache,Jbosscache等等不少java

OsCache與EhCache區別

 ehcache 主要是對數據庫訪問的緩存,相同的查詢語句只需查詢一次數據庫,從而提升了查詢的速度,使用spring的AOP能夠很容易實現這一功能。
 oscache 主要是對頁面的緩存,能夠整頁或者指定網頁某一部分緩存,同時指定他的過時時間,這樣在此時間段裏面訪問的數據都是同樣的。

NoSQL介紹

NoSQL 是 Not Only SQL 的縮寫,意即"不只僅是SQL"的意思,泛指非關係型的數據庫。強調Key-Value Stores和文檔數據庫的優勢,而不是單純的反對RDBMS。mysql

NoSQL產品是傳統關係型數據庫的功能閹割版本,經過減小用不到或不多用的功能,來大幅度提升產品性能linux

NoSQL產品 Redis、mongodb Membase、HBase 程序員

Redis 與Membase區別

Redis支持數據的持久化,能夠將數據存放在硬盤上。web

Memcache不支持數據的之久存儲。redis

Redis數據類型豐富,支持set liset等類型算法

Memcache支持簡單數據類型,須要客戶端本身處理複製對象spring

Redis簡介

Redis

Redis 是徹底開源免費的,遵照BSD協議,是一個高性能的key-value數據庫。sql

Redis 與其餘 key - value 緩存產品有如下三個特色:

Redis支持數據的持久化,能夠將內存中的數據保存在磁盤中,重啓的時候能夠再次加載進行使用。

Redis不只僅支持簡單的key-value類型的數據,同時還提供list,set,zset,hash等數據結構的存儲。

Redis支持數據的備份,即master-slave模式的數據備份。

Redis應用場景

   主要可以體現 解決數據庫的訪問壓力。

   例如:短信驗證碼時間有效期、session共享解決方案

Redis優點

性能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。

豐富的數據類型 – Redis支持二進制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數據類型操做。

原子 – Redis的全部操做都是原子性的,同時Redis還支持對幾個操做全並後的原子性執行。

豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過時等等特性。

Redis與其餘key-value存儲有什麼不一樣?

Redis有着更爲複雜的數據結構而且提供對他們的原子性操做,這是一個不一樣於其餘數據庫的進化路徑。Redis的數據類型都是基於基本數據結構的同時對程序員透明,無需進行額外的抽象。

Redis運行在內存中可是能夠持久化到磁盤,因此在對不一樣數據集進行高速讀寫時須要權衡內存,由於數據量不能大於硬件內存。在內存數據庫方面的另外一個優勢是,相比在磁盤上相同的複雜的數據結構,在內存中操做起來很是簡單,這樣Redis能夠作不少內部複雜性很強的事情。同時,在磁盤格式方面他們是緊湊的以追加的方式產生的,由於他們並不須要進行隨機訪問。

Redis安裝

 windows 安裝redis

  新建start.bat 批處理文件、內容: redis-server.exe  redis.windows.conf

  雙擊start.bat啓動

  修改密碼 # requirepass foobared  修改成requirepass 123456

  注意:修改密碼的時候前面不要加空格

 linux 安裝redis

Redis的官方下載網址是:http://redis.io/download  (這裏下載的是Linux版的Redis源碼包)

Redis服務器端的默認端口是6379

這裏以虛擬機中的Linux系統如何安裝Redis進行講解。

 在windows系統中下載好Redis的源碼包。

1. 經過WinSCP工具,將Redis的源碼包由windows上傳到Linux系統的這個目錄/opt/redis (即根目錄下的lamp文件夾)。

2. 解壓縮。           

tar -zxf redis-2.6.17.tar.gz

3. 切換到解壓後的目錄。

cd redis-2.6.17  ( 通常來講,解壓目錄裏的INSTALL文件或README文件裏寫有安裝說明,可參考之)

4. 編譯。

make

(注意,編譯須要C語言編譯器gcc的支持,若是沒有,須要先安裝gcc。可使用rpm -q gcc查看gcc是否安裝)

(利用yum在線安裝gcc的命令    yum -y install gcc )

(若是編譯出錯,請使用make clean清除臨時文件。以後,找到出錯的緣由,解決問題後再來從新安裝。 )

5. 進入到src目錄。       

cd src

6. 執行安裝。

make install

到此就安裝完成。可是,因爲安裝redis的時候,咱們沒有選擇安裝路徑,故是默認位置安裝。在此,咱們能夠將可執行文件和配置文件移動到習慣的目錄。

cd /usr/local

mkdir -p /usr/local/redis/bin    

mkdir -p /usr/local/redis/etc

cd /lamp/redis-2.6.17

cp ./redis.conf /usr/local/redis/etc

cd src

cp mkreleasehdr.sh redis-benchmark redis-check-aof redis-check-dump redis-cli redis-server redis-sentinel /usr/local/redis/bin

7.開放linux 6379 端口

1.編輯 /etc/sysconfig/iptables 文件:

vi /etc/sysconfig/iptables

加入內容並保存:

-A RH-Firewall-1-INPUT -m state –state NEW -m tcp -p tcp –dport 6379 -j ACCEPT

2.重啓服務:

/etc/init.d/iptables restart

3.查看端口是否開放:

/sbin/iptables -L -n

比較重要的3個可執行文件:

redis-server:Redis服務器程序

redis-cli:Redis客戶端程序,它是一個命令行操做工具。也可使用telnet根據其純文本協議操做。

redis-benchmark:Redis性能測試工具,測試Redis在你的系統及配置下的讀寫性能。

Redis的啓動命令:

/usr/local/redis/bin/redis-server

cd /usr/local/redis/bin

./redis-server /usr/local/redis/etc/redis.conf    爲redis-server指定配置文

修改 redis.conf文件

daemonize yes --- 修改成yes  後臺啓動

requirepass 123456  ----註釋取消掉設置帳號密碼

ps aux | grep '6379'  --- 查詢端口

kill -15 9886 --- 殺死重置

kill -9 9886 --- 強制殺死

service iptables stop 中止防火牆

redis命令鏈接方式

./redis-cli -h 127.0.0.1 -p 6379 -a "123456"  --- redis 使用帳號密碼鏈接

PING 結果表示成功

中止redis

redis-cli shutdown  或者 kill redis進程的pid

關閉防火牆

service  iptables stop

Redis客戶端鏈接方式

 使用redisclient-win32.x86.1.5

Redis的基本數據類型

字符串類型(String)

redis 127.0.0.1:6379> SET mykey "redis" 
OK 
redis 127.0.0.1:6379> GET mykey 
"redis"

在上面的例子中,SETGET是redis中的命令,而mykey是鍵的名稱。

Redis字符串命令用於管理Redis中的字符串值。如下是使用Redis字符串命令的語法。

redis 127.0.0.1:6379> COMMAND KEY_NAME

示例

redis 127.0.0.1:6379> SET mykey "redis" 
OK 
redis 127.0.0.1:6379> GET mykey 
"redis"

在上面的例子中,SETGET是redis中的命令,而mykey是鍵的名稱。

Redis字符串命令

下表列出了一些用於在Redis中管理字符串的基本命令。

編號

命令

描述說明

1

SET key value

此命令設置指定鍵的值。

2

GET key

獲取指定鍵的值。

3

GETRANGE key start end

獲取存儲在鍵上的字符串的子字符串。

4

GETSET key value

設置鍵的字符串值並返回其舊值。

5

GETBIT key offset

返回在鍵處存儲的字符串值中偏移處的位值。

6

MGET key1 [key2..]

獲取全部給定鍵的值

7

SETBIT key offset value

存儲在鍵上的字符串值中設置或清除偏移處的位

8

SETEX key seconds value

使用鍵和到期時間來設置值

9

SETNX key value

設置鍵的值,僅當鍵不存在時

10

SETRANGE key offset value

在指定偏移處開始的鍵處覆蓋字符串的一部分

11

STRLEN key

獲取存儲在鍵中的值的長度

12

MSET key value [key value …]

爲多個鍵分別設置它們的值

13

MSETNX key value [key value …]

爲多個鍵分別設置它們的值,僅當鍵不存在時

14

PSETEX key milliseconds value

設置鍵的值和到期時間(以毫秒爲單位)

15

INCR key

將鍵的整數值增長1

16

INCRBY key increment

將鍵的整數值按給定的數值增長

17

INCRBYFLOAT key increment

將鍵的浮點值按給定的數值增長

18

DECR key

將鍵的整數值減1

19

DECRBY key decrement

按給定數值減小鍵的整數值

20

APPEND key value

將指定值附加到鍵

列表類型(List)

Redis列表是簡單的字符串列表,按照插入順序排序。你能夠添加一個元素到列表的頭部(左邊)或者尾部(右邊)

一個列表最多能夠包含 232 - 1 個元素 (4294967295, 每一個列表超過40億個元素)。

redis 127.0.0.1:6379> LPUSH runoobkey redis
(integer) 1
redis 127.0.0.1:6379> LPUSH runoobkey mongodb
(integer) 2
redis 127.0.0.1:6379> LPUSH runoobkey mysql
(integer) 3
redis 127.0.0.1:6379> LRANGE runoobkey 0 10

1) "mysql"
2) "mongodb"
3) "redis"

Redis 列表命令

 

下表列出了列表相關的基本命令:

1

BLPOP key1 [key2 ] timeout 
移出並獲取列表的第一個元素,若是列表沒有元素會阻塞列表直到等待超時或發現可彈出元素爲止。

2

BRPOP key1 [key2 ] timeout 
移出並獲取列表的最後一個元素,若是列表沒有元素會阻塞列表直到等待超時或發現可彈出元素爲止。

3

BRPOPLPUSH source destination timeout 
從列表中彈出一個值,將彈出的元素插入到另一個列表中並返回它;若是列表沒有元素會阻塞列表直到等待超時或發現可彈出元素爲止。

4

LINDEX key index 
經過索引獲取列表中的元素

5

LINSERT key BEFORE|AFTER pivot value 
在列表的元素前或者後插入元素

6

LLEN key 
獲取列表長度

7

LPOP key 
移出並獲取列表的第一個元素

8

LPUSH key value1 [value2] 
將一個或多個值插入到列表頭部

9

LPUSHX key value 
將一個值插入到已存在的列表頭部

10

LRANGE key start stop 
獲取列表指定範圍內的元素

11

LREM key count value 
移除列表元素

12

LSET key index value 
經過索引設置列表元素的值

13

LTRIM key start stop 
對一個列表進行修剪(trim),就是說,讓列表只保留指定區間內的元素,不在指定區間以內的元素都將被刪除。

14

RPOP key 
移除並獲取列表最後一個元素

15

RPOPLPUSH source destination 
移除列表的最後一個元素,並將該元素添加到另外一個列表並返回

16

RPUSH key value1 [value2] 
在列表中添加一個或多個值

17

RPUSHX key value 
爲已存在的列表添加值

Redis 集合(Set)

RedisSetstring類型的無序集合。集合成員是惟一的,這就意味着集合中不能出現重複的數據。

Redis 集合是經過哈希表實現的,因此添加,刪除,查找的複雜度都是O(1)

集合中最大的成員數爲 232 - 1 (4294967295, 每一個集合可存儲40多億個成員)

 

實例

redis 127.0.0.1:6379> SADD runoobkey redis
(integer) 1
redis 127.0.0.1:6379> SADD runoobkey mongodb
(integer) 1
redis 127.0.0.1:6379> SADD runoobkey mysql
(integer) 1
redis 127.0.0.1:6379> SADD runoobkey mysql
(integer) 0
redis 127.0.0.1:6379> SMEMBERS runoobkey

1) "mysql"
2) "mongodb"
3) "redis"

在以上實例中咱們經過 SADD 命令向名爲 runoobkey 的集合插入的三個元素。


Redis 集合命令

 

下表列出了 Redis 集合基本命令:

序號

命令及描述

1

SADD key member1 [member2] 
向集合添加一個或多個成員

2

SCARD key 
獲取集合的成員數

3

SDIFF key1 [key2] 
返回給定全部集合的差集

4

SDIFFSTORE destination key1 [key2] 
返回給定全部集合的差集並存儲在 destination

5

SINTER key1 [key2] 
返回給定全部集合的交集

6

SINTERSTORE destination key1 [key2] 
返回給定全部集合的交集並存儲在 destination

7

SISMEMBER key member 
判斷 member 元素是不是集合 key 的成員

8

SMEMBERS key 
返回集合中的全部成員

9

SMOVE source destination member 
member 元素從 source 集合移動到 destination 集合

10

SPOP key 
移除並返回集合中的一個隨機元素

11

SRANDMEMBER key [count] 
返回集合中一個或多個隨機數

12

SREM key member1 [member2] 
移除集合中一個或多個成員

13

SUNION key1 [key2] 
返回全部給定集合的並集

14

SUNIONSTORE destination key1 [key2] 
全部給定集合的並集存儲在 destination 集合中

15

SSCAN key cursor [MATCH pattern] [COUNT count] 
迭代集合中的元素

Redis 有序集合(sorted set)

Redis 有序集合和集合同樣也是string類型元素的集合,且不容許重複的成員。

不一樣的是每一個元素都會關聯一個double類型的分數。redis正是經過分數來爲集合中的成員進行從小到大的排序。

有序集合的成員是惟一的,但分數(score)卻能夠重複。

集合是經過哈希表實現的,因此添加,刪除,查找的複雜度都是O(1)集合中最大的成員數爲 232 - 1 (4294967295, 每一個集合可存儲40多億個成員)

 

實例

redis 127.0.0.1:6379> ZADD runoobkey 1 redis
(integer) 1
redis 127.0.0.1:6379> ZADD runoobkey 2 mongodb
(integer) 1
redis 127.0.0.1:6379> ZADD runoobkey 3 mysql
(integer) 1
redis 127.0.0.1:6379> ZADD runoobkey 3 mysql
(integer) 0
redis 127.0.0.1:6379> ZADD runoobkey 4 mysql
(integer) 0
redis 127.0.0.1:6379> ZRANGE runoobkey 0 10 WITHSCORES

1) "redis"
2) "1"
3) "mongodb"
4) "2"
5) "mysql"
6) "4"

在以上實例中咱們經過命令 ZADD  redis 的有序集合中添加了三個值並關聯上分數。


Redis 有序集合命令

 

下表列出了 redis 有序集合的基本命令:

序號

命令及描述

1

ZADD key score1 member1 [score2 member2] 
向有序集合添加一個或多個成員,或者更新已存在成員的分數

2

ZCARD key 
獲取有序集合的成員數

3

ZCOUNT key min max 
計算在有序集合中指定區間分數的成員數

4

ZINCRBY key increment member 
有序集合中對指定成員的分數加上增量 increment

5

ZINTERSTORE destination numkeys key [key ...] 
計算給定的一個或多個有序集的交集並將結果集存儲在新的有序集合 key

6

ZLEXCOUNT key min max 
在有序集合中計算指定字典區間內成員數量

7

ZRANGE key start stop [WITHSCORES] 
經過索引區間返回有序集合成指定區間內的成員

8

ZRANGEBYLEX key min max [LIMIT offset count] 
經過字典區間返回有序集合的成員

9

ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] 
經過分數返回有序集合指定區間內的成員

10

ZRANK key member 
返回有序集合中指定成員的索引

11

ZREM key member [member ...] 
移除有序集合中的一個或多個成員

12

ZREMRANGEBYLEX key min max 
移除有序集合中給定的字典區間的全部成員

13

ZREMRANGEBYRANK key start stop 
移除有序集合中給定的排名區間的全部成員

14

ZREMRANGEBYSCORE key min max 
移除有序集合中給定的分數區間的全部成員

15

ZREVRANGE key start stop [WITHSCORES] 
返回有序集中指定區間內的成員,經過索引,分數從高到底

16

ZREVRANGEBYSCORE key max min [WITHSCORES] 
返回有序集中指定分數區間內的成員,分數從高到低排序

17

ZREVRANK key member 
返回有序集合中指定成員的排名,有序集成員按分數值遞減(從大到小)排序

18

ZSCORE key member 
返回有序集中,成員的分數值

19

ZUNIONSTORE destination numkeys key [key ...] 
計算給定的一個或多個有序集的並集,並存儲在新的 key

20

ZSCAN key cursor [MATCH pattern] [COUNT count] 
迭代有序集合中的元素(包括元素成員和元素分值)

Redis 哈希(Hash)

Redis hash 是一個string類型的fieldvalue的映射表,hash特別適合用於存儲對象。

Redis 中每一個 hash 能夠存儲 232 - 1 鍵值對(40多億)。

 

實例

127.0.0.1:6379>  HMSET runoobkey name "redis tutorial" 
127.0.0.1:6379>  HGETALL runoobkey
1) "name"
2) "redis tutorial"
3) "description"
4) "redis basic commands for caching"
5) "likes"
6) "20"
7) "visitors"
8) "23000"

hset  key  mapHey MapValue

在以上實例中,咱們設置了 redis 的一些描述信息(name, description, likes, visitors) 到哈希表的 runoobkey 中。


Redis hash 命令

 

下表列出了 redis hash 基本的相關命令:

序號

命令及描述

1

HDEL key field2 [field2] 
刪除一個或多個哈希表字段

2

HEXISTS key field 
查看哈希表 key 中,指定的字段是否存在。

3

HGET key field 
獲取存儲在哈希表中指定字段的值。

4

HGETALL key 
獲取在哈希表中指定 key 的全部字段和值

5

HINCRBY key field increment 
爲哈希表 key 中的指定字段的整數值加上增量 increment

6

HINCRBYFLOAT key field increment 
爲哈希表 key 中的指定字段的浮點數值加上增量 increment

7

HKEYS key 
獲取全部哈希表中的字段

8

HLEN key 
獲取哈希表中字段的數量

9

HMGET key field1 [field2] 
獲取全部給定字段的值

10

HMSET key field1 value1 [field2 value2 ] 
同時將多個 field-value (-)對設置到哈希表 key 中。

11

HSET key field value 
將哈希表 key 中的字段 field 的值設爲 value

12

HSETNX key field value 
只有在字段 field 不存在時,設置哈希表字段的值。

13

HVALS key 
獲取哈希表中全部值

14

HSCAN key cursor [MATCH pattern] [COUNT count] 
迭代哈希表中的鍵值對。

Java操做Redis

 

Redis Jedis

package com.hongmoshui.test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;

public class TestRedis
{
    private Jedis jedis;

    @Before
    public void setup()
    {
        // 鏈接redis服務器,127.0.0.1:6379
        jedis = new Jedis("127.0.0.1", 6379);
        // 權限認證
        jedis.auth("123456");
    }

    /**
     * redis存儲字符串
     */
    @Test
    public void testString()
    {
        // -----添加數據----------
        jedis.set("name", "xinxin");// 向key-->name中放入了value-->xinxin
        System.out.println(jedis.get("name"));// 執行結果:xinxin

        jedis.append("name", " is my lover"); // 拼接
        System.out.println(jedis.get("name"));

        jedis.del("name"); // 刪除某個鍵
        System.out.println(jedis.get("name"));
        // 設置多個鍵值對
        jedis.mset("name", "liuling", "age", "23", "qq", "476777XXX");
        jedis.incr("age"); // 進行加1操做
        System.out.println(jedis.get("name") + "-" + jedis.get("age") + "-" + jedis.get("qq"));

    }

    /**
     * redis操做Map
     */
    @Test
    public void testMap()
    {
        // -----添加數據----------
        Map<String, String> map = new HashMap<String, String>();
        map.put("name", "xinxin");
        map.put("age", "22");
        map.put("qq", "123456");
        jedis.hmset("user", map);
        // 取出user中的name,執行結果:[minxr]-->注意結果是一個泛型的List
        // 第一個參數是存入redis中map對象的key,後面跟的是放入map中的對象的key,後面的key能夠跟多個,是可變參數
        List<String> rsmap = jedis.hmget("user", "name", "age", "qq");
        System.out.println(rsmap);

        // 刪除map中的某個鍵值
        jedis.hdel("user", "age");
        System.out.println(jedis.hmget("user", "age")); // 由於刪除了,因此返回的是null
        System.out.println(jedis.hlen("user")); // 返回key爲user的鍵中存放的值的個數2
        System.out.println(jedis.exists("user"));// 是否存在key爲user的記錄
                                                 // 返回true
        System.out.println(jedis.hkeys("user"));// 返回map對象中的全部key
        System.out.println(jedis.hvals("user"));// 返回map對象中的全部value

        Iterator<String> iter = jedis.hkeys("user").iterator();
        while (iter.hasNext())
        {
            String key = iter.next();
            System.out.println(key + ":" + jedis.hmget("user", key));
        }
    }

    /**
     * jedis操做List
     */
    @Test
    public void testList()
    {
        // 開始前,先移除全部的內容
        jedis.del("java framework");
        System.out.println(jedis.lrange("java framework", 0, -1));
        // 先向key java framework中存放三條數據
        jedis.lpush("java framework", "spring");
        jedis.lpush("java framework", "struts");
        jedis.lpush("java framework", "hibernate");
        // 再取出全部數據jedis.lrange是按範圍取出,
        // 第一個是key,第二個是起始位置,第三個是結束位置,jedis.llen獲取長度
        // -1表示取得全部
        System.out.println(jedis.lrange("java framework", 0, -1));

        jedis.del("java framework");
        jedis.rpush("java framework", "spring");
        jedis.rpush("java framework", "struts");
        jedis.rpush("java framework", "hibernate");
        System.out.println(jedis.lrange("java framework", 0, -1));
    }

    /**
     * jedis操做Set
     */
    @Test
    public void testSet()
    {
        // 添加
        jedis.sadd("user", "liuling");
        jedis.sadd("user", "xinxin");
        jedis.sadd("user", "ling");
        jedis.sadd("user", "zhangxinxin");
        jedis.sadd("user", "who");
        // 移除noname
        jedis.srem("user", "who");
        // 獲取全部加入的value
        System.out.println(jedis.smembers("user"));
        // 判斷who 是不是user集合的元素
        System.out.println(jedis.sismember("user", "who"));
        System.out.println(jedis.srandmember("user"));
        // 返回集合的元素個數
        System.out.println(jedis.scard("user"));
    }

    @Test
    public void test() throws InterruptedException
    {
        // jedis 排序
        // 注意,此處的rpush和lpush是List的操做。是一個雙向鏈表(但從表現來看的)
        jedis.del("a");// 先清除數據,再加入數據進行測試
        jedis.rpush("a", "1");
        jedis.lpush("a", "6");
        jedis.lpush("a", "3");
        jedis.lpush("a", "9");
        System.out.println(jedis.lrange("a", 0, -1));
        // [9,3, 6,1]
        System.out.println(jedis.sort("a"));
        // [1,3,6,9]
        // 輸入排序後結果
        System.out.println(jedis.lrange("a", 0, -1));
    }

//    @Test
//    public void testRedisPool()
//    {
//        RedisUtil.getJedis().set("newname", "中文測試");
//        System.out.println(RedisUtil.getJedis().get("newname"));
//    }
}

SpringBoot集成Redis

Maven依賴jar包

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <!-- <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> 
            <version>2.9.0</version> </dependency> -->
    </dependencies>

新增配置文件信息

########################################################
###Redis (RedisConfiguration)
########################################################
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=123456
spring.redis.pool.max-idle=8
spring.redis.pool.min-idle=0
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
spring.redis.timeout=5000

Java代碼

package com.hongmoshui.service;

import java.util.List;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

@Service
public class RedisService
{
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public void setObject(String key, Object value)
    {
        this.setObject(key, value, null);
    }

    public void setObject(String key, Object value, Long time)
    {
        if (StringUtils.isEmpty(key) || value == null)
        {
            return;
        }
        if (value instanceof String)
        {
            // 存放string類型
            String stringValue = (String) value;
            if (time == null)
            {
                stringRedisTemplate.opsForValue().set(key, stringValue);
            }
            else
            {
                stringRedisTemplate.opsForValue().set(key, stringValue, time, TimeUnit.SECONDS);
            }

            return;
        }
        if (value instanceof List)
        {
            // 存放list類型
            List<String> listValue = (List<String>) value;
            for (String string : listValue)
            {
                stringRedisTemplate.opsForList().leftPush(key, string);
            }
        }

    }

    public void delKey(String key)
    {
        stringRedisTemplate.delete(key);
    }

    public String getString(String key)
    {
        return stringRedisTemplate.opsForValue().get(key);

    }

}

Redis主從複製

  克隆三臺linux虛擬機

克隆虛擬機

生成新的mack地址

主從複製配置

redis主從複製

概述

一、redis的複製功能是支持多個數據庫之間的數據同步。一類是主數據庫(master)一類是從數據庫(slave),主數據庫能夠進行讀寫操做,當發生寫操做的時候自動將數據同步到從數據庫,而從數據庫通常是隻讀的,並接收主數據庫同步過來的數據,一個主數據庫能夠有多個從數據庫,而一個從數據庫只能有一個主數據庫。

二、經過redis的複製功能能夠很好的實現數據庫的讀寫分離,提升服務器的負載能力。主數據庫主要進行寫操做,而從數據庫負責讀操做。

主從複製過程

主從複製過程:見下圖

過程:

1:當一個從數據庫啓動時,會向主數據庫發送sync命令,

2:主數據庫接收到sync命令後會開始在後臺保存快照(執行rdb操做),並將保存期間接收到的命令緩存起來

3:當快照完成後,redis會將快照文件和全部緩存的命令發送給從數據庫。

4:從數據庫收到後,會載入快照文件並執行收到的緩存的命令。

修改redis.conf

修改從redis中的 redis.conf文件

slaveof 192.168.33.130 6379  

masterauth 123456--- 主redis服務器配置了密碼,則須要配置

Redis哨兵機制

什麼是哨兵機制

Redis的哨兵(sentinel) 系統用於管理多個 Redis 服務器,該系統執行如下三個任務:

·        監控(Monitoring): 哨兵(sentinel) 會不斷地檢查你的Master和Slave是否運做正常。

·        提醒(Notification):當被監控的某個 Redis出現問題時, 哨兵(sentinel) 能夠經過 API 向管理員或者其餘應用程序發送通知。

·        自動故障遷移(Automatic failover):當一個Master不能正常工做時,哨兵(sentinel) 會開始一次自動故障遷移操做,它會將失效Master的其中一個Slave升級爲新的Master, 並讓失效Master的其餘Slave改成複製新的Master; 當客戶端試圖鏈接失效的Master時,集羣也會向客戶端返回新Master的地址,使得集羣可使用Master代替失效Master。

哨兵(sentinel) 是一個分佈式系統,你能夠在一個架構中運行多個哨兵(sentinel) 進程,這些進程使用流言協議(gossipprotocols)來接收關於Master是否下線的信息,並使用投票協議(agreement protocols)來決定是否執行自動故障遷移,以及選擇哪一個Slave做爲新的Master.

每一個哨兵(sentinel) 會向其它哨兵(sentinel)、master、slave定時發送消息,以確認對方是否」活」着,若是發現對方在指定時間(可配置)內未迴應,則暫時認爲對方已掛(所謂的主觀認爲宕機」 Subjective Down,簡稱sdown).

若「哨兵羣」中的多數sentinel,都報告某一master沒響應,系統才認爲該master"完全死亡"(:客觀上的真正down,Objective Down,簡稱odown),經過必定的vote算法,從剩下的slave節點中,選一臺提高爲master,而後自動修改相關配置.

雖然哨兵(sentinel) 釋出爲一個單獨的可執行文件 redis-sentinel ,但實際上它只是一個運行在特殊模式下的 Redis 服務器,你能夠在啓動一個普通 Redis 服務器時經過給定 --sentinel 選項來啓動哨兵(sentinel).

哨兵(sentinel) 的一些設計思路和zookeeper很是相似

單個哨兵(sentinel)

 

哨兵模式修改配置

實現步驟:

1.拷貝到etc目錄

cp sentinel.conf  /usr/local/redis/etc

2.修改sentinel.conf配置文件

sentinel monitor mymast  192.168.110.133 6379 1  #主節點 名稱 IP 端口號 選舉次數

3. 修改心跳檢測 5000毫秒

sentinel down-after-milliseconds mymaster 5000

4.sentinel parallel-syncs mymaster 2 --- 作多多少合格節點

5. 啓動哨兵模式

./redis-server /usr/local/redis/etc/sentinel.conf --sentinel &

6. 中止哨兵模式

Redis事務

Redis事務

Redis 事務能夠一次執行多個命令, 而且帶有如下兩個重要的保證:

事務是一個單獨的隔離操做:事務中的全部命令都會序列化、按順序地執行。事務在執行的過程當中,不會被其餘客戶端發送來的命令請求所打斷。

事務是一個原子操做:事務中的命令要麼所有被執行,要麼所有都不執行。

一個事務從開始到執行會經歷如下三個階段:

開始事務。

命令入隊。

執行事務。

實例

如下是一個事務的例子, 它先以 MULTI 開始一個事務, 而後將多個命令入隊到事務中, 最後由 EXEC 命令觸發事務, 一併執行事務中的全部命令:

redis 127.0.0.1:6379> MULTI
OK

redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUED

redis 127.0.0.1:6379> GET book-name
QUEUED

redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED

redis 127.0.0.1:6379> SMEMBERS tag
QUEUED

redis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
   2) "C++"
   3) "Programming"

Redis 事務命令

下表列出了 redis 事務的相關命 

序號    命令及描述
1      DISCARD #取消事務,放棄執行事務塊內的全部命令。
2      EXEC #執行全部事務塊內的命令。
3      MULTI #標記一個事務塊的開始。
4      UNWATCH #取消 WATCH 命令對全部 key 的監視。
5      WATCH key [key ...] #監視一個(或多個) key ,若是在事務執行以前這個(或這些) key 被其餘命令所改動,那麼事務將被打斷。

Redis持久化

什麼是Redis持久化

什麼是Redis持久化,就是將內存數據保存到硬盤。

Redis 持久化存儲 (AOF 與 RDB 兩種模式)

RDB持久化

RDB 是在某個時間 點將數據寫入一個臨時文件,持久化結束後,用這個臨時文件替換上次持久化的文件,達到數據恢復。
優勢:使用單獨子進程來進行持久化,主進程不會進行任何 IO 操做,保證了 redis 的高性能
缺點:RDB 是間隔一段時間進行持久化,若是持久化之間 redis 發生故障,會發生數據丟失。因此這種方式更適合數據要求不嚴謹的時候

這裏說的這個執行數據寫入到臨時文件的時間點是能夠經過配置來本身肯定的,經過配置redis n 秒內若是超過 m key 被修改這執行一次 RDB 操做。這個操做就相似於在這個時間點來保存一次 Redis 的全部數據,一次快照數據。全部這個持久化方法也一般叫作 snapshots。

RDB 默認開啓,redis.conf 中的具體配置參數以下;

#dbfilename:持久化數據存儲在本地的文件
dbfilename dump.rdb
#dir:持久化數據存儲在本地的路徑,若是是在/redis/redis-3.0.6/src下啓動的redis-cli,則數據會存儲在當前src目錄下
dir ./
##snapshot觸發的時機,save    
##以下爲900秒後,至少有一個變動操做,纔會snapshot  
##對於此值的設置,須要謹慎,評估系統的變動操做密集程度  
##能夠經過「save 「」」來關閉snapshot功能  
#save時間,如下分別表示更改了1個key時間隔900s進行持久化存儲;更改了10個key300s進行存儲;更改10000個key60s進行存儲。
save 900 1
save 300 10
save 60 10000
##當snapshot時出現錯誤沒法繼續時,是否阻塞客戶端「變動操做」,「錯誤」可能由於磁盤已滿/磁盤故障/OS級別異常等  
stop-writes-on-bgsave-error yes  
##是否啓用rdb文件壓縮,默認爲「yes」,壓縮每每意味着「額外的cpu消耗」,同時也意味這較小的文件尺寸以及較短的網絡傳輸時間  
rdbcompression yes  

AOF持久化

Append-only file,將「操做 + 數據」以格式化指令的方式追加到操做日誌文件的尾部,在 append 操做返回後(已經寫入到文件或者即將寫入),才進行實際的數據變動,「日誌文件」保存了歷史全部的操做過程;當 server 須要數據恢復時,能夠直接 replay 此日誌文件,便可還原全部的操做過程。AOF 相對可靠,它和 mysql 中 bin.log、apache.log、zookeeper 中 txn-log 簡直殊途同歸。AOF 文件內容是字符串,很是容易閱讀和解析。
優勢:能夠保持更高的數據完整性,若是設置追加 file 的時間是 1s,若是 redis 發生故障,最多會丟失 1s 的數據;且若是日誌寫入不完整支持 redis-check-aof 來進行日誌修復;AOF 文件沒被 rewrite 以前(文件過大時會對命令進行合併重寫),能夠刪除其中的某些命令(好比誤操做的 flushall)。
缺點:AOF 文件比 RDB 文件大,且恢復速度慢。

咱們能夠簡單的認爲 AOF 就是日誌文件,此文件只會記錄「變動操做」(例如:set/del 等),若是 server 中持續的大量變動操做,將會致使 AOF 文件很是的龐大,意味着 server 失效後,數據恢復的過程將會很長;事實上,一條數據通過屢次變動,將會產生多條 AOF 記錄,其實只要保存當前的狀態,歷史的操做記錄是能夠拋棄的;由於 AOF 持久化模式還伴生了「AOF rewrite」。
AOF 的特性決定了它相對比較安全,若是你指望數據更少的丟失,那麼能夠採用 AOF 模式。若是 AOF 文件正在被寫入時忽然 server 失效,有可能致使文件的最後一次記錄是不完整,你能夠經過手工或者程序的方式去檢測並修正不完整的記錄,以便經過 aof 文件恢復可以正常;同時須要提醒,若是你的 redis 持久化手段中有 aof,那麼在 server 故障失效後再次啓動前,須要檢測 aof 文件的完整性。

AOF 默認關閉,開啓方法,修改配置文件 reds.conf:appendonly yes

##此選項爲aof功能的開關,默認爲「no」,能夠經過「yes」來開啓aof功能  
##只有在「yes」下,aof重寫/文件同步等特性纔會生效  
appendonly yes  

##指定aof文件名稱  
appendfilename appendonly.aof  

##指定aof操做中文件同步策略,有三個合法值:always everysec no,默認爲everysec  
appendfsync everysec  
##在aof-rewrite期間,appendfsync是否暫緩文件同步,"no"表示「不暫緩」,「yes」表示「暫緩」,默認爲「no」  
no-appendfsync-on-rewrite no  

##aof文件rewrite觸發的最小文件尺寸(mb,gb),只有大於此aof文件大於此尺寸是纔會觸發rewrite,默認「64mb」,建議「512mb」  
auto-aof-rewrite-min-size 64mb  

##相對於「上一次」rewrite,本次rewrite觸發時aof文件應該增加的百分比。  
##每一次rewrite以後,redis都會記錄下此時「新aof」文件的大小(例如A),那麼當aof文件增加到A*(1 + p)以後  
##觸發下一次rewrite,每一次aof記錄的添加,都會檢測當前aof文件的尺寸。  
auto-aof-rewrite-percentage 100  

AOF 是文件操做,對於變動操做比較密集的 server,那麼必將形成磁盤 IO 的負荷加劇;此外 linux 對文件操做採起了「延遲寫入」手段,即並不是每次 write 操做都會觸發實際磁盤操做,而是進入了 buffer 中,當 buffer 數據達到閥值時觸發實際寫入(也有其餘時機),這是 linux 對文件系統的優化,可是這卻有可能帶來隱患,若是 buffer 沒有刷新到磁盤,此時物理機器失效(好比斷電),那麼有可能致使最後一條或者多條 aof 記錄的丟失。經過上述配置文件,能夠得知 redis 提供了 3 中 aof 記錄同步選項:

always:每一條 aof 記錄都當即同步到文件,這是最安全的方式,也覺得更多的磁盤操做和阻塞延遲,是 IO 開支較大。

everysec:每秒同步一次,性能和安全都比較中庸的方式,也是 redis 推薦的方式。若是遇到物理服務器故障,有可能致使最近一秒內 aof 記錄丟失(可能爲部分丟失)。

no:redis 並不直接調用文件同步,而是交給操做系統來處理,操做系統能夠根據 buffer 填充狀況 / 通道空閒時間等擇機觸發同步;這是一種普通的文件操做方式。性能較好,在物理服務器故障時,數據丟失量會因 OS 配置有關。

其實,咱們能夠選擇的太少,everysec 是最佳的選擇。若是你很是在乎每一個數據都極其可靠,建議你選擇一款「關係性數據庫」吧。
AOF 文件會不斷增大,它的大小直接影響「故障恢復」的時間, 並且 AOF 文件中歷史操做是能夠丟棄的。AOF rewrite 操做就是「壓縮」AOF 文件的過程,固然 redis 並無採用「基於原 aof 文件」來重寫的方式,而是採起了相似 snapshot 的方式:基於 copy-on-write,全量遍歷內存中數據,而後逐個序列到 aof 文件中。所以 AOF rewrite 可以正確反應當前內存數據的狀態,這正是咱們所須要的;*rewrite 過程當中,對於新的變動操做將仍然被寫入到原 AOF 文件中,同時這些新的變動操做也會被 redis 收集起來(buffer,copy-on-write 方式下,最極端的多是全部的 key 都在此期間被修改,將會耗費 2 倍內存),當內存數據被所有寫入到新的 aof 文件以後,收集的新的變動操做也將會一併追加到新的 aof 文件中,此後將會重命名新的 aof 文件爲 appendonly.aof, 此後全部的操做都將被寫入新的 aof 文件。若是在 rewrite 過程當中,出現故障,將不會影響原 AOF 文件的正常工做,只有當 rewrite 完成以後纔會切換文件,由於 rewrite 過程是比較可靠的。*

觸發 rewrite 的時機能夠經過配置文件來聲明,同時 redis 中能夠經過 bgrewriteaof 指使人工干預。

redis-cli -h ip -p port bgrewriteaof

由於 rewrite 操做 /aof 記錄同步 /snapshot 都消耗磁盤 IO,redis 採起了「schedule」策略:不管是「人工干預」仍是系統觸發,snapshot 和 rewrite 須要逐個被執行。

AOF rewrite 過程並不阻塞客戶端請求。系統會開啓一個子進程來完成。

AOF與RDB區別

OF RDB 各有優缺點,這是有它們各自的特色所決定:

1) AOF 更加安全,能夠將數據更加及時的同步到文件中,可是 AOF 須要較多的磁盤 IO 開支,AOF 文件尺寸較大,文件內容恢復數度相對較慢。
*2) snapshot,安全性較差,它是「正常時期」數據備份以及 master-slave 數據同步的最佳手段,文件尺寸較小,恢復數度較快。

能夠經過配置文件來指定它們中的一種,或者同時使用它們(不建議同時使用),或者所有禁用,在架構良好的環境中,master 一般使用 AOF,slave 使用 snapshot,主要緣由是 master 須要首先確保數據完整性,它做爲數據備份的第一選擇;slave 提供只讀服務(目前 slave 只能提供讀取服務),它的主要目的就是快速響應客戶端 read 請求;可是若是你的 redis 運行在網絡穩定性差 / 物理環境糟糕狀況下,建議你 master 和 slave 均採起 AOF,這個在 master 和 slave 角色切換時,能夠減小「人工數據備份」/「人工引導數據恢復」的時間成本;若是你的環境一切很是良好,且服務須要接收密集性的 write 操做,那麼建議 master 採起 snapshot,而 slave 採用 AOF。

Redis發佈訂閱

Redis 發佈訂閱(pub/sub)是一種消息通訊模式:發送者(pub)發送消息,訂閱者(sub)接收消息。

Redis 客戶端能夠訂閱任意數量的頻道。

下圖展現了頻道 channel1 , 以及訂閱這個頻道的三個客戶端 —— client2 、 client5 和 client1 之間的關係:

當有新消息經過 PUBLISH 命令發送給頻道 channel1 時, 這個消息就會被髮送給訂閱它的三個客戶端:

實例

如下實例演示了發佈訂閱是如何工做的。在咱們實例中咱們建立了訂閱頻道名爲 redisChat:

redis 127.0.0.1:6379> SUBSCRIBE redisChat

Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1

如今,咱們先從新開啓個 redis 客戶端,而後在同一個頻道 redisChat 發佈兩次消息,訂閱者就能接收到消息。

redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique"

(integer) 1

redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis by runoob.com"

(integer) 1

# 訂閱者的客戶端會顯示以下消息
1) "message"
2) "redisChat"
3) "Redis is a great caching technique"
1) "message"
2) "redisChat"
3) "Learn redis by runoob.com"
發佈訂閱命令
下表列出了 redis 發佈訂閱經常使用命令:

1

PSUBSCRIBE pattern [pattern ...] 
訂閱一個或多個符合給定模式的頻道。

2

PUBSUB subcommand [argument [argument ...]] 
查看訂閱與發佈系統狀態。

3

PUBLISH channel message 
將信息發送到指定的頻道。

4

PUNSUBSCRIBE [pattern [pattern ...]] 
退訂全部給定模式的頻道。

5

SUBSCRIBE channel [channel ...] 
訂閱給定的一個或多個頻道的信息。

6

UNSUBSCRIBE [channel [channel ...]] 
指退訂給定的頻道。

相關文章
相關標籤/搜索