Redis詳細筆記

簡介:

Redis是基於內存可持續化的分佈式非關係型數據庫,結構化數據: 將一批數據集合抽取共有特點形成結構–學生信息,人體信息.mysql中每一張表格都是結構化數據,redis不僅支持像mysql結構化查詢,也支持非結構化數據:無法公用一批共有特點的數據集合–如日誌數據.網頁

redis是採用key-value存儲數據::redis支持的基本結構,對key值是字符串,value有五種不同的內部數據結構:string,hash,list,set,zset;

可持久化: redis是主要將數據存儲在內存中(特點:快,可靠性不高),爲了防止內存數據宕機丟失,創建的持久化功能,可以根據需求,將內存數據保存在磁盤中,一旦故障,可以實現數據的恢復功能.

支持分佈式:redis服務端是單例單線程的,利用線程非阻塞原理實現高吞吐量(1秒鐘萬條數據的讀寫操作).單個的redis節點(啓動的一個server進程)不足以實現某個服務器的cpu性能,redis都是以集羣節點的結構存在的(分佈式的,高可用的結構)

雪崩/緩存擊穿:當某個一個功能,運轉流程中包含了緩存數據庫的技術(有大量的緩存數據),一旦緩存數據丟失,海量請求湧入訪問,造成緩存數據未命中,海量請求湧入數據庫,造成數據庫壓力過大–宕機–重啓–海量請求未消失–宕機;

redis應用場景::
• 系統的緩存
• 內存的數據鎖結構解決各種複雜的邏輯判斷
• 隊列(list)
• 標籤(某個用戶訪問網站收集的興趣愛好,社交網站共同好友) • 排行系統(熱搜的排行,點擊量排行)

數據操作

Redis基礎命令+五種類型的命令
操作數據:redis具備五種數據類型,每種數據類型都是針對key-value結構中的value而言,對於系統生成value各種數據的不同實現場景做對應
String
Hash
List
Set
Zset

String類型(包含基礎命令)

在這裏插入圖片描述
**keys *** :查看當前redis節點(啓動的每一個redis服務都叫redis節點。)所有已存在的key值。

127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set name hanlaoshi
OK
127.0.0.1:6379> set location beijing
OK
127.0.0.1:6379> keys *
1) "location"
2) "name"

**set key value:**存儲一個key-value結構的數據
get key:獲取當前key對應的value數據

127.0.0.1:6379> get age
"22"
127.0.0.1:6379> set age 33
OK
127.0.0.1:6379> get age
"33"
127.0.0.1:6379>

select 整數:redis中默認存在16個數據分庫(database),index號0-15,在一個服務器節點上,可以區分多種類型,多種功能的數據庫倉庫,默認登錄的是0號分庫(現在使用不多)

127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
(empty list or set)
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> keys *
1) "location"
2) "age"
3) "name"
127.0.0.1:6379>

exists key:判斷當前節點是否包含key的數據。get也可以完成這個任務。redis在key-value結構的value存儲容量是512M。使用get判斷存在會先讀在判斷數據是否爲空,浪費資源

127.0.0.1:6379> exists haha kaka age
(integer) 1
127.0.0.1:6379> exists age
(integer) 1
127.0.0.1:6379> exists haha
(integer) 0
127.0.0.1:6379>

del key:刪除key值對應的key-value數據;

127.0.0.1:6379> del age
(integer) 1
127.0.0.1:6379> del haha
(integer) 0
127.0.0.1:6379> keys *
1) "location"
2) "name"
127.0.0.1:6379>

type key:查看當前key的類型

127.0.0.1:6379> type name
string

save:將當前的內存數據保存到磁盤文件(根目錄dump.rdb)

127.0.0.1:6379> keys *
1) "location"
2) "name"
127.0.0.1:6379> save
OK

save前,save後觀察redis根目錄的dump.rdb大小可以發現已經存儲到持久化文件

flushall:將當前redis所有數據清空,包括持久化文件,內存數據(創建集羣的時候使用)在這裏插入代碼片

127.0.0.1:6379> keys *
1) "location"
2) "name"
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> set name wanglaoshi
OK
127.0.0.1:6379[1]> set age 11
OK
127.0.0.1:6379[1]> keys *
1) "age"
 分區 day06 的第 6 頁 
1) "age"
2) "name"
127.0.0.1:6379[1]> save
OK
127.0.0.1:6379[1]> flushall
OK
127.0.0.1:6379[1]> keys *
(empty list or set)
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379>

flushdb:清空一個分庫的數據,不刪除持久化文件內容(進行測試使用)

127.0.0.1:6379[1]> flushdb
OK
127.0.0.1:6379[1]> keys *
(empty list or set)
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> keys (
(empty list or set)
127.0.0.1:6379> keys *
1) "name"

redis中string字符串如果是純數字,也有一些簡單的數字操作命令
incr decr key:自增1,自減1;計步器

127.0.0.1:6379> set num 100
OK
127.0.0.1:6379> incr num
(integer) 101
127.0.0.1:6379> decr num
(integer) 100

多步計步器:incrby decrby key 整數:對key的value數字自增自減去整數步數

127.0.0.1:6379> incrby num 10
(integer) 110
127.0.0.1:6379> decrby num 10
(integer) 100

mset mget:單節點批量操作(不支持多節點分佈式,只能對本節點數據進行處理)(測試時確定當前節點擁有的數據)

127.0.0.1:6379> mset num1 200 num2 300 
num3 400
OK
127.0.0.1:6379> keys *
1) "num3"
2) "name"
3) "num"
4) "100"
5) "num1"
6) "num2"
127.0.0.1:6379> mget num1 num2 num3
1) "200"
2) "300"
3) "400"

redis作爲內存緩存數據庫雖然速度快,還是有缺點的,內存容量,不可能比硬盤容量大的.redis最常見的保護內存的機制(熱點數據保存,冷點數據超時清除

expire key:對key添加超時過期的設定配合ttl key可以查看 key值剩餘時間,已經超時的內容刪除,ttl剩餘時間是-2 -1表示永久數據

127.0.0.1:6379> ttl num1
(integer) -2
127.0.0.1:6379> keys *
1) "num3"
2) "name"
3) "num"
4) "100"
5) "num2"
127.0.0.1:6379> ttl name
(integer) -1

append key value :對key對應的value進行追加數據操作

Hash數據類型

對應value爲hash結構的數據都是面向對象的數據結構
在這裏插入圖片描述
hset key field value:key 是 相當於變量名,filed是value中的key-value對中的key,但是面向對象時表示一個對象的屬性名稱, value表示屬性的值

127.0.0.1:6379> hset user username hanlaoshi
(integer) 1
127.0.0.1:6379> hset user age 18
(integer) 1
127.0.0.1:6379> hset user location beijing
(integer) 1
127.0.0.1:6379> type user
hash

hget key field
127.0.0.1:6379> hget user username
"hanlaoshi"

hexists key field:判斷hash類型中的屬性值是否存在

127.0.0.1:6379> hexists user haha
(integer) 0
127.0.0.1:6379> hexists user age
(integer) 1
(integer) 1

hmset和hmget:批量設置,不支持分佈式

127.0.0.1:6379> hmset student age 19 name 
wangxiaoxiao gender male
OK
127.0.0.1:6379> hmget student age name gender
1) "19"
2) "wangxiaoxiao"
3) "male

Hdel key field:刪除屬性和值

127.0.0.1:6379> hdel student age
(integer) 1
127.0.0.1:6379> hget student age
(nil)
``
**Hkeys Hvals**:單獨獲取對象的屬性名,或者屬性的值`
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/2020040311034097.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0ODAwOTg2,size_16,color_FFFFFF,t_70)

```bash
127.0.0.1:6379> hkeys user
1) "name"
2) "username"
3) "age"
4) "location"
127.0.0.1:6379> hvals user
1) "haha"
2) "hanlaoshi"
3) "18"
4) "beijing"

Hlen key:獲取屬性個數(長度)

127.0.0.1:6379> hlen user
(integer) 4
127.0.0.1:6379> hlen student
(integer) 2

List鏈表數據結構

在這裏插入圖片描述
查看鏈表list和設置鏈表數據(左表示上,右表示下)
lpush key value [vlaues]:對於一個鏈表,從左(上)開始插入數據;

lrange key 起始下標 結束下標(展示範圍,結束下
標是-1表示到尾部)

127.0.0.1:6379> lpush mylist01 100 200 300
(integer) 3
127.0.0.1:6379> lrange mylist 0 1
(empty list or set)
127.0.0.1:6379> lrange mylist01 0 1
1) "300"
2) "200

Rpush key value:從下向上插入數據

127.0.0.1:6379> rpush mylist01 400
(integer) 4
127.0.0.1:6379> lrange mylist01 0 -1
1) "300"
2) "200"
3) "100"
4) "400

Linsert:從鏈表中間操作數據,從左側找到第一個
相同元素插入數據(before after)

127.0.0.1:6379> linsert mylist01 before 100 
one
(integer) 5
127.0.0.1:6379> lrange mylist01 0 -1
1) "300"
2) "200"
3) "one"
4) "100"
5) "400"
127.0.0.1:6379> linsert mylist01 after 100 two
(integer) 7
127.0.0.1:6379> lrange mylist01 0 -1
1) "100"
2) "two"
3) "300"
4) "200"
5) "one"
5) "one"
6) "100"
7) "400"

lset key index value將下標對應的數據修改

127.0.0.1:6379> lset mylist01 1 2
OK
127.0.0.1:6379> lrange mylist01 0 -1
1) "100"
2) "2"
3) "300"
4) "200"
5) "one"
6) "100"
7) "400"

**Lrem:**這個沒有rrem,內部邏輯涉及到了從下往上刪除
lrem key count value:從左向右尋找count個在這裏插入代碼片value相同值元素刪除。
count>0從左往右刪
count=0 全刪
count<0從右往左刪

127.0.0.1:6379> lpush mylist01 200 200 200
(integer) 9
127.0.0.1:6379> lrange mylist01 0 -1
1) "200"
2) "200"
3) "200"
4) "2"
5) "300"
5) "300"
6) "200"
7) "one"
8) "100"
9) "400"
127.0.0.1:6379> lrem mylist01 -2 200
(integer) 2
127.0.0.1:6379> lrange mylist01 0 -1
1) "200"
2) "200"
3) "2"
4) "300"
5) "one"
6) "100"
7) "400"
127.0.0.1:6379> lrem mylist01 0 200
(integer) 2

ltrim 保留鏈表的範圍內的數據元素,ltrim key 起始位置 結束位置(-1表示到尾部)

127.0.0.1:6379> ltrim mylist01 4 6
OK
127.0.0.1:6379> lrange mylist01 0 -1
1) "400
``lpop 從list頭部刪除元素,返回刪除結果(類似remove) 秒殺邏輯,可以利用list對象實現阻止超賣現象`

```bash
127.0.0.1:6379> lrange mylist01 0 -1
1) "six"
2) "five"
2) "five"
3) "four"
4) "three"
5) "two"
6) "one"
7) "400"
127.0.0.1:6379> lpop mylist01
"six"
127.0.0.1:6379> lrange mylist01 0 -1
1) "five"
2) "four"
3) "three"
4) "two"
5) "one"
6) "400"

兩個鏈表的數據交互
rpoplpush:從第一個list的尾部刪除數據,移動到第二個list的頭部添加

127.0.0.1:6379> rpoplpush mylist01 mylist02
"400"
127.0.0.1:6379> lrange mylist02 0 -1
1) "400"
2) "5"
3) "4"
4) "3"
5) "2"
6) "1

Set結構

集合用來保存多個字符串元素,和列表不同的是不允許有重複元素,並且集合中元素是無序的

在這裏插入圖片描述
最常見的應用場景,比如愛好,興趣,社交平臺中關注人存在一個集合中,將其所有粉絲存在一個集合

單集合操作
sadd key member添加

127.0.0.1:6380> sadd favor histroy english 
math
(integer) 3

srem key member刪除

127.0.0.1:6380> srem favor histroy
(integer) 1

scard key 返回元素個數

127.0.0.1:6380> scard favor
(integer) 2

sismember key element判斷元素是否在集合中

127.0.0.1:6380> sismember favor histroy
(integer) 0
127.0.0.1:6380> sismember favor math
(integer) 1

srandmember key 隨機抽取幾個元素

127.0.0.1:6380> sismember favor math
(integer) 1
127.0.0.1:6380> srandmember favor 1
1) "math"
127.0.0.1:6380> srandmember favor 1
1) "english

smembers key獲取所有元素

127.0.0.1:6380> sadd favor english histry
(integer) 2
127.0.0.1:6380> smembers favor
1) "english"
2) "histry"
3) "math"

集合間的操作
sinter set1 set2 交集

在這裏插入代碼片127.0.0.1:6380> sadd favor1 english histroy 
math
(integer) 3
127.0.0.1:6380> sadd favor2 english math 
chinese
(integer) 3
127.0.0.1:6380> sinter favor1 favor2
1) "english
2) "math"

suinon set1 set2 並集

127.0.0.1:6380> sunion favor1 favor2
1) "english"
2) "chinese"
3) "histroy"
4) "math

sdiff set1 set2 差集屬於set1不屬於set2的元素
爲差集

127.0.0.1:6380> sdiff favor1 favor2
1) "histroy"
127.0.0.1:6380> sdiff favor2 favor1
1) "chinese"
127.0.0.1:6380>

ZSet結構(sorted set)

在集合的基礎上綁定一個score作爲排序的依據比較典型的使用場景是排行榜系統。例如視頻網站需要對用戶上傳的視頻做排行榜
在這裏插入圖片描述
單集合操作
zadd key score memeber添加成員

127.0.0.1:6380> zadd result 10 xiaolaoshi
(integer) 1
127.0.0.1:6380> zadd result 20 wanglaoshi
(integer) 1
127.0.0.1:6380> zadd result 30 chenlaoshi
(integer) 1

zscore key member計算某個成員的分數

127.0.0.1:6380> zadd result 30 chenlaoshi
(integer) 1
127.0.0.1:6380> zscore result chenlaoshi
"30"
127.0.0.1:6380> zadd result 40 chenlaoshi
(integer) 0
127.0.0.1:6380> zscore result chenlaoshi
"40"

計算成員的排名 zrank key member

127.0.0.1:6380> zrank result xiaolaoshi
(integer) 0
127.0.0.1:6380> zrank result wanglaoshi
(integer) 1

刪除成員 zrem key member

127.0.0.1:6380> zrem result wanglaoshi
(integer) 1
127.0.0.1:6380> zrank result wanglaoshi
(nil)

增加成員的 分數zincrby key increment member

127.0.0.1:6380> zincrby result 50 xiaolaoshi
"60

返回指定排名範圍的成員 zrange key start end

返回指定排名範圍的成員 zrange key start end
127.0.0.1:6380> zrange result 0 1
1) "chenlaoshi"
2) "xiaolaoshi"

返回指定分數範圍的成員 zrangebysore key min max
返回指定分數範圍成員個數 zcount key min max
刪除指定排名內的升序元素 zremrangebyrank key start end

127.0.0.1:6380> zremrangebyrank result 0 1
(integer) 2
127.0.0.1:6380> zrange result 0 1
(empty list or set)

刪除指定分數範圍的成員 zremrangebyscore key min max key min max

集合間的操作
交集 zinterstore destination numkeys key 制定一個交集並集查詢結果的輸出集合desitination指定查詢交集並集的總zset個數numkeys ,給定符合數量的多個zset集合

127.0.0.1:6380> zadd result1 10 xiao
(integer) 1
127.0.0.1:6380> zadd result1 20 wang
(integer) 1
127.0.0.1:6380> zadd result2 30 xiao
(integer) 1
127.0.0.1:6380> zadd result2 40 wang
(integer) 1
127.0.0.1:6380> zinterstore out1 2 result1 
result2
(integer) 2
127.0.0.1:6380> zrange out1 0 -1 withscores
1) "xiao"
2) "40"
3) "wang"
4) "60

並集 zunionstore destionation numkeys key

redis多實例部署

單實例單線程的redis進程不足以高效率使用cpu和內存資源,所以一般來講redis在同一臺機器上要啓動多個進程完成多實例部署;默認佔用6379的情況下無法完成直接的3個實例啓動,這裏我們需要了解如何通過指定配置文件,將多實例部署在linux上

啓動redis服務的命令redis-server 沒有加載任何配置文件指定各種各樣的配置信息(端口指定,ip綁定,後臺運行)
例如在根目錄存在一個配置文件的模板(大部分與默認啓動的配置相同)redis.conf

啓動默認配置結點
#redis-server 配置文件的名稱
/redis根目錄/redis.conf
在這裏插入圖片描述
redis中的數據單位解釋

在這裏插入圖片描述
在這裏插入圖片描述

配置文件配置

bind 用#註釋掉
在這裏插入圖片描述
如果需要綁定監聽的ip(客戶端只有通過被綁定的ip纔可以利用redis-cli -h ip地址鏈接服務器)
bing 127.0.0.1 106.75.101.219(外網可訪問當前服務器的ip)

一旦用#註釋bind,沒有任何限制,只要可以鏈接服務器,都允許使用

保護模式不啓動
在這裏插入圖片描述保護模式開啓,需要登錄密碼,改成no

6379是默認端口(要啓動其他的redis實例需要修改端口)
在這裏插入圖片描述
當客戶端空閒時間達到一小時,就會自動斷開連接,0秒錶示不啓用超時配置
在這裏插入圖片描述
daemonize 設置成yes讓redis服務器啓動有守護進程管理
在這裏插入圖片描述
對應不同的redis實例,pid的文件名稱需要和端口同名每個進程在linux或者其他操作系統中都會佔用pid號,當系統中的進程過多時,需要查找redis進程號可能比較麻煩,直接打開pid文件查看即可
在這裏插入圖片描述
3logfile 需要指定,利用端口號命名,放到redis根目錄
在這裏插入圖片描述
save 900(秒) 1(變動的數據條數) 當900以內,至少有1條數據變動,flush保存數據到文件
save 300 10
300秒以內至少10條數據變動,保存文件
save 60 10000
在這裏插入圖片描述
指定dump的持久化文件,每個服務單獨指向一個文件,重啓時,數據不會錯亂
在這裏插入圖片描述
啓動第二和第三個redis實例,redis-server redis.conf(指定啓動文件)
拷貝redis.conf,用redis6380.conf,redis6381.conf
將拷貝的文件中只修改與端口有關內容
用vim打開,命令:%s/6379/6380/g,將所有6379改爲6380

6381結點類似

啓動另外兩個節點
#redis-server redis6380.conf
#redis-server redis6381.conf
#ps -ef|grep redis
在這裏插入圖片描述
指定端口登錄客戶端redis-cli -p [端口號] -h [ip]
在這裏插入圖片描述
在這裏插入圖片描述

redis的java客戶端–jedis

SpringBoot整合redis

  1. List item

導入依賴:

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-redis</artifactId>
 <version>1.4.7.RELEASE</version>
</dependency
  1. 連接java客戶端到redis服務
  2. 創建一個jedis的對象,提供ip地址,和端口等信息
@Test
//創建Jedis對象
Jedis jedis=new Jedis("10.9.151.60", 6380);
//jedis具備了操作6380的能力,
jedis.set("name","yuzb");
System.out.println(jedis.get("name"));
public void connectRedis(){
}

利用redis實現緩存

需求:查詢某個商品數據,使用緩存數據返回響應;請求發送之後,到達系統中,利用jedis對象連接redis實現緩存功能

  1. 生成商品對象的緩存key-value中調用數據的key
    key值設計=“業務邏輯”+“業務id”

  2. 判斷緩存是否存在這個key值,如果有key值:說明曾經至少訪問過一次,已經從數據庫中將數據查詢並且存放到緩存,如果沒有key值:需要調用數據庫持久層封裝product對象,並且存放到緩存供後續訪問使用

public Product queryById(String productId) {
		//創建一個連接對象使用來操作redis緩存技術
		Jedis jedis=new Jedis("10.9.151.60",6381);
		//生成當前業務邏輯的key值,業務信息+productId
		String productKey="product_"+productId;
		//想辦法將product序列化成String的json字符串,使用jackson的api
		ObjectMapper mapper=new ObjectMapper();
		try{
			//判斷緩存數據是否存在
			if(jedis.exists(productKey)){//表示可以使用緩存
				//Map<String, String> productMap = jedis.hgetAll(productKey);
				//JSON字符串解析反序列化
				String json=jedis.get(productKey);
				Product product=mapper.readValue(json, Product.class);
				return product;
			}else{//商品key不存在,需要訪問數據庫,將數據存放redis
				Product product=productMapper.queryById(productId);
				//cong product對象映射到json字符串 writeValueAsString"":""
				String json=mapper.writeValueAsString(product);
				System.out.println(json);//{"":"","":"","":""}
				//將json存儲到redis中
				jedis.set(productKey, json);
				return product;
			}
		}catch(Exception e){
			e.printStackTrace();
			return null;
		}
			
	}

上面緩存方案存在問題:
1.不應該直接new一個jedis,而是交給框架去處理
2.只有一個嗯結點,如果宕機就不可用,造成緩存失效,併發下容易擊穿數據庫

分片的自定義計算(三個節點)

在這裏插入圖片描述
數據分片:數據層的分佈式體系中,有多個節點維護的是整體數據,每一個節點維護一部分數據–分片數據

自定義的分片計算邏輯—hash取餘
key.hashCode():key值不變,hashCode結果不變並且是整數
key.hashCode()&Integer.MAX_VALUE:31保真運算,得到正整數
(key.hashCode()&Integer.MAX_VALUE)%N:N是節點個數,分片個數,取餘結果時[0,1,2,…N-1]
可以通過這樣的一個公式,將任意的key值,只要key值不變永遠對應[0,1,2,…N-1]中一個整數

public void set(String key,String value){
		//根據hash取餘結果的邏輯實現存儲在不同的結點0,6379,1,6380,6381
		Jedis jedis6379=new Jedis("10.9.151.60", 6379);
		Jedis jedis6380=new Jedis("10.9.151.60", 6380);
		Jedis jedis6381=new Jedis("10.9.151.60", 6381);
		//hash取餘得到3個結點的取餘結果,
		int result=(key.hashCode()&Integer.MAX_VALUE)%3;
		if(result==0){//6379
			jedis6379.set(key, value);
			System.out.println("數據綁定在6379存儲");
		}
		if(result==1){//6380
			jedis6380.set(key, value);
			System.out.println("數據綁定在6380存儲");
		}
		if(result==2){//6381
			jedis6381.set(key, value);
			System.out.println("數據綁定在6381存儲");
		}
	}

hash取餘的算法存在的問題:在擴容,縮容,數據遷移特別大,導致數據緩存未命中的概率極高;

hash一致性分片對象ShardedJedis
jedis的客戶端如果連接的是redis的集羣,可以在內部直接生成一個hash一致性的算法,解決key值與結點的對應關係.ShardedJedis的分片對象就封裝了對多個結點集羣操作時的hash一致性計算

@Test
	public void shardeJedisTest(){
		//收集結點信息
		List<JedisShardInfo> infoList=new ArrayList<JedisShardInfo>();
		//收集6379,6380,6381
		infoList.add(new JedisShardInfo("10.9.151.60", 6379, 500, 500, 5));
		infoList.add(new JedisShardInfo("10.9.151.60", 6380, 500, 500, 1));
		infoList.add(new JedisShardInfo("10.9.151.60", 6381, 500, 500, 1));
		//創建分片對象shardedJedis
		ShardedJedis sJedis=new ShardedJedis(infoList);
		//sJedis.set("name", "yuzb");
		//System.out.println(sJedis.get("name"));
		for(int i=0;i<10000;i++){
			String key="key_"+i;
			sJedis.set(key, "");
		}
	}

分片連接池
單個分片對象無法滿足系統中操作集羣使用hash一致性分片計算的要求;引入jedis提供的分片連接池,初始化一些分片對象,每次調用需要獲取resource資源,用完了還回池子
生成代碼步驟
1 收集結點信息;
2 連接池的各種屬性設置:最大連接數,最大空閒,最小空閒等的的配置對象
生成
3 創建連接池對象
4 從連接池獲取資源調用代碼
5 返回資源到連接池

//連接池使用
	@Test
	public void connectPool(){
		//收集節點信息
		List<JedisShardInfo> infoList=new ArrayList<JedisShardInfo>();
		//收集6379,6380,6381
		infoList.add(new JedisShardInfo("10.9.151.60", 6379, 500, 500, 5));
		infoList.add(new JedisShardInfo("10.9.151.60", 6380, 500, 500, 1));
		infoList.add(new JedisShardInfo("10.9.151.60", 6381, 500, 500, 1));
		//連接池配置對象
		GenericObjectPoolConfig config=new GenericObjectPoolConfig();
		config.setMaxTotal(200);
		config.setMaxIdle(8);
		config.setMinIdle(3);
		//連接池對象
		ShardedJedisPool pool=new ShardedJedisPool(config, infoList);
		//從連接池獲取分片對象操作集羣
		ShardedJedis sJedis = pool.getResource();
		sJedis.set("name", "ghaha");
		pool.returnResource(sJedis);
	}
	
	

以上存在硬編碼問題,如將redis結點直接寫在代碼中,連接池不是交給框架維護
SpringBoot整合redis改進版:

  1. 引入的技術準備配置文件application.properties
  2. 在配置文件寫上redis配置
#redis-customize
redis.nodes=10.9.151.60:6379,10.9.151.60:6380,10.9.151.60:6381
redis.maxTotal=200
redis.maxIdle=8
redis.minIdle=3
  1. 讀取properties屬性:
    @ConfigurationProperties(prefix="")對屬性賦值,調用getter/setter將讀取的properties數據存放到生成bean中
/**
 * 在當前工程掃描範圍之內的configuration配置類
 * 讀取前綴是redis的所有key值,根據私有屬性名稱的getter/stter
 * 賦值給屬性
 * @author yuzb
 *
 */
@Configuration
@ConfigurationProperties(prefix="redis")
public class RedisCumConfiguration {
	//根據前綴讀取數據,私有屬性名稱,必須和
	//properties中的值相同
	private String nodes;
	private Integer maxTotal;
	private Integer maxIdle;
	private Integer minIdle;
	
	//編寫初始化JedisShardPool對象的方法,@Bean將返回對象
	//作爲框架管理的bean
	@Bean 
	public ShardedJedisPool initJedisPool(){
		//利用本類中讀取的屬性,創建連接池對象
		//先做一個config對象
		GenericObjectPoolConfig config=new GenericObjectPoolConfig();
		config.setMaxIdle(maxIdle);
		config.setMaxTotal(maxTotal);
		//config.setMinIdle(minIdle);
		//解析nodes,生成一個list對象
		//準備一個空內容
		List<JedisShardInfo> infoList=new ArrayList<JedisShardInfo>();
		String[] node = nodes.split(",");//{"10.9.9.9:6379","10l.9.9.9:6380",""}
		for (String hostAndPort : node) {
			String host=hostAndPort.split(":")[0];
			int port=Integer.parseInt(hostAndPort.split(":")[1]);
			infoList.add(new JedisShardInfo(host, port));
		}
		//list,config,構造連接池對象返回
		return new ShardedJedisPool(config,infoList);
	}
	
	
	public String getNodes() {
		return nodes;
	}
	public void setNodes(String nodes) {
		this.nodes = nodes;
	}
	public Integer getMaxTotal() {
		return maxTotal;
	}
	public void setMaxTotal(Integer maxTotal) {
		this.maxTotal = maxTotal;
	}
	public Integer getMaxIdle() {
		return maxIdle;
	}
	public void setMaxIdle(Integer maxIdle) {
		this.maxIdle = maxIdle;
	}
	public Integer getMinIdle() {
		return minIdle;
	}
	public void setMinIdle(Integer minIdle) {
		this.minIdle = minIdle;
	}
	
  1. 利用讀取的屬性生成框架維護的對象JedisShardPool連接池.
/**
 * jedis工具類,用於生成連接池和Jedis對象,並提供redis的常用操作
 * @author yuzb
 *
 */
@Component
public class RedisCumUtils {
	@Autowired
	private ShardedJedisPool pool;
	//query,addOrUpdate,delete,isExists
	public String query(String key){
		ShardedJedis jedis = pool.getResource();
		try{
			return jedis.get(key);
		}catch(Exception e){
			//異常處理邏輯
			return null;
		}finally{
			pool.returnBrokenResource(jedis);
		}
	}
	public void addOrUpdate(String key,String value){
		ShardedJedis jedis = pool.getResource();
		try{
			jedis.set(key, value);
		}catch(Exception e){
			//異常處理邏輯
		}finally{
			pool.returnBrokenResource(jedis);
		}
	}
	public void delete(String key){
		ShardedJedis jedis = pool.getResource();
		try{
			jedis.del(key);
		}catch(Exception e){
			//異常處理邏輯
		}finally{
			pool.returnBrokenResource(jedis);
		}
	}
	public Boolean isExist(String key){
		ShardedJedis jedis = pool.getResource();
		try{
			return jedis.exists(key);
		}catch(Exception e){
			//異常處理邏輯
			return false;
		}finally{
			pool.returnBrokenResource(jedis);
		}
	}
}

	@Autowired
	private RedisCumUtils jedis;
	public Product queryById(String productId) {
		//創建一個連接對象使用來操作redis緩存技術
		//Jedis jedis=new Jedis("10.9.151.60",6381);
		//通過注入進來的pool獲取數據連接資源ShardedJedis
		//ShardedJedis jedis=pool.getResource();
		//生成當前業務邏輯的key值,業務信息+productId
		String productKey="product_"+productId;
		//想辦法將product序列化成String的json字符串,使用jackson的api
		ObjectMapper mapper=new ObjectMapper();
		try{
			//判斷緩存數據是否存在
			if(jedis.isExist(productKey)){//表示可以使用緩存
				//Map<String, String> productMap = jedis.hgetAll(productKey);
				//JSON字符串解析反序列化
				String json=jedis.query(productKey);
				Product product=mapper.readValue(json, Product.class);
				return product;
			}else{//商品key不存在,需要訪問數據庫,將數據存放redis
				Product product=productMapper.queryById(productId);
				//cong product對象映射到json字符串 writeValueAsString"":""
				String json=mapper.writeValueAsString(product);
				System.out.println(json);//{"":"","":"","":""}
				//將json存儲到redis中
				jedis.addOrUpdate(productKey, json);
				return product;
			}
		}catch(Exception e){
			e.printStackTrace();
			return null;
		}
		
			
		}

hash一致性:集羣中節點的數量越多,使用hash取餘算法經過擴容縮容時,需要遷移的數據量就越大,否則,會造成大量的數據緩存未命中–雪崩/緩存擊穿,hash一致性恰好與hash取餘相反:節點越多,擴容縮容遷移越少;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章