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型別
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)。是可以修改的字串,內部結構實現上類似於Java的ArrayList,採用預分配冗餘空間的方式來減少記憶體的頻繁分配.
如上圖中所示,內部為當前字串實際分配的空間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型別的資料,結構上還需要兩個額外的指標prev和next。
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所不能提供的。
Redis的Set是string型別的無序集合。它底層其實是一個value為null的hash表,所以新增,刪除,查詢的複雜度都是O(1)。一個演算法,隨著資料的增加,執行時間的長短,如果是O(1),資料增加,查詢資料的時間不變.使用場景也是比較單一的,就是用在一些去重的場景裡,例如每個使用者只能參與一次活動、一個使用者只能中獎一次等等去重場景。
2.3.1 Set的資料結構
Set資料結構是dict字典,字典是用雜湊表實現的。
Java中HashSet的內部實現使用的是HashMap,只不過所有的value都指向同一個物件。Redis的set結構也是一樣,它的內部也使用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型別的field和value的對映表,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底層使用了兩個資料結構
(1)hash,hash的作用就是關聯元素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); }
全部redis指令可檢視:http://www.redis.cn/commands.html