Redis-04Redis数据结构--哈希hash
文章目錄
- 哈希概述
- 客戶端操作hash
- Redis hash 結構命令
- Spring操作reids的hash
- Step1 修改defaultSerializer
- Step2 操作hash
- 注意
- 代碼
哈希概述
Redis 中哈希結構就如同 Java 的 map 一樣 , 一個對象里面有許多鍵值對,它是特別適合存儲對象的.
如果內存足夠大 ,那么一個 Redis 的 hash 結構可以存儲2的32次方-1個鍵值對 ( 40多億)。
在 Redis 中, hash 是一個 String 類型的 field 和 value 的映射表,因此我們存儲的數據實際在 Redis 內存中都是一個個字符串而己。
假設artisan有 3 個字段 : 編號( id)、名稱 (name )、性別( sex),這樣就可以使用一個 hash 結構保存它。
在 Redis 中它就是一個這樣的結構,其中 artisan代表的是這個 hash 結構在 Redis 內存的 key,通過它就可以找到這個 hash 結構,而 hash 結構由一系列的 field 和 value 組成
客戶端操作hash
127.0.0.1:6379> HMSET artisan id 123 name littleArtisan sex female OK 127.0.0.1:6379> HGETALL artisan 1) "id" 2) "123" 3) "name" 4) "littleArtisan" 5) "sex" 6) "female" 127.0.0.1:6379>Redis hash 結構命令
官網:https://redis.io/commands#hash
| hdel key field 1 [ field2 …] | 刪除 hash 結構中的某個(些)字段 | 可以進行多個字段的刪除 |
| hexists key field | 判斷 hash 結構中是否存在 field 字段 | 存在返回 1 ,否則返回0 |
| hgetall key | 獲取所有 hash 結構中的鍵值 | 返回鍵和值 |
| hincrby key field increment | 指定給 hash 結構中的某一字段加上一個整數 | 要求該字段也是整數字符串 |
| hincrbyfloat key field increment | 指定給 hash 結構中的某一字段加上一個浮點數 | 要求該字段是數字型字符串 |
| hkeys key | 返回 hash 中所有的鍵 | |
| hlen key | 返問 hash 中鍵值對的數量 | |
| hmget key field1 [field2…] | 返回 hash 中指定的鍵的值,可以是多個 | 依次返回值 |
| hmset key field1 value1 [field2 value2…] | hash 結構設置多個鍵值對 | |
| hset key filed value | 在 hash 結構中設置鍵值對 | 單個設值 |
| hsetnx key field value | 當 hash 結構中不存在對應的鍵,才設置值 | |
| hvals key | 獲取 hash 結構中所有的值 |
在 Redis 中的哈希結構和字符串有著比較明顯的不同。
-
首先,命令都是以 h 開頭,代表操作的是 hash 結構
-
其次,大多數命令多了一個層級 field,這是hash 結構的一個內部鍵,也就是說Redis 需要通過 key 索引到對應的 hash 結構,再通過 field來確定使用 hash 結構的哪個鍵值對
注意事項:
- 哈希結構的大小,如果哈希結構是個很大的鍵值對,那么使用它要十分注意。 尤其是關于 hkeys 、 hgetall 、 hvals 等返回所有哈希結構數據的命令,會造成大量數據的讀取。這需要考慮性能和讀取數據大小對 JVM 內存的影響 。
- 對于數字的操作命令 hincrby 而言,要求存儲的也是整數型的字符串
- 對于hincrbyfloat 而言,則要求使用浮點數或者整數,否則命令會失敗。
Spring操作reids的hash
Step1 修改defaultSerializer
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><context:property-placeholder location="classpath:redis/redis.properties" /><!--2,注意新版本2.3以后,JedisPoolConfig的property name,不是maxActive而是maxTotal,而且沒有maxWait屬性,建議看一下Jedis源碼或百度。 --><!-- redis連接池配置 --><bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"><!--最大空閑數 --><property name="maxIdle" value="${redis.maxIdle}" /><!--連接池的最大數據庫連接數 --><property name="maxTotal" value="${redis.maxTotal}" /><!--最大建立連接等待時間 --><property name="maxWaitMillis" value="${redis.maxWaitMillis}" /><!--逐出連接的最小空閑時間 默認1800000毫秒(30分鐘) --><property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}" /><!--每次逐出檢查時 逐出的最大數目 如果為負數就是 : 1/abs(n), 默認3 --><property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}" /><!--逐出掃描的時間間隔(毫秒) 如果為負數,則不運行逐出線程, 默認-1 --><property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}" /><property name="testOnBorrow" value="true"></property><property name="testOnReturn" value="true"></property><property name="testWhileIdle" value="true"></property></bean><!--redis連接工廠 --><bean id="jedisConnectionFactory"class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"destroy-method="destroy"><property name="poolConfig" ref="jedisPoolConfig"></property><!--IP地址 --><property name="hostName" value="${redis.host.ip}"></property><!--端口號 --><property name="port" value="${redis.port}"></property><!--如果Redis設置有密碼 --><property name="password" value="${redis.password}" /> <!--客戶端超時時間單位是毫秒 --><property name="timeout" value="${redis.timeout}"></property><property name="usePool" value="true" /><!--<property name="database" value="0" /> --></bean><!-- 鍵值序列化器設置為String 類型 --><bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/><!-- redis template definition --><bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"p:connection-factory-ref="jedisConnectionFactory"p:keySerializer-ref="stringRedisSerializer"p:defaultSerializer-ref="stringRedisSerializer"p:valueSerializer-ref="stringRedisSerializer"></bean></beans>在 Redis 中, hash 是一個 String 類型的 field 和 value 的映射表。 Spring 對 Redis 進行了封裝,所以有必要對 RedisTemplate 的配置項進行修改。修改defaultSerializer-ref
如果不指定的話就是
否則拋出如下異常
Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.EOFExceptionStep2 操作hash
package com.artisan.redis.baseStructure.hash;import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set;import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.data.redis.core.RedisTemplate;public class SpringRedisHashDemo {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:spring/spring-redis-hash.xml");RedisTemplate redisTemplate = (RedisTemplate) ctx.getBean("redisTemplate");// 127.0.0.1:6379> HMSET obj k1 value1 k2 value2 k3 value3// OKString key = "obj";Map<String, String> map = new HashMap<String, String>();map.put("k1", "value1");map.put("k2", "value2");map.put("k3", "value3");redisTemplate.opsForHash().putAll(key, map);// 127.0.0.1:6379> HSET obj k4 6// (integer) 1redisTemplate.opsForHash().put(key, "k4", String.valueOf(6));// 127.0.0.1:6379> HEXISTS obj k2// (integer) 1boolean exist = redisTemplate.opsForHash().hasKey(key, "k2");System.out.println(key + " 這個鍵中是否存在 k2這個field:" + exist);// 127.0.0.1:6379> HGETALL obj// 1) "k1"// 2) "value1"// 3) "k2"// 4) "value2"// 5) "k3"// 6) "value3"// 7) "k4"// 8) "6"Map<String,String> map2 = redisTemplate.opsForHash().entries(key);if (map2 != null) {scanMap(map2);}// 127.0.0.1:6379> HINCRBY obj k4 8// (integer) 14System.out.println(redisTemplate.opsForHash().increment(key, "k4", 8));// 127.0.0.1:6379> HINCRBYFLOAT obj k4 6.2// "20.2"System.out.println(redisTemplate.opsForHash().increment(key, "k4", 6.2));// 127.0.0.1:6379> HKEYS obj// 1) "k1"// 2) "k2"// 3) "k3"// 4) "k4"Set<String> set = redisTemplate.opsForHash().keys(key);for (String str : set) {System.out.println(str);}// 127.0.0.1:6379> HMGET obj k1 k2 k4// 1) "value1"// 2) "value2"// 3) "20.2"List<String> list = new ArrayList<String>();list.add("k1");list.add("k2");list.add("k4");List<String> list2 = redisTemplate.opsForHash().multiGet(key, list);scanList(list2);// 127.0.0.1:6379> HLEN obj// (integer) 4System.out.println(redisTemplate.opsForHash().size(key));// 127.0.0.1:6379> HSETNX obj k2 test// (integer) 0System.out.println(redisTemplate.opsForHash().putIfAbsent(key, "k2", "test"));// 127.0.0.1:6379> HSETNX obj k5 test// (integer) 1System.out.println(redisTemplate.opsForHash().putIfAbsent(key, "k5", "test"));// 127.0.0.1:6379> HGETALL obj// 1) "k1"// 2) "value1"// 3) "k2"// 4) "value2"// 5) "k3"// 6) "value3"// 7) "k4"// 8) "20.2"// 9) "k5"// 10) "test"Map<String, String> map3 = redisTemplate.opsForHash().entries(key);if (map3 != null) {scanMap(map3);}// 127.0.0.1:6379> HVALS obj// 1) "value1"// 2) "value2"// 3) "value3"// 4) "20.2"// 5) "test"List<String> list3 = redisTemplate.opsForHash().values(key);scanList(list3);// 127.0.0.1:6379> HDEL obj k5// (integer) 1redisTemplate.opsForHash().delete(key, "k5");// 127.0.0.1:6379> HGETALL obj// 1) "k1"// 2) "value1"// 3) "k2"// 4) "value2"// 5) "k3"// 6) "value3"// 7) "k4"// 8) "20.2"Map<String, String> map4 = redisTemplate.opsForHash().entries(key);if (map4 != null) {scanMap(map4);}// 127.0.0.1:6379> HGET obj k4// "20.2"System.out.println(redisTemplate.opsForHash().get(key, "k4"));}private static void scanList(List<String> list2) {for (String string : list2) {System.out.println(string);}}private static void scanMap(Map<String, String> map4) {for (Map.Entry<String, String> entry : map4.entrySet()) {System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());}} }輸出
INFO : org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@73a8dfcc: startup date [Thu Sep 20 19:13:10 CST 2018]; root of context hierarchy INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [spring/spring-redis-hash.xml] obj 這個鍵中是否存在 k2這個field:true Key = k2, Value = value2 Key = k3, Value = value3 Key = k4, Value = 6 Key = k1, Value = value1 14 20.2 k1 k2 k3 k4 value1 value2 20.2 4 false true Key = k2, Value = value2 Key = k5, Value = test Key = k3, Value = value3 Key = k1, Value = value1 Key = k4, Value = 20.2 value1 value2 value3 20.2 test Key = k3, Value = value3 Key = k2, Value = value2 Key = k4, Value = 20.2 Key = k1, Value = value1 20.2- hmset 命令,在 Java 的 API 中,是使用 map 保存多個鍵值對。
- hgetall 命令會返回所有的鍵值對,并保存到一個 map 對象中,如果 hash 結構很大,那么要考慮它對 JVM 的內存影響。
- hincrby 和 hincrbyFloat 命令都采用 increment 方法, Spring 會識別它具體使用何種方法。
- redisTemplate.opsForHash().values(key)方法相當于 hvals 命令,它會返回所有的值,并保存到一個 List 對象中;
- redisTemplate.opsForHash().keys(key)方法相當于 hkeys命令,它會獲取所有的鍵,保存到一個 Set 對象中 。
- 在 Spring 中使用 redisTemplate.opsForHash().putAll(key, map )方法相當于執行了hmset 命令,使用了 map ,由于配置了默認的序列化器為字符串,所以它也只會用字符串進行轉化,這樣才能執行對應的數值加法,如果使用其他序列化器,則后面的命令可能會拋出異常。
- 在使用大的 hash 結構時,需要考慮返回數據的大小,以避免返回太多的數據,引發JVM內存溢出或者 Redis 的性能問題。
注意
使用 Spring 提供的 RedisTemplate 去展示多個命令可以學習到如何使用 RedisTemplate 操作 Redis 。 實際工作中并不是那么用的,因為每一 個操作會嘗試從連接池里獲取 一 個新的 Redis 連接,多個命令應該使用SessionCallback 接口進行操作 。
代碼
代碼托管到了 https://github.com/yangshangwei/redis_learn
總結
以上是生活随笔為你收集整理的Redis-04Redis数据结构--哈希hash的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VMware-使用VMware在本地搭建
- 下一篇: Redis-06Redis数据结构--集