redisphp
市場上流行的nosql 產品以下html
Redis是用C 語言開發的、一個開源、支持網絡、基於內存、鍵值對型的NOSQL數據庫。前端
參考學習網站:http://www.runoob.com/redis/redis-tutorial.htmljava
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 服務也會關閉,因此要使用後臺啓動。
1) 啓動redis ,在bin下執行命令./redis-server redis.conf
2)使用命令查看6379端口是否啓動,6379端口是redsi默認端口ps -ef | grep redis 或經過 查詢redis 的運行的端口號ps -aux |grep 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是一種高級的key-value的存儲系統
其中的key是字符串類型,儘量知足以下幾點:
1)key不要太長,最好不要超過1024個字節,不然這不只會消耗內存還會下降查找效率
2)key不要過短,若是過短會下降key的可讀性
3)在項目中,key最好有一個統一的命名規範(根據企業的需求)
其中value 支持五種數據類型:
1)字符串型 string
2)字符串列表 list
3)字符串集合 set
4)有序字符串集合 sorted set
5)哈希類型 hash
命令 |
說明 |
示例 |
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 |
爲對應key的value 追加內容 |
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的值
在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表示刪除2個list中重複元素 m |
ltrim |
只保留指定區間內的元素,不在指定區間以內的元素都將被刪除 |
Ltrim list 2 5 |
linsert |
在列表的元素前或者後插入元素 |
Linsert list before b a |
rpoplpush |
將鏈表中的尾部元素彈出並添加到頭部 |
Rpoplpush list |
Rpoplpush 使用場景
Redis鏈表常常會被用於消息隊列的服務,以完成多程序之間的消息交換。假設一個應用程序正在執行LPUSH操做向鏈表中添加新的元素,咱們一般將這樣的程序稱之爲」生產者(Producer)",而另一個應用程序正在執行RPOP操做從鏈表中取出元素,咱們稱這樣的程序爲」消費者(Consumer)",若是此時,消費者程序在取出消息元素後馬上崩潰,因爲該消息已經被取出且沒有被正常處理,那麼咱們就能夠認爲該消息己經丟失,由此可能會致使業務數據丟失,或業務狀態的不一致等現象的發生。然而經過使用RPOPLPUSH命令,消費者程序在從主消息隊列中取出消息以後再將其插入到備份隊列中直到消費者程序完成正常的處理邏輯後再將該消息從備份隊列中刪除。同時咱們還能夠提供一個守護進程,當發現備份隊列中的消息過時時,能夠從新將其再放回到主消息隊列中,以便其它的消費者程序繼續處理。
在Redis中,咱們能夠將Set類型看做爲沒有排序的字符集合,和List類型同樣,咱們也能夠在該類型的數據值上執行添加、刪除或判斷某一元素是否存在等操做。和List類型不一樣的是,Set集介中不容許重複。換句話說,若是屢次添加相同元素,Set中將僅保留該元素的一份拷貝。和List類型相比,Set類型在功能上還存在着一個很是重要的特性,即在服務器端完成多個Sets之間的聚合計算操做,如。Unions(並集), intersection(交集)和differences(差集)。因爲這些操做均在服務端完成,所以效率極高,並且也節省了大量的網絡10開銷。
n smembers key:獲取set中全部的成員
n sismember key member:判斷參數中指定的成員是否在該s et中,1表示存在,0表示不存在或者該key自己就不存在。(不管集合中有多少元素均可以極速的返回結果)
Sorted-Set和Set類型極爲類似,它們都是字符串的集合,都不容許重複的成員出如今一個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]:按照元素分數從大到小的順序返回索引從start到stop之間的全部元素(包含兩端的元素)
Redis中的Hash類型能夠當作具備String Key和String Value的map容器。因此該類型很是適合於存儲值對象的信息。
hdel key field [field ... ]:能夠刪除一個或多個字段,返回值是被刪除的字段個數
=======================================================================================================================================================
=======================================================================================================================================================
一個Redis:實例能夠包括多個數據庫,客戶端能夠指定鏈接某個redis實例的哪一個數據庫,就比如一個mysql中建立多個數據庫,客戶端鏈接時指定鏈接哪一個數據庫。
一個redis實例最多可提供16個數據庫,下標從0到15,客戶端默認鏈接第0號數據庫,也能夠經過select選擇鏈接哪一個數據庫
訂閱示例在一個終端訂閱消息
在從新打開一個終端發佈消息
這時訂閱終端會收到消息
和其它數據庫同樣,Redis做爲NoSQL數據庫也一樣提供了事務機制。在Redis中MULTI/EXEC/DISCARD/這三個命令是咱們實現事務的基石。
MULTI: 開啓事務
EXEC:提交事務
DISCARD:回滾事務
打開一個終端
再打開一個終端
因爲age 在事務中,並無提交,因此查到的仍是15
Redis的高性能是因爲其將全部數據都存儲在了內存中,爲了使Redis在重啓以後仍能保證數據不丟失,須要將數據從內存中同步到硬盤中,這一過程就是持久化。 Redis支持兩種方式的持久化,一種是RDB方式,一種是AOF方式。能夠單獨使用其中一種或將兩者結合使用。
1) RDB持久化(默認支持,無需配置)
該機制是指在指定的時間間隔內將內存中的數據集快照寫入磁盤。
2) AOF持久化
該機制將以日誌的形式記錄服務器所處理的每個寫操做,在Redis服務器啓動之初會讀取該文件來從新構建數據庫,以保證啓動後數據庫中的數據是完整的。
3) 無持久化
咱們能夠經過配置的方式禁用Redis服務器的持久化功能,這樣就能夠將Redis視爲一個功能增強版的memcached了。
4) redis能夠同時使用RDB和AOF
v Snapshotting(RDB)機制的運行原理
1.Redis經過fork產生子進程;
2.父進程繼續處理client請求,子進程負責將快照寫入臨時文件;
3.子進程寫完後,用臨時文件替換原來的快照文件,而後子進程退出。
編輯redis.conf文件
v Append-Only File(AOF)機制的運行原理
1. Redis 經過fork一個子進程
2.父進程繼續處理client請求,子進程把AOF內容寫入緩衝區;
3.子進程寫完退出,父進程接收退出消息,將緩衝區AOF寫入臨時文件;
4.臨時文件重命名成appendonly.aof,原來文件被覆蓋,整個過程完。
編輯redis.conf文件
持久化技術 |
優點 |
缺點 |
RDB |
1、RDB產生的文件小。 2、RDB恢復快,而且簡單,例如你能夠快 速的將RDB文件傳輸到其餘主機,作數據的恢復。 3、在進行RDB備份的時候,主進程僅僅需 要建立一個子進程,全部的I/O操做都由子進程完成 |
1、不能徹底保證數據安全,在兩個備份點之間可能會發 生數據丟失 2、當數據量很大時,建立子進程可能會是一個很是耗時 的操做,甚至可能須要1秒,在這個期間,Redis沒法 向客戶端提供服務。 |
AOF |
1、數據的備份粒度更小,數據安全性更高。 2、AOF只會對日誌文件進行追加操做,不 會修改已經寫好的內容。即便在掉電的狀況下,AOF日誌仍然是可用的 |
1、AOF文件一般比相同的數據集的RDB文件更大。 2、AOF寫日誌可能會很慢,這跟fsync的機制有關 |
在下圖中,若是一臺服務器對外提供服務,受網絡和服務器內存、性能的限制,很難完成高併發的要求,因此水平擴展服務的數量,即集羣,有了集羣后,又引入負載均衡器,負載均衡器經過內部的算法,能夠把請求均發到不一樣服務器,這樣就能夠緩解單個服務器併發的問題。可是這樣會出現session 的共享問題,即若是用戶登陸請求的是第一個web服務器器,那麼會在服務器端會建立用戶的 session ,而若是接下來其餘的請求有可能會被負載均衡器,把請求分配到第二個服務器,第二個服務器並無該用的session。這樣就會出現問題。
最好的解決辦法以下圖:
全部的session 存儲在redis中,當服務器須要session時,從resdis中獲取。
1) 在tomcat7的lib 下導入以下jar 包
注意最後一個 jar,網上下載的大部分有問題,因此本教程提供
二、在tomcat的conf/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 地址
二、測試代碼示例
客戶端與redis節點直連,不須要中間proxy層.客戶端不須要鏈接集羣全部節點,鏈接集羣中任何一個可用節點便可
解決redis的單點問題。在一個或多個節點出現宕機的狀況下,集羣內部經過投票的機制可以快速的進行選舉和不停機的狀況下進行服務持續提供
(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 會根據節點數量大體均等的將哈希槽映射到不一樣的節點
Redis集羣中至少應該有三個節點。要保證集羣的高可用,須要每一個節點有一個備份機。Redis集羣至少須要6臺服務器。
因爲條件限制,這裏搭建僞分佈式,即使用一臺虛擬機運行6個redis實例。須要修改redis的端口號7001-7006
1.在usr/local/ 建立目錄
mkdir redis_cluster
在redis_cluster 下建立6個目錄
mkdir 7001
mkdir 7002
mkdir 7003
mkdir 7004
mkdir 7005
mkdir 7006
2.拷貝一個redis到 redis_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.conf的port端口爲目錄對應的端口即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 |
redis集羣管理工具redis-trib.rb ,該文件在/usr/local/redis-4.0.1/src
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
|
運行批量啓動redis服務文件
執行以下命令
./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
[root@localhost bin]# ./redis-cli -p 7001 -c
Redis不只是使用命令來操做,如今基本上主流的語言都有客戶端支持,好比Java, C. C#, C++,php, Node.js. Go等
在官方網站裏列一些JAVA的客戶端,有Jedis. Redisson. Jredis. JDBC-Redis.等其中官方推薦使用Jedis和Redisson。在企業中用的最多的就是Jedis,下面咱們就重點學習下Jedis
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> |
1) 默認狀況下redis運行在保護模式(這種模式下,訪問不須要密碼),可是這種模式只容許本地訪問
protected-mode yes 改成 no
2) 配置了只在127.0.0.1上綁定監聽,取消一下
默認 bind 127.0.0.1 改成 #bind 127.0.0.1
firewall-cmd --zone=public --add-port=6379/tcp --permanent firewall-cmd --reload
|
@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); } } |
@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); } } |
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.maxIdle=30 redis.minIdle=10 redis.maxTotal=100 redis.url=192.168.133.135 redis.port=6379 |
第一步:使用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(); } }
經常使用的操做redis的方法提取出一個接口,分別對應單機版和集羣版建立兩個實現類。
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); }
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; } }
@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); } }
注意:集羣版和單機版不能共用
一、查詢數據庫以前先查詢緩存。
二、查詢到結果,直接響應結果。
三、查詢不到,緩存中沒有則須要查詢數據庫。
四、把查詢結果添加到緩存中。
五、返回結果。
例子:
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; } }
是當一臺服務器中止服務後,對於業務及用戶毫無影響。 中止服務的緣由可能因爲網卡、路由器、機房、CPU負載太高、內存溢出、天然災害等不可預期的緣由致使,在不少時候也稱單點問題
1)解決單點問題主要有2種方式:
這種一般是一臺主機、一臺或多臺備機,在正常狀況下主機對外提供服務,並把數據同步到備機,當主機宕機後,備機馬上開始服務。
Redis HA中使用比較多的是keepalived,它使主機備機對外提供同一個虛擬IP,客戶端經過虛擬IP進行數據操做,正常期間主機一直對外提供服務,宕機後VIP自動漂移到備機上。
優勢是對客戶端毫無影響,仍然經過VIP操做。
缺點也很明顯,在絕大多數時間內備機是一直沒使用,被浪費着的。
這種採起一主多從的辦法,主從之間進行數據同步。 當Master宕機後,經過選舉算法(Paxos、Raft)從slave中選舉出新Master繼續對外提供服務,主機恢復後以slave的身份從新加入。
主從另外一個目的是進行讀寫分離,這是當單機讀寫壓力太高的一種通用型解決方案。 其主機的角色只提供寫操做或少許的讀,把多餘讀請求經過負載均衡算法分流到單個或多個slave服務器上。
缺點是主機宕機後,Slave雖然被選舉成新Master了,但對外提供的IP服務地址卻發生變化了,意味着會影響到客戶端。 解決這種狀況須要一些額外的工做,在當主機地址發生變化後及時通知到客戶端,客戶端收到新地址後,使用新地址繼續發送新請求。
不管是主備仍是主從都牽扯到數據同步的問題,這也分2種狀況:
l 同步方式:當主機收到客戶端寫操做後,以同步方式把數據同步到從機上,當從機也成功寫入後,主機才返回給客戶端成功,也稱數據強一致性。 很顯然這種方式性能會下降很多,當從機不少時,能夠不用每臺都同步,主機同步某一臺從機後,從機再把數據分發同步到其餘從機上,這樣提升主機性能分擔同步壓力。 在redis中是支持這種配置的,一臺master,一臺slave,同時這臺salve又做爲其餘slave的master。
l 異步方式:主機接收到寫操做後,直接返回成功,而後在後臺用異步方式把數據同步到從機上。 這種同步性能比較好,但沒法保證數據的完整性,好比在異步同步過程當中主機忽然宕機了,也稱這種方式爲數據弱一致性。
分佈式(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、修改slave的redis配置文件:
slaveof 169.254.250.250 6379 (映射到主服務器上)
若是master設置了驗證密碼,還需配置masterauth。樓主的master設置了驗證密碼爲admin,因此配置masterauth admin。
配置完以後啓動slave的Redis服務,OK,主從配置完成。下面測試一下:
經過info 命令查看當前主服務的信息:
經過info 命令查看當前從服務的信息:
主服務器:
從服務器: