Redis的配置項清單如下:
(1)port:端口配置項,查看和設置Redis監聽端口,默認端口爲6379。
(2)bind:主機地址配置項,查看和綁定的主機地址,默認地址的值爲127.0.0.1。
(3)timeout:連接空閒多長要關閉連接,表示客戶端閒置一段時間後,要關閉連接。如果指定爲0,表示時長不限制。這個選項的默認值爲0,表示默認不限制連接的空閒時長。
(4)dbfilename:指定保存緩存數據庫的本地文件名,默認值爲dump.rdb。
(5)dir:指定保存緩存數據的本地文件所存放的目錄,默認值爲安裝目錄。
(6)rdbcompression:指定存儲至本地數據庫時是否壓縮數據,默認爲yes,Redis採用LZF壓縮,如果爲了節省CPU時間,可以關閉該選項,但會導致數據庫文件變得巨大。
(7)save:指定在多長時間內,有多少次Key-Value更新操作,就將數據同步到本地數據庫文件。save配置項的格式爲save<seconds><changes>:seconds表示時間段的長度,changes表示變化的次數。
(8)requirepass:設置Redis連接密碼,如果配置了連接密碼,客戶端在連接Redis時需要通過AUTH<password>命令提供密碼,默認這個選項是關閉"
(9)slaveof:在主從複製的模式下,設置當前節點爲slave(從)節點時,設置master(主)節點的IP地址及端口,在Redis啓動時,它會自動從master(主)節點進行數據同步。如果已經是slave(從)服務器,則會丟掉舊數據集,從新的master主服務器同步緩存數據。
設置爲slave節點命令的格式爲:slaveof<masterip><masterport>
(10)masterauth:在主從複製的模式下,當master(主)服務器節點設置了密碼保護時,slave(從)服務器連接master(主)服務器的密碼。
master服務器節點設置密碼的格式爲:masterauth<master-password>
(11)databases:設置緩存數據庫的數量,默認數據庫數量爲16個。databases配置選項,可以設置多個緩存數據庫,不同的數據庫存放不同應用的緩存數據。當清除緩存數據時,使用flushdb命令,只會清除當前數據庫中的數據,而不會影響到其他數據庫;而flushall命令,則會清除這個Redis實例所有數據庫(從0-15)的數據。
在Java編程中,配置連接Redis的uri連接字符串時,可以指定到具體的數據庫,格式爲: redis://用戶名:密碼@host:port/Redis庫。
通過安裝目錄下的redis-cli命令客戶端,可以連接到Redis本地服務。如果需要在遠程Redis服務上執行命令,我們使用的也是redis-cli命令。Windows/Linux命令的格式爲: redis-cli -h host -p port -a password。
Redis中有5種數據類型:String(字符串類型)、Hash(哈希類型)、List(列表類型)、Set(集合類型)、Zset(有序集合類型)。
Redis,建議使用冒號作爲superkey和subkey直接的分隔符,如下: superkey:subkey:subsubkey:subsubsubkey:……。
Key的命名規範使用冒號分割,大致的優勢如下:
(1)方便分層展示。Redis的很多客戶端可視化管理工具,如Redis Desktop Manager,是以冒號作爲分類展示的,方便快速查到要查閱的Redis Key對應的Value值。
(2)方便刪除與維護。可以對於某一層次下面的Key,使用通配符進行批量查詢和批量刪除。
Jedis開源庫提供了一個負責管理Jedis連接對象的池,名爲JedisPool類,位於redis.clients.jedis包中。
- JedisPoolConfig配置類重要參數:
(1)maxTotal:資源池中最大的連接數,默認值爲8。
(2)maxIdle:資源池允許最大空閒的連接數,默認值爲8。
(3)minIdle:資源池確保最少空閒的連接數,默認值爲0。如果JedisPool開啓了空閒連接的有效性檢測,如果空閒連接無效,就銷燬。銷燬連接後,連接數量就少了,如果小於minIdle數量,就新建連接,維護數量不少於minIdle的數量。minIdle確保了線程池中有最小的空閒Jedis實例的數量。
(4)blockWhenExhausted:當資源池用盡後,調用者是否需要等待,默認true。當爲true時,maxWaitMillis纔會生效。
(5)maxWaitMillis:資源池連接用盡後最大等待時間,默認值-1,表示永遠不超時,不建議使用默認值。
(6)testOnBorrow:向資源池借用連接時,是否做有效性檢測(ping命令),如果是無效連接,會被移除,默認值爲false,表示不做檢測。如果爲true,則得到的Jedis實例均是可用的。在業務量小的應用場景,建議設置爲true,確保連接可用;在業務量很大的應用場景,建議設置爲false(默認值),少一次ping命令的開銷,有助於提升性能。
(7)testOnReturn:向資源池歸還連接時,是否做有效性檢測(ping命令),如果是無效連接,會被移除,默認值爲false,表示不做檢測。同樣,在業務量很大的應用場景,建議設置爲false(默認值),少一次ping命令的開銷。
(8)testWhileIdle:如果爲true,表示用一個專門的線程對空閒的連接進行有效性的檢測掃描,如果有效性檢測失敗,即表示無效連接,會從資源池中移除。
(9)timeBetweenEvictionRunsMillis:表示兩次空閒連接掃描的活動之間,要睡眠的毫秒數,默認爲30000毫秒,也就是30秒鐘。
(10)minEvictableIdleTimeMillis:表示一個Jedis連接至少停留在空閒狀態的最短時間,然後才能被空閒連接掃描線程進行有效性檢測,默認值爲60000毫秒,即60秒。
(11)numTestsPerEvictionRun:表示空閒檢測線程每次最多掃描的Jedis連接數,默認值爲-1,表示掃描全部的空閒連接。
(12)jmxEnabled:是否開啓jmx監控,默認值爲true,建議開啓。
RedisPool實例: public class JredisPoolBuilder { private static JedisPool pool = null; static { buildPool(); //創建連接池 hotPool();//預熱連接池 } //創建連接池 private static JedisPool buildPool() { if (pool == null) { long start = System.currentTimeMillis(); JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(50); config.setMaxIdle(50); config.setMaxWaitMillis(1000 * 10); // 在borrow一個jedis實例時,是否提前進行validate操作; // 如果爲true,則得到的jedis實例均是可用的; config.setTestOnBorrow(true); //new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH); pool = new JedisPool(config, "127.0.0.1", 6379, 10000); long end = System.currentTimeMillis(); Logger.info("buildPool 毫秒數:", end - start); } return pool; } //獲取連接 public synchronized static Jedis getJedis() { return pool.getResource(); } //連接池的預熱 public static void hotPool() { long start = System.currentTimeMillis(); List<Jedis> minIdleJedisList = new ArrayList<Jedis>(MAX_IDLE); Jedis jedis = null; for (int i = 0; i < MAX_IDLE; i++) { try { jedis = pool.getResource(); minIdleJedisList.add(jedis); jedis.ping(); } catch (Exception e) { Logger.error(e.getMessage()); } finally { } } for (int i = 0; i < MAX_IDLE; i++) { try { jedis = minIdleJedisList.get(i); jedis.close(); } catch (Exception e) { Logger.error(e.getMessage()); } finally { } } long end = System.currentTimeMillis(); Logger.info("hotPool 毫秒數:", end - start); } } |
- 一般的Java開發,都會使用了Spring框架,可以使用spring-data-redis開源庫來簡化Redis操作的代碼邏輯,做到最大程度的業務聚焦。
1)使用spring-data-redis庫的第一步是,要在Maven的pom文件中加上spring-data-redis庫的依賴。
2)使用spring-data-redis庫的第二步,即配置spring-data-redis庫的連接池實例和RedisTemplate模板實例。spring-data-redis庫在JedisPool提供連接池的基礎上封裝了自己的連接池——RedisConnectionFactory連接工廠;並且spring-data-redis封裝了一個短期、非線程安全的連接類,名爲RedisConnection連接類。
- RedisConnection的API命令操作的對象都是字節級別的Key鍵和Value值。爲了更進一步地減少開發的工作,spring-data-redis庫在RedisConnection連接類的基礎上,針對不同的緩存類型,設計了五大數據類型的命令API集合,用於完成不同類型的數據緩存操作,並封裝在RedisTemplate模板類。
- RedisTemplate模板類位於核心包org.springframework.data.redis.core中,它封裝了五大數據類型的命令API集合:
(1)ValueOperations字符串類型操作API集合。
(2)ListOperations列表類型操作API集合。
(3)SetOperations集合類型操作API集合。
(4)ZSetOperations有序集合類型API集合。
(5)HashOperations哈希類型操作API集合。
- RedisConnection連接類更加底層,它負責二進制層面的Redis操作,Key、Value都是二進制字節數組。而RedisTemplate模板類,在RedisConnection的基礎上,使用在spring-redis.xml中配置的序列化、反序列化的工具類,完成上層類型(如String、Object、POJO類等)的Redis操作。
- Spring的Redis緩存註解
(1)@CachePut作用是設置緩存。先執行方法,並將執行結果緩存起來。Redis緩存中的Key鍵即爲@CachePut註解配置的key屬性值,一般是一個字符串,或者是結果爲字符串的一個SpEL(SpringEL)。一般來說,可以給@CachePut設置三個屬性,Value、Key和Condition。
(2)@CacheEvict的作用是刪除緩存。在執行方法前,刪除緩存。使用@CacheEvict註解清除緩存時,可以通過合理配置清除指定Cache名稱下的所有Key。
(3)@Cacheable的作用更多是查詢緩存。首先檢查註解中的Key鍵是否在緩存中,如果是,則返回Key的緩存值,不再執行方法;否則,執行方法並將方法結果緩存。
(4)@Caching註解,是一個緩存處理的組合註解。通過@Caching,可以一次指定多個Spring Cache註解的組合。@Caching註解擁有三個屬性:cacheable、put和evict。
Redis緩存的一些操作實例: public class CacheOperationService { private RedisTemplate redisTemplate; public void setRedisTemplate(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } // --------------RedisTemplate 基礎操作 -------------------- /** * 取得指定格式的所有的key * @param patens 匹配的表達式 * @return key 的集合 */ public Set getKeys(Object patens) { try { return redisTemplate.keys(patens); } catch (Exception e) { e.printStackTrace(); return null; } }
/** * 指定緩存失效時間 * * @param key 鍵 * @param time 時間(秒) * @return */ public boolean expire(String key, long time) { try { if (time > 0) { redisTemplate.expire(key, time, TimeUnit.SECONDS); } return true; } catch (Exception e) { e.printStackTrace(); return false; } }
/** * 根據key 獲取過期時間 * * @param key 鍵 不能爲null * @return 時間(秒) 返回0代表爲永久有效 */ public long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); }
/** * 判斷key是否存在 * * @param key 鍵 * @return true 存在 false不存在 */ public boolean hasKey(String key) { try { return redisTemplate.hasKey(key); } catch (Exception e) { e.printStackTrace(); return false; } }
/** * 刪除緩存 * * @param key 可以傳一個值 或多個 * @return 刪除的個數 */ public void del(String... key) { if (key != null && key.length > 0) { if (key.length == 1) { redisTemplate.delete(key[0]); } else { redisTemplate.delete(CollectionUtils.arrayToList(key)); } } } // --------------RedisTemplate 操作 String -------------------- /** * 普通緩存獲取 * * @param key 鍵 * @return 值 */ public Object get(String key) { return key == null ? null : redisTemplate.opsForValue().get(key); }
/** * 普通緩存放入 * * @param key 鍵 * @param value 值 * @return true成功 false失敗 */ public boolean set(String key, Object value) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; }
}
/** * 普通緩存放入並設置時間 * * @param key 鍵 * @param value 值 * @param time 時間(秒) time要大於0 如果time小於等於0 將設置無限期 * @return true成功 false 失敗 */ public boolean set(String key, Object value, long time) { try { if (time > 0) { redisTemplate.opsForValue() .set(key, value, time, TimeUnit.SECONDS); } else { set(key, value); } return true; } catch (Exception e) { e.printStackTrace(); return false; } }
/** * 遞增 * * @param key 鍵 * @param delta 自增因子,要增加幾(大於0) * @return 自增的結果 */ public long incr(String key, long delta) { if (delta < 0) { throw new RuntimeException("自增因子必須大於0"); } return redisTemplate.opsForValue().increment(key, delta); }
/** * 自減 * * @param key 鍵 * @param delta 自減, 要減少幾(小於0) * @return */ public long decr(String key, long delta) { if (delta < 0) { throw new RuntimeException("自減因子必須大於0"); } return redisTemplate .opsForValue().increment(key, -delta); }
// --------------RedisTemplate 操作 Map --------------------
/** * HashGet * * @param key 鍵 不能爲null * @param field 項 不能爲null * @return 值 */ public Object hget(String key, String field) { return redisTemplate.opsForHash().get(key, field); }
/** * 獲取hashKey對應的所有鍵值 * * @param key 鍵 * @return 對應的多個鍵值 */ public Map<Object, Object> hmget(String key) { return redisTemplate.opsForHash().entries(key); }
/** * HashSet * * @param key 鍵 * @param map 對應多個鍵值 * @return true 成功 false 失敗 */ public boolean hmset(String key, Map<String, Object> map) { try { redisTemplate.opsForHash().putAll(key, map); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
/** * HashSet 並設置時間 * * @param key 鍵 * @param map 對應多個鍵值 * @param time 時間(秒) * @return true成功 false失敗 */ public boolean hmset(String key, Map<String, Object> map, long time) { try { redisTemplate.opsForHash().putAll(key, map); if (time > 0) { expire(key, time); } return true; } catch (Exception e) { e.printStackTrace(); return false; } }
/** * 向一張hash表中放入數據,如果不存在將創建 * * @param key 鍵 * @param field 項 * @param value 值 * @return true 成功 false失敗 */ public boolean hset(String key, String field, Object value) { try { redisTemplate.opsForHash().put(key, field, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
/** * 向一張hash表中放入數據,如果不存在將創建 * * @param key 鍵 * @param field 項 * @param value 值 * @param time 時間(秒) 注意:如果已存在的hash表有時間,這裏將會替換原有的時間 * @return true 成功 false失敗 */ public boolean hset(String key, String field, Object value, long time) { try { redisTemplate.opsForHash().put(key, field, value); if (time > 0) { expire(key, time); } return true; } catch (Exception e) { e.printStackTrace(); return false; } }
/** * 刪除hash表中的值 * * @param key 鍵 不能爲null * @param field 項 可以使多個 不能爲null */ public void hdel(String key, Object... field) { redisTemplate.opsForHash().delete(key, field); }
/** * 判斷hash表中是否有該項的值 * * @param key 鍵 不能爲null * @param field 項 不能爲null * @return true 存在 false不存在 */ public boolean hHasKey(String key, String field) { return redisTemplate.opsForHash().hasKey(key, field); }
/** * hash自增 如果不存在,就會創建一個 並把新增後的值返回 * * @param key 鍵 * @param field 項 * @param by 要增加幾(大於0) * @return */ public double hincr(String key, String field, double by) { return redisTemplate.opsForHash().increment(key, field, by); }
/** * hash自減 * * @param key 鍵 * @param field 項 * @param by 要減少記(小於0) * @return */ public double hdecr(String key, String field, double by) { return redisTemplate.opsForHash().increment(key, field, -by); }
// --------------RedisTemplate 操作 Set --------------------
/** * 根據key獲取Set中的所有值 * * @param key 鍵 * @return */ public Set<Object> sGet(String key) { try { return redisTemplate.opsForSet().members(key); } catch (Exception e) { e.printStackTrace(); return null; } }
/** * 根據value從一個set中查詢,是否存在 * * @param key 鍵 * @param value 值 * @return true 存在 false不存在 */ public boolean sHasKey(String key, Object value) { try { return redisTemplate.opsForSet().isMember(key, value); } catch (Exception e) { e.printStackTrace(); return false; } }
/** * 將數據放入set緩存 * * @param key 鍵 * @param values 值 可以是多個 * @return 成功個數 */ public long sSet(String key, Object... values) { try { return redisTemplate.opsForSet().add(key, values); } catch (Exception e) { e.printStackTrace(); return 0; } }
/** * 將set數據放入緩存 * * @param key 鍵 * @param time 時間(秒) * @param values 值 可以是多個 * @return 成功個數 */ public long sSetAndTime(String key, long time, Object... values) { try { Long count = redisTemplate.opsForSet().add(key, values); if (time > 0) expire(key, time); return count; } catch (Exception e) { e.printStackTrace(); return 0; } }
/** * 獲取set緩存的長度 * * @param key 鍵 * @return */ public long sGetSetSize(String key) { try { return redisTemplate.opsForSet().size(key); } catch (Exception e) { e.printStackTrace(); return 0; } }
/** * 移除值爲value的 * * @param key 鍵 * @param values 值 可以是多個 * @return 移除的個數 */ public long setRemove(String key, Object... values) { try { Long count = redisTemplate.opsForSet().remove(key, values); return count; } catch (Exception e) { e.printStackTrace(); return 0; } } // --------------RedisTemplate 操作list --------------------
/** * 獲取list緩存的內容 * * @param key 鍵 * @param start 開始 * @param end 結束 0 到 -1代表所有值 * @return */ public List<Object> lGet(String key, long start, long end) { try { return redisTemplate.opsForList().range(key, start, end); } catch (Exception e) { e.printStackTrace(); return null; } }
/** * 獲取list緩存的長度 * * @param key 鍵 * @return */ public long lGetListSize(String key) { try { return redisTemplate.opsForList().size(key); } catch (Exception e) { e.printStackTrace(); return 0; } }
/** * 通過索引 獲取list中的值 * * @param key 鍵 * @param index 索引 index>=0時, 0 表頭,1 第二個元素,依次類推;index<0時,-1,表尾,-2倒數第二個元素,依次類推 * @return */ public Object lGetIndex(String key, long index) { try { return redisTemplate.opsForList().index(key, index); } catch (Exception e) { e.printStackTrace(); return null; } }
/** * 將list放入緩存 * * @param key 鍵 * @param value 值 * @return */ public boolean lSet(String key, Object value) { try { redisTemplate.opsForList().rightPush(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
/** * 將list放入緩存 * * @param key 鍵 * @param value 值 * @param time 時間(秒) * @return */ public boolean lSet(String key, Object value, long time) { try { redisTemplate.opsForList().rightPush(key, value); if (time > 0) expire(key, time); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
/** * 將list放入緩存 * * @param key 鍵 * @param value 值 * @return */ public boolean lSet(String key, List<Object> value) { try { redisTemplate.opsForList().rightPushAll(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
/** * 將list放入緩存 * * @param key 鍵 * @param value 值 * @param time 時間(秒) * @return */ public boolean lSet(String key, List<Object> value, long time) { try { redisTemplate.opsForList().rightPushAll(key, value); if (time > 0) expire(key, time); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
/** * 根據索引修改list中的某條數據 * * @param key 鍵 * @param index 索引 * @param value 值 * @return */ public boolean lUpdateIndex(String key, long index, Object value) { try { redisTemplate.opsForList().set(key, index, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
/** * 移除N個值爲value * * @param key 鍵 * @param count 移除多少個 * @param value 值 * @return 移除的個數 */ public long lRemove(String key, long count, Object value) { try { Long remove = redisTemplate.opsForList().remove(key, count, value); return remove; } catch (Exception e) { e.printStackTrace(); return 0; } } } |
Redis緩存Spring註解的一些操作實例:
@Service @CacheConfig(cacheNames = "userCache") public class UserServiceImplWithAnno implements UserService {
public static final String USER_UID_PREFIX = "'userCache:'+";
/** * CRUD 之 新增/更新 * * @param user 用戶 */ @CachePut(key = USER_UID_PREFIX + "T(String).valueOf(#user.uid)") @Override public User saveUser(final User user) { //保存到數據庫 //返回值,將保存到緩存 Logger.info("user : save to redis"); return user; }
/** * 帶條件緩存 * * @param user 用戶 * @return 用戶 */ @CachePut(key = "T(String).valueOf(#user.uid)", condition = "#user.uid>1000") public User cacheUserWithCondition(final User user) { //保存到數據庫 //返回值,將保存到緩存 Logger.info("user : save to redis"); return user; }
/** * CRUD 之 查詢 * * @param id id * @return 用戶 */ @Cacheable(key = USER_UID_PREFIX + "T(String).valueOf(#id)") @Override public User getUser(final long id) { //如果緩存沒有,則從數據庫中加載 Logger.info("user : is null"); return null; }
/** * CRUD 之 刪除 * * @param id id */
@CacheEvict(key = USER_UID_PREFIX + "T(String).valueOf(#id)") @Override public void deleteUser(long id) {
//從數據庫中刪除 Logger.info("delete User:", id); }
/** * 刪除userCache中的全部緩存 */ @CacheEvict(value = "userCache", allEntries = true) public void deleteAll() {
} /** * 一個方法上,加上三類cache處理 */ @Caching(cacheable = @Cacheable(key = "'userCache:'+ #uid"), put = @CachePut(key = "'userCache:'+ #uid"), evict = { @CacheEvict(key = "'userCache:'+ #uid"), @CacheEvict(key = "'addressCache:'+ #uid"), @CacheEvict(key = "'messageCache:'+ #uid") } ) public User updateRef(String uid) { //....業務邏輯 return null; } } |