高併發秒殺之秒殺優化
1 優化分析
前三張基本將秒殺的系統開發完成但是之前那種設計真的可以承受高併發下的秒殺麼本篇文章結合該高併發系統考慮,哪些是可能出現的高併發點呢?
上圖中,所有的紅色的部分都可能是出現高併發的點。
1.1為什麼單獨獲取系統時間
在詳情頁,可能出現使用者大量的重新整理情況,此時系統應該部署在CDN節點上,此時要做一個靜態化處理,當再次重新整理時它獲取的CDN靜態資源(css/js/picture),但是,時間要保持實時的,所以要單獨的做一個處理,單獨從伺服器系統上獲取時間,這也就是為什麼要在詳情頁單獨獲取系統時間了。
1.2 CDN是什麼
簡介:CDN(內容釋出網路),是一個加速使用者獲取資料的系統;既可以是靜態資源,又可以是動態資源,這取決於我們的決策策略。經常大部分視訊加速都依賴於CDN,比如優酷,愛奇藝等,據此加速;
原理:CDN部署在距離使用者最近的網路節點上,使用者上網的時候通過網路運營商(電信,長城等)訪問距離使用者最近的要給都會網路網路地址節點上,然後通過都會網路跳到主幹網上,主幹網則根據訪問IP找到訪問資源所在伺服器,但是,很大一部分內容在上一層節點已經找到,此時不用往下繼續查詢,直接返回所訪問的資源即可,減小了伺服器的負擔。一般網際網路公司都會建立自己的CDN機群或者租用CDN。
1.3 獲取系統時間不用優化
獲取系統訪問時間的操作不用優化,因為訪問一次記憶體Cacheline大約10ns,1秒內可以做很大資料量級的時間獲取操作,所以,不用做什麼優化!
1.4 秒殺地址(Redis快取技術)
對於秒殺地址暴露的介面是否可以快取呢?
秒殺介面是無法快取在CDN當中的,因為CDN適合快取不易變化的資源,通常是靜態資源,比如css/jquery資源,每一個url對應了一個不變的內容,秒殺的介面地址是在每次都發生變化的,不適合放在CDN快取。
但是適合放在伺服器端做快取(後端快取),比如redis等,下一次訪問的時候直接去伺服器端快取裡面查詢,如果伺服器端快取有了就直接拿走,沒有的話再做正常資料訪問處理;另外一個原因就是,一致性維護成本很低。
秒殺地址介面的優化策略:
請求地址,先訪問redis,如果redis快取中沒有所需資源或者訪問訪問超時,則直接進入mysql獲取系統資源,將獲取的內容更新在redis當中(策略:超時穿透,主動更新)。
1.5 秒殺操作
1.5.1 秒殺操作分析
(a)秒殺操作優化分析
對於這種寫操作,是無法使用CDN優化的,另外,也不適合在後端快取,因為快取了其他資料,可能會出現資料不一致的情況。
秒殺資料資料操作的一個困難的點就是一行資料大量使用者出現競爭的情況,同時出現大量的(b)update操作,這樣該如何優化呢?
(架構+維護點)
設計一個原子計數器(redis/NoSQL來實現)用來記錄行為資訊(用分散式MQ實現這個訊息佇列,即把訊息放在MQ當中),然後後端服務消費此訊息並落地(用Mysql實現,落地:記錄購買者,能夠扛著很大的訪問量)
但是這個而技術的有自己的弱點,也就是成本方面:
運維成本和穩定型:NoSQL,MQ等;開發成本在資料一致性和回滾方案等;冪等性難以保證:重複秒殺的問題;不適合新手的架構。
(c)為什麼不用MySql來解決秒殺操作?
因為Mysql執行update的減庫存比較低效,一條update操作的壓力測試結果是可以抗住4wQPS,也就是說,一個商品在1秒內,可以被買4w次;
看一下Java控制事務的行為分析:
(執行庫存減1操作)
Update table set num=num-1 where id=10 andnum>0,緊接著會進行一個inser購買明細的操作,然後commit/rollback;
然後第二個使用者Updatetable set num=num-1 where id=10 and num>0,緊接著等待行鎖,獲得鎖lock,來繼續執行,然後後面的使用者……
這樣下來的話,整個秒殺操作可以說是一種序列化的執行序列。
1.5.2 分析瓶頸所在
Update減庫存—>insert購買明細—>commit/rollback:這兩個過程都存在網路延遲和GC;但並非java和sql本身慢,而是java和通訊之間比較慢;
所以,java執行時間+網路延遲時間+GC=這行操作的執行時間(大概在2ms,1秒鐘有500次減操作,對於秒殺系統來說這個效能呈指數級下降,並不好)。
1.5.3 優化思路分析
我們知道行級鎖是在commit之後釋放的,那麼我們的優化方向就是減少行級鎖的持有時間。
同城機房需要花0.5-2msmax(1000qps),update之後JVM-GC(50ms) max(20qps);
異地機房一次(北京上海之間額一次update Sql需要20ms。
如何判斷update更新庫存成功?
兩個條件:——Update自身不報錯,客戶端確認影響記錄數
優化思路:
把客戶端邏輯放在Mysql服務端,避免網路延遲和GC影響。
那麼,如何把邏輯放在Mysql服務端呢?
1.5.4 兩種優化解決方案
(1)定製SQL方案:update/+[auto_commit]/,需要修改Mysql原始碼;這樣可以在SQL執行完之後,直接在服務端完成commit,不用客戶端邏輯判斷之後來執行是否commit/rollback。 但是這個增加了修改Mysql原始碼的成本(不推薦)。
(2)使用儲存過程:整個事務在MySQL端完成(把整個熱點執行放在一個過程當中一次性完成,只需要返回執行的整個結果就行了,這樣可以避免網路延遲和GC干擾)。
1.6 優化分析總結
前端控制:暴露介面(動靜態資料分離)
按鈕防重複(避免重複請求)
動靜態資料分離:CDN快取,後端快取(redis技術實現的查詢)。
事務競爭優化:減少事務鎖時間(用Mysql來解決)。
2.2 優化編碼
第一,在Pom.xml檔案引入Redis在Java環境下的客戶端Jedis.
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
第二,新增一個物件序列化的快取類RedisDao.java:
為什麼要使用物件序列化?
序列化的目的是將一個實現了Serializable介面的物件轉換成一個位元組序列,可以。 把該位元組序列儲存起來(例如:儲存在一個檔案裡),以後可以隨時將該位元組序列恢復為原來的物件。
序列化的物件佔原有空間的十分之一,壓縮速度可以達到兩個數量級,同時節省了CPU
Redis 快取物件時需要將其序列化,而何為序列化,實際上就是將物件以位元組形式儲存。這樣,不管物件的屬性是字串、整型還是圖片、視訊等二進位制型別,
都可以將其儲存在位元組陣列中。物件序列化後便可以持久化儲存或網路傳輸。需要還原物件時,只需將位元組陣列再反序列化即可。
/**
* User: cxhc.
* Date: 2017/10/5.
* Time: 上午 12:11.
* Explain: Redis操作的介面
*/
public interface BaseRedisDao<K, V> {
/**
* 使用者排序通過註冊時間的 權重值
* @param date
* @return
*/
double getCreateTimeScore(long date);
/**
* 獲取Redis中所有的鍵的key
* @return
*/
Set<K> getAllKeys();
/**
* 獲取所有的普通key-value
* @return
*/
Map<K,V> getAllString();
/**
* 獲取所有的Set -key-value
* @return
*/
Map<K,Set<V>> getAllSet();
/**
* 獲取所有的ZSet正序 -key-value 不獲取權重值
* @return
*/
Map<K,Set<V>> getAllZSetReverseRange();
/**
* 獲取所有的ZSet倒序 -key-value 不獲取權重值
* @return
*/
Map<K,Set<V>> getAllZSetRange();
/**
* 獲取所有的List -key-value
* @return
*/
Map<K,List<V>> getAllList();
/**
* 獲取所有的Map -key-value
* @return
*/
Map<K,Map<K,V>> getAllMap();
/**
* 新增一個list
* @param key
* @param objectList
*/
void addList(K key, List<V> objectList);
/**
* 向list中增加值
* @param key
* @param obj
* @return 返回在list中的下標
*/
long addList(K key,V obj);
/**
*
* 向list中增加值
* @param key
* @param obj
* @return 返回在list中的下標
*/
long addList(K key,V ...obj);
/**
*
* 輸出list
* @param key List的key
* @param s 開始下標
* @param e 結束的下標
* @return
*/
List<V> getList(K key, long s, long e);
/**
* 輸出完整的list
* @param key
*/
List<V> getList(K key);
/**
* 獲取list集合中元素的個數
* @param key
* @return
*/
long getListSize(K key);
/**
* 移除list中某值
* 移除list中 count個value為object的值,並且返回移除的數量,
* 如果count為0,或者大於list中為value為object數量的總和,
* 那麼移除所有value為object的值,並且返回移除數量
* @param key
* @param object
* @return 返回移除數量
*/
long removeListValue(K key,V object);
/**
* 移除list中某值
* @param key
* @param object
* @return 返回移除數量
*/
long removeListValue(K key,V... object);
/**
* 批量刪除key對應的value
* @param keys
*/
void remove(final K... keys);
/**
* 刪除快取
* 根據key精確匹配刪除
* @param key
*/
void remove(final K key);
/**
* 通過分數刪除ZSet中的值
* @param key
* @param s
* @param e
*/
void removeZSetRangeByScore(String key,double s , double e);
/**
* 設定Set的過期時間
* @param key
* @param time
* @return
*/
Boolean setSetExpireTime(String key,Long time);
/**
* 設定ZSet的過期時間
* @param key
* @param time
* @return
*/
Boolean setZSetExpireTime(String key,Long time);
/**
* 判斷快取中是否有key對應的value
* @param key
* @return
*/
boolean exists(final K key);
/**
* 讀取String快取 可以是物件
* @param key
* @return
*/
V get(final K key);
/**
* 讀取String快取 可以是物件
* @param key
* @return
*/
List<V> get(final K... key);
/**
* 讀取快取 可以是物件 根據正則表示式匹配
* @param regKey
* @return
*/
List<Object> getByRegular(final K regKey);
/**
* 寫入快取 可以是物件
* @param key
* @param value
*/
void set(final K key, V value);
/**
* 寫入快取
* @param key
* @param value
* @param expireTime 過期時間 -單位s
* @return
*/
void set(final K key, V value, Long expireTime);
/**
* 設定一個key的過期時間(單位:秒)
* @param key
* @param expireTime
* @return
*/
boolean setExpireTime(K key, Long expireTime);
/**
* 獲取key的型別
* @param key
* @return
*/
DataType getType(K key);
/**
* 刪除map中的某個物件
* @param key map對應的key
* @param field map中該物件的key
*/
void removeMapField(K key, V... field);
/*
* 獲取map物件
* @param key map對應的key
* @return
*/
Map<K,V> getMap(K key);
/*
* 獲取map物件
* @param key map對應的key
* @return
*/
Long getMapSize(K key);
/**
* 獲取map快取中的某個物件
* @param key map對應的key
* @param field map中該物件的key
* @return
*/
<T> T getMapField(K key, K field);
/**
* 判斷map中對應key的key是否存在
* @param key map對應的key
* @return
*/
Boolean hasMapKey(K key,K field);
/**
* 獲取map對應key的value
* @param key map對應的key
* @return
*/
List<V> getMapFieldValue(K key);
/**
* 獲取map的key
* @param key map對應的key
* @return
*/
Set<V> getMapFieldKey(K key);
/**
* 新增map
* @param key
* @param map
*/
void addMap(K key, Map<K,V> map);
/**
* 向key對應的map中新增快取物件
* @param key cache物件key
* @param field map對應的key
* @param value 值
*/
void addMap(K key, K field, Object value);
/**
* 向key對應的map中新增快取物件
* @param key cache物件key
* @param field map對應的key
* @param time 過期時間-整個MAP的過期時間
* @param value 值
*/
void addMap(K key, K field, V value,long time);
/**
* 向set中加入物件
* @param key 物件key
* @param obj 值
*/
void addSet(K key, V... obj);
/**
* 處理事務時鎖定key
* @param key
*/
void watch(String key);
/**
* 移除set中的某些值
* @param key 物件key
* @param obj 值
*/
long removeSetValue(K key, V obj);
/**
* 移除set中的某些值
* @param key 物件key
* @param obj 值
*/
long removeSetValue(K key, V... obj);
/**
* 獲取set的物件數
* @param key 物件key
*/
long getSetSize(K key);
/**
* 判斷set中是否存在這個值
* @param key 物件key
*/
Boolean hasSetValue(K key,V obj);
/**
* 獲得整個set
* @param key 物件key
*/
Set<V> getSet(K key);
/**
* 獲得set 並集
* @param key
* @param otherKey
* @return
*/
Set<V> getSetUnion(K key,K otherKey);
/**
* 獲得set 並集
* @param key
* @param set
* @return
*/
Set<V> getSetUnion(K key,Set<Object> set);
/**
* 獲得set 交集
* @param key
* @param otherKey
* @return
*/
Set<V> getSetIntersect(K key,K otherKey);
/**
* 獲得set 交集
* @param key
* @param set
* @return
*/
Set<V> getSetIntersect(K key,Set<Object> set);
/**
* 模糊移除 支援*號等匹配移除
* @param blears
*/
void removeBlear(K... blears);
/**
* 修改key名 如果不存在該key或者沒有修改成功返回false
* @param oldKey
* @param newKey
* @return
*/
Boolean renameIfAbsent(String oldKey,String newKey);
/**
* 模糊移除 支援*號等匹配移除
* @param blear
*/
void removeBlear(K blear);
/**
* 根據正則表示式來移除key-value
* @param blears
*/
void removeByRegular(String... blears);
/**
* 根據正則表示式來移除key-value
* @param blears
*/
void removeByRegular(String blears);
/**
* 根據正則表示式來移除 Map中的key-value
* @param key
* @param blears
*/
void removeMapFieldByRegular(K key,K... blears);
/**
* 根據正則表示式來移除 Map中的key-value
* @param key
* @param blear
*/
void removeMapFieldByRegular(K key,K blear);
/**
* 移除key 對應的value
* @param key
* @param value
* @return
*/
Long removeZSetValue(K key, V... value);
/**
* 移除key ZSet
* @param key
* @return
*/
void removeZSet(K key);
/**
*刪除,鍵為K的集合,索引start<=index<=end的元素子集
* @param key
* @param start
* @param end
* @return
*/
void removeZSetRange(K key,Long start,Long end);
/**
* 並集 將key對應的集合和key1對應的集合合併到key2中
* 如果分數相同的值,都會保留
* 原來key2的值會被覆蓋
* @param key
* @param key1
* @param key2
*/
void setZSetUnionAndStore(String key,String key1, String key2);
/**
* 獲取整個有序集合ZSET,正序
* @param key
*/
<T> T getZSetRange(K key);
/**
* 獲取有序集合ZSET
* 鍵為K的集合,索引start<=index<=end的元素子集,正序
* @param key
* @param start 開始位置
* @param end 結束位置
*/
<T> T getZSetRange(K key,long start,long end);
/**
* 獲取整個有序集合ZSET,倒序
* @param key
*/
Set<Object> getZSetReverseRange(K key);
/**
* 獲取有序集合ZSET
* 鍵為K的集合,索引start<=index<=end的元素子集,倒序
* @param key
* @param start 開始位置
* @param end 結束位置
*/
Set<V> getZSetReverseRange(K key,long start,long end);
/**
* 通過分數(權值)獲取ZSET集合 正序 -從小到大
* @param key
* @param start
* @param end
* @return
*/
Set<V> getZSetRangeByScore(String key, double start, double end);
/**
* 通過分數(權值)獲取ZSET集合 倒序 -從大到小
* @param key
* @param start
* @param end
* @return
*/
Set<V> getZSetReverseRangeByScore(String key, double start, double end);
/**
* 鍵為K的集合,索引start<=index<=end的元素子集
* 返回泛型介面(包括score和value),正序
* @param key
* @param start
* @param end
* @return
*/
Set<ZSetOperations.TypedTuple<V>> getZSetRangeWithScores(K key, long start, long end);
/**
* 鍵為K的集合,索引start<=index<=end的元素子集
* 返回泛型介面(包括score和value),倒序
* @param key
* @param start
* @param end
* @return
*/
Set<ZSetOperations.TypedTuple<V>> getZSetReverseRangeWithScores(K key, long start, long end);
/**
* 鍵為K的集合
* 返回泛型介面(包括score和value),正序
* @param key
* @return
*/
Set<ZSetOperations.TypedTuple<V>> getZSetRangeWithScores(K key);
/**
* 鍵為K的集合
* 返回泛型介面(包括score和value),倒序
* @param key
* @return
*/
Set<ZSetOperations.TypedTuple<V>> getZSetReverseRangeWithScores(K key);
/**
* 鍵為K的集合,sMin<=score<=sMax的元素個數
* @param key
* @param sMin
* @param sMax
* @return
*/
long getZSetCountSize(K key,double sMin,double sMax);
/**
* 獲取Zset 鍵為K的集合元素個數
* @param key
* @return
*/
long getZSetSize(K key);
/**
* 獲取鍵為K的集合,value為obj的元素分數
* @param key
* @param value
* @return
*/
double getZSetScore(K key,V value);
/**
* 元素分數增加,delta是增量
* @param key
* @param value
* @param delta
* @return
*/
double incrementZSetScore(K key,V value,double delta);
/**
* 新增有序集合ZSET
* 預設按照score升序排列,儲存格式K(1)==V(n),V(1)=S(1)
* @param key
* @param score
* @param value
* @return
*/
Boolean addZSet(String key ,double score, Object value);
/**
* 新增有序集合ZSET
* @param key
* @param value
* @return
*/
Long addZSet(K key,TreeSet<V> value);
/**
* 新增有序集合ZSET
* @param key
* @param score
* @param value
* @return
*/
Boolean addZSet(K key,double[] score, Object[] value);
}
redis dao 實現類
/**
* User: cxhc.
* Date: 2017/10/5.
* Time: 上午 12:51.
* Explain: Redis的工具類
* 增刪改 -不能在這裡面抓取異常 -因為可能有事務處理
*/
@Repository("redisHandle")
public class RedisHandle implements BaseRedisDao<String, Object> {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource(name="redisTemplate")
protected RedisTemplate redisTemplate;
/**
* 出異常,重複操作的次數
*/
private static Integer times = 5;
public double getCreateTimeScore(long date) {
return date/ 100000.0;
}
public Set<String> getAllKeys() {
return redisTemplate.keys("*");
}
public Map<String, Object> getAllString() {
Set<String> stringSet = getAllKeys();
Map<String, Object> map = new HashMap<String, Object>();
Iterator<String> iterator = stringSet.iterator();
while (iterator.hasNext()) {
String k = iterator.next();
if (getType(k) == DataType.STRING) {
map.put(k, get(k));
}
}
return map;
}
public Map<String, Set<Object>> getAllSet() {
Set<String> stringSet = getAllKeys();
Map<String, Set<Object>> map = new HashMap<String, Set<Object>>();
Iterator<String> iterator = stringSet.iterator();
while (iterator.hasNext()) {
String k = iterator.next();
if (getType(k) == DataType.SET) {
map.put(k, getSet(k));
}
}
return map;
}
public Map<String, Set<Object>> getAllZSetRange() {
Set<String> stringSet = getAllKeys();
Map<String, Set<Object>> map = new HashMap<String, Set<Object>>();
Iterator<String> iterator = stringSet.iterator();
while (iterator.hasNext()) {
String k = iterator.next();
if (getType(k) == DataType.ZSET) {
logger.debug("k:"+k);
map.put(k, getZSetRange(k));
}
}
return map;
}
public Map<String, Set<Object>> getAllZSetReverseRange() {
Set<String> stringSet = getAllKeys();
Map<String, Set<Object>> map = new HashMap<String, Set<Object>>();
Iterator<String> iterator = stringSet.iterator();
while (iterator.hasNext()) {
String k = iterator.next();
if (getType(k) == DataType.ZSET) {
map.put(k, getZSetReverseRange(k));
}
}
return map;
}
public Map<String, List<Object>> getAllList() {
Set<String> stringSet = getAllKeys();
Map<String, List<Object>> map = new HashMap<String, List<Object>>();
Iterator<String> iterator = stringSet.iterator();
while (iterator.hasNext()) {
String k = iterator.next();
if (getType(k) == DataType.LIST) {
map.put(k, getList(k));
}
}
return map;
}
public Map<String, Map<String, Object>> getAllMap() {
Set<String> stringSet = getAllKeys();
Map<String, Map<String, Object>> map = new HashMap<String, Map<String, Object>>();
Iterator<String> iterator = stringSet.iterator();
while (iterator.hasNext()) {
String k = iterator.next();
if (getType(k) == DataType.HASH) {
map.put(k, getMap(k));
}
}
return map;
}
public void addList(String key, List<Object> objectList) {
for (Object obj : objectList) {
addList(key, obj);
}
}
public long addList(String key, Object obj) {
return redisTemplate.boundListOps(key).rightPush(obj);
}
public long addList(String key, Object... obj) {
return redisTemplate.boundListOps(key).rightPushAll(obj);
}
public List<Object> getList(String key, long s, long e) {
return redisTemplate.boundListOps(key).range(s, e);
}
public List<Object> getList(String key) {
return redisTemplate.boundListOps(key).range(0, getListSize(key));
}
public long getListSize(String key) {
return redisTemplate.boundListOps(key).size();
}
public long removeListValue(String key, Object object) {
return redisTemplate.boundListOps(key).remove(0, object);
}
public long removeListValue(String key, Object... objects) {
long r = 0;
for (Object object : objects) {
r += removeListValue(key, object);
}
return r;
}
public void remove(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
remove(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
public void removeBlear(String... blears) {
for (String blear : blears) {
removeBlear(blear);
}
}
public Boolean renameIfAbsent(String oldKey, String newKey) {
return redisTemplate.renameIfAbsent(oldKey, newKey);
}
public void removeBlear(String blear) {
redisTemplate.delete(redisTemplate.keys(blear));
}
public void removeByRegular(String... blears) {
for (String blear : blears) {
removeBlear(blear);
}
}
public void removeByRegular(String blear) {
Set<String> stringSet = getAllKeys();
for (String s : stringSet) {
if (Pattern.compile(blear).matcher(s).matches()) {
redisTemplate.delete(s);
}
}
}
public void removeMapFieldByRegular(String key, String... blears) {
for (String blear : blears) {
removeMapFieldByRegular(key, blear);
}
}
public void removeMapFieldByRegular(String key, String blear) {
Map<String, Object> map = getMap(key);
Set<String> stringSet = map.keySet();
for (String s : stringSet) {
if (Pattern.compile(blear).matcher(s).matches()) {
redisTemplate.boundHashOps(key).delete(s);
}
}
}
public Long removeZSetValue(String key, Object... value) {
return redisTemplate.boundZSetOps(key).remove(value);
}
public void removeZSet(String key) {
removeZSetRange(key, 0L, getZSetSize(key));
}
public void removeZSetRange(String key, Long start, Long end) {
redisTemplate.boundZSetOps(key).removeRange(start, end);
}
public void setZSetUnionAndStore(String key,String key1, String key2) {
redisTemplate.boundZSetOps(key).unionAndStore(key1,key2);
}
public Set<Object> getZSetRange(String key) {
return getZSetRange(key, 0, getZSetSize(key));
}
public Set<Object> getZSetRange(String key, long s, long e) {
return redisTemplate.boundZSetOps(key).range(s, e);
}
public Set<Object> getZSetReverseRange(String key) {
return getZSetReverseRange(key, 0, getZSetSize(key));
}
public Set<Object> getZSetReverseRange(String key, long start, long end) {
return redisTemplate.boundZSetOps(key).reverseRange(start, end);
}
public Set<Object> getZSetRangeByScore(String key, double start, double end) {
return redisTemplate.boundZSetOps(key).rangeByScore(start, end);
}
public Set<Object> getZSetReverseRangeByScore(String key, double start, double end) {
return redisTemplate.boundZSetOps(key).reverseRangeByScore(start, end);
}
public Set<ZSetOperations.TypedTuple<Object>> getZSetRangeWithScores(String key, long start, long end) {
return redisTemplate.boundZSetOps(key).rangeWithScores(start, end);
}
public Set<ZSetOperations.TypedTuple<Object>> getZSetReverseRangeWithScores(String key, long start, long end) {
return redisTemplate.boundZSetOps(key).reverseRangeWithScores(start, end);
}
public Set<ZSetOperations.TypedTuple<Object>> getZSetRangeWithScores(String key) {
return getZSetRangeWithScores(key, 0, getZSetSize(key));
}
public Set<ZSetOperations.TypedTuple<Object>> getZSetReverseRangeWithScores(String key) {
return getZSetReverseRangeWithScores(key, 0, getZSetSize(key));
}
public long getZSetCountSize(String key, double sMin, double sMax) {
return redisTemplate.boundZSetOps(key).count(sMin, sMax);
}
public long getZSetSize(String key) {
return redisTemplate.boundZSetOps(key).size();
}
public double getZSetScore(String key, Object value) {
return redisTemplate.boundZSetOps(key).score(value);
}
public double incrementZSetScore(String key, Object value, double delta) {
return redisTemplate.boundZSetOps(key).incrementScore(value, delta);
}
public Boolean addZSet(String key, double score, Object value) {
return redisTemplate.boundZSetOps(key).add(value, score);
}
public Long addZSet(String key, TreeSet<Object> value) {
return redisTemplate.boundZSetOps(key).add(value);
}
public Boolean addZSet(String key, double[] score, Object[] value) {
if (score.length != value.length) {
return false;
}
for (int i = 0; i < score.length; i++) {
if (addZSet(key, score[i], value[i]) == false) {
return false;
}
}
return true;
}
public void remove(String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
}
public void removeZSetRangeByScore(String key,double s , double e) {
redisTemplate.boundZSetOps(key).removeRangeByScore(s,e);
}
public Boolean setSetExpireTime(String key, Long time) {
return redisTemplate.boundSetOps(key).expire(time, TimeUnit.SECONDS);
}
public Boolean setZSetExpireTime(String key, Long time) {
return redisTemplate.boundZSetOps(key).expire(time, TimeUnit.SECONDS);
}
public boolean exists(String key) {
return redisTemplate.hasKey(key);
}
public Object get(int key) {
return this.get(String.valueOf(key));
}
public Object get(long key) {
return this.get(String.valueOf(key));
}
public Object get(String key) {
return redisTemplate.boundValueOps(key).get();
}
public List<Object> get(String... keys) {
List<Object> list = new ArrayList<Object>();
for (String key : keys) {
list.add(get(key));
}
return list;
}
public List<Object> getByRegular(String regKey) {
Set<String> stringSet = getAllKeys();
List<Object> objectList = new ArrayList<Object>();
for (String s : stringSet) {
if (Pattern.compile(regKey).matcher(s).matches() && getType(s) == DataType.STRING) {
objectList.add(get(s));
}
}
return objectList;
}
public void set(long key, Object value) {
this.set(String.valueOf(key) ,value);
}
public void set(int key, Object value) {
this.set(String.valueOf(key) ,value);
}
public void set(String key, Object value) {
redisTemplate.boundValueOps(key).set(value);
}
public void set(String key, Object value, Long expireTime) {
redisTemplate.boundValueOps(key).set(value, expireTime, TimeUnit.SECONDS);
}
public boolean setExpireTime(String key, Long expireTime) {
return redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
}
public DataType getType(String key) {
return redisTemplate.type(key);
}
public void removeMapField(String key, Object... field) {
redisTemplate.boundHashOps(key).delete(field);
}
public Long getMapSize(String key) {
return redisTemplate.boundHashOps(key).size();
}
public Map<String, Object> getMap(String key) {
return redisTemplate.boundHashOps(key).entries();
}
public <T> T getMapField(String key, String field) {
return (T) redisTemplate.boundHashOps(key).get(field);
}
public Boolean hasMapKey(String key, String field) {
return redisTemplate.boundHashOps(key).hasKey(field);
}
public List<Object> getMapFieldValue(String key) {
return redisTemplate.boundHashOps(key).values();
}
public Set<Object> getMapFieldKey(String key) {
return redisTemplate.boundHashOps(key).keys();
}
public void addMap(String key, Map<String, Object> map) {
redisTemplate.boundHashOps(key).putAll(map);
}
public void addMap(String key, String field, Object value) {
redisTemplate.boundHashOps(key).put(field, value);
}
public void addMap(String key, String field, Object value, long time) {
redisTemplate.boundHashOps(key).put(field, value);
redisTemplate.boundHashOps(key).expire(time, TimeUnit.SECONDS);
}
public void watch(String key) {
redisTemplate.watch(key);
}
public void addSet(String key, Object... obj) {
redisTemplate.boundSetOps(key).add(obj);
}
public long removeSetValue(String key, Object obj) {
return redisTemplate.boundSetOps(key).remove(obj);
}
public long removeSetValue(String key, Object... obj) {
if (obj != null && obj.length > 0) {
return redisTemplate.boundSetOps(key).remove(obj);
}
return 0L;
}
public long getSetSize(String key) {
return redisTemplate.boundSetOps(key).size();
}
public Boolean hasSetValue(String key, Object obj) {
Boolean boo = null;
int t =0;
while (true){
try {
boo = redisTemplate.boundSetOps(key).isMember(obj);
break;
} catch (Exception e) {
logger.error("key[" + key + "],obj[" + obj + "]判斷Set中的值是否存在失敗,異常資訊:" + e.getMessage());
t++;
}
if(t>times){
break;
}
}
logger.info("key[" + key + "],obj[" + obj + "]是否存在,boo:" + boo);
return boo;
}
public Set<Object> getSet(String key) {
return redisTemplate.boundSetOps(key).members();
}
public Set<Object> getSetUnion(String key, String otherKey) {
return redisTemplate.boundSetOps(key).union(otherKey);
}
public Set<Object> getSetUnion(String key, Set<Object> set) {
return redisTemplate.boundSetOps(key).union(set);
}
public Set<Object> getSetIntersect(String key, String otherKey) {
return redisTemplate.boundSetOps(key).intersect(otherKey);
}
public Set<Object> getSetIntersect(String key, Set<Object> set) {
return redisTemplate.boundSetOps(key).intersect(set);
}
}
第三、配置fastjson序列化:
聽說某寶的fastjson的序列化和json庫效能挺好的下面整合一下這裡只配置一下序列化,具體springboot怎麼整合fastjson請看我的部落格列表。
首先自定義一個序列化的類
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
public FastJson2JsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return (T) JSON.parseObject(str, clazz);
}
}
在redis config中新增一個bean
@Bean
public RedisSerializer fastJson2JsonRedisSerializer() {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
return new FastJson2JsonRedisSerializer<Object>(Object.class);
}
配置序列化
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory, RedisSerializer fastJson2JsonRedisSerializer) {
StringRedisTemplate redisTemplate = new StringRedisTemplate(factory); redisTemplate.setConnectionFactory(redisConnectionFactory());
//redis 開啟事務
redisTemplate.setEnableTransactionSupport(true);
//hash 使用jdk 的序列化
redisTemplate.setHashValueSerializer(fastJson2JsonRedisSerializer/*new JdkSerializationRedisSerializer()*/);
//StringRedisSerializer key 序列化
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//keySerializer 對key的預設序列化器。預設值是StringSerializer
redisTemplate.setKeySerializer(new StringRedisSerializer());
// valueSerializer
redisTemplate.setValueSerializer(fastJson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
第四,編寫測試類
@Test
public void testQueryById() {
long id = 1000;
try {
Object seckillObj= redisHandle.get(id);
if(seckillObj==