个人博客


介绍Redis常用的5种数据类型的操作API。

1、导入依赖

1
2
3
4
5
6
7
8
9
10
11
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>

2、添加配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
spring:
redis:
# 单机配置
# host: 172.16.122.104
# port: 6379

# 集群多节点配置
cluster:
nodes:
- 172.16.122.101:6379
- 172.16.122.101:6380
- 172.16.122.102:6379
- 172.16.122.102:6380
- 172.16.122.103:6379
- 172.16.122.103:6380
password:
timeout: 2000

如果redis以单机方式部署,则需要配置hostport节点。
如果以cluster方式部署,就需要配置每个节点ip和端口号。

3、RedisTemplate配置

SpringBoot是以条件注解来启用默认的RedisTemplateStringRedisTemplate,具体可以查看RedisAutoConfiguration这个类的源码。如果我们自定义RedisTemplate对象并注入Spring容器,则默认的不会生效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
@Configuration
public class RedisConfig {

@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 配置连接工厂
template.setConnectionFactory(redisConnectionFactory);

// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);

ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会抛出异常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); // 2.1.x
// om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); // 2.2.x
jacksonSeial.setObjectMapper(om);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
// 值采用json序列化
template.setValueSerializer(jacksonSeial);

// 设置hash key 和value序列化模式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jacksonSeial);
template.afterPropertiesSet();
return template;
}

/**
* 对redis字符串类型数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public ValueOperations<String, String> stringOperations(StringRedisTemplate redisTemplate) {
return redisTemplate.opsForValue();
}

/**
* 对POJO类型序列化和反序列化操作,存储还是以字符串形式
*
* @param redisTemplate
* @return
*/
@Bean
public ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForValue();
}

/**
* 对hash类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHash();
}

/**
* 对链表类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForList();
}

/**
* 对无序集合类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForSet();
}

/**
* 对有序集合类型的数据操作
*
* @param redisTemplate
* @return
*/
@Bean
public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForZSet();
}
}

这里同时提供6个对象用以对Redis的操作,这6个对象对应Redis的5种数据结构。这里为了使用方便,ValueOperations类型有2个对象,分别操作字符串和POJO。

  1. stringOperations:对字符串类型数据操作。
  2. valueOperations:对POJO类型序列化和反序列化操作,存储还是以字符串形式。
  3. hashOperations:对hash类型的数据操作。
  4. listOperations:对链表类型的数据操作。
  5. setOperations:对无序集合类型的数据操作。
  6. zSetOperations:对有序集合类型的数据操作。

4、代码示例

4.1、定义POJO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Data
public class User {
private Long id;

/**
* 用户名
*/
private String username;

/**
* 密码,序列化时忽略此属性
*/
@JsonIgnore
private String password;

/**
* 年龄
*/
private Integer age;

/**
* 性别 1=男 2=女 其他=保密
*/
private Integer sex;
}

4.2、测试案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class RedistemplateApplicationTests {
@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private ValueOperations<String, String> stringOperations;

@Autowired
private ValueOperations<String, Object> valueOperations;

@Autowired
private HashOperations<String, String, Object> hashOperations;

@Autowired
private ListOperations<String, Object> listOperations;

@Autowired
private SetOperations<String, Object> setOperations;

@Autowired
private ZSetOperations<String, Object> zSetOperations;

@Test
public void testString() {
String key = "name";
// set 并设置过期时间
stringOperations.set(key, "李白", 1, TimeUnit.SECONDS);
// get
log.info(stringOperations.get(key));
// 查询key剩余时间SECONDS
log.info(redisTemplate.getExpire(key, TimeUnit.MILLISECONDS) + "");
// 删除key
redisTemplate.delete(key);
// 判断key是否存在
log.info(redisTemplate.hasKey(key) + "");
}

/**
* 序列化POJO和反序列化
*/
@Test
public void testPOJO() {
String key = "libai";
User user = new User();
user.setId(1L);
user.setUsername("李白");
user.setPassword("123456");
user.setAge(28);
user.setSex(1);
valueOperations.set(key, user, 1, TimeUnit.SECONDS);
User result = (User) valueOperations.get(key);
log.info(JSONUtil.toJsonPrettyStr(result));
}

@Test
public void testHash() {
String key = "hashKey";

// 单个put
hashOperations.put(key, "name", "李白");
hashOperations.put(key, "age", 30);
hashOperations.put(key, "gender", 1);
// 批量put
Map<String, Object> myMap = new HashMap<>();
myMap.put("address", "长安");
myMap.put("salary", 2000.5);
hashOperations.putAll(key, myMap);
// 指定过期时间
redisTemplate.expire(key, 10, TimeUnit.SECONDS);

// get指定散列的单个key
log.info(hashOperations.get(key, "name") + "");
// 删除指定散列中的某些key
hashOperations.delete(key, "name", "salary");
// 判断指定散列是否包含某个key
log.info(hashOperations.hasKey(key, "salary") + "");

// 获取指定的整个散列
Map<String, Object> map = hashOperations.entries(key);
log.info(JSONUtil.toJsonPrettyStr(map));

// 获取散列所有的key集合
Set<String> keySet = hashOperations.keys(key);
log.info(JSONUtil.toJsonPrettyStr(keySet));

// 获取散列键值对数
Long size = hashOperations.size(key);
log.info(size + "");

// 获取散列所有的value列表
List<Object> values = hashOperations.values(key);
log.info(JSONUtil.toJsonPrettyStr(values));
}

@Test
public void testlist() {
String key = "listKey";

User user1 = new User();
user1.setId(1L);
user1.setUsername("aa");
user1.setPassword("123456");
user1.setAge(28);
user1.setSex(1);

User user2 = new User();
user2.setId(2L);
user2.setUsername("bb");
user2.setPassword("123456");
user2.setAge(55);
user2.setSex(2);
listOperations.rightPush(key, user1);
listOperations.rightPush(key, user2);
// 指定过期时间
redisTemplate.expire(key, 10, TimeUnit.SECONDS);

// 遍历列表
for (int i = 0; i < listOperations.size(key); i++) {
User user = (User) listOperations.index(key, i); // 根据下标访问列表中的元素
// User user = (User) listOperations.leftPop(key); // 出队
log.info(JSONUtil.toJsonPrettyStr(user));
}

// 获取列表指定范围
// List<User> userList = (List) listOperations.range(key, 0, listOperations.size(key));
// 获取所有元素
List<User> userList = (List) listOperations.range(key, 0, -1);
log.info(JSONUtil.toJsonPrettyStr(userList));
}

/**
* 不可重复集合
*/
@Test
public void testSet() {
String key = "setKey";

Long num = setOperations.add(key, "a", "b", "c", "b");
redisTemplate.expire(key, 10, TimeUnit.SECONDS);
log.info("存储数量:{}", num); // 3

// 判断是否成员
log.info(setOperations.isMember(key, "a") + "");

// 获取整个无序集合
Set<Object> members = setOperations.members(key);
log.info(JSONUtil.toJsonPrettyStr(members));

}

/**
* 集合每个value需要关联1个double类型的数字
*/
@Test
public void testZset() {
// 批量添加
ZSetOperations.TypedTuple<Object> tuple1 = new DefaultTypedTuple<>("libai", 1.1);
ZSetOperations.TypedTuple<Object> tuple2 = new DefaultTypedTuple<>("zhaoxb", 14.8);
ZSetOperations.TypedTuple<Object> tuple3 = new DefaultTypedTuple<>("cc", -3.2);
Set<ZSetOperations.TypedTuple<Object>> set = new HashSet<>();
set.add(tuple1);
set.add(tuple2);
set.add(tuple3);
zSetOperations.add("zset", set);

// 单个添加
zSetOperations.add("zset", "jj", 0.7);
zSetOperations.add("zset", "kk", 3.5);
log.info("zset:{}", zSetOperations.range("zset", 0, -1));

// 获取元素排名,从0开始
Long rank = zSetOperations.rank("zset", "zhaoxb");
log.info("zhaoxb排名:[{}]", rank);

// 根据分数值大小统计范围(包含头尾)
Set<Object> rangeByScore = zSetOperations.rangeByScore("zset", 0, 10);
Long rangeSize = zSetOperations.count("zset", 0, 10);
log.info("截取[0,10]范围内元素zset:{},共有:[{}]", rangeByScore, rangeSize);

// 获取指定元素的score
Double score1 = zSetOperations.score("zset", "zhaoxb");
Double score2 = zSetOperations.score("zset", "zxb");
log.info("存在元素的score:[{}],不存在元素的score:[{}]", score1, score2);

// 删除指定元素
zSetOperations.remove("zset", "jj", "kk");
log.info("删除指定元素后,zset:{}", zSetOperations.range("zset", 0, -1));
// 删除score在某一范围内的元素(包含头尾)
zSetOperations.removeRangeByScore("zset", -3.2, 1.1);
log.info("删除score在某一范围内的元素,zset:{}", zSetOperations.range("zset", 0, -1));
// 删除指定排序范围内的元素
zSetOperations.removeRange("zset", 0, -1);
log.info("删除指定排序范围内的元素,zset:{}", zSetOperations.range("zset", 0, -1));
}
}

5、设置Lettuce连接池

Lettuce是基于Netty实现的,1个连接可以同时给多个线程复用并且保证线程安全。如果需要多个连接可以自定义连接池。

  1. 导入依赖。

    1
    2
    3
    4
    <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    </dependency>
  2. 添加配置,可以根据实际需要设置。

    1
    2
    3
    4
    5
    6
    7
    8
    spring:
    redis:
    lettuce:
    pool:
    max-active: 32
    max-idle: 8
    min-idle: 2
    max-wait: 10000

参考链接

代码地址