帶你逐步深入瞭解SSM框架——淘淘商城專案之redis快取
1. 課程計劃
1、 Redis服務搭建
2、 為功能新增快取功能
2. redis介紹
2.1. 什麼是redis
Redis是用C語言開發的一個開源的高效能鍵值對(key-value)資料庫。它通過提供多種鍵值資料型別來適應不同場景下的儲存需求,目前為止Redis支援的鍵值資料型別如
下:
字串型別
雜湊型別
列表型別
集合型別
有序集合型別。
2.2. redis的應用場景
快取(資料查詢、短連線、新聞內容、商品內容等等)。(最多使用)
分散式叢集架構中的session分離。
聊天室的線上好友列表。
任務佇列。(秒殺、搶購、12306等等)
應用排行榜。
網站訪問統計。
資料過期處理(可以精確到毫秒)
2.3. Redis的安裝
redis是C語言開發,建議在linux上執行,本教程使用Centos6.4作為安裝環境。
安裝redis需要先將官網下載的原始碼進行編譯,編譯依賴gcc環境,如果沒有gcc環境,需要安裝gcc:yum install gcc-c++
n 版本說明
本教程使用redis3.0版本。3.0版本主要增加了redis叢集功能。
n 原始碼下載
從官網下載
http://download.redis.io/releases/redis-3.0.0.tar.gz
將redis-3.0.0.tar.gz拷貝到/usr/local下
n 解壓原始碼
tar -zxvf redis-3.0.0.tar.gz
n 進入解壓後的目錄進行編譯
cd /usr/local/redis-3.0.0
make
n 安裝到指定目錄,如 /usr/local/redis
cd /usr/local/redis-3.0.0
make PREFIX=/usr/local/redis install
n redis.conf
redis.conf是redis的配置檔案,redis.conf在redis原始碼目錄。
注意修改port作為redis程序的埠,port預設6379。
n 拷貝配置檔案到安裝目錄下
進入原始碼目錄,裡面有一份配置檔案 redis.conf,然後將其拷貝到安裝路徑下
cd /usr/local/redis
mkdir conf
cp /usr/local/redis-3.0.0/redis.conf /usr/local/redis/bin
n 安裝目錄bin下的檔案列表
redis3.0新增的redis-sentinel是redis叢集管理工具可實現高可用。
配置檔案目錄:
2.4.redis啟動
2.4.1. 前端模式啟動
直接執行bin/redis-server將以前端模式啟動,前端模式啟動的缺點是ssh命令視窗關閉則redis-server程式結束,不推薦使用此方法。如下圖:
2.4.2. 後端模式啟動
修改redis.conf配置檔案,daemonize yes 以後端模式啟動。
執行如下命令啟動redis:
cd /usr/local/redis
./bin/redis-server ./redis.conf
redis預設使用6379埠。
也可更改redis.conf檔案,修改埠號:
2.5. 通過jedis連線redis單機
2.5.1. jar包
pom座標:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.7.0</version>
</dependency>
jar包如下:
2.5.2. 單例項連線
通過建立單例項jedis物件連線redis服務,如下程式碼:
//單例項連線redis
@Test
publicvoid testJedisSingle() {
Jedisjedis =new Jedis("192.168.101.3", 6379);
jedis.set("name","bar");
Stringname = jedis.get("name");
System.out.println(name);
jedis.close();
}
1. 外部連線不上redis的解決方法
由於linux防火牆預設開啟,redis的服務埠6379並不在開放規則之內,所有需要將此埠開放訪問或者關閉防火牆。
關閉防火牆命令:sevice iptablesstop
如果是修改防火牆規則,可以修改:/etc/sysconfig/iptables檔案
2.5.3. 使用連線池連線
通過單例項連線redis不能對redis連線進行共享,可以使用連線池對redis連線進行共享,提高資源利用率,使用jedisPool連線redis服務,如下程式碼:
@Test
publicvoid pool() {
JedisPoolConfigconfig =new JedisPoolConfig();
//最大連線數
config.setMaxTotal(30);
//最大連線空閒數
config.setMaxIdle(2);
JedisPoolpool =new JedisPool(config,"192.168.101.3", 6379);
Jedisjedis =null;
try {
jedis = pool.getResource();
jedis.set("name","lisi");
Stringname = jedis.get("name");
System.out.println(name);
}catch(Exception ex){
ex.printStackTrace();
}finally{
if(jedis !=null){
//關閉連線
jedis.close();
}
}
}
詳細的連線池配置引數參考下節jedis和spring整合中applicationContext.xml的配置內容。
2.5.4. jedis與spring整合
配置spring配置檔案applicationContext.xml
<?xml version="1.0"encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<!--連線池配置 -->
<beanid="jedisPoolConfig"class="redis.clients.jedis.JedisPoolConfig">
<!--最大連線數 -->
<propertyname="maxTotal"value="30"/>
<!--最大空閒連線數 -->
<propertyname="maxIdle"value="10"/>
<!--每次釋放連線的最大數目 -->
<propertyname="numTestsPerEvictionRun"value="1024"/>
<!--釋放連線的掃描間隔(毫秒) -->
<propertyname="timeBetweenEvictionRunsMillis"value="30000"/>
<!--連線最小空閒時間 -->
<propertyname="minEvictableIdleTimeMillis"value="1800000"/>
<!--連線空閒多久後釋放,當空閒時間>該值且空閒連線>最大空閒連線數時直接釋放 -->
<propertyname="softMinEvictableIdleTimeMillis"value="10000"/>
<!--獲取連線時的最大等待毫秒數,小於零:阻塞不確定的時間,預設-1 -->
<propertyname="maxWaitMillis"value="1500"/>
<!--在獲取連線的時候檢查有效性,預設false -->
<propertyname="testOnBorrow"value="true"/>
<!--在空閒時檢查有效性,預設false -->
<propertyname="testWhileIdle"value="true"/>
<!--連線耗盡時是否阻塞, false報異常,ture阻塞直到超時,預設true -->
<propertyname="blockWhenExhausted"value="false"/>
</bean>
<!--redis單機通過連線池 -->
<beanid="jedisPool"class="redis.clients.jedis.JedisPool"destroy-method="close">
<constructor-argname="poolConfig" ref="jedisPoolConfig"/>
<constructor-argname="host" value="192.168.25.145"/>
<constructor-argname="port" value="6379"/>
</bean>
測試程式碼:
private ApplicationContextapplicationContext;
@Before
publicvoid init() {
applicationContext =new ClassPathXmlApplicationContext(
"classpath:applicationContext.xml");
}
@Test
publicvoid testJedisPool() {
JedisPoolpool = (JedisPool)applicationContext.getBean("jedisPool");
try {
jedis= pool.getResource();
jedis.set("name","lisi");
Stringname = jedis.get("name");
System.out.println(name);
}catch(Exception ex){
ex.printStackTrace();
}finally{
if(jedis !=null){
//關閉連線
jedis.close();
}
}
}
3. redis叢集
3.1. 叢集原理
3.1.1. redis-cluster架構圖
架構細節:
(1)所有的redis節點彼此互聯(PING-PONG機制),內部使用二進位制協議優化傳輸速度和頻寬.
(2)節點的fail是通過叢集中超過半數的節點檢測失效時才生效.
(3)客戶端與redis節點直連,不需要中間proxy層.客戶端不需要連線叢集所有節點,連線叢集中任何一個可用節點即可
(4)redis-cluster把所有的物理節點對映到[0-16383]slot上,cluster負責維護node<->slot<->value
Redis叢集中內建了 16384個雜湊槽,當需要在 Redis叢集中放置一個 key-value時,redis先對 key使用 crc16演算法算出一個結果,然後把結果對 16384求餘數,這樣每個 key都會對應一個編號在 0-16383之間的雜湊槽,redis會根據節點數量大致均等的將雜湊槽對映到不同的節點
3.1.2. redis-cluster投票:容錯
(1)領著投票過程是叢集中所有master參與,如果半數以上master節點與master節點通訊超過(cluster-node-timeout),認為當前master節點掛掉.
(2):什麼時候整個叢集不可用(cluster_state:fail)?
a:如果叢集任意master掛掉,且當前master沒有slave.叢集進入fail狀態,也可以理解成叢集的slot對映[0-16383]不完成時進入fail狀態. ps : redis-3.0.0.rc1加入cluster-require-full-coverage引數,預設關閉,開啟叢集相容部分失敗.
b:如果叢集超過半數以上master掛掉,無論是否有slave叢集進入fail狀態.
ps:當叢集不可用時,所有對叢集的操作做都不可用,收到((error) CLUSTERDOWN The cluster is down)錯誤
3.2.ruby環境
redis叢集管理工具redis-trib.rb依賴ruby環境,首先需要安裝ruby環境:
安裝ruby
yum install ruby
yum install rubygems
安裝ruby和redis的介面程式
拷貝redis-3.0.0.gem至/usr/local下
執行:
gem install /usr/local/redis-3.0.0.gem
3.3. 建立叢集:
3.3.1. 叢集結點規劃
這裡在同一臺伺服器用不同的埠表示不同的redis伺服器,如下:
主節點:192.168.101.3:7001 192.168.101.3:7002 192.168.101.3:7003
從節點:192.168.101.3:7004 192.168.101.3:7005 192.168.101.3:7006
在/usr/local下建立redis-cluster目錄,其下建立7001、7002。。7006目錄,如下:
將redis安裝目錄bin下的檔案拷貝到每個700X目錄內,同時將redis原始碼目錄src下的redis-trib.rb拷貝到redis-cluster目錄下。
修改每個700X目錄下的redis.conf配置檔案:
port XXXX
#bind 192.168.101.3
cluster-enabled yes
3.3.2. 啟動每個結點redis服務
分別進入7001、7002、...7006目錄,執行:
./redis-server ./redis.conf
檢視redis程序:
3.3.3. 執行建立叢集命令
執行redis-trib.rb,此指令碼是ruby指令碼,它依賴ruby環境。
./redis-trib.rb create --replicas 1 192.168.101.3:7001 192.168.101.3:7002192.168.101.3:7003 192.168.101.3:7004 192.168.101.3:7005 192.168.101.3:7006
./redis-trib.rb create --replicas 1 192.168.131.102:7001 192.168.131.102:7002 192.168.131.102:7003 192.168.131.102:7004 192.168.131.102:7005 192.168.131.102:7006 |
說明:
redis叢集至少需要3個主節點,每個主節點有一個從節點總共6個節點
replicas指定為1表示每個主節點有一個從節點
注意:
如果執行時報如下錯誤:
[ERR]Node XXXXXX is not empty. Either the node already knows other nodes (check withCLUSTER NODES) or contains some key in database 0
解決方法是刪除生成的配置檔案nodes.conf,如果不行則說明現在建立的結點包括了舊叢集的結點資訊,需要刪除redis的持久化檔案後再重啟redis,比如:appendonly.aof、dump.rdb
建立叢集輸出如下:
>>> Creating cluster
Connecting to node 192.168.101.3:7001: OK
Connecting to node 192.168.101.3:7002: OK
Connecting to node 192.168.101.3:7003: OK
Connecting to node 192.168.101.3:7004: OK
Connecting to node 192.168.101.3:7005: OK
Connecting to node 192.168.101.3:7006: OK
>>> Performing hash slotsallocation on 6 nodes...
Using 3 masters:
192.168.101.3:7001
192.168.101.3:7002
192.168.101.3:7003
Adding replica 192.168.101.3:7004 to192.168.101.3:7001
Adding replica 192.168.101.3:7005 to192.168.101.3:7002
Adding replica 192.168.101.3:7006 to192.168.101.3:7003
M: cad9f7413ec6842c971dbcc2c48b4ca959eb5db4192.168.101.3:7001
slots:0-5460 (5461 slots) master
M: 4e7c2b02f0c4f4cfe306d6ad13e0cfee90bf5841192.168.101.3:7002
slots:5461-10922 (5462 slots) master
M: 1a8420896c3ff60b70c716e8480de8e50749ee65192.168.101.3:7003
slots:10923-16383 (5461 slots) master
S: 69d94b4963fd94f315fba2b9f12fae1278184fe8192.168.101.3:7004
replicates cad9f7413ec6842c971dbcc2c48b4ca959eb5db4
S: d2421a820cc23e17a01b597866fd0f750b698ac5192.168.101.3:7005
replicates 4e7c2b02f0c4f4cfe306d6ad13e0cfee90bf5841
S: 444e7bedbdfa40714ee55cd3086b8f0d5511fe54192.168.101.3:7006
replicates 1a8420896c3ff60b70c716e8480de8e50749ee65
Can I set the above configuration? (type'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different configepoch to each node
>>> Sending CLUSTER MEET messagesto join the cluster
Waiting for the cluster to join...
>>> Performing Cluster Check(using node 192.168.101.3:7001)
M: cad9f7413ec6842c971dbcc2c48b4ca959eb5db4192.168.101.3:7001
slots:0-5460 (5461 slots) master
M: 4e7c2b02f0c4f4cfe306d6ad13e0cfee90bf5841192.168.101.3:7002
slots:5461-10922 (5462 slots) master
M: 1a8420896c3ff60b70c716e8480de8e50749ee65192.168.101.3:7003
slots:10923-16383 (5461 slots) master
M: 69d94b4963fd94f315fba2b9f12fae1278184fe8192.168.101.3:7004
slots: (0 slots) master
replicates cad9f7413ec6842c971dbcc2c48b4ca959eb5db4
M: d2421a820cc23e17a01b597866fd0f750b698ac5192.168.101.3:7005
slots: (0 slots) master
replicates 4e7c2b02f0c4f4cfe306d6ad13e0cfee90bf5841
M: 444e7bedbdfa40714ee55cd3086b8f0d5511fe54192.168.101.3:7006
slots: (0 slots) master
replicates 1a8420896c3ff60b70c716e8480de8e50749ee65
[OK] All nodes agree about slotsconfiguration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
3.4. 查詢叢集資訊
叢集建立成功登陸任意redis結點查詢叢集中的節點情況。
客戶端以叢集方式登陸:
說明:
./redis-cli -c -h192.168.101.3 -p 7001,其中-c表示以叢集方式連線redis,-h指定ip地址,-p指定埠號
cluster nodes 查詢叢集結點資訊
cluster info 查詢叢集狀態資訊
3.5. 新增主節點
叢集建立成功後可以向叢集中新增節點,下面是新增一個master主節點
新增7007結點,參考叢集結點規劃章節新增一個“7007”目錄作為新節點。
執行下邊命令:
./redis-trib.rb add-node 192.168.101.3:7007 192.168.101.3:7001
檢視叢集結點發現7007已新增到叢集中:
3.5.1. hash槽重新分配
新增完主節點需要對主節點進行hash槽分配這樣該主節才可以儲存資料。
redis叢集有16384個槽,叢集中的每個結點分配自已槽,通過檢視叢集結點可以看到槽佔用情況。
給剛新增的7007結點分配槽:
第一步:連線上叢集
./redis-trib.rb reshard 192.168.101.3:7001(連線叢集中任意一個可用結點都行)
第二步:輸入要分配的槽數量
輸入 500表示要分配500個槽
第三步:輸入接收槽的結點id
這裡準備給7007分配槽,通過cluster nodes檢視7007結點id為15b809eadae88955e36bcdbb8144f61bbbaf38fb
輸入:15b809eadae88955e36bcdbb8144f61bbbaf38fb
第四步:輸入源結點id
這裡輸入all
第五步:輸入yes開始移動槽到目標結點id
3.6. 新增從節點
叢集建立成功後可以向叢集中新增節點,下面是新增一個slave從節點。
新增7008從結點,將7008作為7007的從結點。
./redis-trib.rbadd-node --slave --master-id主節點id新增節點的ip和埠叢集中已存在節點ip和埠
執行如下命令:
./redis-trib.rb add-node --slave--master-id cad9f7413ec6842c971dbcc2c48b4ca959eb5db4 192.168.101.3:7008 192.168.101.3:7001
cad9f7413ec6842c971dbcc2c48b4ca959eb5db4 是7007結點的id,可通過cluster nodes檢視。
注意:如果原來該結點在叢集中的配置資訊已經生成cluster-config-file指定的配置檔案中(如果cluster-config-file沒有指定則預設為nodes.conf),這時可能會報錯:
[ERR]Node XXXXXX is not empty. Either the node already knows other nodes (check withCLUSTER NODES) or contains some key in database 0
解決方法是刪除生成的配置檔案nodes.conf,刪除後再執行./redis-trib.rb add-node指令
檢視叢集中的結點,剛新增的7008為7007的從節點:
3.7. 刪除結點:
./redis-trib.rb del-node 127.0.0.1:70054b45eb75c8b428fbd77ab979b85080146a9bc017
刪除已經佔有hash槽的結點會失敗,報錯如下:
[ERR] Node 127.0.0.1:7005 is not empty!Reshard data away and try again.
需要將該結點佔用的hash槽分配出去(參考hash槽重新分配章節)。
3.8. jedisCluster
3.8.1. 測試程式碼
//連線redis叢集
@Test
publicvoid testJedisCluster() {
JedisPoolConfigconfig =new JedisPoolConfig();
//最大連線數
config.setMaxTotal(30);
//最大連線空閒數
config.setMaxIdle(2);
//叢集結點
Set<HostAndPort>jedisClusterNode =new HashSet<HostAndPort>();
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7001));
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7002));
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7003));
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7004));
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7005));
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7006));
JedisClusterjc =new JedisCluster(jedisClusterNode, config);
JedisClusterjcd =new JedisCluster(jedisClusterNode);
jcd.set("name","zhangsan");
Stringvalue = jcd.get("name");
System.out.println(value);
}
3.8.2. 使用spring
配置applicationContext.xml
<!--連線池配置 -->
<beanid="jedisPoolConfig"class="redis.clients.jedis.JedisPoolConfig">
<!--最大連線數 -->
<property