Redis

redisphp

1 NoSQL技術簡介

  • Schemaless (弱結構)
  • In-Memory (基於內存)
  • 弱化事務
  • 適用於Cluster(集羣)環境
  • 沒有複雜的鏈接查詢操做
  • 腳本語言支持

市場上流行的nosql 產品以下html

 

  • Cassandra:  基於圖片存儲
  • Hbase:基於列存儲
  • mongoDB:基於文檔存儲
  • CouchDB:基於文檔存儲
  • Riak:基於鍵值存儲
  • Redis:基於鍵值存儲

 

Redis  簡介

1.1 Redis是什麼?

  Redis是用C 語言開發的、一個開源、支持網絡、基於內存、鍵值對型的NOSQL數據庫。前端

參考學習網站:http://www.runoob.com/redis/redis-tutorial.htmljava

2.2 Redis的特色

  • Redis是一個高性能的Key/VaIue數據庫
  • 基於內存
  • 數據類型豐富(string\list\set\sortset\hash
  • 持久化
  • 單線程
  • 訂閱/發佈模型

2.3 RedisMemcached對比

  • Redis不只僅支持簡單的k/v類型的數據,同時還提供list, set, hash等數據結構存儲
  • Redis支持數據的主從複製,即master-slave模式的數據備份;
  • Redis支持數據的持久化,能夠將內存中的數據保存到磁盤中,重啓的時候能夠再次加載原有數據;

 

  • Redis單個value的最大限制是1 GB,  memcached只能保存1MB的數據

 

 

2.4 技術架構

 

2.4.1 傳統的架構

 

 

 

 

 

2.4.2 Redis 存儲非關係型數據架構

 

2.4.3 Redis 充當緩存服務器架構

 

 

 

 

 

 

Redis 安裝

 

3.1 環境準備

 

  • VMWare(虛擬機)
  • CentOS7
  • Gcc(編譯器
  • WinSCP/filezilla FTP/ssh
  • redis-4.0.1.tar.gz (安裝)

 

3.2 安裝步驟

 

1)安裝redis編譯的c環境,yum install gcc-c++node

 

2)將redis-4.0.1.tar.gz上傳到Linux系統中mysql

 

3)解壓到/usr/local下  tar -xvf redis-4.0.1.tar.gz -C /usr/locallinux

 

4)進入redis-4.0.1目錄 使用make命令編譯redisc++

 

5)在redis-4.0.1目錄中 使用make PREFIX=/usr/local/redis install命令安裝redis/usr/local/redis若是上個步驟安裝失敗執行make MALLOC=libcweb

 

6)拷貝redis-4.0.1中的redis.conf到安裝目錄redis/bin中redis

 

7)設置redis.conf,即設置redis 啓動方式讓它在後臺啓動

說明若是不在後臺啓動,則當前窗口若是關閉則redis 服務也會關閉因此要使用後臺啓動。

3.3 啓動 redis服務

1) 啓動redis ,在bin下執行命令./redis-server  redis.conf

2)使用命令查看6379端口是否啓動,6379端口redsi默認端口ps -ef | grep redis  經過 查詢redis 的運行的端口號ps -aux |grep redis

3.4  關閉redis服務 

3.4..1 強制關閉 (可能致使持久化數據丟失) Kill  -9  pid

3.4.2 使用redis客戶關閉

 ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

安裝過程演示:

1 安裝redis編譯的c環境,yum install gcc-c++ 在鏈接網絡狀況下輸入命令自動下載

 

 

 2將redis-4.0.1.tar.gz上傳到Linux系統中

 

 

 3解壓到/usr/local下  tar -xvf redis-4.0.1.tar.gz -C /usr/local

 

4進入redis-4.0.1目錄 使用make命令編譯redis 

 

 

5)在redis-4.0.1目錄中 使用make PREFIX=/usr/local/redis install命令安裝redis到/usr/local/redis中若是上個步驟安裝失敗執行make MALLOC=libc

6拷貝redis-4.0.1中的redis.conf到安裝目錄redis/bin中

 

7設置redis.conf,即設置redis 啓動方式讓它在後臺啓動

 

 

 

 

 

 

直接啓動:不能繼續輸入命令

 

使用後臺打開設置 vim

 

找到該位置 輸入i修改

 

按配置啓動

 

 

 

 -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

 

 

 

=======================================================================================================================================================

=======================================================================================================================================================

 

Redis 經常使用命令

 

4.1  簡要說明

 

 

redis是一種高級的key-value的存儲系統

 

其中的key是字符串類型,儘量知足以下幾點:

 

1)key不要太長,最好不要超過1024字節,不然這不只會消耗內存還會下降查找效率

 

2)key不要過短,若是過短會下降key的可讀性

 

3)在項目中,key最好有一個統一的命名規範(根據企業的需求)

其中value 支持五種數據類型:

 

 

1)字符串型 string

 

2)字符串列表 list

 

3)字符串集合 set

 

4)有序字符串集合 sorted set

 

5)哈希類型 hash

 

 

4.1  存儲String

命令

說明

示例

set

設置一個key/value

set  name lucy

get

根據key獲取對應的 value

get name

mset

一次設置多個key value

met name lucy age 12

mget

一次獲取多個key value

mget name age

getset

獲取原始 key的值,同時設置新值

getset name john

strlen

獲取key value的長度

Strlen name

append

爲對應keyvalue 追加內容

Append name love

getrange

截取value內容

Getrange name 0 3

setex

設置key存活的時間()

Setex name 3 lucy

psetex

設置key存活的時間(毫秒)

 

setnx

只有當key存在時再等效set

 

msetnx

同時設置多個key

 

decr

 進行數值類型的-1 操做

decr age

decrby

根據提供的數據繼續減法操做

 

lncr

進行數值類型的+1 操做

 

lncrby

根據提供的數據繼續加法操做

 

lncrbyfloat

根據提供的數據加入浮點數

incrbyfloat  age 1.5

Flushall    清空全部的鍵      keys * :  列出全部的 key的

 

4.3  存儲list(鏈表)

 

 

 

  Redis中,List類型是按照插入順序排序的字符串鏈表。和數據結構中的普通鏈表同樣,咱們能夠在其頭部(Ieft)和尾部(right)添加新的元素。在插入時,若是該鍵並不存在,Redis將爲該鍵建立

一個新的鏈表。若是鏈表中全部的元素均被移除,那麼該鍵也將會被從數據庫中刪除。

List中能夠包含的最大元素數量是42949672950, 從元素插入和刪除的效率來看,若是咱們是在鏈表的兩頭插入或刪除元素,這將會是很是高效的操做,即便鏈表中己經存儲了百萬條記錄,該操做也能夠在常量時間內完成。然而須要說明的是,若是元素插入或刪除操做是做用於鏈表中間,那將會是很是低效的。

 

命令

說明

示例

lpush

從左側加入值到key列表

Lpush list a

lpushx

lpush必須保證key存在

Lpushx list a

rpush

從右側加入值到key列表

 

rpushx

同rpush必須保證key存在

 

lpop

從左側返回和移除第一個元素

Lpop list

rpop

從右側返回和移除第一個元素

 

lrang

獲取某一個下標區間的元素

Lrange 0 2       lrang 0 -1 表示所有顯示

llen

獲取列表元素個數

 

lset

設置某一個下標的元素(覆蓋原有元素)

 

lrem

刪除重複的元素

Lrem list 2 m表示刪除2list重複元素 m

ltrim

只保留指定區間內的元素,不在指定區間以內的元素都將被刪除

Ltrim list 2 5

linsert

在列表的元素前或者後插入元素

Linsert list before  b  a

rpoplpush

將鏈表中的尾部元素彈出並添加到頭部

Rpoplpush list 

 

Rpoplpush 使用場景

Redis鏈表常常會被用於消息隊列的服務,以完成多程序之間的消息交換。假設一個應用程序正在執行LPUSH操做向鏈表中添加新的元素,咱們一般將這樣的程序稱之爲」生產者(Producer)",而另一個應用程序正在執行RPOP操做從鏈表中取出元素,咱們稱這樣的程序爲」消費者(Consumer)",若是此時,消費者程序在取出消息元素後馬上崩潰,因爲該消息已經被取出且沒有被正常處理,那麼咱們就能夠認爲該消息己經丟失,由此可能會致使業務數據丟失,或業務狀態的不一致等現象的發生。然而經過使用RPOPLPUSH命令,消費者程序在從主消息隊列中取出消息以後再將其插入到備份隊列中直到消費者程序完成正常的處理邏輯後再將該消息從備份隊列中刪除。同時咱們還能夠提供一個守護進程,當發現備份隊列中的消息過時時,能夠從新將其再放回到主消息隊列中,以便其它的消費者程序繼續處理。

 

4.4  存儲set

Redis中,咱們能夠將Set類型看做爲沒有排序的字符集合,和List類型同樣,咱們也能夠在該類型的數據值上執行添加、刪除或判斷某一元素是否存在等操做。和List類型不一樣的是,Set集介中不容許重複。換句話說,若是屢次添加相同元素,Set中將僅保留該元素的一份拷貝。和List類型相比,Set類型在功能上還存在着一個很是重要的特性,即在服務器端完成多個Sets之間的聚合計算操做,如。Unions(並集),  intersection(交集)和differences(差集)因爲這些操做均在服務端完成,所以效率極高,並且也節省了大量的網絡10開銷。

  • sadd key values[valuel, value2...]:set中添加數據,若是該key的值己有則不會重複添加
  • srem key members[memberl,  member2...]:刪除:指定的成員
  • 得到集合中的元素

n smembers key:獲取set中全部的成員

n sismember key member:判斷參數中指定的成員是否在該s et中,1表示存在,0表示不存在或者該key自己就不存在。(不管集合中有多少元素均可以極速的返回結果)

 

  • 集合的差集運算A-B          sdiff keyl key2..:返回keylkey2中相差的成員,key順序有關即返回差集

 

 

 

  • 集合的交集運算A n B            sinter keyl key2 key3...:AB交集。

 

 

 

  • 集合的並集運算 A U B             sunion keyl key2 key3… 返回並集

 

 

 

  • scard key:獲取set中成員的數量
  • SRANDMEMBER  key:隨機返回set中的一個成員

 

 

 

  • sdiffstore destination keyl key2..:key1, key2相差的成員存儲在destination

 

 

 

  • sinterstore destination key1 key2…:將返回的交集存儲在destination

 

 

 

  • sunionstore destination key[key二」:將返回的並集存儲在destination

 

 

 

4.5 存儲sortedset

    Sorted-SetSet類型極爲類似,它們都是字符串的集合,都不容許重複的成員出如今一個Set中。它們主要差異是Sorted-Set中的每個成員都會有一個分數(score)與之關聯,Redis正是經過分數來爲集合中的成員進行從小到大的排序。然而須要額外指出的是,儘管Sorted-Set中的成員必須是惟一的,可是分數(score)倒是能夠重複的

    Sorted-Set中添加、刪除或更新一個成員都是很是快速的操做,其時間複雜度爲集合中成員數量的對數。因爲Sorted-Set中的成員在集合中的位置是有序的,所以,即使是訪問位於集合中部的成員也仍然是很是高效的。事實上,Redis所具備的這一特徵在不少其它類型的數據庫中是很難實現的,換句話說,在該點上要想達到和Red is一樣的高效,在其它數據庫中進行建模是很是困難的。

例如:遊戲排名、微博熱點話題等使用場景。

  • 添加元素

zadd key score member score2 member2 ...:將全部成員以及該成員的分數存放到sorted-set中。若是該元素已經存在則會用新的分數替換原有的分數。返回值是新加入到集合中的元素個數,不包含以前己經存在的元素。

  • 得到元素

zscore key member:返回指定成員的分數   zcard key:獲取集合中的成員數量

  • 刪除成員

zrem key member[member...]:移除集合中指定的成員,能夠指定多個成員口

  • 範圍查找

zrange key start end [withscores]:  獲取集合中腳標爲start-e n d的成員,[withscores]參數代表返回的成員包含其分數。

  

zrevrange key start stop [withscores]:按照元素分數從大到小的順序返回索引從startstop之間的全部元素(包含兩端的元素)

  • zremrangebyrank key start stop:按照排名範圍刪除元素

 

  • zremrangebyscore key min max:按照分數範圍刪除元素

 

 

 

 

4.6 存儲hash

Redis中的Hash類型能夠當作具備String KeyString Valuemap容器。因此該類型很是適合於存儲值對象的信息。

  • hset key field value:爲指定的key設定field/value(鍵值對)

        

  • hmset key fieid value [field2 value2 ...]:設置key中的多個filed/value

        

  • hget key field:返回指定的key中的field的值

        

  • hmget key fileds:獲取key中的多個filed的值

        

  • hgetall key:獲取key中的全部filed vaule

 

          

 

 

 

       hdel key field [field ... ]:能夠刪除一個或多個字段,返回值是被刪除的字段個數

 

 

  • del key:刪除整個list

 

 

  • hincrby key field increment:設置keyfiled的值增長i ncrement

 

           

  • hexists key field:判斷指定的key中的filed是否存在
  • hlen key:獲取key所包含的field的數量
  • hkeys key:得到全部的key
  • hvals key:得到全部的value

 

=======================================================================================================================================================

=======================================================================================================================================================

 

 

 

Redis 特性

 

5.1 數據庫

 

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

 

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

 

 

  • 默認數據庫下內容爲

 

  • 選擇1數據庫,並查看庫中的內容

 

  • 0數據的某個key轉移1 數據中

 

 

 

 

5.2 其餘命令

  • ping 測試鏈接是否存活  表示鏈接可用
  • dbsize 表示當前數據庫中key數量
  • flushdb 刪除當前數據庫中全部key
  • type key:返回key的類型

 

 

 

5.3 訂閱與發佈

 

 

  • subscribe channel:訂閱頻道
  • subscribe channel*:批量訂閱頻道,例:subscribe s*,訂閱以s開頭的頻道
  • publish channel content:在指定的頻道中發佈消息
  • 取消訂閱  unsubscribe [channel]

 

 

訂閱示例在一個終端訂閱消息

 

從新打開一個終端發佈消息

 

這時訂閱終端會收到消息

 

 

 

5.4 Redis事務

 

5.4.1 簡介

 

和其它數據庫同樣,Redis做爲NoSQL數據庫也一樣提供了事務機制。在RedisMULTI/EXEC/DISCARD/這三個命令是咱們實現事務的基石。

 

MULTI: 開啓事務

 

EXEC:提交事務

 

DISCARD:回滾事務

 

 

5.4.2 示例

 

5.4.3 示例3

打開一個終端

 

再打開一個終端

 

因爲age 在事務中,並無提交,因此查到的仍是15

 

 

 

5.5 Redis 持久化

 

5.5.1 概念

 

Redis的高性能是因爲其將全部數據都存儲在了內存中,爲了使Redis在重啓以後仍能保證數據不丟失,須要將數據從內存中同步到硬盤中,這一過程就是持久化。 Redis支持兩種方式的持久化,一種是RDB方式,一種是AOF方式。能夠單獨使用其中一種或將兩者結合使用。

 

1)  RDB持久化(默認支持,無需配置)

該機制是指在指定的時間間隔內將內存中的數據集快照寫入磁盤。

2) AOF持久化

該機制將以日誌的形式記錄服務器所處理的每個寫操做,在Redis服務器啓動之初會讀取該文件來從新構建數據庫,以保證啓動後數據庫中的數據是完整的。

3) 無持久化

咱們能夠經過配置的方式禁用Redis服務器的持久化功能,這樣就能夠將Redis視爲一個功能增強版的memcached了。

 

4) redis能夠同時使用RDBAOF

 

 

5.5.2 使用RDB 持久化

 

Snapshotting(RDB)機制的運行原理

 

1.Redis經過fork產生子進程;

 

2.父進程繼續處理client請求,子進程負責將快照寫入臨時文件;

 

3.子進程寫完後,用臨時文件替換原來的快照文件,而後子進程退出。

 

  • Snapshotting(RDB)機制的開發步驟

編輯redis.conf文件

 

 

 

 

v Append-Only File(AOF)機制的運行原理

1. Redis 經過fork一個子進程

2.父進程繼續處理client請求,子進程把AOF內容寫入緩衝區;

3.子進程寫完退出,父進程接收退出消息,將緩衝區AOF寫入臨時文件;

4.臨時文件重命名成appendonly.aof,原來文件被覆蓋,整個過程完。

 

1.1.1 Append-Only File(AOF)機制的開發步驟

編輯redis.conf文件

 

 

 

 

持久化技術

優點

缺點

RDB

1RDB產生的文件小。

2RDB恢復快,而且簡單,例如你能夠快 速的將RDB文件傳輸到其餘主機,作數據的恢復。

3、在進行RDB備份的時候,主進程僅僅需 要建立一個子進程,全部的I/O操做都由子進程完成

1、不能徹底保證數據安全,在兩個備份點之間可能會發 生數據丟失

2、當數據量很大時,建立子進程可能會是一個很是耗時 的操做,甚至可能須要1秒,在這個期間,Redis沒法 向客戶端提供服務。

AOF

1、數據的備份粒度更小,數據安全性更高。

2AOF只會對日誌文件進行追加操做,不 會修改已經寫好的內容。即便在掉電的狀況下,AOF日誌仍然是可用的

1AOF文件一般比相同的數據集的RDB文件更大。

2AOF寫日誌可能會很慢,這跟fsync的機制有關

 

 

 

 

5.6 集羣環境下的Session管理

 

 

在下圖中,若是一臺服務器對外提供服務,受網絡和服務器內存、性能的限制,很難完成高併發的要求,因此水平擴展服務的數量,即集羣,有了集羣后,又引入負載均衡器,負載均衡器經過內部的算法,能夠把請求均發到不一樣服務器,這樣就能夠緩解單個服務器併發的問題。可是這樣會出現session 的共享問題,即若是用戶登陸請求的是第一個web服務器器,那麼會在服務器端會建立用戶的 session ,而若是接下來其餘的請求有可能會被負載均衡器,把請求分配到第二個服務器,第二個服務器並無該用的session這樣就會出現問題。

 

最好的解決辦法以下圖:

 

全部的session 存儲在redis中,當服務器須要session時,從resdis中獲取。

 

 

  • 搭建步驟:

 

1) tomcat7lib 下導入以下jar

 

注意最後一個 jar,網上下載的大部分有問題,因此本教程提供

 

 

 

二、tomcatconf/context.xml 中加入

 

 

<Valve className="com.radiadesign.catalina.session.RedisSessionHandlerValve"  />

 <Manager className="com.radiadesign.catalina.session.RedisSessionManager"

 host="192.168.133.135"

 port="6379"

 maxInactiveInterval="60"/>

 

 

注意:host換成對應的IP 地址

 

 

二、測試代碼示例

 

  • 創建web工程、jsp頁面,向session中存入內容

  • 運行工程,訪問test.jsp

 

  • 查看redis中的session id

 

 

 

 

 

6 Redis集羣的搭建

6.1  redis-cluster架構圖

 

 

客戶端與redis節點直連,不須要中間proxy層.客戶端不須要鏈接集羣全部節點,鏈接集羣中任何一個可用節點便可

 

 

6.2 redis-cluster投票:容錯

 

解決redis的單點問題。在一個或多個節點出現宕機的狀況下,集羣內部經過投票的機制可以快速的進行選舉和不停機的狀況下進行服務持續提供

 

 

    • (1)投票過程是集羣中全部master參與,若是半數以上master節點與master節點通訊超時(cluster-node-timeout),則認爲當前master節點掛掉.
    • (2):何時整個集羣不可用(cluster_state:fail)?
      a:若是集羣任意master掛掉,且當前master沒有slave.集羣進入 fail狀態,也能夠理解成集羣的slot映射[0-16383]不完成時進入fail狀態.
      b:若是集羣超過半數以上master掛掉,不管是否有slave集羣進入fail狀態.
      ps:當集羣不可用時,全部對集羣的操做作都不可用,收到((error) CLUSTERDOWN The cluster is down)錯誤

 

 

6.3 架構細節

 

(1)全部的redis節點彼此互聯(PING-PONG機制),內部使用二進制協議優化傳輸速度和帶寬.

 

(2)redis-cluster把全部的物理節點映射到[0-16383]slot上,cluster 負責維護node<->slot<->value

 

Redis 集羣中內置了 16384 個哈希槽,當須要在 Redis 集羣中放置一個 key-value 時,redis 先對 key 使用 crc16 算法算出一個結果,而後把結果對 16384 求餘數,這樣每一個 key 都會對應一個編號在 0-16383 之間的哈希槽,redis 會根據節點數量大體均等的將哈希槽映射到不一樣的節點

 

 

6.4 Redis集羣的搭建

 

Redis集羣中至少應該有三個節點。要保證集羣的高可用,須要每一個節點有一個備份機。Redis集羣至少須要6臺服務器。

 

因爲條件限制,這裏搭建僞分佈式,即使用一臺虛擬機運行6redis實例。須要修改redis的端口號7001-7006

 

 

6.4.1 建立6redis實例

 

1.usr/local/ 建立目錄

 

mkdir  redis_cluster

 

redis_cluster 下建立6個目錄 

 

 

mkdir 7001

 

mkdir 7002

 

mkdir 7003

 

mkdir 7004

 

mkdir 7005

 

mkdir 7006

 

 

2.拷貝一個redisredis_cluster

 

cp  -r  redis   redis_cluster/

 

3.配置redis.conf

 

vim  redis.conf

 

 

1) daemonize  yes

 

2) port 7001

 

3) #bind 127.0.0.1

 

4) cluster-enabled  yes

 

5)protected-mode yes    改成  no

 

 

四、把配置好的redis分別拷貝到7001~7006 目錄下

 

並修改7002~7006 resdis.confport端口爲目錄對應的端口即7002~7006

 

五、啓動redis

 

爲了方便啓動,建立批量啓動文件 redis_startall.sh.

 

 

目錄位置

內容爲:

 

 

cd 7001/redis/bin

./redis-server redis.conf

cd ../../..

cd 7002/redis/bin

./redis-server redis.conf

          

cd ../../..

cd 7003/redis/bin

./redis-server redis.conf  

cd ../../..

cd 7004/redis/bin

./redis-server redis.conf

cd ../../..

cd 7005/redis/bin

./redis-server redis.conf

cd ../../..

cd 7006/redis/bin

./redis-server redis.conf

 

 

6.批量關閉redis服務

 

 

在相同的目錄下建立redis_shutdown.sh

 

內容爲:

 

 

cd 7001/redis/bin

./redis-cli -p 7001  shutdown

cd ../../..

cd 7002/redis/bin

./redis-cli -p 7002  shutdown

cd ../../..

cd 7003/redis/bin

./redis-cli -p 7003 shutdown

cd ../../..

cd 7004/redis/bin

./redis-cli -p 7004  shutdown

cd ../../..

cd 7005/redis/bin

./redis-cli -p 7005 shutdown

cd ../../..

cd 7006/redis/bin

./redis-cli -p 7006 shutdown

6.4.2 安裝ruby環境

redis集羣管理工具redis-trib.rb ,該文件在/usr/local/redis-4.0.1/src

  1. 複製src目錄中的redis-trib.rb /usr/local/redis_cluster目錄下  

    ll *.rb

          

要想運行redis-trib.rb 須要安裝其依賴ruby環境 

2安裝ruby環境

yum install -y ruby

yum install -y rubygems

3安裝ruby的包

經過ssh工具把redis-4.0.0.gem 上傳到linux

切換到該文件目錄下 運行 gem install redis-4.0.0.gem

  問題:

redis requires Ruby version >= 2.2.2問題

1.安裝curl

sudo yum install curl

2. 安裝RVM

gpg2 --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3

curl -L get.rvm.io | bash -s stable 

3. 

source /usr/local/rvm/scripts/rvm

4. 查看rvm庫中已知的ruby版本

rvm list known

5. 安裝一個ruby版本

rvm install 2.3.3

6. 使用一個ruby版本

rvm use 2.3.3

7. 查詢版本

ruby --version

8. 卸載一個已知版本

rvm remove 2.0.0

9. 再安裝redis就能夠了

gem install redis

 

6.4.3 啓動Redis

 運行批量啓動redis服務文件

 

 

6.4.4 建立集羣  

執行以下命令

./redis-trib.rb create --replicas 1 192.168.133.110:7001 192.168.133.110:7002 192.168.133.110:7003 192.168.133.110:7004 192.168.133.110:7005 192.168.133.110:7006

 

 

若是建立失敗,須要清空每一個節點下的node.conf

 

再從從新執行  ./redis-trib.rb create --replicas 1 192.168.133.138:7001 192.168.133.138:7002 192.168.133.138:7003 192.168.133.138:7004 192.168.133.138:7005 192.168.133.138:7006

 

6.4.5 集羣的鏈接

[root@localhost bin]# ./redis-cli -p 7001 -c

 

Jedis

 Redis不只是使用命令來操做,如今基本上主流的語言都有客戶端支持,好比Java, C. C#, C++,php,  Node.js.  Go

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

7.1  經過 maven導入jar

<dependency>

<groupId>redis.clients</groupId>

<artifactId>jedis</artifactId>

<version>2.9.0</version>

</dependency>

7.2 單個redis示例測試string

 

 

7.3  改動redis.conf 

1) 默認狀況下redis運行在保護模式(這種模式下,訪問不須要密碼),可是這種模式只容許本地訪問

  protected-mode yes    改成  no

2) 配置了只在127.0.0.1上綁定監聽,取消一下

默認 bind 127.0.0.1      改成  #bind 127.0.0.1

 

7.4  防火

firewall-cmd --zone=public --add-port=6379/tcp --permanent

firewall-cmd --reload

 

7.5  示例2測試list

@Test

public void testList(){

//1設置 IP地址及端口號

Jedis jedis =new Jedis("192.168.133.132",6379);

//存值操做

jedis.lpush("names", "A","B","C");

//獲取值

    List<String> list = jedis.lrange("names", 0, -1);

    for (String name : list) {

     System.out.println(name);

}

}

7.6   示例3 測試hash

@Test

public void testHash(){

//1設置 IP地址及端口號

Jedis jedis =new Jedis("192.168.133.132",6379);

Map<String,String> map = new HashMap<String,String>();

map.put("lucy", "12");

map.put("lily", "11");

//存值操做

jedis.hmset("students", map);

//獲取值

    List<String> list = jedis.hmget("students", "lucy","lily" );

    for (String name : list) {

     System.out.println(name);

}

}

 

7.7  Jedis鏈接池使用

    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    public class JedisPoolUtils {
        private static JedisPool pool = null;
            static{
                //加載配置文件
            InputStream in = JedisPoolUtils.class.getClassLoader().getResourceAsStream("redis.properties");
            Properties pro = new Properties();
            try {
                pro.load(in);
            } catch (IOException e) {
                e.printStackTrace();
            }
            //獲取鏈接池配置對象,並設置
            JedisPoolConfig poolConfig = new JedisPoolConfig();
            poolConfig.setMaxIdle(Integer.parseInt(pro.get("redis.maxIdle").toString()));//最大空閒鏈接數, 默認8個
            poolConfig.setMinIdle(Integer.parseInt(pro.get("redis.minIdle").toString()));//最小空閒鏈接數, 默認0個
            poolConfig.setMaxTotal(Integer.parseInt(pro.get("redis.maxTotal").toString()));//最大鏈接數, 默認8個
            pool = new JedisPool(poolConfig,pro.getProperty("redis.url") , Integer.parseInt(pro.get("redis.port").toString()));
        }
        //對外提供獲取鏈接方法
        public static Jedis getJedis(){
            return pool.getResource();
        }
        //對外提供關閉鏈接方法
            public static void colseJedis(Jedis jedis){
                if(jedis!=null){
                    jedis.close();
                }
            }
        
    }
  • 下面內容存儲成redis.properties 並放到資源文件路徑

 

redis.maxIdle=30

redis.minIdle=10

redis.maxTotal=100

redis.url=192.168.133.135

redis.port=6379

 

 

7.8  jedis鏈接集羣版

 

 

第一步:使用JedisCluster對象。須要一個Set<HostAndPort>參數,Redis節點的列表。

 

第二步:直接使用JedisCluster對象操做redis。在系統中單例存在。

 

第三步:打印結果

 

第四步:系統關閉前,關閉JedisCluster對象。

 

 

 注意須要開放相關端口號:

 

 

 

 

public class JedisTest {
        @Test
        public void test1() throws IOException{
            // 第一步:使用JedisCluster對象。須要一個Set<HostAndPort>參數。Redis節點的列表。
                    Set<HostAndPort> nodes = new HashSet<>();
                    nodes.add(new HostAndPort("192.168.133.110", 7001));
                    nodes.add(new HostAndPort("192.168.133.110", 7002));
                    nodes.add(new HostAndPort("192.168.133.110", 7003));
                    nodes.add(new HostAndPort("192.168.133.110", 7004));
                    nodes.add(new HostAndPort("192.168.133.110", 7005));
                    nodes.add(new HostAndPort("192.168.133.110", 7006));
                    JedisCluster jedisCluster = new JedisCluster(nodes);
                    // 第二步:直接使用JedisCluster對象操做redis。在系統中單例存在。
                    jedisCluster.set("name", "lucy");
                    String result = jedisCluster.get("name");
                    // 第三步:打印結果
                    System.out.println(result);
                    // 第四步:系統關閉前,關閉JedisCluster對象。
                    jedisCluster.close();
        }
    }

 

 

7.9   業務邏輯中添加緩存

 

 

7.9.1 接口封裝

 

經常使用的操做redis的方法提取出一個接口,分別對應單機版和集羣版建立兩個實現類。

 

 

7.9.2 接口定義

 

 

public interface JedisClient {
    String set(String key, String value);
    String get(String key);
    Boolean exists(String key);
    Long expire(String key, int seconds);
    Long ttl(String key);//返回key 的過時時間
    Long incr(String key);
    Long hset(String key, String field, String value);
    String hget(String key, String field);
    Long hdel(String key, String... field);
}

 

7.9.3  單機版的實現類

 

 

public class JedisClientPool implements JedisClient {
        
        @Autowired
        private JedisPool jedisPool;

        @Override
        public String set(String key, String value) {
            Jedis jedis = jedisPool.getResource();
            String result = jedis.set(key, value);
            jedis.close();
            return result;
        }

        @Override
        public String get(String key) {
            Jedis jedis = jedisPool.getResource();
            String result = jedis.get(key);
            jedis.close();
            return result;
        }

        @Override
        public Boolean exists(String key) {
            Jedis jedis = jedisPool.getResource();
            Boolean result = jedis.exists(key);
            jedis.close();
            return result;
        }

        @Override
        public Long expire(String key, int seconds) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.expire(key, seconds);
            jedis.close();
            return result;
        }

        @Override
        public Long ttl(String key) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.ttl(key);
            jedis.close();
            return result;
        }

        @Override
        public Long incr(String key) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.incr(key);
            jedis.close();
            return result;
        }

        @Override
        public Long hset(String key, String field, String value) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.hset(key, field, value);
            jedis.close();
            return result;
        }

        @Override
        public String hget(String key, String field) {
            Jedis jedis = jedisPool.getResource();
            String result = jedis.hget(key, field);
            jedis.close();
            return result;
        }

        @Override
        public Long hdel(String key, String... field) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.hdel(key, field);
            jedis.close();
            return result;
        }

    }    
    

 

7.9.4  集羣版實現類

 

 

    @Component
    public class JedisClientCluster implements JedisClient {

        @Autowired
        private JedisCluster jedisCluster;
        @Override
        public String set(String key, String value) {
            return jedisCluster.set(key, value);
        }

        @Override
        public String get(String key) {
            return jedisCluster.get(key);
        }

        @Override
        public Boolean exists(String key) {
            return jedisCluster.exists(key);
        }

        @Override
        public Long expire(String key, int seconds) {
            return jedisCluster.expire(key, seconds);
        }

        @Override
        public Long ttl(String key) {
            return jedisCluster.ttl(key);
        }

        @Override
        public Long incr(String key) {
            return jedisCluster.incr(key);
        }

        @Override
        public Long hset(String key, String field, String value) {
            return jedisCluster.hset(key, field, value);
        }

        @Override
        public String hget(String key, String field) {
            return jedisCluster.hget(key, field);
        }

        @Override
        public Long hdel(String key, String... field) {
            return jedisCluster.hdel(key, field);
        }

    }

 

注意:集羣版和單機版不能共用

 

 

 

 

7.9.5  業務系統中使用緩存

 

 

  • 思路分析

 

一、查詢數據庫以前先查詢緩存。

 

二、查詢到結果,直接響應結果。

 

三、查詢不到,緩存中沒有則須要查詢數據庫。

 

四、把查詢結果添加到緩存中。

 

五、返回結果

 

 

例子:

 

 

package com.hrxb.zj.controller.menu;
import java.util.List;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.hrxb.zj.pojo.ScMenu;
import com.hrxb.zj.pojo.ScUser;
import com.hrxb.zj.service.menu.MenuService;
import com.hrxb.zj.utils.EasyUITree;
import com.hrxb.zj.utils.JedisClientCluster;
import com.hrxb.zj.utils.MenuUtil;

@RequestMapping("menu")
@Scope("prototype")
@Controller
public class MeunController {
    @Autowired
    private MenuService menuService;
    @Autowired
    private JedisClientCluster jedisClientCluster;
    @Autowired
    private Gson gson;
    //從配置文件中獲取對應的key
    @Value("${menu_list}")
    private String menu_list;
    /**
     * 根據用戶返回對應菜單
     * 
     * @param session
     * @return
     */
    @ResponseBody
    @RequestMapping("/selectMenuList")
    public List<EasyUITree> selectMenu(HttpSession session) {
        // 從sessio獲取當前登陸人的信息
        ScUser scUser = (ScUser) session.getAttribute("user");
        // 查詢緩存
        try {
            String result = jedisClientCluster.hget(menu_list, scUser.getUsername());
            if (null != result && result.length() > 0){
                //把json串轉爲 Java對象
            List<EasyUITree> retList = gson.fromJson(result,  
                    new TypeToken<List<EasyUITree>>() {  
                    }.getType());  
                return retList;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    
        //查詢數據庫 獲取當前登陸人的菜單
        List<ScMenu> menuList = menuService.selectAllByResp(scUser);
        // 把對象轉爲easy的樹形屬性
        List<EasyUITree> treeList = MenuUtil.createTreeMenu(menuList, 0);
        
        //放入緩存
        try {
            jedisClientCluster.hset(menu_list, scUser.getUsername(), gson.toJson(treeList));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return treeList;
    }

}

 

 

 

 

 

 

7.10  高可用(High Availability

 

 

是當一臺服務器中止服務後,對於業務及用戶毫無影響。 中止服務的緣由可能因爲網卡、路由器、機房、CPU負載太高、內存溢出、天然災害等不可預期的緣由致使,在不少時候也稱單點問題

 

1)解決單點問題主要有2種方式:

 

1.1.1 主備方式

 

這種一般是一臺主機、一臺或多臺備機,在正常狀況下主機對外提供服務,並把數據同步到備機,當主機宕機後,備機馬上開始服務。 
Redis HA中使用比較多的是keepalived,它使主機備機對外提供同一個虛擬IP,客戶端經過虛擬IP進行數據操做,正常期間主機一直對外提供服務,宕機後VIP自動漂移到備機上。

 

優勢是對客戶端毫無影響,仍然經過VIP操做。
缺點也很明顯,在絕大多數時間內備機是一直沒使用,被浪費着的。

 

1.1.2 主從方式

 

這種採起一主多從的辦法,主從之間進行數據同步。 Master宕機後,經過選舉算法(Paxos、Raft)從slave中選舉出新Master繼續對外提供服務,主機恢復後以slave的身份從新加入。
主從另外一個目的是進行讀寫分離,這是當單機讀寫壓力太高的一種通用型解決方案。 其主機的角色只提供寫操做或少許的讀,把多餘讀請求經過負載均衡算法分流到單個或多個slave服務器上。

 

缺點是主機宕機後,Slave雖然被選舉成新Master了,但對外提供的IP服務地址卻發生變化了,意味着會影響到客戶端。 解決這種狀況須要一些額外的工做,在當主機地址發生變化後及時通知到客戶端,客戶端收到新地址後,使用新地址繼續發送新請求。

 

1.1.3 數據同步

 

不管是主備仍是主從都牽扯到數據同步的問題,這也分2種狀況:

 

同步方式:當主機收到客戶端寫操做後,以同步方式把數據同步到從機上,當從機也成功寫入後,主機才返回給客戶端成功,也稱數據強一致性。 很顯然這種方式性能會下降很多,當從機不少時,能夠不用每臺都同步,主機同步某一臺從機後,從機再把數據分發同步到其餘從機上,這樣提升主機性能分擔同步壓力。 redis中是支持這種配置的,一臺master,一臺slave,同時這臺salve又做爲其餘slave的master。

 

異步方式:主機接收到寫操做後,直接返回成功,而後在後臺用異步方式把數據同步到從機上。 這種同步性能比較好,但沒法保證數據的完整性,好比在異步同步過程當中主機忽然宕機了,也稱這種方式爲數據弱一致性。

 

1.1.4 分佈式

 

分佈式(distributed), 是當業務量、數據量增長時,能夠經過任意增長減小服務器數量來解決問題。

 

至少部署兩臺Redis服務器構成一個小的集羣,主要有2個目的:

 

  • 高可用性:在主機掛掉後,自動故障轉移,使前端服務對用戶無影響。
  • 讀寫分離:將主機讀壓力分流到從機上。

 

可在客戶端組件上實現負載均衡,根據不一樣服務器的運行狀況,分擔不一樣比例的讀請求壓力。

 

邏輯圖:

 

 

 

l 配置redis 的主從

Redis的主從複製功能很是強大,一個master能夠擁有多個slave,而一個slave又能夠擁有多個slave,如此下去,造成了強大的多級服務器集羣架構。下面樓主簡單的進行一下配置。

一、上面安裝好的一個Redis做爲master其ip:169.254.250.250,而後使用VMWare的虛擬機克隆功能將剛剛那個linux系統克隆一份做爲slave

並修改其IP 169.254.250.251

  2、修改slaveredis配置文件:

    slaveof 169.254.250.250  6379  (映射到主服務器上)

    若是master設置了驗證密碼,還需配置masterauth。樓主的master設置了驗證密碼爲admin,因此配置masterauth admin

配置完以後啓動slaveRedis服務,OK,主從配置完成。下面測試一下:

經過info 命令查看當前主服務的信息: 

 

 

 

經過info 命令查看當前從服務的信息:

 

 

 

 

 

主服務器:

 

 

 

從服務器:

 

相關文章
相關標籤/搜索