Redis
1 NoSQL
1.1 什么是NoSQL
Not Only SQL 泛指非關系型數據庫。
隨着web2.0的快速發展,非關系型、分布式數據存儲得到了快速的發展,它們不保證關系數據的ACID特性。NoSQL概念在2009年被提了出來。NoSQL最常見的解釋是“non-relational”,“Not Only SQL”也被很多人接受。(“NoSQL”一詞最早於1998年被用於一個輕量級的關系數據庫的名字。)
1.2 為什么要使用NoSQL
1.2.1傳統的數據庫遇到的瓶頸
傳統的關系數據庫具有不錯的性能,高穩定型,久經歷史考驗,而且使用簡單,功能強大,同時也積累了大量的成功案例。在互聯網領域,MySQL成為了絕對靠前的王者,毫不誇張的說,MySQL為互聯網的發展做出了卓越的貢獻。
在90年代,一個網站的訪問量一般都不大,用單個數據庫完全可以輕松應付。在那個時候,更多的都是靜態網頁,動態交互類型的網站不多。
到了最近10年,網站開始快速發展。火爆的論壇、博客、sns、微博逐漸引領web領域的潮流。在初期,論壇的流量其實也不大,如果你接觸網絡比較早,你可能還記得那個時候還有文本型存儲的論壇程序,可以想象一般的論壇的流量有多大。
- 高並發讀寫
Web2.0網站,數據庫並發負載非常高,往往達到每秒上萬次的讀寫請求
- 高容量存儲和高效存儲
Web2.0網站通常需要在后台數據庫中存儲海量數據,如何存儲海量數據並進行高效的查詢往往是一個挑戰
- 高擴展性和高可用性
隨着系統的用戶量和訪問量與日俱增,需要數據庫能夠很方便的進行擴展、維護
1.2.2 NoSql數據庫的優勢
- 易擴展
NoSQL數據庫種類繁多,但是一個共同的特點都是去掉關系數據庫的關系型特性。數據之間無關系,這樣就非常容易擴展。也無形之間,在架構的層面上帶來了可擴展的能力。
- 大數據量,高性能
NoSQL數據庫都具有非常高的讀寫性能,尤其在大數據量下,同樣表現優秀。這得益於它的無關系性,數據庫的結構簡單。一般MySQL使用Query Cache,每次表的更新Cache就失效,是一種大粒度的Cache,在針對web2.0的交互頻繁的應用,Cache性能不高。而NoSQL的Cache是記錄級的,是一種細粒度的Cache,所以NoSQL在這個層面上來說就要性能高很多了。
- 靈活的數據模型
NoSQL無需事先為要存儲的數據建立字段,隨時可以存儲自定義的數據格式。而在關系數據庫里,增刪字段是一件非常麻煩的事情。如果是非常大數據量的表,增加字段簡直就是一個噩夢。這點在大數據量的web2.0時代尤其明顯。
- 高可用
NoSQL在不太影響性能的情況,就可以方便的實現高可用的架構。比如Cassandra,HBase模型,通過復制模型也能實現高可用。
1.3 常見的NoSQL產品
2 redis
2.1 redis簡介
全稱:REmote DIctionary Server(遠程字典服務器)。是完全開源免費的,用C語言編寫的, 遵守BCD協議。是一個高性能的(key/value)分布式內存數據庫,
基於內存運行並支持持久化的NoSQL數據庫,是當前最熱門的NoSql數據庫之一,也被人們稱為數據結構服務器。
redis的特點:
i)Redis支持數據的持久化,可以將內存中的數據保持在磁盤中,重啟的時候可以再次加載進行使用
ii)Redis不僅僅支持簡單的key-value類型的數據,同時還提供list,set,zset,hash等數據結構的存儲
2) Redis支持數據的備份,即master-slave模式的數據備份
2.1 Redis優勢
性能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
豐富的數據類型 – Redis支持二進制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數據類型操作。
原子 – Redis的所有操作都是原子性的,同時Redis還支持對幾個操作全並后的原子性執行。
豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過期等等特性
2.2 redis應用場景
新浪微博、facebook、電商系統的商品查詢……
2.3 redis下載
(1)Http://redis.io/
2.4 redis版本
3.x(支持集群) 2.x不支持集群 (課程中是用3.0版本)
副版本號為偶數時,表示是穩定版本,建議在生產環境中使用
副版本號為基數時,表示是測試版本,不建議在生產環境中是用
2.5 redis安裝
redis沒有基於windows的版本。在Linux系統中使用。課程中是用centOS。
2.5.1 單機版安裝
2.5.1.1 安裝gcc
安裝文件是一個壓縮包,但由於redis本身是采用c++ 編寫的,所以解壓完成后,需要編譯和安裝。所以先要在系統中安裝 c++的編譯器 gcc-c++
gcc的安裝方式分為兩種:
1 在線安裝(有網絡環境)
yum install -y gcc-c++
2離線安裝(無網絡環境)
可以到 http://vault.centos.org/6.5/os/x86_64/Packages/
下載需要的安裝文件。
需要的安裝文件如下:
| ppl-0.10.2-11.el6.x86_64.rpm ppl-devel-0.10.2-11.el6.x86_64.rpm gmp-devel-4.3.1-7.el6_2.2.x86_64.rpm cloog-ppl-devel-0.15.7-1.2.el6.x86_64.rpm cpp-4.4.7-4.el6.x86_64.rpm glibc-devel-2.12-1.132.el6.x86_64.rpm glibc-headers-2.12-1.132.el6.x86_64.rpm libstdc++-4.4.7-4.el6.x86_64.rpm libstdc++-devel-4.4.7-4.el6.x86_64.rpm gcc-4.4.7-4.el6.x86_64.rpm gcc-c++-4.4.7-4.el6.x86_64.rpm kernel-headers-2.6.32-431.el6.x86_64.rpm mpfr-2.4.1-6.el6.x86_64.rpm
|
逐個安裝:
使用命令為:
rpm –ivh ***.rpm
2.5.1.2 安裝redis
1 使用 root 用戶登錄,把redis安裝文件(壓縮文件)拷貝到linux系統中
(這里拷貝到了root用戶的opt目錄中)
2 解壓這個文件 tar –zxvf redis-3.0.0.tar.gz
3 進入解壓后的目錄,對里面的文件進行編譯和安裝
這里采用編譯和安裝同時進行的方式。
命令:
make install PREFIX=/usr/local/redis
/usr/local/redis是redis的安裝路徑,目錄名不是一定要叫redis可以自己定義
4 進入redis的安裝路徑中bin路徑,查看里面的文件
其中 redis-server文件就是redis的啟動文件
5 ./redis-server 運行這個文件,如果看到一個圖形界面,界面中顯示redis的版本、軟件位數、監聽的端口(6379)、PID等信息
說明redis的安裝和啟動成功。
注意: 這里的啟動叫做前置啟動。
前置啟動的特點,當redis 啟動后,linux操作界面將不能輸入執行其他命令!!!
2.5.2 redis啟動
redis啟動分為前置啟動和后置啟動。
2.5.2.1 前置啟動(一般不用)
./redis-server
特點: redis啟動后,將不能輸入其他命令。
退出前置啟動的方式 ctrl+c
2.5.2.2后置啟動
1 到redis的解壓目錄中拷貝 redis.conf 到 redis的安裝目錄中(和redis-server在同一個目錄)
2打開這個文件(vim命令),修改這個文件中 daemonize 的值為yes(默認為no)
3在啟動redis時, 使用 ./redis-server redis.conf(啟動時,指定配置文件)
4測試后置啟動是否成功:多種方式
(1) ps aux|grep redis 查看這個進程是否存在
(2) 使用redis自帶的客戶端工具
10.0.1 redis清空
redis清空
進入redis目錄下
redis-cli
flushall
./redis-cli進入
輸入ping 如果返回一個pong 則表示redis啟動成功
退出redis客戶端的方式: 1 ctrl+c 2 quit 3 exit
2.5.3 停止redis
l 強制結束程序。強制終止redis進程可能會導致redis持久化數據丟失。
l 正確停止redis的方式應該是向redis發送SHUTDOWN命令,方法為(關閉默認的端口)
[root@zrgk redis]# ./bin/redis-cli shutdown
2.6 redis文件介紹 (redis.conf)
1) daemonize yes 修改啟動方式
2) port 6379 redis端口
3) database 16 redis默認開啟16個庫,也就是16個存儲空間來存儲數據
注意:這16個之間是數據隔離的
4) cluster-enabled yes 配置redis集群 ,這個配置默認是被注釋的,也就是默認不開啟集群
5) save 900 1 這個表示redis的持久化方案 (RDB)
6)dbfilename dump.rdb redis持久化時,存放數據的文件
7)appendonly no 是否開啟redis的aof 持久化方案
8) appendfilename “appendonly.aof” aof持久化方案存放的文件
2.7 redis持久化方案
redis的持久化方案:兩種 RDB方案 和 AOF方案
2.7.1 什么是redis的持久化方案
redis 除了可以作為緩存技術,也可以作為非關系型數據庫。
作為緩存技術,數據默認是存放在內存中的,(這樣可以提升存取速度)
但內存不是持久化設備,不能永久保存數據,一旦機器發生問題,
將會造成數據丟失。因此,redis為了解決這個文件,提供了數據的持久化方案(memcahe 沒有這個能力)
2.7.2 RDB方案
默認開啟的一種持久化方案。它會根據時間軸,以及key的數量改變,來完成持久化動作。
save 900 1 該備份策略表示 在 900 秒內,如果有一個或多個key的值發生了變化就觸發redis的持久化機制。
2.7.3 AOF方案
該持久化方案,redis默認情況下沒有開啟。需要手動開啟。
redis.conf 中的 appendonly no 配置成 yes
aof的持久化策略為:redis會記錄當前用戶執行的,且改變數據的命令。
這種持久化的密度會更細。當然也會對redis的性能產生影響。
注意:即使采用aof的持久化策略,一旦內存崩潰,也會至少丟失一秒的數據。
因此,在很多系統中,RDB方案和AOF方案,同時開啟
2.8 redis常用命令
2.8.1redis中的常用類型
String(字符串)
Hash(哈希)
List(鏈表)
Set(集合)
SortSet(有序集合)
2.8.2 常用命令展示
| set 將一個鍵值對以字符串類型存儲 get 通過key獲取value 。只能操作字符串類型 ping 測試redis是否鏈接 如果已鏈接返回 PONG echo 測試redis是否鏈接 如果已鏈接返回 echo命令后給定的值 keys 返回所有的key 可以加*通配 exists 判斷string類型一個key是否存在 如果存在返回1 否則返回0 del 刪除一個key或者多個 expire 設置一個key的過期時間 ttl 查看建的失效時間 select 選擇數據庫(0-15) move 將當前數據庫中的key轉移到其他數據庫中 persist 移除給定key的過期時間 randomkey 隨機返回key空間的一個key type 返回值的類型 quit 退出連接 dbsize 返回當前數據庫中的key的數目 info 獲取服務器的信息和統計 flushdb 刪除當前選擇的數據庫中的key flushall 刪除所有數據庫中的所有key
|
如何使用以上命令?
先進入redis客戶端
./redis-cli,在客戶端中使用以上命令
2.8.3 String(字符串)
key value形式存儲數據
關於key的說明:
1 key 不要太長,不要超過1024個字節。這樣會消耗內存,也會降低查詢效率
2 key 不要太短。這樣會降低可讀性。
3一個項目中,key的命名采用同一的規范。
關於value的說明:
String類型存儲時,value的最大長度為512M
命令:
1、 set (set key value) 存鍵值對到redis中
set name “zhangsan” “” 可加可不加
set password 123
set aa bb
2、 get (get key) 根據鍵取值
get name
3、 incr 讓當前鍵值以1的數量遞增,並返回遞增后的值
set aa 11
incr aa 返回 12
4、 incrby 可以指定參數一次增加的數值,並返回遞增后的值
set aa 11
incrby aa 10 返回 21
5、 decr 讓當前鍵值以1的數量遞減 並返回遞減后的值
6、 decrby 可以指定參數一次遞減的數值,並返回遞減后的值
7、 incrbyfloat 可以遞增一個雙精度浮點數
8、 append 作用是向鍵值的末尾追加value。如果鍵不存在則將該鍵的值設置為value。返回值是追加后字符串的總長度。
set aa hello
append aa world 返回10 表示拼接后的值得長度
get aa 返回helloworld
9、 mget/mset 作用與get/set相似,不過mget/mset可以同時獲得/設置多個鍵的鍵值
mset xxx 111 yyy 222 zzz 333
mget xxx yyy zzz
返回
“111”
“222”
“333”
2.8.4 Hash
redis的哈希是鍵值對的集合。 redis的哈希值是字符串字段和字符串值之間的映射,因此它們被用來表示對象
1、 hset 存儲一個哈希鍵值對的集合
hset user name zhangsan
hset user password 123
user 根key
name、password key
zhangsan 123 value
存儲結構為:
2、hmset 存儲一個或多個哈希是鍵值對的集合
hmset user name password 123 address beijing
存儲結構同上
3、hgetall 獲取一個哈希是鍵值對的集合
hgetall user
返回
“name”
“zhangsan”
“password”
“123”
“address”
“beijing”
注意:在使用hash結構存儲數據時,很多時候使用如下方式存儲數據
hmset user:1 name aaa password 111
hmset user:2 name bbb password 222
hmset user:3 name ccc password 333
這樣的存儲方式的意義在於 這三條數據的根key擁有相同的前綴user
在使用Redis Desktop Manager(基於windows的redis桌面工具),在使用這個工具連接redis時,需要關閉linux的防火牆
查看時,顯示成文件夾結構
4、hexists 判斷哈希表中的字段名是否存在 如果存在返回1 否則返回0
hexists user:1 name
5、hkeys 只返回字段名
hkeys user:1
6、 hvals 只返回字段值
hvals user:1
7、hdel 刪除一個或多個字段
hdel user:1 name
8、hlen 獲得字段長度
hlen user:1 name
2.8.5 List(鏈表) 不常用
1、lpush 向鏈表左側添加
lpush user zhangsan
lpush user lisi
lpush user wangwu
2、rpush 向鏈表右側添加
rpush user zhangsan
rpush user lisi
rpush user wangwu
3、lpop 從左邊彈出一個元素
4、rpop 從右邊彈出一個元素
5、llen 返回鏈表中元素的個數 相當於關系型數據庫中 select count(*)
6、lrange將返回索引從start到stop之間的所有元素。Redis的列表起始索引為0。
lrange 0 10
lrange也支持負索引 lrange nn -2 -1 如 -1表示最右邊第一個元素 -2表示最右邊第二個元素,依次類推。
lrem 刪除列表中前count個值為value的元素,返回值是實際刪除的元素個數。根據count值得不同,老人命令的執行方式會略有不同
1.count > 0 時lrem命令會從列表左邊開始刪除前count個值為value的元素
2.count < 0 時lrem命令會從列表右邊開始刪除前count個值為value的元素
3.count = 0 時lrem命令會刪除所有值為value的元素
lindex 如果要將列表類型當做數組來用,lindex命令是必不可少的。lindex命令用來返回指定索引的元素,索引從0開始
如果是負數表示從右邊開始計算的索引,最右邊元素的索引是-1。
lset 是另一個通過索引操作列表的命令,它會將索引為index的元素賦值為value。
2.8.6 Set(集合) 不常用
redis的集合是字符串的無序集合。在Redis您可以添加,刪除和測試文件是否存在,在成員O(1)的時間復雜度。
示例:
redis 127.0.0.1:6379> sadd tutoriallist redis
(integer) 1
redis 127.0.0.1:6379> sadd tutoriallist mongodb
(integer) 1
redis 127.0.0.1:6379> sadd tutoriallist rabitmq
(integer) 1
redis 127.0.0.1:6379> sadd tutoriallist rabitmq
(integer) 0
redis 127.0.0.1:6379> smembers tutoriallist
1) "rabitmq"
2) "mongodb"
3) "redis"
2.8.7 SortSet(有序集合) 更不常用
Redis的有序集合類似於Redis的集合,字符串不重復的集合。
示例:
redis 127.0.0.1:6379> zadd tutoriallist 0 redis
(integer) 1
redis 127.0.0.1:6379> zadd tutoriallist 0 mongodb
(integer) 1
redis 127.0.0.1:6379> zadd tutoriallist 0 rabitmq
(integer) 1
redis 127.0.0.1:6379> zadd tutoriallist 0 rabitmq
(integer) 0
redis 127.0.0.1:6379> ZRANGEBYSCORE tutoriallist 0 1000
1) "redis"
2) "mongodb"
3) "rabitmq"
2.8.8 keys通用操作
2.8.9 多個數據庫
切換至0數據庫下面
將newkey移動到1號數據庫
2.8 redis集群
2.9.1 為什么要集群
點單故障:主備模式(master slave)
高可用:集群
2.9.2 集群架構圖
2.9.3 投票機制
由於投票機制的存在,redis的主機個數必須是單數。最少需要三台主機。為每個主機安排一個備機的話,至少需要六台機器才能完成集群。
2.9.4 集群環境准備
redis-trib.rb:在redis解壓后的src目錄下有個叫redis-trib.rb文件。該文件可以幫助我們創建redis集群。
2.9.4.1 安裝ruby解釋器
由於redis-trib.rb 是一個ruby語言所寫的一個腳本程序,那么我們的linux中需要安裝ruby的解釋器
在線安裝命令:yum install -y ruby
在沒有網絡的環境下,也可以手動安裝。但版本匹配問題,相當復雜
| 1、下載ruby解釋器的壓縮包(ruby-2.4.0.tar.gz) 2、解壓 (tar -zxvf ruby-2.4.0.tar.gz) 3、進入解壓目錄 (cd ruby-2.4.0) 4、執行如下命令: a. ./configure b. make c. make install d. ruby –version測試安裝是否成功
|
2.9.4.2安裝ruby的包管理器
在線安裝:
yum install -y rubygems
沒有網絡環境時,也可以手動安裝,步驟很復雜
| 1、下載ruby的包管理器 (rubygems-2.6.10.tar) 2、解壓(tar -zxvf rubygems-2.6.10.tar.gz) 3、進入解壓目錄(cd rubygems-2.6.10.tar.gz) 4、執行 ruby setup.rb 5、測試是否安裝成功 gem –version |
2.9.4.3 執行redis3.0.0.gem
創建redis集群時,需要運行的redis-trib.rb文件需要用到一些包。這些包在redis3.0.0.gem在這個文件中。該文件並沒有存放在解壓的redis的目錄下。需要我們額外下載。
下載完成后,執行 gem install redis3.0.0.gem
2.9.5 集群搭建
實際工作搭建集群,需要多台物理設備,在每台物理設備上安裝一個redis進行集群的搭建
在這里搭建的方式叫做偽集群(因為無法提供多台設備,在一個虛擬機中安裝多個redis實例)
但偽集群的搭建方式和物理集群的搭建方式幾乎相同。學會了偽集群的搭建,就可以完成真正集群的搭建。
1、 創建6個redis實例
由於redis在創建集群時,需要考慮到單點故障以及高可用。所以需要采用主備模式。同時redis在管理集群判斷節點是否健康采用的是投票策略,所以主節點的數量一定是單數的。
如:創建redis的最小的集群,應該是三個實例,在加上主備模式,那么一共應該是6個redis實例。
2、我們可以將已經安裝好的redis拷貝6份。注意:在拷貝創建集群節點時。在該實例中一定更要將備份文件刪除掉(dump.rdb)。否則創建集群時會出問題。
3、 開啟六個實例中的集群 在redis.conf中配置cluster-enabled yes
4、 當前我們是在一台虛擬機中同時運行六個redis實例。所以我們需要將端口做一個改變。可以改變成任何端口。只要沒有被占用就可以。
5、 redis在創建集群時,要求只能對已啟動的redis創建集群。所以我們需要將六個redis啟動
6、 每次分別啟動6個實例比較麻煩,我們可以創建一個startall.sh(類似於windows中的.bat文件)
vim startall.sh
cd redis01
./redis-server redis.conf
cd ../redis02
./redis-server redis.conf
cd ../redis03
./redis-server redis.conf
…
7、.sh文件在創建完成后,並不是可執行的。需要做如下配置
文件的執行必須要分配一個可執行權限
命令為:chmod +x startall.sh
+x:表示為分配一個可執行的權限。
8、把redis-trib.rb拷貝到集群目錄下,使用這個文件來創建集群
./redis-trib.rb create --replicas 1 192.168.10.155:8001 192.168.10.155:8002 192.168.10.155:8003 192.168.10.155:8004 192.168.10.155:8005 192.168.10.155:8006
上面的紅色的1表示創建集群時,主備模式的策略,1表示1主1備
Can I set the above configuration? (type 'yes' to accept): 到這一步輸入yes,開始創建集群
9、redis集群在做數據存儲時采用的hash算法來進行值的存儲
redis集群會准備16384個槽。他會將這些槽平分到主節點上。
當有數據要進行存儲時,redis會使用CRC16算法,先將key做16進制的轉換,然后使用該值與16384取模,該值就決定了當前要將這個key與value存儲到哪個節點中。
10、如何連接到集群中的節點?
redis允許我們使用任何一個節點下的客戶端工具連接到集群中的任何一個節點下。
./redis-cli -h 192.168.10.155 -p 8001 -c
注意:在連接集群時一定要給定一個參數 -c
該參數表示告知客戶端當前我連接的是集群中的一個節點。
11、查看集群中的節點信息
cluster nodes
12、如何關閉集群
redis運行使用任何一個實例下的客戶端工具,來關閉集群中的任何一個節點。
我們可以編寫一個一次性關閉集群中所有節點中的腳本
shutdownall.sh
redis01/redis-cli -h IP -p Port shutdown
3 java語言操作redis
Java語言操作redis 要使用 jedis.jar這個jar包(這里使用jedis2.7.2.jar版本)
由於后面涉及到池的操作,所以還需要導入 commons-pool2.jar
3.1連接redis單機版(直連方式)
| @Test public void test1(){ // 連單機版(直連) Jedis jedis = new Jedis("192.168.50.131",6379); jedis.set("bbb", "222"); String value = jedis.get("bbb"); System.out.println(value); jedis.close(); } |
3.2連接redis單機版(池連方式)
| @Test public void test2(){ // 單機(池連)
JedisPool pool = new JedisPool("192.168.50.131", 6379); Jedis j = pool.getResource();
String str = j.get("ccc");
System.out.println(str);
j.close();
}
|
3.3連接redis集群版
| @Test public void test3(){ //連集群
Set<HostAndPort> set = new HashSet<HostAndPort>();
set.add(new HostAndPort("192.168.50.131",8001)); set.add(new HostAndPort("192.168.50.131",8002)); set.add(new HostAndPort("192.168.50.131",8003)); set.add(new HostAndPort("192.168.50.131",8004)); set.add(new HostAndPort("192.168.50.131",8005)); set.add(new HostAndPort("192.168.50.131",8006)); JedisCluster cluster = new JedisCluster(set); cluster.set("name","zhangsan"); String value = cluster.get("name"); System.out.println(value+"<<<");
cluster.hset("user","name","aaa");
String value2 = cluster.hget("user","name");
System.out.println(value2+"..."); // 一定不要關集群 } |
3.4和spring整合 單機版池連方式
| <bean id="jedisPool" class="redis.clients.jedis.JedisPool"> <constructor-arg name="host"> <value>192.168.50.131</value> </constructor-arg> <constructor-arg name="port"> <value>6379</value> </constructor-arg> </bean>
@Test public void test4(){ // spring整合 JedisPool
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); JedisPool pool = (JedisPool)ac.getBean("jedisPool"); Jedis j = pool.getResource();
String value = j.get("aaa"); System.out.println(value+"###"); j.close(); } |
3.5和spring整合 單機版池連方式(使用自定義的池策略)
| <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> <!-- jedis客戶端單機版 --> <bean id="redisClient" class="redis.clients.jedis.JedisPool"> <constructor-arg name="host" value="192.168.50.131"></constructor-arg> <constructor-arg name="port" value="6379"></constructor-arg> <constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg> </bean>
|
3.6和spring整合 集群版
| <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster"> <constructor-arg name="nodes"> <set> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host"> <value>192.168.50.131</value> </constructor-arg> <constructor-arg name="port"> <value>8001</value> </constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host"> <value>192.168.50.131</value> </constructor-arg> <constructor-arg name="port"> <value>8002</value> </constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host"> <value>192.168.50.131</value> </constructor-arg> <constructor-arg name="port"> <value>8003</value> </constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host"> <value>192.168.50.131</value> </constructor-arg> <constructor-arg name="port"> <value>8004</value> </constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host"> <value>192.168.50.131</value> </constructor-arg> <constructor-arg name="port"> <value>8005</value> </constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host"> <value>192.168.50.131</value> </constructor-arg> <constructor-arg name="port"> <value>8006</value> </constructor-arg> </bean> </set> </constructor-arg> </bean> @Test public void test6(){ // Spring整合 JedisCluster ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
JedisCluster cluster = (JedisCluster)ac.getBean("jedisCluster");
String name=cluster.hget("user","name");
System.out.println(name+"%%%"); }
|
4 redis在web項目中的應用
redis的操作一般放在業務層,由於在各個業務層操作redis的代碼都相似,所以,一般會提取出一個類。這里叫做 JedisDao(接口) JedisSingeDaoImpl(對單機操作的實現類)或
JedisClusterDaoImpl(對集群操作的實現類)
在這個類中封裝對Jedis操作的各種方法
| public String set(String key,String value); public String get(String key); public long hset(String hkey,String key,String value); public String hget(String hkey,String key); public long incr(String key); public long expire(String key,int second); public long ttl(String key); public long del(String key); public long hdel(String hkey,String key);
|
在service的方法中這樣使用:
以查詢全部為例:
| @Override public String getAllUser() { try{ //從redis中取值 String json = this.jedisDao.hget("user","all"); if(json!=null && !"".equals(json)){ return json; } }catch(Exception e){ e.printStackTrace(); } List<User> list = dao.findAllUser(); String str = JsonUtils.objectToJson(list); try{ // 存入redis中 this.jedisDao.hset("user","all",str);
}catch(Exception e){ e.printStackTrace(); } return str; } |
在UserDao的正常查詢方法之前和之后,添加操作redis的代碼
從redis中查詢數據,如果查到,則不會走UserDao中查詢數據的方法
如果沒有查到,執行UserDao中的查詢方法,查到后,把數據存入redis中
注意:這里操作redis的代碼都放在了try{} catch(){} 中。因為當redis出問題時,不能中斷正常的流程!!!
當對數據做了增刪改操作時,要同步redis中的數據,這里不需要去修改redis中的數據,只需要將數據從redis中刪除即可!!!
| @Override public void modifyUser(User user) {
jedisDao.hdel("user","all"); // 同步數 據,這里沒有必要去修改redis中的數據,直接將redis中數據刪除 dao.updateUser(user);
} |
這里用到了一個json轉換的工具類,使用的事jackson(很多框架中對json的處理都使用這個)。項目中也做了相應的代碼!!!
| package util;
import java.util.List;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonUtils {
// 定義jackson對象 private static final ObjectMapper MAPPER = new ObjectMapper();
/** * 將對象轉換成json字符串。 * <p>Title: pojoToJson</p> * <p>Description: </p> * @param data * @return */ public static String objectToJson(Object data) { try { String string = MAPPER.writeValueAsString(data); return string; } catch (JsonProcessingException e) { e.printStackTrace(); } return null; }
/** * 將json結果集轉化為對象 * * @param jsonData json數據 * @param clazz 對象中的object類型 * @return{key:value,key:value} */ public static <T> T jsonToPojo(String jsonData, Class<T> beanType) { try { T t = MAPPER.readValue(jsonData, beanType); return t; } catch (Exception e) { e.printStackTrace(); } return null; }
/** * 將json數據轉換成pojo對象list * <p>Title: jsonToList</p> * <p>Description: </p> * @param jsonData * @param beanType * @return[{},{}] */ public static <T>List<T> jsonToList(String jsonData, Class<T> beanType) { JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType); try { List<T> list = MAPPER.readValue(jsonData, javaType); return list; } catch (Exception e) { e.printStackTrace(); }
return null; }
} |
測試工具類
| package util;
import java.util.ArrayList; import java.util.Date; import java.util.List;
import domain.User;
public class TestJackson {
public static void main(String[] args) {
/* User user = new User(1,"zhangsan",20,new Date());
String str = JsonUtils.objectToJson(user);
System.out.println(str);*/
/* String jsonStr = "{\"id\":1,\"name\":\"zhangsan\",\"age\":20,\"birthday\":1489930990995}";
User user = (User)JsonUtils.jsonToPojo(jsonStr, User.class);
System.out.println(user.getId()+"\t"+user.getName()+"\t"+user.getAge()+"\t"+user.getBirthday());
*/
/*User user2 = new User(2,"lisi",22,new Date()); User user1 = new User(1,"zhangsan",21,new Date()); User user3 = new User(3,"wangwu",23,new Date());
List<User> list = new ArrayList<User>(); list.add(user1); list.add(user2); list.add(user3);
String str = JsonUtils.objectToJson(list);
System.out.println(str);*/
String jsonStr = "[{\"id\":1,\"name\":\"zhangsan\",\"age\":21,\"birthday\":1489931282127},{\"id\":2,\"name\":\"lisi\",\"age\":22,\"birthday\":1489931282127},{\"id\":3,\"name\":\"wangwu\",\"age\":23,\"birthday\":1489931282127}]";
List<User> list = JsonUtils.jsonToList(jsonStr, User.class);
for(User user:list){ System.out.println(user.getId()+"\t"+user.getName()+"\t"+user.getAge()+"\t"+user.getBirthday()); }
}
} |
