如果有疑問或者感覺哪里有問題歡迎指點,一起探討。
一:選擇合適的jar包
選擇合適的jar包,而且如果spring和redis這兩個jar包版本不對應的話運行中會報錯。
以下是我使用的版本。
<jedis.version>2.8.1</jedis.version>
<commons.pool2.version>2.4.2</commons.pool2.version>
<spring.redis.version>1.7.5.RELEASE</spring.redis.version>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>${spring.redis.version}</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>${commons.pool2.version}</version>
</dependency>
查看spring對應的redis信息可以去 Maven Repository 官方查詢
選擇版本點進去在頁面下方可以看到對應的redis版本信息:
因為本人比較習慣使用spring低版本中的配置文件里的 JedisConnectionFactory 所以並沒有選擇使用高版本的 jar。
二:redis 配置文件信息
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!-- properties參數文件路徑 --> <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:config/*.properties</value> </list> </property> </bean> <!-- redis數據源 --> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!-- 最大空閑數 --> <property name="maxIdle" value="${redis.pool.maxIdle}" /> <!-- 最大空連接數 --> <property name="maxTotal" value="${redis.pool.maxTotal}" /> <!-- 最大等待時間 --> <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}" /> <!-- 返回連接時,檢測連接是否成功 --> <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" /> <!-- 返回數據時,檢測連接是否成功 --> <property name="testOnReturn" value="${redis.pool.testOnReturn}" /> </bean> <!-- Spring-redis連接池管理工廠 --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"> <!-- IP地址 --> <property name="hostName" value="${redis.master.ip}" /> <!-- 端口號 --> <property name="port" value="${redis.master.port}" /> <property name="password" value="${redis.master.password}" /> <!-- 超時時間 默認2000--> <property name="timeout" value="${redis.master.timeout}" /> <!-- 連接池配置引用 --> <property name="poolConfig" ref="poolConfig" /> <!-- usePool:是否使用連接池 --> <property name="usePool" value="true"/> </bean> <!-- redis template definition --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="jedisConnectionFactory" /> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="valueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> </property> <property name="hashKeySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="hashValueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> </property> <!--開啟事務 --> <!--<property name="enableTransactionSupport" value="true"></property>--> </bean> <!--自定義redis工具類,在需要緩存的地方注入此類 --> <bean id="redisService" class=" com.xxx.xxx.xxx.xxx.RedisUtils"> <constructor-arg name="redisTemplate" ref="redisTemplate" /> </bean> </beans>
三:redis.properties 數據文件參數
這里的參數是我們線上系統的使用參數,數值比較大,如果只是小系統使用可以設置的小一點,如果太小的話運行會報異常,按需調整就行。
#redis.master.ip=127.0.0.1 #redis.master.port=6379 #redis.master.password=123456 # JEDIS_BorrowPool:maxActive redis.pool.maxActive=1024 # JEDIS_BorrowPool:maxIdle redis.pool.maxIdle=200 # JEDIS_BorrowPool:maxTotal redis.pool.maxTotal=200 # JEDIS_BorrowPool:maxWaitMillis redis.pool.maxWaitMillis=7200 # JEDIS_BorrowPool:testOnBorrow redis.pool.testOnBorrow=false # JEDIS_BorrowPool:testOnReturn redis.pool.testOnReturn=false # JEDIS_Connection:timeout redis.master.timeout=10000
四:RedisUtils工具類
(其中指定 RedisSerializer 序列化,這個序列化程序序列化出的數據比較直觀,不會亂碼,在Redis Desktop Manager 軟件上可以直接查看數據詳情)。
package com.xxx.xxx.service.impl; import com.xxx.xxx.xxx.utils.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.core.RedisConnectionUtils; import org.springframework.data.redis.core.RedisOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.SessionCallback; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import redis.clients.jedis.Jedis; import java.text.SimpleDateFormat; import java.util.*; import java.util.concurrent.TimeUnit; /** * @author ShouSi * @Title: Redis工具類Service,使用Spring整合,redisTemplate:事務操作,匿名內部類操作 * @date 2021/12/03 17:51 */ public class RedisUtils { private static final Logger logger = LogManager.getLogger(RedisUtils.class); private static RedisTemplate<String, Object> redisTemplate; public RedisUtils(RedisTemplate redisTemplate) { RedisSerializer stringSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(stringSerializer); redisTemplate.setValueSerializer(stringSerializer); redisTemplate.setHashKeySerializer(stringSerializer); redisTemplate.setHashValueSerializer(stringSerializer); this.redisTemplate = redisTemplate; } /** * 獲取所有key,支持正則表達式 * @param key * @return Set<String> key */ public static Set<String> keys(String key) { Set<String> keys = null; if(StringUtils.isBlank(key)){ key = "*"; } try { keys = redisTemplate.keys(key); } catch (Exception e) { logger.error("keys:獲取所有key異常,key:{}", key,e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } return keys; } /** * 向key指向的set集合中插入若干條數據 * @param key * @param members */ public static void sadd(String key, String... members) { //redis操作發生異常時要把異常捕獲,不要響應正常的業務邏輯 try { Long add = redisTemplate.boundSetOps(key).add(members); } catch (Exception e) { logger.error("sadd:向緩存中添加數據時出現異常,key:{} value:{}", key, members,e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } } /** * 從key指向的set集合中取出所有數據並刪除此key指向的set集合 * @param key * @return */ @SuppressWarnings("unchecked") public static Set<String> smembersAndDel(String key) { Set<String> set = null; try { Object o = redisTemplate.execute(new SessionCallback<Object>() { @Override public Object execute(RedisOperations redisOperations) throws DataAccessException { redisOperations.multi(); //1、獲取數據 redisOperations.opsForSet().members(key); //2、刪除數據 redisOperations.delete(key); final List<Object> result = redisOperations.exec(); //返回vKey的值 return result.get(1).toString(); } }); set = (Set<String>) o ; } catch (Exception e) { logger.error("smembersAndDel:從緩存中取出數據或者刪除數據是出現異常", e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } return set; } // /** // * 添加一對key:value,並設置過期時間 // * @param key 鍵 // * @param expire 過期時間 // * @param value 值 // */ // public static Boolean setex(String key, int expire, String value) { // Boolean boo = false; // try { // boo = redisTemplate.opsForValue().setIfAbsent(key, value, expire); // // } catch (Exception e) { // logger.error("setex:向緩存中添加數據時出現異常,key:{},value:{},過期時間:{}秒", key, value, expire, e); // } finally { // RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); // } // return boo; // } /** * 獲得key指向的value * @param key * @return */ public static String get(String key) { String value = null; try { Object o = redisTemplate.opsForValue().get(key); value = o.toString(); } catch (Exception e) { logger.error("get:從緩存中獲取數據時出現異常,key:{},value:{}", key, value, e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } return value; } /** * 設置值,如果key存在就覆蓋原有的值 * @param key * @param value */ public static void set(String key, String value){ String resp = null; try { redisTemplate.opsForValue().set(key,value); } catch (Exception e) { logger.error("set:向緩存中添加數據時出現異常,key:{},value:{}", key, value, e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } } /** * 查看某個key的剩余生存時間,單位【秒】. * @param key * @return 永久生存返回-1 沒有該值返回-2 */ public static Long ttl(String key){ Long expire = null; try { expire = redisTemplate.opsForValue().getOperations().getExpire(key); } catch (Exception e) { logger.error("ttl:查詢key剩余過期時間出現異常,key:{}", key,e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } return expire; } /** * 查詢鍵是否存在 * @param key * @return boolean */ public static boolean exists (String key){ Boolean exist = null; try { exist = redisTemplate.hasKey(key); if(exist){ return true; } else { return false; } } catch (Exception e) { logger.error("exists:從緩存中判斷key是否存在時出現異常,key:{}", key,e); throw new RuntimeException(e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } } /** * 根據key的前綴刪除所有相關的key * @param keyPattern 參數支持正則表達式 */ public static void del(String keyPattern) { try { Set<String> keys = keys(keyPattern); if (keys != null && keys.size() > 0) { for (String key : keys) { redisTemplate.delete(key); } } } catch (Exception e) { logger.error("del:從緩存中刪除數據時出現異常,keyPattern:{}", keyPattern,e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } } /** * 給指定key設置過期時間,秒或毫秒 * @param keyPattern key * @param seconds 過期時間 * @param timeUnit TimeUtil */ public static Boolean expire(String keyPattern,int seconds,TimeUnit timeUnit) { Boolean expire = false; try { expire = redisTemplate.expire(keyPattern, seconds, timeUnit); } catch (Exception e) { logger.error("expire:設置到期時間異常,keyPattern:{},seconds:{}", keyPattern,seconds,e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } return expire; } /** * 給指定key設置過期時間 * @param keyPattern key * @param date 時間 */ public static Boolean expireAt(String keyPattern, Date date) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Boolean expire = false; try { expire = redisTemplate.expireAt(keyPattern, date); } catch (Exception e) { logger.error("expireAt:設置到期時間異常,keyPattern:{},date:{}", keyPattern,sdf.format(date),e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } return expire; } /** Hash散列操作 **/ /** Hash 獲取所有值 **/ public static List<Object> hvals(String keyPattern) { List<Object> hvals = null; try { hvals = redisTemplate.opsForHash().values(keyPattern); } catch (Exception e) { logger.error("hvals:從緩存中查詢has異常,keyPattern:{}", keyPattern,e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } return hvals; } /** Hash單個寫入 **/ public static void hset(String key,String field,String value){ try { redisTemplate.opsForHash().put(key,field,value); }catch (Exception e){ logger.error("hset:寫入緩存Hash異常,key:{}",key,e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } } /** Hash多個寫入 **/ public static Boolean hmset(String key, Map<String, String> valMap){ Boolean resp = false; try { redisTemplate.opsForHash().putAll(key,valMap); }catch (Exception e){ logger.error("hmset:寫入緩存Hash異常,key:{}",key,e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } return resp; } /** Hash不存在則寫入 **/ public static Boolean hsetnx(String key,String field,String value){ Boolean resp = false; try { resp = redisTemplate.opsForHash().putIfAbsent(key, field, value); }catch (Exception e){ logger.error("hsetnx:Hash寫入緩存異常,key:{}",key,e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } return resp; } /** Hash判斷字段是否存在 **/ public static boolean hexists(String key,String field){ Boolean resp = false; try { resp = redisTemplate.opsForHash().hasKey(key, field); }catch (Exception e){ logger.error("hexists:查詢緩存Hash異常,key:{}",key,e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } return resp; } /** * Hash:指定key,field獲取對應單條數據 * @param key * @param field * @return */ public static String hget(String key,String field){ String hget = null; try { hget = (String)redisTemplate.opsForHash().get(key, field); }catch (Exception e){ logger.error("hget:獲取單個字段值Hash異常,key:{},field:{}",key,field,e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } return hget; } /** * 返回哈希表中,一個或多個給定字段的值。 * 如果指定的字段不存在於哈希表,那么返回一個 nil 值。 * @param key * @param field * @return */ public static List<Object> hmget(String key,List<Object> field){ List<Object> hmget = null; try { hmget = redisTemplate.opsForHash().multiGet(key, field); }catch (Exception e){ logger.error("hmget:Hash獲取指定key,field數據緩存異常,key:{},field:{}",key,field,e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } return hmget; } /** * Hash:獲取指定key數據所有鍵值 * @param key * @return */ public static Map<Object, Object> hgetall(String key){ Jedis jedis = null; Map<Object, Object> resultMap = new HashMap<>(); try { resultMap = redisTemplate.opsForHash().entries(key); }catch (Exception e){ logger.error("hgetAll:Hash獲取指定key數據所有鍵值失敗,key:{}",key,e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } return resultMap; } /** * 刪除HASH緩存對應key下的單個field數據 * @param key * @param field */ public static Boolean hdel(String key,String field){ Boolean result = false; try { Long hdel = redisTemplate.opsForHash().delete(key, field); if(hdel.equals(1)){ result = true; } }catch (Exception e){ logger.error("hdel:Hash刪除緩存失敗,key:{},field:{}",key,field,e); } finally { RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory()); } return result; } }
為了方便使用,我這里專門做了一個Hask操作的key值參數類,這樣可以使數據更好的按照指定的維度進行分類,比如店鋪維度,后綴加MySql店鋪表id,一個店鋪一個Hash表。
五:RedisKeyAndField 類
package com.xxx.xxx.domain.jedis;
/**
* @description: RedisKeyAndField
* @author: ShouSi
* @createDate: 2021/10/21
*/
public class RedisKeyAndField {
public static final String Order_Info_ = "Order_Info_"; // redis 訂單 店鋪維度 _后綴加店鋪ID
public static final String OrderStock_Info_ = "OrderStock_Info_"; // redis 訂單商品明細 店鋪維度 _后綴加店鋪ID
public static final String OrderGift_Info_ = "OrderGift_Info_"; // redis 訂單贈品明細 店鋪維度 _后綴加店鋪ID
public static final String Shop_Info = "Shop_Info"; // redis 店鋪
public static final String ShopCross_Info = "ShopCross_Info"; // redis 跨境店鋪
public static final String Tenant_Info = "Tenant_Info"; // redis 租戶
public static final String Goods_Info = "Goods_Info"; // redis 商品資料
public static final String Stock_Info = "Stock_Info"; // redis 庫存
public static final String StockBatch_Info = "StockBatch_Info"; // redis 庫存批次
public static final String Pretreat_Info_ = "Pretreat_Info"; // redis 預設置策略 貨主維度 _后綴加tenantid
public static final String Paramset_Info = "Paramset_Info"; // redis 參數配置
public static final String Activity_Info_ = "Activity_Info_"; // redis 贈送策略 貨主維度 _后綴加tenantid
public static final String StockPolicy1_ = "StockPolicy1_"; // redis 自動發貨策略
public static final String StockPolicy2_ = "StockPolicy2_"; // redis 自動駁回策略
public static final String StockPolicy3_ = "StockPolicy3_"; // redis 自動合並策略
public static final String StockPolicy4_ = "StockPolicy4_"; // redis 自動拆分策略
}
注意
店鋪信息,賬號信息這種不經常更改的信息如果要放到redis上,如果系統有修改或者新增的操作,要同步更新redis,並且在redis上盡量給這種數據一個過期時間,過期后在查詢的時候如果沒有數據,去數據庫查,並且同步一份到redis。