Hash操做java
* 看別人的總沒有本身實操來的印象深入redis
redis的五大數據類型 字符串、列表、集合、有序集合、哈希spring
redis的哈希類型指的是鍵值自己又是一個鍵值對結構:express
如:vaule = {{key1,value1},{key2,value2}...{keyn,valuen}}apache
相對於java能夠理解爲map:Key=>(Map<HK,HV>)app
哈希類型的內部編碼有兩種:less
ziplist(壓縮列表):當哈希類型元素個數小於hash-max-ziplist-entries配置(默認512個)、同時全部值都小於hash-max-ziplist-value配置(默認64字節)時,Redis會使用ziplist做爲哈希的內部實現,ziplist使用更加緊湊的結構實現多個元素的連續存儲,因此在節省內存方面比hashtable更加優秀。運維
hashtable(哈希表):當哈希類型沒法知足ziplist的條件時,Redis會使用hashtable做爲哈希的內部實現,由於此時ziplist的讀寫效率會降低,而hashtable的讀寫時間複雜度爲O(1)。ide
在redis開發與運維中的圖解:測試
redis對哈希的命令在redisTemplate中都有映射,見下方表:
//如下表格使用hash代替redisTemplate.opsForHash() : HashOperations hash = redisTemplate.opsForHash();
命令 | 操做 | 返回值 |
---|---|---|
hash.delete(H key, Object... hashKeys) | 刪除,能夠傳入多個map的key【hdel】 | Long |
hash.hasKey(key, hashKey) | 查看hash中的某個hashKey是否存在【hexists】 | Boolean |
hash.get(key, hashKey) | 獲取值【hget】 | Object(HV 泛型約束對象) |
hash.multiGet(H key, Collection<HK> hashKeys) | 批量獲取集合中的key對應的值【hmget】 | List<HV> |
hash.increment(H key, HK hashKey, long delta) | 對值進行+(delta值)操做【】 | Long |
hash.increment(H key, HK hashKey, double delta) | ~ | double |
hash.keys(key) | 返回map內hashKey的集合【hkeys】 | Set<HK> |
hash.lengthOfValue(H key, HK hashKey) | 返回查詢鍵關聯的值的長度,爲null則返回0【hstrlen】 | Long |
hash.size(H key) | 獲取hashKey的個數【hlen】 | Long |
hash.putAll(H key, Map<? extends HK, ? extends HV> m) | 至關於map的putAll【hmset】 | void |
hash.put(H key, HK hashKey, HV value) | 設置值,添加hashKey-value,hashKay至關於map的key 【hset】 | void |
hash.putIfAbsent(H key, HK hashKey, HV value) | 僅當hashKey不存在時設置值 | Boolean |
hash.values(key) | 返回value的集合【hvals】 | List<HV> |
hase.entries(H key) | 獲取map【hgetall】 | Map<HK, HV> |
hash.scan(H key, ScanOptions options) | 基於遊標的迭代查詢【hscan】 | Cursor<Map.Entry<HK, HV>>(返回的Cursor要手動關閉,見下面示例2) |
hash.getOperations() | 返回RedisOperation,它就是redis操做的接口 | RedisOperations<H, ?> |
對於命令的時間複雜度見下表:
示例測試操做1:
//key String k = "phoneMap"; //map的key String hashKey = "13838383838"; Map<String, String> phoneMap = new HashMap<>(); phoneMap.put("13838383839","1"); phoneMap.put(hashKey,"2"); //添加map //redisTemplate.opsForHash().putAll(k, phoneMap); //在key的value中添加單個hash值 redisTemplate.opsForHash().put(k, "19199999999","2"); //獲取map Map m1 = redisTemplate.opsForHash().entries(k); Object o = redisTemplate.opsForHash().get(k, hashKey); log.info("get key:{}", o); log.info("map:{}", m1);
log打印的結果:
2019-12-13 15:45:36.836 INFO 4156 --- [ main] : get key:null
2019-12-13 15:45:36.836 INFO 4156 --- [ main] : map:{13838121333=1, 13838121556=2, 19199999999=2}
若是這裏使用了redisTemplate.opsForHash().putAll(k, phoneMap); ,則get key打印的結果就是2。這裏打印的map是包含以前測試的值。
示例測試操做2:
@Test public void hashTest(){ String k = "phoneMap"; String hashKey = "19199999999"; HashOperations<String, String, String> hash = redisTemplate.opsForHash(); hash.increment(k,hashKey,1); log.info("19199999999:{}", hash.get(k,hashKey)); Set set = hash.keys(k); log.info("phoneMap:{}", set); Long sizeLong = hash.size(k); log.info("sizeLong:{}",sizeLong); Cursor<Map.Entry<String, String>> cursor = hash.scan(k,ScanOptions.scanOptions().count(1000).build()); while (cursor.hasNext()){ Map.Entry<String, String> entry = cursor.next(); log.info("entry:{}:{}", entry.getKey(), entry.getValue()); } try { if (!cursor.isClosed()){ cursor.close(); } } catch (IOException e) { log.error("關閉cursor異常"); e.printStackTrace(); } }
打印結果:
2019-12-13 16:59:35.573 INFO 1540 --- [ main] : 19199999999:6
2019-12-13 16:59:35.577 INFO 1540 --- [ main] : phoneMap:[13838121333, 13838121556, 19199999999, 13838383839, 13838383838]
2019-12-13 16:59:35.578 INFO 1540 --- [ main] : sizeLong:5
2019-12-13 16:59:35.587 INFO 1540 --- [ main] : entry:13838121373:1
2019-12-13 16:59:35.587 INFO 1540 --- [ main] : entry:13838121556:2
2019-12-13 16:59:35.587 INFO 1540 --- [ main] : entry:19199999999:6
2019-12-13 16:59:35.587 INFO 1540 --- [ main] : entry:13838383839:1
2019-12-13 16:59:35.587 INFO 1540 --- [ main] : entry:13838383838:2
# 使用文件形式展現:
private final String REDIS_PHONE_CONUM_PREFIX_KEY = "phoneSms:Check"; /** * 添加手機號發送次數 * @param phone * @return */ public void setPhoneSmsCountInc(String phone){ HashOperations hash = redisTemplate.opsForHash(); if (!redisTemplate.hasKey(REDIS_PHONE_CONUM_PREFIX_KEY)){ //設置過時key時間 redisTemplate.expire(REDIS_PHONE_CONUM_PREFIX_KEY, 24 * 60 * 60, TimeUnit.SECONDS); } hash.increment(REDIS_PHONE_CONUM_PREFIX_KEY, phone,1); }
這是HashOperations的源碼:
/* * Copyright 2011-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.data.redis.core; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import org.springframework.lang.Nullable; /** * Redis map specific operations working on a hash. * * @author Costin Leau * @author Christoph Strobl * @author Ninad Divadkar */ public interface HashOperations<H, HK, HV> { /** * Delete given hash {@code hashKeys}. * * @param key must not be {@literal null}. * @param hashKeys must not be {@literal null}. * @return {@literal null} when used in pipeline / transaction. */ Long delete(H key, Object... hashKeys); /** * Determine if given hash {@code hashKey} exists. * * @param key must not be {@literal null}. * @param hashKey must not be {@literal null}. * @return {@literal null} when used in pipeline / transaction. */ Boolean hasKey(H key, Object hashKey); /** * Get value for given {@code hashKey} from hash at {@code key}. * * @param key must not be {@literal null}. * @param hashKey must not be {@literal null}. * @return {@literal null} when key or hashKey does not exist or used in pipeline / transaction. */ @Nullable HV get(H key, Object hashKey); /** * Get values for given {@code hashKeys} from hash at {@code key}. * * @param key must not be {@literal null}. * @param hashKeys must not be {@literal null}. * @return {@literal null} when used in pipeline / transaction. */ List<HV> multiGet(H key, Collection<HK> hashKeys); /** * Increment {@code value} of a hash {@code hashKey} by the given {@code delta}. * * @param key must not be {@literal null}. * @param hashKey must not be {@literal null}. * @param delta * @return {@literal null} when used in pipeline / transaction. */ Long increment(H key, HK hashKey, long delta); /** * Increment {@code value} of a hash {@code hashKey} by the given {@code delta}. * * @param key must not be {@literal null}. * @param hashKey must not be {@literal null}. * @param delta * @return {@literal null} when used in pipeline / transaction. */ Double increment(H key, HK hashKey, double delta); /** * Get key set (fields) of hash at {@code key}. * * @param key must not be {@literal null}. * @return {@literal null} when used in pipeline / transaction. */ Set<HK> keys(H key); /** * Returns the length of the value associated with {@code hashKey}. If either the {@code key} or the {@code hashKey} * do not exist, {@code 0} is returned. * * @param key must not be {@literal null}. * @param hashKey must not be {@literal null}. * @return {@literal null} when used in pipeline / transaction. * @since 2.1 */ @Nullable Long lengthOfValue(H key, HK hashKey); /** * Get size of hash at {@code key}. * * @param key must not be {@literal null}. * @return {@literal null} when used in pipeline / transaction. */ Long size(H key); /** * Set multiple hash fields to multiple values using data provided in {@code m}. * * @param key must not be {@literal null}. * @param m must not be {@literal null}. */ void putAll(H key, Map<? extends HK, ? extends HV> m); /** * Set the {@code value} of a hash {@code hashKey}. * * @param key must not be {@literal null}. * @param hashKey must not be {@literal null}. * @param value */ void put(H key, HK hashKey, HV value); /** * Set the {@code value} of a hash {@code hashKey} only if {@code hashKey} does not exist. * * @param key must not be {@literal null}. * @param hashKey must not be {@literal null}. * @param value * @return {@literal null} when used in pipeline / transaction. */ Boolean putIfAbsent(H key, HK hashKey, HV value); /** * Get entry set (values) of hash at {@code key}. * * @param key must not be {@literal null}. * @return {@literal null} when used in pipeline / transaction. */ List<HV> values(H key); /** * Get entire hash stored at {@code key}. * * @param key must not be {@literal null}. * @return {@literal null} when used in pipeline / transaction. */ Map<HK, HV> entries(H key); /** * Use a {@link Cursor} to iterate over entries in hash at {@code key}. <br /> * <strong>Important:</strong> Call {@link Cursor#close()} when done to avoid resource leak. * * @param key must not be {@literal null}. * @param options * @return {@literal null} when used in pipeline / transaction. * @since 1.4 */ Cursor<Map.Entry<HK, HV>> scan(H key, ScanOptions options); /** * @return never {@literal null}. */ RedisOperations<H, ?> getOperations(); }