甘肃建设住房厅网站,扁平 网站模板,安阳做网站推广,网站开发毕业设计中期汇报表抽奖 
一堆用户参与进来#xff0c;然后随机抽取几个幸运用户给予实物/虚拟的奖品#xff1b;此时#xff0c;开发人员就需要写上一个抽奖的算法#xff0c;来实现幸运用户的抽取#xff1b;其实我们完全可以利用Redis的集合#xff08;Set#xff09;#xff0c;就能轻… 抽奖 
一堆用户参与进来然后随机抽取几个幸运用户给予实物/虚拟的奖品此时开发人员就需要写上一个抽奖的算法来实现幸运用户的抽取其实我们完全可以利用Redis的集合Set就能轻松实现抽奖的功能 
功能实现需要的API 
SADD key member1 [member2]添加一个或者多个参与用户SRANDMEMBER KEY [count]随机返回一个或者多个用户SPOP key随机返回一个或者多个用户并删除返回的用户 
SRANDMEMBER 和 SPOP 主要用于两种不同的抽奖模式SRANDMEMBER 适用于一个用户可中奖多次的场景就是中奖之后不从用户池中移除继续参与其他奖项的抽取而 SPOP 就适用于仅能中一次的场景一旦中奖就将用户从用户池中移除后续的抽奖就不可能再抽到该用户 通常 SPOP 会用的会比较多。 Redis-Cli操作 
127.0.0.1:6379 SADD raffle user1
(integer) 1
127.0.0.1:6379 SADD raffle user2 user3 user4 user5 user6 user7 user8 user9 user10
(integer) 9
127.0.0.1:6379 SRANDMEMBER raffle 2
1) user5
2) user2
127.0.0.1:6379 SPOP raffle 2
1) user3
2) user4
127.0.0.1:6379 SPOP raffle 2
1) user10
2) user9 代码实现 
Slf4j
SpringBootTest
public class RaffleMain {private final String KEY_RAFFLE_PROFIX  raffle:;AutowiredRedisTemplate redisTemplate;Testvoid test() {Integer raffleId  1;join(raffleId, 1000, 1001, 2233, 7890, 44556, 74512);List lucky  lucky(raffleId, 2);log.info(活动{} 的幸运中奖用户是{}, raffleId, lucky);}public void join(Integer raffleId, Integer... userIds) {String key  KEY_RAFFLE_PROFIX  raffleId;redisTemplate.opsForSet().add(key, userIds);}public List lucky(Integer raffleId, long num) {String key  KEY_RAFFLE_PROFIX  raffleId;// 随机抽取 抽完之后将用户移除奖池List list  redisTemplate.opsForSet().pop(key, num);// 随机抽取 抽完之后用户保留在池子里//List list  redisTemplate.opsForSet().randomMembers(key, num);return list;}} 点赞收藏 有互动属性活动一般都会有点赞/收藏/喜欢等功能来提升用户之间的互动。 
传统的实现用户点赞之后在数据库中记录一条数据同时一般都会在主题库中记录一个点赞/收藏汇总数来方便显示 
Redis方案基于Redis的集合Set记录每个帖子/文章对应的收藏、点赞的用户数据同时set还提供了检查集合中是否存在指定用户用户快速判断用户是否已经点赞过 
功能实现需要的API 
SADD key member1 [member2]添加一个或者多个成员点赞SCARD key获取所有成员的数量点赞数量SISMEMBER key member判断成员是否存在是否点赞SREM key member1 [member2] 移除一个或者多个成员点赞数量 Redis-Cli操作 
127.0.0.1:6379 sadd like:article:1 user1
(integer) 1
127.0.0.1:6379 sadd like:article:1 user2
(integer) 1
# 获取成员数量点赞数量
127.0.0.1:6379 SCARD like:article:1
(integer) 2
# 判断成员是否存在是否点赞
127.0.0.1:6379 SISMEMBER like:article:1 user1
(integer) 1
127.0.0.1:6379 SISMEMBER like:article:1 user3
(integer) 0
# 移除一个或者多个成员取消点赞
127.0.0.1:6379 SREM like:article:1 user1
(integer) 1
127.0.0.1:6379 SCARD like:article:1
(integer) 1 代码实现 
Slf4j
SpringBootTest
public class LikeMain {private final String KEY_LIKE_ARTICLE_PROFIX  like:article:;AutowiredRedisTemplate redisTemplate;Testvoid test() {long articleId  100;Long likeNum  like(articleId, 1001, 1002, 2001, 3005, 4003);unLike(articleId, 2001);likeNum  likeNum(articleId);boolean b2001  isLike(articleId, 2001);boolean b3005  isLike(articleId, 3005);log.info(文章{} 点赞数量{} 用户2001的点赞状态{} 用户3005的点赞状态{}, articleId, likeNum, b2001, b3005);}/*** 点赞** param articleId 文章ID* return 点赞数量*/public Long like(Long articleId, Integer... userIds) {String key  KEY_LIKE_ARTICLE_PROFIX  articleId;Long add  redisTemplate.opsForSet().add(key, userIds);return add;}public Long unLike(Long articleId, Integer... userIds) {String key  KEY_LIKE_ARTICLE_PROFIX  articleId;Long remove  redisTemplate.opsForSet().remove(key, userIds);return remove;}public Long likeNum(Long articleId) {String key  KEY_LIKE_ARTICLE_PROFIX  articleId;Long size  redisTemplate.opsForSet().size(key);return size;}public Boolean isLike(Long articleId, Integer userId) {String key  KEY_LIKE_ARTICLE_PROFIX  articleId;return redisTemplate.opsForSet().isMember(key, userId);}} 排行榜 排名、排行榜、热搜榜是很多活动、游戏都有的功能常用于用户活动推广、竞技排名、热门信息展示等功能 比如上面的热搜榜热度数据来源于全网用户的贡献但用户只关心热度最高的前50条。 
常规的做法就是将用户的名次、分数等用于排名的数据更新到数据库然后查询的时候通过Order by  limit 取出前50名显示如果是参与用户不多更新不频繁的数据采用数据库的方式也没有啥问题但是一旦出现爆炸性热点资讯比如大陆收复湾湾xxx某些绿了等等短时间会出现爆炸式的流量瞬间的压力可能让数据库扛不住 
Redis方案将热点资讯全页缓存采用Redis的有序队列Sorted Set来缓存热度SCORES即可瞬间缓解数据库的压力同时轻松筛选出热度最高的50条 
功能实现需要的命令 
ZADD key score1 member1 [score2 member2]添加并设置SCORES支持一次性添加多个ZREVRANGE key start stop [WITHSCORES] 根据SCORES降序排列ZRANGE key start stop [WITHSCORES] 根据SCORES降序排列 
Redis-Cli操作 
# 单个插入
127.0.0.1:6379 ZADD ranking 1 user1  
(integer) 1
# 批量插入
127.0.0.1:6379 ZADD ranking 10 user2 50 user3 3 user4 25 user5
(integer) 4
# 降序排列 不带SCORES
127.0.0.1:6379 ZREVRANGE ranking 0 -1 
1) user3
2) user5
3) user2
4) user4
5) user1
# 降序排列 带SCORES
127.0.0.1:6379 ZREVRANGE ranking 0 -1 WITHSCORES
1) user3
2) 50
3) user5
4) 25
5) user2
6) 10
7) user4
8) 3
9) user1
10) 1
# 升序
127.0.0.1:6379 ZRANGE ranking 0 -1 WITHSCORES
1) user1
2) 1
3) user4
4) 3
5) user2
6) 10
7) user5
8) 25
9) user3
10) 50 代码实现 
SpringBootTest
Slf4j
public class RankingTest {private final String KEY_RANKING  ranking;AutowiredRedisTemplate redisTemplate;Testvoid test() {add(1001, (double) 60);add(1002, (double) 80);add(1003, (double) 100);add(1004, (double) 90);add(1005, (double) 70);// 取所有SetDefaultTypedTuple range  range(0, -1);log.info(所有用户排序{}, range);// 前三名range  range(0, 2);log.info(前三名排序{}, range);}public Boolean add(Integer userId, Double score) {Boolean add  redisTemplate.opsForZSet().add(KEY_RANKING, userId, score);return add;}public SetDefaultTypedTuple range(long min, long max) {// 降序SetDefaultTypedTuple set  redisTemplate.opsForZSet().reverseRangeWithScores(KEY_RANKING, min, max);// 升序//SetDefaultTypedTuple set  redisTemplate.opsForZSet().rangeWithScores(KEY_RANKING, min, max);return set;}
} 用户签到BitMap 
很多活动为了拉动用户活跃度会加入比如连续签到的功能 传统做法用户每次签到时往是数据库插入一条签到数据展示的时候把本月或者指定周期的签到数据获取出来用于判断用户是否签到、以及连续签到情况此方式简单理解容易 
Redis做法由于签到数据的关注点就2个是否签到0/1、连续性因此就完全可以利用BitMap位图来实现 如上图所示将一个月的31天用31个位4个字节来表示偏移量offset代表当前是第几天0/1表示当前是否签到连续签到只需从右往左校验连续为1的位数 
由于String类型的最大上限是512M转换为bit则是2^32个bit位。 
所需命令 
SETBIT key offset value向指定位置offset存入一个0或1GETBIT key offset获取指定位置offset的bit值BITCOUNT key [start] [end]统计BitMap中值为1的bit位的数量BITFIELD: 操作查询修改自增BitMap中bit 数组中的指定位置offset的值这里最不容易理解的就是BITFIELD详情可参考https://deepinout.com/redis-cmd/redis-bitmap-cmd/redis-cmd-bitfield.html 而且这部分还必须理解了否则该需求的核心部分就没办法理解了 需求假如当前为8月4号检测本月的签到情况用户分别于1、3、4号签到过 Redis-Cli操作 
# 8月1号的签到
127.0.0.1:6379 SETBIT RangeId:Sign:1:8899 0 1
(integer) 1# 8月3号的签到
127.0.0.1:6379 SETBIT RangeId:Sign:1:8899 2 1
(integer) 1# 8月4号的签到
127.0.0.1:6379 SETBIT RangeId:Sign:1:8899 3 1
(integer) 1# 查询各天的签到情况
# 查询1号
127.0.0.1:6379 GETBIT RangeId:Sign:1:8899 0
(integer) 1
# 查询2号
127.0.0.1:6379 GETBIT RangeId:Sign:1:8899 1
(integer) 0
# 查询3号
127.0.0.1:6379 GETBIT RangeId:Sign:1:8899 2
(integer) 1
# 查询4号
127.0.0.1:6379 GETBIT RangeId:Sign:1:8899 3
(integer) 1# 查询指定区间的签到情况
127.0.0.1:6379 BITFIELD RangeId:Sign:1:8899 get u4 0
1) (integer) 11 1-4号的签到情况为1011二进制  11十进制 代码实现-按月签到 
Slf4j
Service
public class SignByMonthServiceImpl {AutowiredStringRedisTemplate stringRedisTemplate;private int dayOfMonth() {DateTime dateTime  new DateTime();return dateTime.dayOfMonth().get();}/*** 按照月份和用户ID生成用户签到标识 UserId:Sign:560:2021-08** param userId 用户id* return*/private String signKeyWitMouth(String userId) {DateTime dateTime  new DateTime();DateTimeFormatter fmt  DateTimeFormat.forPattern(yyyy-MM);StringBuilder builder  new StringBuilder(UserId:Sign:);builder.append(userId).append(:).append(dateTime.toString(fmt));return builder.toString();}/*** 设置标记位* 标记是否签到** param key* param offset* param tag* return*/public Boolean sign(String key, long offset, boolean tag) {return this.stringRedisTemplate.opsForValue().setBit(key, offset, tag);}/*** 统计计数** param key 用户标识* return*/public long bitCount(String key) {return stringRedisTemplate.execute((RedisCallbackLong) redisConnection - redisConnection.bitCount(key.getBytes()));}/*** 获取多字节位域*/public ListLong bitfield(String buildSignKey, int limit, long offset) {return this.stringRedisTemplate.opsForValue().bitField(buildSignKey, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(limit)).valueAt(offset));}/*** 判断是否被标记** param key* param offest* return*/public Boolean container(String key, long offest) {return this.stringRedisTemplate.opsForValue().getBit(key, offest);}/*** 用户今天是否签到** param userId* return*/public int checkSign(String userId) {DateTime dateTime  new DateTime();String signKey  this.signKeyWitMouth(userId);int offset  dateTime.getDayOfMonth() - 1;int value  this.container(signKey, offset) ? 1 : 0;return value;}/*** 查询用户当月签到日历** param userId* return*/public MapString, Boolean querySignedInMonth(String userId) {DateTime dateTime  new DateTime();int lengthOfMonth  dateTime.dayOfMonth().getMaximumValue();MapString, Boolean signedInMap  new HashMap(dateTime.getDayOfMonth());String signKey  this.signKeyWitMouth(userId);ListLong bitfield  this.bitfield(signKey, lengthOfMonth, 0);if (!CollectionUtils.isEmpty(bitfield)) {long signFlag  bitfield.get(0)  null ? 0 : bitfield.get(0);DateTimeFormatter fmt  DateTimeFormat.forPattern(yyyy-MM-dd);for (int i  lengthOfMonth; i  0; i--) {DateTime dateTime1  dateTime.withDayOfMonth(i);signedInMap.put(dateTime1.toString(fmt), signFlag  1  1 ! signFlag);signFlag  1;}}return signedInMap;}/*** 用户签到** param userId* return*/public boolean signWithUserId(String userId) {int dayOfMonth  this.dayOfMonth();String signKey  this.signKeyWitMouth(userId);long offset  (long) dayOfMonth - 1;boolean re  false;if (Boolean.TRUE.equals(this.sign(signKey, offset, Boolean.TRUE))) {re  true;}// 查询用户连续签到次数,最大连续次数为7天long continuousSignCount  this.queryContinuousSignCount(userId, 7);return re;}/*** 统计当前月份一共签到天数** param userId*/public long countSignedInDayOfMonth(String userId) {String signKey  this.signKeyWitMouth(userId);return this.bitCount(signKey);}/*** 查询用户当月连续签到次数** param userId* return*/public long queryContinuousSignCountOfMonth(String userId) {int signCount  0;String signKey  this.signKeyWitMouth(userId);int dayOfMonth  this.dayOfMonth();ListLong bitfield  this.bitfield(signKey, dayOfMonth, 0);if (!CollectionUtils.isEmpty(bitfield)) {long signFlag  bitfield.get(0)  null ? 0 : bitfield.get(0);DateTime dateTime  new DateTime();// 连续不为0即为连续签到次数当天未签到情况下for (int i  0; i  dateTime.getDayOfMonth(); i) {if (signFlag  1  1  signFlag) {if (i  0) break;} else {signCount  1;}signFlag  1;}}return signCount;}/*** 以7天一个周期连续签到次数** param period 周期* return*/public long queryContinuousSignCount(String userId, Integer period) {//查询目前连续签到次数long count  this.queryContinuousSignCountOfMonth(userId);//按最大连续签到取余if (period ! null  period  count) {long num  count % period;if (num  0) {count  period;} else {count  num;}}return count;}
} 测试 SpringBootTest
Slf4j
public class SignTest2 {Autowiredprivate SignByMonthServiceImpl signByMonthService;Autowiredprivate StringRedisTemplate redisTemplate;/*** 测试用户按月签到*/Testpublic void querySignDay() {//模拟用户签到//for(int i5;i19;i){redisTemplate.opsForValue().setBit(UserId:Sign:560:2022-08, 0, true);//}System.out.println(560用户今日是否已签到:  this.signByMonthService.checkSign(560));MapString, Boolean stringBooleanMap  this.signByMonthService.querySignedInMonth(560);System.out.println(本月签到情况:);for (Map.EntryString, Boolean entry : stringBooleanMap.entrySet()) {System.out.println(entry.getKey()  :   (entry.getValue() ? √ : -));}long countSignedInDayOfMonth  this.signByMonthService.countSignedInDayOfMonth(560);System.out.println(本月一共签到:  countSignedInDayOfMonth  天);System.out.println(目前连续签到:  this.signByMonthService.queryContinuousSignCount(560, 7)  天);}
} 代码实现-指定时间签到 
Slf4j
Service
public class SignByRangeServiceImpl {AutowiredStringRedisTemplate stringRedisTemplate;/*** 根据区间的id 以及用户id 拼接key** param rangeId 区间ID 一般是指定活动的ID等* param userId  用户的ID* return*/private String signKey(Integer rangeId, Integer userId) {StringBuilder builder  new StringBuilder(RangeId:Sign:);builder.append(rangeId).append(:).append(userId);return builder.toString();}/*** 获取当前时间与起始时间的间隔天数** param start 起始时间* return*/private int intervalTime(LocalDateTime start) {return (int) (LocalDateTime.now().toLocalDate().toEpochDay() - start.toLocalDate().toEpochDay());}/*** 设置标记位* 标记是否签到** param key    签到的key* param offset 偏移量 一般是指当前时间离起始时间活动开始的天数* param tag    是否签到  true:签到  false:未签到* return*/private Boolean setBit(String key, long offset, boolean tag) {return this.stringRedisTemplate.opsForValue().setBit(key, offset, tag);}/*** 统计计数** param key 统计的key* return*/private long bitCount(String key) {return stringRedisTemplate.execute((RedisCallbackLong) redisConnection - redisConnection.bitCount(key.getBytes()));}/*** 获取多字节位域** param key    缓存的key* param limit  获取多少* param offset 偏移量是多少* return*/private ListLong bitfield(String key, int limit, long offset) {return this.stringRedisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(limit)).valueAt(offset));}/*** 判断是否签到** param key    缓存的key* param offest 偏移量 指当前时间距离起始时间的天数* return*/private Boolean container(String key, long offest) {return this.stringRedisTemplate.opsForValue().getBit(key, offest);}/*** 根据起始时间进行签到** param rangeId* param userId* param start* return*/public Boolean sign(Integer rangeId, Integer userId, LocalDateTime start) {int offset  intervalTime(start);String key  signKey(rangeId, userId);return setBit(key, offset, true);}/*** 根据偏移量签到** param rangeId* param userId* param offset* return*/public Boolean sign(Integer rangeId, Integer userId, long offset) {String key  signKey(rangeId, userId);return setBit(key, offset, true);}/*** 用户今天是否签到** param userId* return*/public Boolean checkSign(Integer rangeId, Integer userId, LocalDateTime start) {long offset  intervalTime(start);String key  this.signKey(rangeId, userId);return this.container(key, offset);}/*** 统计当前月份一共签到天数** param userId*/public long countSigned(Integer rangeId, Integer userId) {String signKey  this.signKey(rangeId, userId);return this.bitCount(signKey);}public MapString, Boolean querySigned(Integer rangeId, Integer userId, LocalDateTime start) {int days  intervalTime(start);MapString, Boolean signedInMap  new HashMap(days);String signKey  this.signKey(rangeId, userId);ListLong bitfield  this.bitfield(signKey, days  1, 0);if (!CollectionUtils.isEmpty(bitfield)) {long signFlag  bitfield.get(0)  null ? 0 : bitfield.get(0);DateTimeFormatter fmt  DateTimeFormatter.ofPattern(yyyy-MM-dd);for (int i  days; i  0; i--) {LocalDateTime localDateTime  start.plusDays(i);signedInMap.put(localDateTime.format(fmt), signFlag  1  1 ! signFlag);signFlag  1;}}return signedInMap;}/*** 查询用户当月连续签到次数** param userId* return*/public long queryContinuousSignCount(Integer rangeId, Integer userId, LocalDateTime start) {int signCount  0;String signKey  this.signKey(rangeId, userId);int days  this.intervalTime(start);ListLong bitfield  this.bitfield(signKey, days  1, 0);if (!CollectionUtils.isEmpty(bitfield)) {long signFlag  bitfield.get(0)  null ? 0 : bitfield.get(0);DateTime dateTime  new DateTime();// 连续不为0即为连续签到次数当天未签到情况下for (int i  0; i  dateTime.getDayOfMonth(); i) {if (signFlag  1  1  signFlag) {if (i  0) break;} else {signCount  1;}signFlag  1;}}return signCount;}
} 测试 
SpringBootTest
Slf4j
public class SignTest {AutowiredSignByRangeServiceImpl signByRangeService;Testvoid test() {DateTimeFormatter isoDateTime  DateTimeFormatter.ISO_DATE_TIME;// 活动开始时间LocalDateTime start  LocalDateTime.of(2022, 8, 1, 1, 0, 0);Integer rangeId  1;Integer userId  8899;log.info(签到开始时间: {}, start.format(isoDateTime));log.info(活动ID: {} 用户ID: {}, rangeId, userId);// 手动指定偏移量签到signByRangeService.sign(rangeId, userId, 0);// 判断是否签到Boolean signed  signByRangeService.checkSign(rangeId, userId, start);log.info(今日是否签到: {}, signed ? √ : -);// 签到Boolean sign  signByRangeService.sign(rangeId, userId, start);log.info(签到操作之前的签到状态{} -表示今日第一次签到√表示今天已经签到过了, sign ? √ : -);// 签到总数long countSigned  signByRangeService.countSigned(rangeId, userId);log.info(总共签到: {} 天, countSigned);// 连续签到的次数long continuousSignCount  signByRangeService.queryContinuousSignCount(rangeId, userId, start);log.info(连续签到: {} 天, continuousSignCount);// 签到的详情MapString, Boolean stringBooleanMap  signByRangeService.querySigned(rangeId, userId, start);for (Map.EntryString, Boolean entry : stringBooleanMap.entrySet()) {log.info(签到详情 {} : {}, entry.getKey(), (entry.getValue() ? √ : -));}}
} GEO搜附近 
活动中经常有展示附近用户、商家的诉求 如果自己想要根据经纬度来实现一个搜索附近的功能是非常麻烦的但是Redis 在3.2的版本新增了Redis GEO用于存储地址位置信息并对支持范围搜索基于GEO就能轻松且快速的开发一个搜索附近的功能 GEO API 及Redis-cli 操作 
geoadd新增位置坐标 
127.0.0.1:6379 GEOADD drinks 116.62445 39.86206 starbucks 117.3514785 38.7501247 yidiandian 116.538542 39.75412 xicha
(integer) 3 geopos获取位置坐标 
127.0.0.1:6379 GEOPOS drinks starbucks
1) 1) 116.624451577663421632) 39.86206038535793539
127.0.0.1:6379 GEOPOS drinks starbucks yidiandian mxbc
1) 1) 116.624451577663421632) 39.86206038535793539
2) 1) 117.351480424404144292) 38.75012383773680114
3) (nil) geodist计算两个位置之间的距离 
单位参数 
m 米默认单位。km 千米。mi 英里。ft 英尺。 
127.0.0.1:6379 GEODIST drinks starbucks yidiandian
138602.4133
127.0.0.1:6379 GEODIST drinks starbucks xicha
14072.1255
127.0.0.1:6379 GEODIST drinks starbucks xicha m
14072.1255
127.0.0.1:6379 GEODIST drinks starbucks xicha km
14.0721 georadius根据用户给定的经纬度坐标来获取指定范围内的地理位置集合 
参数说明 m 米默认单位。km 千米。mi 英里。ft 英尺。WITHDIST: 在返回位置元素的同时 将位置元素与中心之间的距离也一并返回。WITHCOORD: 将位置元素的经度和纬度也一并返回。WITHHASH: 以 52 位有符号整数的形式 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试 实际中的作用并不大。COUNT 限定返回的记录数。ASC: 查找结果根据距离从近到远排序。DESC: 查找结果根据从远到近排序。 127.0.0.1:6379 GEORADIUS drinks 116 39 100 km WITHDIST
1) 1) xicha2) 95.8085
127.0.0.1:6379 GEORADIUS drinks 116 39 100 km WITHDIST WITHCOORD
1) 1) xicha2) 95.80853) 1) 116.538540422916412352) 39.75411928478748536
127.0.0.1:6379 GEORADIUS drinks 116 39 100 km WITHDIST WITHCOORD WITHHASH
1) 1) xicha2) 95.80853) (integer) 40691518008823014) 1) 116.538540422916412352) 39.75411928478748536127.0.0.1:6379 GEORADIUS drinks 116 39 120 km WITHDIST WITHCOORD  COUNT 1
1) 1) xicha2) 95.80853) 1) 116.538540422916412352) 39.75411928478748536127.0.0.1:6379 GEORADIUS drinks 116 39 120 km WITHDIST WITHCOORD  COUNT 1 ASC
1) 1) xicha2) 95.80853) 1) 116.538540422916412352) 39.75411928478748536127.0.0.1:6379 GEORADIUS drinks 116 39 120 km WITHDIST WITHCOORD  COUNT 1 DESC
1) 1) starbucks2) 109.87033) 1) 116.624451577663421632) 39.86206038535793539 georadiusbymember根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。功能和上面的georadius类似只是georadius是以经纬度坐标为中心这个是以某个地点为中心 geohash返回一个或多个位置对象的 geohash 值。 
127.0.0.1:6379 GEOHASH drinks starbucks xicha
1) wx4fvbem6d0
2) wx4f5vhb8b0 代码实现 
SpringBootTest
Slf4j
public class GEOTest {private final String KEY  geo:drinks;AutowiredRedisTemplate redisTemplate;Testpublic void test() {add(starbucks, new Point(116.62445, 39.86206));add(yidiandian, new Point(117.3514785, 38.7501247));add(xicha, new Point(116.538542, 39.75412));get(starbucks, yidiandian, xicha);GeoResults nearByXY  getNearByXY(new Point(116, 39), new Distance(120, Metrics.KILOMETERS));ListGeoResult content  nearByXY.getContent();for (GeoResult geoResult : content) {log.info({}, geoResult.getContent());}GeoResults nearByPlace  getNearByPlace(starbucks, new Distance(120, Metrics.KILOMETERS));content  nearByPlace.getContent();for (GeoResult geoResult : content) {log.info({}, geoResult.getContent());}getGeoHash(starbucks, yidiandian, xicha);del(yidiandian, xicha);}private void add(String name, Point point) {Long add  redisTemplate.opsForGeo().add(KEY, point, name);log.info(成功添加名称{} 的坐标信息信息{}, name, point);}private void get(String... names) {ListPoint position  redisTemplate.opsForGeo().position(KEY, names);log.info(获取名称为{} 的坐标信息{}, names, position);}private void del(String... names) {Long remove  redisTemplate.opsForGeo().remove(KEY, names);log.info(删除名称为{} 的坐标信息数量{}, names, remove);}/*** 根据坐标 获取指定范围的位置** param point* param distance* return*/private GeoResults getNearByXY(Point point, Distance distance) {Circle circle  new Circle(point, distance);RedisGeoCommands.GeoRadiusCommandArgs args  RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance(). // 包含距离includeCoordinates(). // 包含坐标sortAscending(). // 排序 还可选sortDescending()limit(5); // 获取前多少个GeoResults geoResults  redisTemplate.opsForGeo().radius(KEY, circle, args);log.info(根据坐标获取{} {} 范围的数据{}, point, distance, geoResults);return geoResults;}/*** 根据一个位置获取指定范围内的其他位置** param name* param distance* return*/private GeoResults getNearByPlace(String name, Distance distance) {RedisGeoCommands.GeoRadiusCommandArgs args  RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance(). // 包含距离includeCoordinates(). // 包含坐标sortAscending(). // 排序 还可选sortDescending()limit(5); // 获取前多少个GeoResults geoResults  redisTemplate.opsForGeo().radius(KEY, name, distance, args);log.info(根据位置{} 获取 {} 范围的数据{}, name, distance, geoResults);return geoResults;}/*** 获取GEO HASH** param names* return*/private ListString getGeoHash(String... names) {ListString hash  redisTemplate.opsForGeo().hash(KEY, names);log.info(names{} 对应的hash{}, names, hash);return hash;}
} 执行日志 
成功添加名称starbucks 的坐标信息信息Point [x116.624450, y39.862060]
成功添加名称yidiandian 的坐标信息信息Point [x117.351479, y38.750125]
成功添加名称xicha 的坐标信息信息Point [x116.538542, y39.754120]获取名称为[starbucks, yidiandian, xicha] 的坐标信息[Point [x116.624452, y39.862060], Point [x117.351480, y38.750124], Point [x116.538540, y39.754119]]根据坐标获取Point [x116.000000, y39.000000] 120.0 KILOMETERS 范围的数据GeoResults: [averageDistance: 102.8394 KILOMETERS, results: GeoResult [content: RedisGeoCommands.GeoLocation(namexicha, pointPoint [x116.538540, y39.754119]), distance: 95.8085 KILOMETERS, ],GeoResult [content: RedisGeoCommands.GeoLocation(namestarbucks, pointPoint [x116.624452, y39.862060]), distance: 109.8703 KILOMETERS, ]]
RedisGeoCommands.GeoLocation(namexicha, pointPoint [x116.538540, y39.754119])
RedisGeoCommands.GeoLocation(namestarbucks, pointPoint [x116.624452, y39.862060])根据位置starbucks 获取 120.0 KILOMETERS 范围的数据GeoResults: [averageDistance: 7.03605 KILOMETERS, results: GeoResult [content: RedisGeoCommands.GeoLocation(namestarbucks, pointPoint [x116.624452, y39.862060]), distance: 0.0 KILOMETERS, ],GeoResult [content: RedisGeoCommands.GeoLocation(namexicha, pointPoint [x116.538540, y39.754119]), distance: 14.0721 KILOMETERS, ]]
RedisGeoCommands.GeoLocation(namestarbucks, pointPoint [x116.624452, y39.862060])
RedisGeoCommands.GeoLocation(namexicha, pointPoint [x116.538540, y39.754119])names[starbucks, yidiandian, xicha] 对应的hash[wx4fvbem6d0, wwgkqqhxzd0, wx4f5vhb8b0]删除名称为[yidiandian, xicha] 的坐标信息数量2