對於redis的儲存key/value鍵值對,經過多次踩坑之後,我們總結了一套規則;這篇文章主要講解定義key/value鍵值對時的定義規則和注意事項。
前面一篇文章講了如何定義Redis的客戶端和Dubbo整合儲存器;當我們真正開始開發的時候,就會突然發現,有點不知道如何去定義Redis的Key和Value值格式,不要著急,馬上我們就可以明白如何去定義和使用,下面我們開始講解如何定義。
1、redis value 值格式
在Java常規開發中,我們需要有物件導向的思想,相對於物件來說,比較常用且能快速轉換的格式就是 JSON
了;比較常用的Java處理JSON資料有三個比較流行的類庫FastJSON
、Gson
和Jackson
,他們的優劣點這裡我就不一一介紹了,本文用阿里的FastJSON
。
上面提到了JSON
,這是因為在Redis的儲存中,我們使用它來儲存value值,為什麼要這樣做呢?主要是因為json格式有如下幾種好處:
· 1.標準,主流資料交換格式
· 2.簡單,結構清晰,相對於XML來說更加的輕量級,易於解析
· 3.語言無關,任何語言都能輕鬆搞它
· 4.型別安全,值是有型別的,比如整數、字串、布林等
複製程式碼
下面我們來看看如何使用json來儲存value,程式碼如下:
/**
* 在redis資料庫中插入 key和value 並且設定過期時間
*
* @param key k
* @param value v
* @param exp 過期時間 s
* @return boolean
*/
@Override
public boolean set(String key, V value, int exp) {
Jedis jedis = null;
// 將 value 轉換成 json 物件
// String jKey = JSON.toJSONString(key);
String jValue = JSON.toJSONString(value);
// 操作是否成功
boolean isSucess = true;
if (StringUtils.isEmpty(key)) {
LOG.info("key is empty");
return false;
}
try {
// 獲取客戶端物件
jedis = redisCache.getResource();
// 執行插入
jedis.setex(key, exp, jValue);
} catch (Exception e) {
LOG.info("client can't connect server");
isSucess = false;
if (null != jedis) {
// 釋放jedis物件
redisCache.brokenResource(jedis);
}
return false;
} finally {
if (isSucess) {
// 返還連線池
redisCache.returnResource(jedis);
}
}
return true;
}
複製程式碼
程式碼中redis value在儲存前我們對其做了一次轉換,將物件V轉換為json物件後儲存;下面我們來看看在redis中value值的格式:
上面我們可以看到在redis視覺化工具rdm(Redis Desktop Manager)中,key鍵對應的value用json非常清晰的顯示出來了,非常方便我們查閱redis中儲存的資料。
不知道大家注意到沒有,上面的程式碼中有一行我是註釋掉了,程式碼如下:
// String jKey = JSON.toJSONString(key);
複製程式碼
這一行的意思是將key鍵也json化,不是說json非常友好嗎?那為什麼要註釋這一行呢?下面為大家解釋為何要這樣做。
首先,使用json格式的資料都會變成一個josn格式的String字串,比如 "zhangsan"
,當這個字串作為key儲存時,預設會帶有json的特性,那就是雙引號 ""
也會帶入到redis的key設定中,所以在rdm中我們看到的key值都會預設帶上 ""
,這樣著實不是非常的美觀,特別是對於我們的複雜業務而言,後面會給大家講如何在rdm中使用key值規則定義業務線資料夾。
2、redis key 鍵格式
上面講了簡單的key儲存,如 zhangsan
的儲存,此時普通的需求可以滿足;然而在實際業務中,往往key鍵的儲存會非常的複雜,比如我們現在有一個需求:
需求:根據基礎資料系統中的資料字典型別查詢對應的字典集合
複製程式碼
這時,我們需要關注的業務就變得複雜了,就不能使用常規的key鍵儲存方式,上面的需求大致可以拆分為:
1.系統:基礎資料系統
2.模組:資料字典
3.方法:根據資料字典型別查詢
4.引數:字典型別
複製程式碼
為什麼要這樣拆分呢?為了可讀性;也為了抽象出key儲存規則;因為業務複雜情況下,我們定義的key鍵太多時就不便於管理,也不便於查詢,以 系統-模組-方法-引數
這樣的規則定義,我們可以很清晰的瞭解redis key儲存的值是做了什麼事情,而且rdm中也可以以此來分組,後面會講到。
下面貼上根據此規則定義抽象出的redis工具類:
package com.yclimb.mdm.redis;
/**
* Redis 工具類
*
* @author yclimb
* @date 2018/4/19
*/
public class RedisUtils {
/**
* 主資料系統標識
*/
public static final String KEY_PREFIX = "mdm";
/**
* 分割字元,預設[:],使用:可用於rdm分組檢視
*/
private static final String KEY_SPLIT_CHAR = ":";
/**
* redis的key鍵規則定義
* @param module 模組名稱
* @param func 方法名稱
* @param args 引數..
* @return key
*/
public static String keyBuilder(String module, String func, String... args) {
return keyBuilder(null, module, func, args);
}
/**
* redis的key鍵規則定義
* @param module 模組名稱
* @param func 方法名稱
* @param objStr 物件.toString()
* @return key
*/
public static String keyBuilder(String module, String func, String objStr) {
return keyBuilder(null, module, func, new String[]{objStr});
}
/**
* redis的key鍵規則定義
* @param prefix 專案字首
* @param module 模組名稱
* @param func 方法名稱
* @param objStr 物件.toString()
* @return key
*/
public static String keyBuilder(String prefix, String module, String func, String objStr) {
return keyBuilder(prefix, module, func, new String[]{objStr});
}
/**
* redis的key鍵規則定義
* @param prefix 專案字首
* @param module 模組名稱
* @param func 方法名稱
* @param args 引數..
* @return key
*/
public static String keyBuilder(String prefix, String module, String func, String... args) {
// 專案字首
if (prefix == null) {
prefix = KEY_PREFIX;
}
StringBuilder key = new StringBuilder(prefix);
// KEY_SPLIT_CHAR 為分割字元
key.append(KEY_SPLIT_CHAR).append(module).append(KEY_SPLIT_CHAR).append(func);
for (String arg : args) {
key.append(KEY_SPLIT_CHAR).append(arg);
}
return key.toString();
}
/**
* redis的key鍵規則定義
* @param redisEnum 列舉物件
* @param objStr 物件.toString()
* @return key
*/
public static String keyBuilder(RedisEnum redisEnum, String objStr) {
return keyBuilder(redisEnum.getKeyPrefix(), redisEnum.getModule(), redisEnum.getFunc(), objStr);
}
}
複製程式碼
上面程式碼中有此文字描述 分割字元,預設[:],使用:可用於rdm分組檢視
;redis key預設使用冒號分割,好處在於可以在rdm中以資料夾的形式分組檢視,如圖:
3、使用列舉類來定義規則
上面的工具類中的有如下程式碼,使用了列舉的形式來賦值:
/**
* redis的key鍵規則定義
* @param redisEnum 列舉物件
* @param objStr 物件.toString()
* @return key
*/
public static String keyBuilder(RedisEnum redisEnum, String objStr) {
return keyBuilder(redisEnum.getKeyPrefix(), redisEnum.getModule(), redisEnum.getFunc(), objStr);
}
複製程式碼
下面是列舉類程式碼:
package com.yclimb.mdm.redis;
/**
* Redis 列舉類
*
* @author yclimb
* @date 2018/4/19
*/
public enum RedisEnum {
/**
* 資料字典Service - 根據字典型別查詢字典資料
*/
MDM_MSTDATADICTIONARYSERVICE_QUERYLISTBYENTITYREDIS(
RedisUtils.KEY_PREFIX, "MstDataDictionaryService", "queryListByEntityRedis", "資料字典Redis快取");
/**
* 系統標識
*/
private String keyPrefix;
/**
* 模組名稱
*/
private String module;
/**
* 方法名稱
*/
private String func;
/**
* 描述
*/
private String remark;
RedisEnum(String keyPrefix, String module, String func, String remark) {
this.keyPrefix = keyPrefix;
this.module = module;
this.func = func;
this.remark = remark;
}
getter and setter....
}
複製程式碼
使用上面的列舉類,可以根據模組和方法定義需要的列舉型別,便於管理和維護,使用起來也非常方便,使用程式碼如下:
@Override
public List<MstDataDictionary> queryListByEntityRedis(MstDataDictionary mstDataDictionary) {
// redis key 獲取
String redisKey = RedisUtils.keyBuilder(RedisEnum.MDM_MSTDATADICTIONARYSERVICE_QUERYLISTBYENTITYREDIS, (null == mstDataDictionary ? "" : mstDataDictionary.toString()));
// 查詢redis快取
List<MstDataDictionary> mstDataDictionaryList = (List<MstDataDictionary>) redisCacheService.get(redisKey);
// 如果沒有快取則查詢資料庫後賦值
if (mstDataDictionaryList == null || mstDataDictionaryList.size() <= 0) {
mstDataDictionaryList = mstDataDictionaryMapper.queryListByEntity(mstDataDictionary);
redisCacheService.set(redisKey, mstDataDictionaryList);
}
return mstDataDictionaryList;
}
複製程式碼
OK,到這裡就差不多講完了,根據上面所說的分模組方式,自定義redis的key鍵名稱,value值格式使用json來儲存;
對於key鍵的規則定義也可以使用 Constants
常量類來實現,具體規則看個人愛好和需求。
結語
具體的定義規則工具類程式碼都在上面了,也是原始碼。
到此本文就結束了,關注公眾號檢視更多推送!!!