Redis 概述

什么是 Redis

Redis(Remote Dictionary Server) 是一个使用 C 语言编写的,开源的(BSD 许可)高性能非关系型(NoSQL)的键值对数据库。

Redis 可以存储键和五种不同类型的值之间的映射。键的类型只能为字符串,值支持五种数据类型:字符串、列表、散列表、集合、有序集合。
与传统数据库不同的是 Redis 的数据是存在内存中的,所以读写速度非常快,因此 redis 被广泛应用于缓存方向,每秒可以处理超过 10 万次读写操作,是已知性能最快的 Key-Value DB。另外,Redis 也经常用来做分布式锁。除此之外,Redis 支持事务 、持久化、LUA 脚本、LRU 驱动事件、多种集群方案。

Redis 有哪些优缺点

优点

  • 读写性能优异, Redis 能读的速度是 110000 次 / s,写的速度是 81000 次 / s。
  • 支持数据持久化,支持 AOF 和 RDB 两种持久化方式。
  • 数据结构丰富,除了支持 string 类型的 value 外还支持 hash、set、zset、list 等数据结构。
  • 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
  • 支持事务,Redis 的所有操作都是原子性的,同时 Redis 还支持对几个操作合并后的原子性执行。

缺点

  • 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此 Redis 适合的场景主要局限在较小数据量的高性能操作和运算上。
  • Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
  • Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的 IP 才能恢复。
  • 主机宕机,宕机前有部分数据未能及时同步到从机,切换 IP 后还会引入数据不一致的问题,降低了系统的可用性。

为什么要用 Redis / 为什么要用缓存

主要从 “高性能” 和“高并发”这两点来看待这个问题。

高性能
假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。再将该用户访问的数据存在缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!

image.png
高并发
直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。
image.png

##

引入依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置 application.yml

1
2
3
4
5
spring:
redis:
database: 0
host: localhost
port: 6379

添加配置类

  • redisTemplate 默认使用了 jdk 序列化方式存储了 kv
  • 于是我们要自行对 redistTemplate 进行配置,方便我们的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(mapper);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
redisTemplate.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}

Redis 模板类

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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
package com.dzgu.myblog.service;

import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;


import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* @description: redis操作接口
* @Author: dzgu
* @Date: 2021/11/7 16:46
*/
public interface RedisService {
/**
* 保存属性
*
* @param key key值
* @param value value值
* @param time 时间戳
*/
void set(String key, Object value, long time);

/**
* 保存属性
*
* @param key key值
* @param value value值
*/
void set(String key, Object value);

/**
* 获取属性
*
* @param key key值
* @return 返回对象
*/
Object get(String key);

/**
* 删除属性
*
* @param key key值
* @return 返回成功
*/
Boolean del(String key);

/**
* 批量删除属性
*
* @param keys key值集合
* @return 返回删除数量
*/
Long del(List<String> keys);

/**
* 设置过期时间
*
* @param key key值
* @param time 时间戳
* @return 返回成功
*/
Boolean expire(String key, long time);

/**
* 获取过期时间
*
* @param key key值
* @return 返回时间戳
*/
Long getExpire(String key);

/**
* 判断key是否存在
*
* @param key key值
* @return 返回
*/
Boolean hasKey(String key);

/**
* 按delta递增
*
* @param key key值
* @param delta delta值
* @return 返回递增后结果
*/
Long incr(String key, long delta);

/**
* 按delta递减
*
* @param key key值
* @param delta delta值
* @return 返回递减后结果
*/
Long decr(String key, long delta);

/**
* 获取Hash结构中的属性
*
* @param key 外部key值
* @param hashKey 内部key值
* @return 返回内部key的value
*/
Object hGet(String key, String hashKey);

/**
* 向Hash结构中放入一个属性
*
* @param key 外部key
* @param hashKey 内部key
* @param value 内部key的value
* @param time 过期时间
* @return 返回是否成功
*/
Boolean hSet(String key, String hashKey, Object value, long time);

/**
* 向Hash结构中放入一个属性
*
* @param key 外部key
* @param hashKey 内部key
* @param value 内部key的value
*/
void hSet(String key, String hashKey, Object value);

/**
* 直接获取整个Hash结构
*
* @param key 外部key值
* @return 返回hashMap
*/
Map<String, Object> hGetAll(String key);

/**
* 直接设置整个Hash结构
*
* @param key 外部key
* @param map hashMap值
* @param time 过期时间
* @return 返回是否成功
*/
Boolean hSetAll(String key, Map<String, Object> map, long time);

/**
* 直接设置整个Hash结构
*
* @param key 外部key
* @param map hashMap值
*/
void hSetAll(String key, Map<String, ?> map);

/**
* 删除Hash结构中的属性
*
* @param key 外部key值
* @param hashKey 内部key值
*/
void hDel(String key, Object... hashKey);

/**
* 判断Hash结构中是否有该属性
*
* @param key 外部key
* @param hashKey 内部key
* @return 返回是否存在
*/
Boolean hHasKey(String key, String hashKey);

/**
* Hash结构中属性递增
*
* @param key 外部key
* @param hashKey 内部key
* @param delta 递增条件
* @return 返回递增后的数据
*/
Long hIncr(String key, String hashKey, Long delta);

/**
* Hash结构中属性递减
*
* @param key 外部key
* @param hashKey 内部key
* @param delta 递增条件
* @return 返回递减后的数据
*/
Long hDecr(String key, String hashKey, Long delta);

/**
* zset添加分数
*
* @param key 关键
* @param value 价值
* @param score 分数
* @return {@link Double}
*/
Double zIncr(String key, Object value, Double score);

/**
* zset减少分数
*
* @param key 关键
* @param value 价值
* @param score 分数
* @return {@link Double}
*/
Double zDecr(String key, Object value, Double score);

/**
* zset根据分数排名获取指定元素信息
*
* @param key 关键
* @param start 开始
* @param end 结束
* @return {@link Map<Object, Double>}
*/
Map<Object, Double> zReverseRangeWithScore(String key, long start, long end);

/**
* 获取zset指定元素分数
*
* @param key 关键
* @param value 价值
* @return {@link Double}
*/
Double zScore(String key, Object value);

/**
* 获取zset所有分数
*
* @param key 关键
* @return {@link Map}
*/
Map<Object, Double> zAllScore(String key);

/**
* 获取Set结构
*
* @param key key
* @return 返回set集合
*/
Set<Object> sMembers(String key);

/**
* 向Set结构中添加属性
*
* @param key key
* @param values value集
* @return 返回增加数量
*/
Long sAdd(String key, Object... values);

/**
* 向Set结构中添加属性
*
* @param key key
* @param time 过期时间
* @param values 值集合
* @return 返回添加的数量
*/
Long sAddExpire(String key, long time, Object... values);

/**
* 是否为Set中的属性
*
* @param key key
* @param value value
* @return 返回是否存在
*/
Boolean sIsMember(String key, Object value);

/**
* 获取Set结构的长度
*
* @param key key
* @return 返回长度
*/
Long sSize(String key);

/**
* 删除Set结构中的属性
*
* @param key key
* @param values value集合
* @return 删除掉的数据量
*/
Long sRemove(String key, Object... values);

/**
* 获取List结构中的属性
*
* @param key key
* @param start 开始
* @param end 结束
* @return 返回查询的集合
*/
List<Object> lRange(String key, long start, long end);

/**
* 获取List结构的长度
*
* @param key key
* @return 长度
*/
Long lSize(String key);

/**
* 根据索引获取List中的属性
*
* @param key key
* @param index 索引
* @return 对象
*/
Object lIndex(String key, long index);

/**
* 向List结构中添加属性
*
* @param key key
* @param value value
* @return 增加后的长度
*/
Long lPush(String key, Object value);

/**
* 向List结构中添加属性
*
* @param key key
* @param value value
* @param time 过期时间
* @return 增加后的长度
*/
Long lPush(String key, Object value, long time);

/**
* 向List结构中批量添加属性
*
* @param key key
* @param values value 集合
* @return 增加后的长度
*/
Long lPushAll(String key, Object... values);

/**
* 向List结构中批量添加属性
*
* @param key key
* @param time 过期时间
* @param values value集合
* @return 增加后的长度
*/
Long lPushAll(String key, Long time, Object... values);

/**
* 从List结构中移除属性
*
* @param key key
* @param count 总量
* @param value value
* @return 返回删除后的长度
*/
Long lRemove(String key, long count, Object value);

/**
* 向bitmap中新增值
*
* @param key key
* @param offset 偏移量
* @param b 状态
* @return 结果
*/
Boolean bitAdd(String key, int offset, boolean b);

/**
* 从bitmap中获取偏移量的值
*
* @param key key
* @param offset 偏移量
* @return 结果
*/
Boolean bitGet(String key, int offset);

/**
* 获取bitmap的key值总和
*
* @param key key
* @return 总和
*/
Long bitCount(String key);

/**
* 获取bitmap范围值
*
* @param key key
* @param limit 范围
* @param offset 开始偏移量
* @return long类型集合
*/
List<Long> bitField(String key, int limit, int offset);

/**
* 获取所有bitmap
*
* @param key key
* @return 以二进制字节数组返回
*/
byte[] bitGetAll(String key);

/**
* 向hyperlog中添加数据
*
* @param key key
* @param value 值
* @return {@link Long}
*/
Long hyperAdd(String key, Object... value);

/**
* 获取hyperlog元素数量
*
* @param key key
* @return {@link Long} 元素数量
*/
Long hyperGet(String... key);

/**
* 删除hyperlog数据
*
* @param key key
*/
void hyperDel(String key);

/**
* 增加坐标
*
* @param key key
* @param x x
* @param y y
* @param name 地点名称
* @return 返回结果
*/
Long geoAdd(String key, Double x, Double y, String name);

/**
* 根据城市名称获取坐标集合
*
* @param key key
* @param place 地点
* @return 坐标集合
*/
List<Point> geoGetPointList(String key, Object... place);

/**
* 计算两个城市之间的距离
*
* @param key key
* @param placeOne 地点1
* @param placeTow 地点2
* @return 返回距离
*/
Distance geoCalculationDistance(String key, String placeOne, String placeTow);

/**
* 获取附该地点附近的其他地点
*
* @param key key
* @param place 地点
* @param distance 附近的范围
* @param limit 查几条
* @param sort 排序规则
* @return 返回附近的地点集合
*/
GeoResults<RedisGeoCommands.GeoLocation<Object>> geoNearByPlace(String key, String place, Distance distance, long limit, Sort.Direction sort);

/**
* 获取地点的hash
*
* @param key key
* @param place 地点
* @return 返回集合
*/
List<String> geoGetHash(String key, String... place);
}

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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
package com.dzgu.myblog.service.impl;

import com.dzgu.myblog.service.RedisService;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @description: redis操作
* @Author: dzgu
* @Date: 2021/11/7 17:55
*/
@Service
public class RedisServiceImpl implements RedisService {
@Resource
private RedisTemplate<String, Object> redisTemplate;

@Override
public void set(String key, Object value, long time) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
}

@Override
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}

@Override
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}

@Override
public Boolean del(String key) {
return redisTemplate.delete(key);
}

@Override
public Long del(List<String> keys) {
return redisTemplate.delete(keys);
}

@Override
public Boolean expire(String key, long time) {
return redisTemplate.expire(key, time, TimeUnit.SECONDS);
}

@Override
public Long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}

@Override
public Boolean hasKey(String key) {
return redisTemplate.hasKey(key);
}

@Override
public Long incr(String key, long delta) {
return redisTemplate.opsForValue().increment(key, delta);
}

@Override
public Long decr(String key, long delta) {
return redisTemplate.opsForValue().increment(key, -delta);
}

@Override
public Object hGet(String key, String hashKey) {
return redisTemplate.opsForHash().get(key, hashKey);
}

@Override
public Boolean hSet(String key, String hashKey, Object value, long time) {
redisTemplate.opsForHash().put(key, hashKey, value);
return expire(key, time);
}

@Override
public void hSet(String key, String hashKey, Object value) {
redisTemplate.opsForHash().put(key, hashKey, value);
}

@Override
public Map hGetAll(String key) {
return redisTemplate.opsForHash().entries(key);
}

@Override
public Boolean hSetAll(String key, Map<String, Object> map, long time) {
redisTemplate.opsForHash().putAll(key, map);
return expire(key, time);
}

@Override
public void hSetAll(String key, Map<String, ?> map) {
redisTemplate.opsForHash().putAll(key, map);
}

@Override
public void hDel(String key, Object... hashKey) {
redisTemplate.opsForHash().delete(key, hashKey);
}

@Override
public Boolean hHasKey(String key, String hashKey) {
return redisTemplate.opsForHash().hasKey(key, hashKey);
}

@Override
public Long hIncr(String key, String hashKey, Long delta) {
return redisTemplate.opsForHash().increment(key, hashKey, delta);
}

@Override
public Long hDecr(String key, String hashKey, Long delta) {
return redisTemplate.opsForHash().increment(key, hashKey, -delta);
}

@Override
public Double zIncr(String key, Object value, Double score) {
return redisTemplate.opsForZSet().incrementScore(key, value, score);
}

@Override
public Double zDecr(String key, Object value, Double score) {
return redisTemplate.opsForZSet().incrementScore(key, value, -score);
}

@Override
public Map<Object, Double> zReverseRangeWithScore(String key, long start, long end) {
return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end)
.stream()
.collect(Collectors.toMap(ZSetOperations.TypedTuple::getValue, ZSetOperations.TypedTuple::getScore));
}

@Override
public Double zScore(String key, Object value) {
return redisTemplate.opsForZSet().score(key, value);
}

@Override
public Map<Object, Double> zAllScore(String key) {
return Objects.requireNonNull(redisTemplate.opsForZSet().rangeWithScores(key, 0, -1))
.stream()
.collect(Collectors.toMap(ZSetOperations.TypedTuple::getValue, ZSetOperations.TypedTuple::getScore));
}

@Override
public Set<Object> sMembers(String key) {
return redisTemplate.opsForSet().members(key);
}

@Override
public Long sAdd(String key, Object... values) {
return redisTemplate.opsForSet().add(key, values);
}

@Override
public Long sAddExpire(String key, long time, Object... values) {
Long count = redisTemplate.opsForSet().add(key, values);
expire(key, time);
return count;
}

@Override
public Boolean sIsMember(String key, Object value) {
return redisTemplate.opsForSet().isMember(key, value);
}

@Override
public Long sSize(String key) {
return redisTemplate.opsForSet().size(key);
}

@Override
public Long sRemove(String key, Object... values) {
return redisTemplate.opsForSet().remove(key, values);
}

@Override
public List<Object> lRange(String key, long start, long end) {
return redisTemplate.opsForList().range(key, start, end);
}

@Override
public Long lSize(String key) {
return redisTemplate.opsForList().size(key);
}

@Override
public Object lIndex(String key, long index) {
return redisTemplate.opsForList().index(key, index);
}

@Override
public Long lPush(String key, Object value) {
return redisTemplate.opsForList().rightPush(key, value);
}

@Override
public Long lPush(String key, Object value, long time) {
Long index = redisTemplate.opsForList().rightPush(key, value);
expire(key, time);
return index;
}

@Override
public Long lPushAll(String key, Object... values) {
return redisTemplate.opsForList().rightPushAll(key, values);
}

@Override
public Long lPushAll(String key, Long time, Object... values) {
Long count = redisTemplate.opsForList().rightPushAll(key, values);
expire(key, time);
return count;
}

@Override
public Long lRemove(String key, long count, Object value) {
return redisTemplate.opsForList().remove(key, count, value);
}

@Override
public Boolean bitAdd(String key, int offset, boolean b) {
return redisTemplate.opsForValue().setBit(key, offset, b);
}

@Override
public Boolean bitGet(String key, int offset) {
return redisTemplate.opsForValue().getBit(key, offset);
}

@Override
public Long bitCount(String key) {
return redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes()));
}

@Override
public List<Long> bitField(String key, int limit, int offset) {
return redisTemplate.execute((RedisCallback<List<Long>>) con ->
con.bitField(key.getBytes(),
BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(limit)).valueAt(offset)));
}

@Override
public byte[] bitGetAll(String key) {
return redisTemplate.execute((RedisCallback<byte[]>) con -> con.get(key.getBytes()));
}

@Override
public Long hyperAdd(String key, Object... value) {
return redisTemplate.opsForHyperLogLog().add(key, value);
}

@Override
public Long hyperGet(String... key) {
return redisTemplate.opsForHyperLogLog().size(key);
}

@Override
public void hyperDel(String key) {
redisTemplate.opsForHyperLogLog().delete(key);
}

@Override
public Long geoAdd(String key, Double x, Double y, String name) {
return redisTemplate.opsForGeo().add(key, new Point(x, y), name);
}

@Override
public List<Point> geoGetPointList(String key, Object... place) {
return redisTemplate.opsForGeo().position(key, place);
}

@Override
public Distance geoCalculationDistance(String key, String placeOne, String placeTow) {
return redisTemplate.opsForGeo()
.distance(key, placeOne, placeTow, RedisGeoCommands.DistanceUnit.KILOMETERS);
}

@Override
public GeoResults<RedisGeoCommands.GeoLocation<Object>> geoNearByPlace(String key, String place, Distance distance, long limit, Sort.Direction sort) {
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates();
// 判断排序方式
if (Sort.Direction.ASC == sort) {
args.sortAscending();
} else {
args.sortDescending();
}
args.limit(limit);
return redisTemplate.opsForGeo()
.radius(key, place, distance, args);
}

@Override
public List<String> geoGetHash(String key, String... place) {
return redisTemplate.opsForGeo()
.hash(key, place);
}

}

#