Redis(二) 資料型別操作指令以及對應的RedisTemplate方法

Brian_Huang發表於2021-06-08

1.Redis key值操作以及RedisTemplate對應的API

本文預設使用RedisTemplate,關於RedisTemplate和StringRedisTemplate的區別如下

RedisTemplate和StringRedisTemplate
二者主要區別是他們使用的序列化類不一樣,RedisTemplate使用的是JdkSerializationRedisSerializer, 
StringRedisTemplate使用的是StringRedisSerializer,兩者的資料是不共通的。 1.RedisTemplate: RedisTemplate使用的是JDK的序列化策略,向Redis存入資料會將資料先序列化成位元組陣列然後在存入Redis資料庫,這個時候開啟Redis檢視的時候,
你會看到你的資料不是以可讀的形式展現的,而是以位元組陣列顯示,類似下面:\xAC\xED\x00\x05t\x05sr\x00。
所以使用RedisTemplate可以直接把一個java物件直接儲存在redis裡面。 2.StringRedisTemplate: StringRedisTemplate預設採用的是String的序列化策略,儲存的key和value都是採用此策略序列化儲存的。
StringRedisTemplate是繼承RedisTemplate的,這種對redis的操方式更優雅,
因為RedisTemplate以位元組陣列的形式儲存不利於管理,也不通用。

 如果存入的是簡單的字串使用StringRedisTemplate;

 如果存入的是複雜的物件用RedisTemplate;

1.1 Key操作

1.1.1 常用的key操作

keys *檢視當前庫所有key (匹配:keys *1)

public Set<String> keys(String pattern) {
  return redisTemplate.keys(pattern);
}
exists key判斷某個key是否存在

public Boolean hasKey(String key) {
    return redisTemplate.hasKey(key);
}
type key 檢視你的key是什麼型別

public DataType type(String key) {
    return redisTemplate.type(key);
}
del key 刪除指定的key資料

public void delete(String key) {
    redisTemplate.delete(key);
}
unlink key 根據value選擇非阻塞刪除,僅將keys從keyspace後設資料中刪除,真正的刪除會在後續非同步操作。

public void unlink(String key) {
    redisTemplate.unlink(key);
}
expire key 10 10秒鐘:為給定的key設定過期時間

public Boolean expire(String key, long timeout, TimeUnit unit) {
    return redisTemplate.expire(key, timeout, unit);
}
ttl key 檢視還有多少秒過期,-1表示永不過期,-2表示已過期

public Long getExpire(String key) {
    return redisTemplate.getExpire(key);
}
flushdb  清空當前庫

public void flushdb() {
    Set<String> keys = redisTemplate.keys("*");
    redisTemplate.delete(keys);
}
dbsize  檢視當前資料庫的key的數量

// 令pattern = "*"
public Set<String> keys(String pattern) {
    return redisTemplate.keys(pattern);
}

2.Redis 五大資料型別操作以及RedisTemplate對應的API

2.1 String型別

String是Redis最基本的型別,你可以理解成與Memcached一模一樣的型別,一個key對應一個value。String型別是二進位制安全的。意味著Redis的string可以包含任何資料。比如jpg圖片或者序列化的物件。String型別是Redis最基本的資料型別,一個Redis中字串value最多可以是512M。
應用場景
1.快取結構體資訊:
將結構體json序列化成字串,然後將字串儲存在redis的value中,將結構體的業務唯一標示作為key;這種儲存json的用法用的最多的場景就是快取使用者資訊,
將使用者bean資訊轉成json再序列化為字串作為value儲存在redis中,將使用者id作為key。從程式碼中獲取使用者快取資訊就是一個逆過程,
根據userid作為key獲取到結構體json,然後將json轉成java bean。
2.計數功能: 我們都知道redis是單執行緒模式,並且redis將很多常用的事務操作進行了封裝,這裡我們最常用的就是數值自增或自減,redis的作者封裝了incr可以進行自增,
沒呼叫一次自增1,因為redis是單執行緒執行,所以就算client是多執行緒呼叫那麼也是正確自增,因為incr命令中將read和write做了事務封裝。
同樣可以設定incr的step,每次根據step進行自增,當然如果要達到自減的效果,那麼只要將step設定為負數就可以了。 計數功能使用的場景很多,我們之前經常用在實時計數統計場景,也用在過庫存場景、限流計數場景等等,而且redis的效能也是非常高的,
對於一般的併發量沒那麼高的系統都是適用的。

2.1.1 String的資料結構

String的資料結構為簡單動態字串(Simple Dynamic String,縮寫SDS)。是可以修改的字串,內部結構實現上類似於JavaArrayList,採用預分配冗餘空間的方式來減少記憶體的頻繁分配.

如上圖中所示,內部為當前字串實際分配的空間capacity一般要高於實際字串長度len。當字串長度小於1M時,擴容都是加倍現有的空間,如果超過1M,擴容時一次只會多擴1M的空間。需要注意的是字串最大長度為512M

2.1.2 String常用指令

set <key><value>新增鍵值對

public void set(String key, String value) {
    redisTemplate.opsForValue().set(key, value);
}
set ... nx:當資料庫中key不存在時,可以將key-value新增資料庫

public boolean setIfAbsent(String key, String value) {
    return redisTemplate.opsForValue().setIfAbsent(key, value);
}
set ... xx:當資料庫中key存在時,可以將key-value新增資料庫,與nx引數互斥

public boolean setIfPresent(String key, String value) {
    return redisTemplate.opsForValue().setIfPresent(key, value);
}
set ... ex:設定key的超時秒數
set ... px:設定key的超時毫秒數

public void setEx(String key, String value, long timeout, TimeUnit unit) {
    redisTemplate.opsForValue().set(key, value, timeout, unit);
}
get <key>查詢對應鍵值

public Object get(String key) {
    return redisTemplate.opsForValue().get(key);
}
append <key><value>將給定的<value> 追加到原值的末尾

public Integer append(String key, String value) {
    return redisTemplate.opsForValue().append(key, value);
}
strlen <key>獲得值的長度

public Long size(String key) {
    return redisTemplate.opsForValue().size(key);
} 
incr <key> 將 key 中儲存的數字值增1,只能對數字值操作,如果為空,新增值為1
decr <key> 將 key 中儲存的數字值減1,只能對數字值操作,如果為空,新增值為-1
incrby / decrby <key><步長>將 key 中儲存的數字值增減。自定義步長。

public Long incrBy(String key, long increment) {
    return redisTemplate.opsForValue().increment(key, increment);
}
mset <key1><value1><key2><value2> ..... 同時設定一個或多個 key-value對

public void multiSet(Map<String, String> maps) {
    redisTemplate.opsForValue().multiSet(maps);
}
mget <key1><key2><key3> ..... 同時獲取一個或多個 value

public List<Object> multiSet(Collection<String> keys) {
    return redisTemplate.opsForValue().multiGet(keys);
}
msetnx <key1><value1><key2><value2> ..... 同時設定一個或多個 key-value 對,當且僅當所有給定 key 都不存在。原子性,有一個失敗則都失敗

public boolean multiSetIfAbsent(Map<String, String> maps) {
    return redisTemplate.opsForValue().multiSetIfAbsent(maps);
}
getrange <key><起始位置><結束位置>獲得值的範圍,類似java中的substring,前包,後包

public Object getRange(String key, long start, long end) {
    return redisTemplate.opsForValue().get(key, start, end);
}
setrange <key><起始位置><value> 用 <value> 覆寫<key>所儲存的字串值,從<起始位置>開始(索引從0開始)。

public void setRange(String key, String value, long offset) {
    redisTemplate.opsForValue().set(key, value, offset);
}
getset <key><value> 以新換舊,設定了新值同時獲得舊值。

public Object getAndSet(String key, String value) {
    return redisTemplate.opsForValue().getAndSet(key, value);
}

2.2 List型別

Redis 列表List型別是簡單的字串列表,按照插入順序排序。你可以新增一個元素到列表的頭部(左邊)或者尾部(右邊)。它的底層實際是個雙向連結串列,對兩端的操作效能很高,通過索引下標的操作中間的節點效能會較差。

應用場景

1.list列表結構常用來做非同步佇列使用
將需要延後處理的任務結構體序列化成字串塞進 Redis 的列表,另一個執行緒從這個列表中輪詢資料進行處理。

2.list可用於秒殺搶購場景
在商品秒殺場景最怕的就是商品超賣,為了解決超賣問題,我們經常會將庫存商品快取到類似MQ的佇列中,多執行緒的購買請求都是從佇列中取,
取完了就賣完了,但是用MQ處理的化有點重,這裡就可以使用redis的list資料型別來實現,在秒殺前將本場秒殺的商品放到list中,
因為list的pop操作是原子性的,所以即使有多個使用者同時請求,也是依次pop,list空了pop丟擲異常就代表商品賣完了。

2.2.1 List的資料結構

List的資料結構為快速連結串列quickList首先在列表元素較少的情況下會使用一塊連續的記憶體儲存,這個結構是ziplist,也即是壓縮列表。它將所有的元素緊挨著一起儲存,分配的是一塊連續的記憶體。當資料量比較多的時候才會改成quicklist因為普通的連結串列需要的附加指標空間太大,會比較浪費空間。比如這個列表裡存的只是int型別的資料,結構上還需要兩個額外的指標prevnext

Redis將連結串列和ziplist結合起來組成了quicklist。也就是將多個ziplist使用雙向指標串起來使用。這樣既滿足了快速的插入刪除效能,又不會出現太大的空間冗餘。 

2.2.2 List常用的指令

lpush/rpush <key><value1><value2><value3> .... 從左邊/右邊插入一個或多個值。

public Long lLeftPush(String key, String value) {
    return redisTemplate.opsForList().leftPush(key, value);
}

public Long lLeftPushAll(String key, String... value) {
    return redisTemplate.opsForList().leftPushAll(key, value);
}

public Long lLeftPushAll(String key, Collection<String> value) {
    return redisTemplate.opsForList().leftPushAll(key, value);
}
lpop/rpop <key>從左邊/右邊吐出一個值。值在鍵在,值光鍵亡。

public Object lLeftPop(String key) {
    return redisTemplate.opsForList().leftPop(key);
}
rpoplpush <key1><key2>  從<key1>列表右邊吐出一個值,插到<key2>列表左邊。

public Object lRightPopAndLeftPush(String sourceKey, String destinationKey) {
    return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey);
}
lrange <key><start><stop>按照索引下標獲得元素(從左到右)

lrange <key> 0 -1 0左邊第一個,-1右邊第一個,(0-1表示獲取所有)


public List<Object> lRange(String key, long start, long end) {
    return redisTemplate.opsForList().range(key, start, end);
}
lindex <key><index>按照索引下標獲得元素(從左到右)

public Object lIndex(String key, long index) {
    return redisTemplate.opsForList().index(key, index);
}
llen <key>獲得列表長度

public Long lLen(String key) {
    return redisTemplate.opsForList().size(key);
}
lset<key><index><value>將列表key下標為index的值替換成value

public void lSet(String key, long index, String value) {
    redisTemplate.opsForList().set(key, index, value);
}
linsert <key> before <value><newvalue>  在<value>的前面插入<newvalue>插入值

public Long lLeftPush(String key, String pivot, String value) {
    return redisTemplate.opsForList().leftPush(key, pivot, value);
}
lrem <key><n><value>   n>0 從左邊到右刪除n個value, n<0從右到左刪除n個value

public Long lRemove(String key, long index, String value) {
    return redisTemplate.opsForList().remove(key, index, value);
}

2.3 Set型別

Redis set對外提供的功能與list類似是一個列表的功能,特殊之處在於set是可以自動排重的,當你需要儲存一個列表資料,又不希望出現重複資料時,set是一個很好的選擇,並且set提供了判斷某個成員是否在一個set集合內的重要介面,這個也是list所不能提供的。

RedisSetstring型別的無序集合它底層其實是一個valuenullhash,所以新增,刪除,查詢的複雜度都是O(1)。一個演算法,隨著資料的增加,執行時間的長短,如果是O(1),資料增加,查詢資料的時間不變.使用場景也是比較單一的,就是用在一些去重的場景裡,例如每個使用者只能參與一次活動、一個使用者只能中獎一次等等去重場景。

2.3.1 Set的資料結構

Set資料結構是dict字典,字典是用雜湊表實現的。

JavaHashSet的內部實現使用的是HashMap,只不過所有的value都指向同一個物件。Redisset結構也是一樣,它的內部也使用hash結構,所有的value都指向同一個內部值。

2.3.2 Set常用的指令

sadd <key><value1><value2> ..... 將一個或多個 member 元素加入到集合 key 中,已經存在的 member 元素將被忽略

public Long sAdd(String key, String... values) {
    return redisTemplate.opsForSet().add(key, values);
}
smembers <key> 取出該集合的所有值。

public Set<Object> setMembers(String key) {
    return redisTemplate.opsForSet().members(key);
}
sismember <key><value> 判斷集合<key>是否為含有該<value>值,有1,沒有0

public Boolean sIsMember(String key, Object value) {
    return redisTemplate.opsForSet().isMember(key, value);
}
scard<key> 返回該集合的元素個數。

public Long sSize(String key) {
    return redisTemplate.opsForSet().size(key);
}
srem <key><value1><value2> .... 刪除集合中的某個元素。

public Long sRemove(String key, Object... values) {
    return redisTemplate.opsForSet().remove(key, values);
}
spop <key> 隨機從該集合中吐出一個值。

public Object sPop(String key) {
    return redisTemplate.opsForSet().pop(key);
}
srandmember <key><n> 隨機從該集合中取出n個值。不會從集合中刪除 。

public List<Object>sRandomMembers(String key, long count) {
    return redisTemplate.opsForSet().randomMembers(key, count);
}
smove <source><destination>value 把集合中一個值從一個集合移動到另一個集合

public Boolean sMove(String key, String value, String destKey) {
    return redisTemplate.opsForSet().move(key, value, destKey);
}
sinter <key1><key2> 返回兩個集合的交集元素。

public Set<Object> sIntersect(String key, String otherKey) {
    return redisTemplate.opsForSet().intersect(key, otherKey);
}
sunion <key1><key2> 返回兩個集合的並集元素。

public Set<Object> sUnion(String key, String otherKeys) {
    return redisTemplate.opsForSet().union(key, otherKeys);
}
sdiff <key1><key2> 返回兩個集合的差集元素(key1中的,不包含key2中的)

public Set<Object> sDifference(String key, String otherKey) {
    return redisTemplate.opsForSet().difference(key, otherKey);
}

2.4 Hash型別

Redis hash 是一個鍵值對集合。Redis hash是一個string型別的fieldvalue的對映表,hash特別適合用於儲存物件。類似Java裡面的Map<String,Object>使用者ID為查詢的key,儲存的value使用者物件包含姓名,年齡,生日等資訊,如果用普通的key/value結構來儲存

主要有以下2種儲存方式:

 第一種方式將使用者ID作為查詢key,把其他資訊封裝成一個物件以序列化的方式儲存,這種方式的缺點是,增加了序列化/反序列化的開銷,並且在需要修改其中一項資訊時,需要把整個物件取回,並且修改操作需要對併發進行保護,引入CAS等複雜問題。

第二種方法是這個使用者資訊物件有多少成員就存成多少個key-value對兒,用使用者ID+對應屬性的名稱作為唯一標識來取得對應屬性的值,雖然省去了序列化開銷和併發問題,但是使用者ID為重複儲存,如果存在大量這樣的資料,記憶體浪費還是非常嚇人的。

那麼Redis提供的Hash很好的解決了這個問題,Redis的Hash實際是內部儲存的Value為一個HashMap,並提供了直接存取這個Map成員的介面,通過key(使用者ID) + field(屬性標籤) 就可以操作對應屬性資料了,既不需要重複儲存資料,也不會帶來序列化和併發修改控制的問題,如下圖: 

 

常用使用場景

(1)儲存結構體資訊

hash字典型別也是比較適合儲存結構體資訊的,不同於字串一次序列化整個物件,hash可以對使用者結構中的每個欄位單獨儲存。這樣當我們需要獲取結構體資訊時可以進行部分獲取,而不用序列化所有欄位,而將整個字串儲存的結構體資訊只能一次性全部讀取。

2.4.1 Hash的資料結構

Hash型別對應的資料結構是兩種:ziplist(壓縮列表),hashtable(雜湊表)。當field-value長度較短且個數較少時,使用ziplist,否則使用hashtable

2.4.2 Hash常用的指令

hset <key><field><value>  給<key>集合中的 <field>鍵賦值<value>

public void hPut(String key, String hashKey, String value) {
    redisTemplate.opsForHash().put(key, hashKey, value);
}
hget <key1><field>  從<key1>集合<field>取出 value

public Object hGet(String key, String field) {
    return redisTemplate.opsForHash().get(key, field);
}
hmset <key1><field1><value1><field2><value2>...   批量設定hash的值

public void hPutAll(String key, Map<String, String> maps) {
    redisTemplate.opsForHash().putAll(key, maps);
}
hexists<key1><field>  檢視雜湊表 key 中,給定域 field 是否存在。

public boolean hExists(String key, String field) {
    return redisTemplate.opsForHash().hasKey(key, field);
}
hkeys <key>  列出該hash集合的所有field

public Set<Object> hKeys(String key) {
    return redisTemplate.opsForHash().keys(key);
}
hvals <key>  列出該hash集合的所有value

public List<Object> hValues(String key) {
    return redisTemplate.opsForHash().values(key);
}
hincrby <key><field><increment>  為雜湊表 key 中的域 field 的值加上增量 1 -1

public Double hIncrByFloat(String key, Object field, double delta) {
    return redisTemplate.opsForHash().increment(key, field, delta);
}
hsetnx <key><field><value>  將雜湊表 key 中的域 field 的值設定為 value ,當且僅當域 field 不存在 .

public Boolean hPutIfAbsent(String key, String hashKey, String value) {
    return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
} 

2.5 Zset型別

Redis有序集合zset與普通集合set非常相似,是一個沒有重複元素的字串集合。不同之處是有序集合的每個成員都關聯了一個評分(score,這個評分(score)被用來按照從最低分到最高分的方式排序集合中的成員。集合的成員是唯一的,但是評分可以是重複了

因為元素是有序的, 所以你也可以很快的根據評分(score)或者次序(position)來獲取一個範圍的元素。訪問有序集合的中間元素也是非常快的,因此你能夠使用有序集合作為一個沒有重複成員的智慧列表。

常用使用場景

(1)各類熱門排序場景

例如熱門歌曲榜單列表,value值是歌曲ID,score是播放次數,這樣就可以對歌曲列表按播放次數進行排序。

當然還有類似微博粉絲列表、評論列表等等,可以將value定義為使用者ID、評論ID,score定義為關注時間、評論點贊次數等等。

2.5.1 Zset的資料結構

SortedSet(zset)Redis提供的一個非常特別的資料結構,一方面它等價於Java的資料結構Map<String, Double>,可以給每一個元素value賦予一個權重score,另一方面它又類似於TreeSet,內部的元素會按照權重score進行排序,可以得到每個元素的名次,還可以通過score的範圍來獲取元素的列表。

zset底層使用了兩個資料結構

1hashhash的作用就是關聯元素value和權重score,保障元素value的唯一性,可以通過元素value找到相應的score值。

2)跳躍表,跳躍表的目的在於給元素value排序,根據score的範圍獲取元素列表。

2.5.2 Zset常用的指令

zadd <key><score1><value1><score2><value2>…  將一個或多個 member 元素及其 score 值加入到有序集 key 當中。

public Boolean zAdd(String key, String value, double score) {
    return redisTemplate.opsForZSet().add(key, value, score);
}

public Long zAdd(String key, Set<TypedTuple<Object>> values) {
    return redisTemplate.opsForZSet().add(key, values);
}
zrange <key><start><stop> [WITHSCORES]  返回有序集 key 中,下標在<start><stop>之間的元素 帶WITHSCORES,可以讓分數一起和值返回到結果集。

public Set<TypedTuple<Object>> zRangeWithScores(String key, long start,long end) {
    return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
}
zrangebyscore key min max [withscores] [limit offset count]  返回有序集 key 中,所有 score 值介於 min 和 max 之間(包括等於 min 或 max )的成員。有序整合員按 score 值遞增(從小到大)次序排列。

public Set<TypedTuple<Object>> zRangeByScoreWithScores(String key,double min, double max, long start, long end) {
    return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max,start, end);
}
zrevrangebyscore key max min [withscores] [limit offset count]  同上,改為從大到小排列。

public Set<TypedTuple<Object>> zReverseRangeWithScores(String key,long start, long end) {
    return redisTemplate.opsForZSet().reverseRangeWithScores(key, start,end);
}
zincrby <key><increment><value> 為元素的score加上增量

public Double zIncrementScore(String key, String value, double delta) {
    return redisTemplate.opsForZSet().incrementScore(key, value, delta);
}
zrem <key><value>  刪除該集合下,指定值的元素

public Long zRemove(String key, Object... values) {
    return redisTemplate.opsForZSet().remove(key, values);
}
zcount <key><min><max>  統計該集合,分數區間內的元素個數

public Long zCount(String key, double min, double max) {
    return redisTemplate.opsForZSet().count(key, min, max);
}
zrank <key><value>  返回該值在集合中的排名,從0開始。

public Long zReverseRank(String key, Object value) {
    return redisTemplate.opsForZSet().reverseRank(key, value);
}

 

程式碼地址:https://github.com/showkawa/springdoc-test/blob/main/src/main/java/com/brian/demo/tool/RedisUtil.java

全部redis指令可檢視:http://www.redis.cn/commands.html

相關文章