不支持事务,且使用的是同一个redis对象,容易造成压力


目录

  • 1. 引入pom
  • 2. 设置yml
  • 3. 配置文件
  • 4.模板接口
  • 4.1 公共方法
  • 4.2 string
  • 4.3 hash
  • 4.4 list
  • 4.5 set
  • 4.6 zset
  • 4.7 bitmaps 二进制站位
  • 4.7.1 防止热key 分组分片
  • 4.8 HyperLogLog 记录数据数量,而不记录数据本身
  • 4.7 Geospatial 经纬度
  • 5. 实现类


1. 引入pom

<!--        redis       -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
            <version>2.5.0</version>
        </dependency>
        <!--  redis:  因为lettuce,依赖commons-pool2,所以引入该包即可。         -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.8.0</version>
        </dependency>

2. 设置yml

spring:
  redis:
    host: 127.0.0.1
    port: 6666
    password: 123
    # redis 共 16 个 db库,从0开始
    database: 0
    # 连接redis超时时间
    timeout: 3000
    # 自定义过期时间
    #expire: 300
    lettuce:
      pool:
        max-active: 20 #可分配的最大连接数
        max-wait: -1 #连接池无可用时,最大等待时间
        max-idle: 10 #连接池 最大空闲
        min-idle: 0 #连接池 最小空闲

3. 配置文件

RedisTemplate 是 <object,object> 的形式, 而我们大部分都需要<string,object>的存储方式;
StringRedisTemplate 是 <String,String> 的形式;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
@Configuration
@EnableCaching //有缓存使用缓存
public class RedisConfig {
    /**
     * 生成 redisTemplate<String,Object>
     * @param factory
     * @return
     */
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
        RedisTemplate<String,Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        //Json序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        //String序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //key采用string的序列化形式
        template.setKeySerializer(stringRedisSerializer);
        //hask的key也采用String的序列化形式
        template.setHashKeySerializer(stringRedisSerializer);
        //value的序列化形式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //hash的value系列化形式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
    /**
     * 解决缓存转换异常,配置序列化,解决乱码
     * @param factory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory){
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //配置序列化,解决乱码问题,过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

4.模板接口

4.1 公共方法

import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisCommands;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.ZSetOperations;

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

public interface RedisTemplateUtils{
/**
     * 指定缓存失效时间
     * @param key 键
     * @param time 失效时间
     * @return 是否设置成功
     */
    Boolean expire(String key,long time);

    /**
     * 获取缓存失效时间
     * @param key 键
     * @return 缓存失效时间 (秒) 0代表永久有效
     */
    Long getExpire(String key);

    /**
     * 判断是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    Boolean hasKey(String key);

    /**
     * 删除缓存
     * @param keys 键
     */
    @SuppressWarnings("unchecked")
    void del(String... keys);
}

4.2 string

/**
     * 缓存获取
     * @param key
     * @return 值
     */
    Object get(String key);

    /**
     * 转换成需要的类型
     * @param key 键
     * @param clazz 类型
     * @param <T> 类型对象
     * @return 值
     */
    <T>T get(String key,Class<T> clazz);

    /**
     * 追加字符串
     * @param key
     * @param value
     */
    void append(String key,String value);

    /**
     * 缓存放入
     * @param key 键
     * @param value 值
     * @return 是否成功
     */
    Boolean set(String key, Object value);

    /**
     * 缓存放入 并设置时间
     * @param key 键
     * @param value 值
     * @param time (秒) time要大于0 如果time小于等于0 将设置无限期
     * @return 是否成功
     */
    Boolean set(String key,Object value,long time);

    /**
     * 批量添加
     *
     * isAtom:  原子操作
     *     true: 若某个key已经存在,则集体添加失败
     *     false:  若某个key已经存在则替换为新值,其他不存在的则新增
     *
     * @param map value 尽量进行json转换,方便处理
     * @param isAtom 原子操作
     * @return
     */
    Boolean setOrUpdate(Map<String,Object> map, boolean isAtom);

    /**
     * 批量获取
     * @param keys
     * @return
     */
    List<Object> getList(String... keys);

    /**
     * 如果key已存在就不执行,并返回false 不存在就执行 并返回true
     * @param key 键
     * @param value 值
     * @return 是否成功
     */
    Boolean setNx(String key,Object value);


    /**
     * 如果key已存在就不执行,并返回false 不存在就执行 并返回true
     * @param key 键
     * @param value 值
     * @param time (秒) time要大于0 如果time小于等于0 将设置无限期
     * @return 是否成功
     */
    Boolean setNx(String key, Object value, long time);

    /**
     * 如果key已存在就执行,并返回true 不存在就不执行 并返回false
     * @param key 键
     * @param value 值
     * @return true成功 false失败
     */
    Boolean setXx(String key, Object value);

    /**
     * 如果key已存在就执行,并返回true 不存在就不执行 并返回false
     * @param key 键
     * @param value 值
     * @param time (秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false失败
     */
    Boolean setXx(String key, Object value, long time);

    /**
     * 递增
     * @param key   键
     * @param delta 要增加几(大于0)
     * @return
     */
    Double incr(String key, double delta) ;

    /**
     * 递减
     * @param key   键
     * @param delta 要减少几(大于0)
     * @return
     */
    Double decr(String key, double delta) ;

4.3 hash

/**
     * HashGet
     * @param key 键
     * @param item 项
     * @return 值
     */
    Object hGet(String key,String item);

    /**
     * 获取 hash key 的所有键值
     * @param key
     * @return
     */
    Map<Object,Object> hmGet(String key);

    /**
     * 获取hashkey 所对应的值
     *
     * @param key
     * @param hashKey
     * @return values
     */
    List<Object> hGetMulti(String key,String... hashKey);


    /**
     * 获取 hashkey 中所有的 健
     *
     * @param key
     * @return
     */
    Set hGetItemKeys(String key);

    /**
     * 获取 hashkey 中所有的值
     * @param key
     * @return
     */
    List<Object> hGetItemValues(String key);

    /**
     * hashSet
     *
     * @param key hash key
     * @param map hKey hValue
     * @return true 成功 false 失败
     */
    Boolean hmSet(String key, Map<String,Object> map);

    /**
     * HashSet 并设置时间
     *
     * @param key  键
     * @param map  对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    Boolean hmSet(String key, Map<String, Object> map, long time);

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @return true 成功 false失败
     */
    Boolean hSet(String key, String item, Object value);

    /**
     * 新增
     * hKey
     *    不存在  则新增到map中,
     *     存在    则不新增也不覆盖
     *
     * @param key
     * @param hKey
     * @param hValue
     * @return
     */
    Boolean hSetIfAbsent(String key, String hKey, Object hValue);

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    Boolean hSet(String key, String item, Object value, long time);

    /**
     * 获取长度
     * @param key
     * @return
     */
    Long hSize(String key);

    /**
     * 获取指定key 的 hashKey 的 hashValue 的 length
     *
     * @param key
     * @param hKey
     * @return
     */
    Long hLengthOfValue(String key,String hKey);

    /**
     * 删除hash表中的值
     *
     * @param key  键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    void hDel(String key, Object... item);

    /**
     * 判断hash表中是否有该项的值
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    Boolean hHasKey(String key, String item);

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     *
     * @param key  键
     * @param item 项
     * @param by   要增加几(>0)
     * @return
     */
    Double hIncr(String key, String item, double by);

    /**
     * hash递减
     *
     * @param key  键
     * @param item 项
     * @param by   要减少几 (>0)
     * @return
     */
    Double hDecr(String key, String item, double by);

4.4 list

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

    /**
     * 获取指定下标的元素 左边从0开始 右边从-1开始
     * 超过下标,不报错,返回null
     * @param key
     * @param index
     * @return
     */
    Object lGet(String key, long index);

    /**
     * 获取指定下标的元素 左边从0开始 右边从-1开始
     * 超过下标,不报错,返回null
     * @param key
     * @param object
     * @return
     */
    Object lGet(String key, Object object);



    /**
     * 追加
     *
     * 插入 把 value 插入最左或者最右边
     * @param key
     * @param value
     * @param isRight 是否加到右边
     * @return 长度  -1为报错失败
     */
    void lAppend(String key,Object value, boolean isRight);

    /**
     * 追加
     *
     * 在 pivot 根据 isRight 字段判断是否紧邻左右 插入 newValue
     * 在集合中查找 pivot 值
     * 有多个 pivot 值时,从左边开始查到第一个 pivot 值即可,pivot value 值左边插入新值 newValue
     * 不存在 pivot 时,不插入新值 newValue
     *
     * @param key
     * @param pivot 已经在List中的元素
     * @param newValue 新加入的元素
     * @param isRight 是否要加到右边
     * @return 长度  -1为报错失败
     */
    void lAppendPivot(String key, Object pivot, Object newValue, boolean isRight);

    /**
     * 追加
     * 批量插入新值
     *
     * @param key
     * @param time <=0 则持久化
     * @param isRight 是否加到右边
     * @param values
     * @return 长度  -1为报错失败
     */
    void lAppendAll(String key, long time, boolean isRight, Object... values);

    /**
     * 插入
     * 如果Key存在 则新增,不存在不新增
     *
     * @param key
     * @param value
     * @param isRight
     * @return
     */
    void lSetIfPresent(String key, String value,  boolean isRight);

    /**
     * 延迟操作
     * time秒后移除最左边的一个元素
     * @param key
     * @param time 秒
     * @return
     */
    Object lLeftPop(String key,long time);
    /**
     * 延迟操作
     * time秒后移除最右边的一个元素
     * @param key
     * @param time
     * @return
     */
    Object lRightPop(String key,long time);

    /**
     * 延迟操作
     *
     * 移除k1中最右的值,并将移除的值插入k2中最左侧
     *     k1和k2不是同一个key时,k1右侧移除,k2左侧插入,k2不存在时则新增一个然后在插入
     *     k1和k2是同一个key时,相当于把最右侧的值移到了最左侧
     * @param key1
     * @param key2
     * @param timeout 延迟多久操作 为null时立即操作
     * @return 操作的元素
     */
    Object lRightPopAndLeftPush(String key1,String key2, long timeout);

    /**
     * 替换
     * 指定下标替换新值
     * @param key
     * @param value
     * @param index
     */
    void lReplace(String key, Object value, long index);

    /**
     * 1. 截取 index1 到 index2 之间的数组为 newList,
     * 2. 将 key 的 value 置空
     * 3. 将 key 的 value 置为 newList
     *
     * 注意:  会保留index1和index2所占位的对象
     * 1. 左侧坐标从0开始,右侧从-1开始
     * 2. 当index1超过坐标时(此时与index2无关),都会截取为空,key会被删除
     * 3. 当index1为负时(此时与index2无关),都会截取为空,key会被删除
     * 4. 当index1为正且在下标存在其中,index2为负数时,index1 在 index2 左侧,相当于去左去右,保留了中间的部分
     * 5. 当index1为正且在下标存在其中,index2为负数时,index1 在 index2 右侧时,截取为空, key会被删除
     * 6. 若 list 的长度为 n ,index1为n-, index2为n+,则截取index1 到末尾的列表
     * @param key
     * @param index1
     * @param index2
     */
    void lTrim(String key,long index1,long index2);


    /**
     * 获取指定下标之间的值
     * 包括 start 和 end 位置的值
     *
     * @param key   键
     * @param start 开始
     * @param end   结束 0 到 -1代表所有值
     * @return
     */
    List<Object> lGet(String key, long start, long end);

    /**
     * 删除指定元素
     *
     * count> 0:删除等于从左到右移动的值的第一个元素;
     * count< 0:删除等于从右到左移动的值的第一个元素;
     * count = 0:删除等于value的所有元素。
     * @param key
     * @param count
     * @param value
     */
    void lRemove(String key, int count, Object value);

4.5 set

/**
     * 根据key 获取set的所有值
     * @param key
     * @return
     */
    Set<Object> sGet(String key);

    /**
     * 根据value 从一个set中查询是否存在
     * @param key   键
     * @param value 值
     * @return true 存在 false不存在
     */
    Boolean  sHasKey(String key, Object value);

    /**
     * 将数据放入set缓存
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    Long sSet(String key, Object... values);


    /**
     * 将set数据放入缓存
     *
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    Long sSetAndTime(String key, long time, Object... values);

    /**
     * 获取set长度
     * @param key
     * @return
     */
    Long sGetSetSize(String key);


    /**
     * 移除值为value的
     * @param key 键
     * @return 移除个数
     */
    Long sDel(String key, Object... values);


    /**
     * 并集 --- 把指定的所有key 合并去除重复返回
     * keys.length == 1  --> sGet(keys[0])
     * keys.length > 1  --> 返回去重后的并集
     *
     * @param keys
     * @return
     */
    Set<Object> sGetUnion(String... keys);

    /**
     * 并集 --- 把指定的所有key 合并去除重复返回
     *
     * @param destKey 新生成的列表的 key 名,(会存入redis,若指定key存在则会删除)
     * @param otherKeys 要进行合并的 key
     * @return 新生成的set长度  如果需要获取新生成的对象,请调用 sGet(String key)
     */
    Long sGetUnionAndStore(String destKey, String... otherKeys);

    /**
     * 交集 -- 把指定的keys重复的元素进行合并返回
     * @param keys
     * @return
     */
    Set<Object> sGetDifference(String... keys);

    /**
     * 交集 -- 把指定的keys重复的元素合并返回
     * @param destKey 新生成的列表的 key 名,(会存入redis,若指定key存在则会删除)
     * @param otherKeys 要进行合并的 key
     * @return 交集的长度
     */
    Long sGetDifference(String destKey, String... otherKeys);

    /**
     * 差集  -- 把所有不重复的返回
     * @param keys
     * @return
     */
    Set<Object> sGetIntersect(String... keys);

    /**
     * 差集 -- 把所有不重复的添加到 destkey 中
     * @param destKey 新生成的列表的 key 名,(会存入redis,若指定key存在则会删除)
     * @param otherKeys 要进行差集计算的 key
     * @return 差集的长度
     */
    Long sGetIntersect(String destKey, String... otherKeys);

    /**
     * 将 key1 的 value 移到 key2
     * @param key1
     * @param value
     * @param key2
     * @return true 成功  false 失败
     */
    Boolean sMove(String key1, Object value, String key2);

    /**
     * 随机获取 count 个元素 并 移除
     * @param key
     * @param count 为 null 时获取一个
     * @return 返回单个对象 或 list
     */
    Object sPop(String key, Long count);

    /**
     * 随机获取 count 个元素
     *
     * @param key
     * @param count 为 null 时获取 10个
     * @param isDistinct 是否去重
     * @return 返回单个对象 或 list
     */
    List<Object> sRandomMembers(String key, Long count, boolean isDistinct);

4.6 zset

/**
     * 向指定key中添加元素,按照score值由小到大进行排列
     * 集合中对应元素已存在,会被覆盖,包括score
     * @param key
     * @param value
     * @param score
     * @return 是否成功
     */
    Boolean zSet(String key,Object value, double score);

    /**
     * 向指定key中添加元素,按照score值由小到大进行排列
     * 集合中对应元素已存在,会被覆盖,包括score
     *
     * Set<ZSetOperations.TypedTuple<String>> tuples = new HashSet<>();
     * tuples.add(new DefaultTypedTuple<>("eee",9.6));
     *
     * @param key
     * @param tuples
     * @return
     */
    Boolean zSet(String key, Set<ZSetOperations.TypedTuple<Object>> tuples);

    /**
     * 增加key对应的集合中元素v1的score值,并返回增加后的值
     * v1不存在,直接新增一个元素
     * @param key
     * @param value
     * @param delta
     * @return
     */
    Double zIncrScore(String key, Object value, double delta);

    /**
     * 获取key对应集合中o元素的score值
     * @param key
     * @param value
     * @return
     */
    Double zGetScore(String key, Object value);

    /**
     * 获取集合的大小,地层调用的还是 zCard(K key)
     * @param key
     * @return
     */
    Long zSize(String key);

    /**
     * 获取指定score区间里的元素个数
     * 包括min、max
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    Long zCount(String key,double min,double max);

    /**
     * 获取指定下标之间的值
     * (0,-1)就是获取全部
     *
     * @param key
     * @param start
     * @param end
     * @param isDesc 是否倒序,默认从小到大
     * @return
     */
    Set<Object> zGet(String key,long start,long end, boolean isDesc);

    /**
     * 获取指定score区间的值
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    Set<Object> zGet(String key,double min, double max);

    /**
     * 获取指定score区间的值,然后从给定下标和给定长度获取最终值
     * @param key
     * @param min 最小score 包含
     * @param max 最大score 包含
     * @param offset 偏移 0开始
     * @param count 长度
     * @return
     */
    Set<Object> zGet(String key,double min, double max,long offset, long count);

    /**
     * 获取指定元素在集合中的索引,索引从0开始
     * @param key
     * @param value
     * @param isDesc 是否倒序,默认从小到大
     * @return
     */
    Long zGetRank(String key,Object value, boolean isDesc);

    /**
     * 移除指定值
     * @param key
     * @param values
     * @return
     */
    Long zDel(String key,Object... values);

    /**
     * 移除指定下标的值
     * @param key
     * @param start
     * @param end
     * @return
     */
    Long zDel(String key,long start,long end);

    /**
     * 移除指定score之间的值
     * @param key
     * @param min
     * @param max
     * @return
     */
    Long zDel(String key,double min, double max);

4.7 bitmaps 二进制站位

/**
     * 将指定param的值设置为1,{@param param}会经过hash计算进行存储。
     *
     * @param key bitmap结构的key
     * @param param 要设置偏移的key,该key会经过hash运算。
     * @param value 即该位设置为1,否则设置为0
     * @return 返回设置该value之前的值。
     */
    Boolean bSet(String key,String param,boolean value);

    /**
     * 将指定offset偏移量的值设置为1;
     *
     * @param key    bitmap结构的key
     * @param offset 指定的偏移量。
     * @param value  true:即该位设置为1,否则设置为0
     * @return 返回设置该value之前的值
     */
    Boolean bSet(String key,long offset, boolean value);

    /**
     * 获取指定偏移位上的值,{@param param}会经过hash计算进行存储。
     *
     * @param key   bitmap结构的key
     * @param param 要移除偏移的key,该key会经过hash运算。
     * @return 若偏移位上的值为1,那么返回true。
     */
    Boolean bGet(String key,String param);

    /**
     * 获取指定偏移位上的值
     *
     * @param key   bitmap结构的key
     * @param offset 偏移量
     * @return 若偏移位上的值为1,那么返回true。
     */
    Boolean bGet(String key,long offset);


    /**
     * 统计指定范围中value为1的数量
     *
     * @param key bitMap中的key
     * @return 在指定范围[start,end]内所有value=1的数量
     */
    Long bCount(String key);

    /**
     * 统计指定范围中value为1的数量
     * bitCount(key.getBytes,start,end);  start 和 end  都是 byte (1byte = 8bit)
     * {@fun bSet(key,7,true);}进行存储时,单位是bit。那么只需要统计[0,1]便可以统计到上述set的值。
     *
     * @param key bitMap中的key
     * @param start 0开始
     * @param end 结束下标
     * @return 在指定范围[start,end]内所有value=1的数量
     */
    Long bCount(String key,long start, long end);

    /**
     * 对一个或多个保存二进制的字符串key进行元操作,并将结果保存到saveKey上。
     * <p>
     * bitop=>and newKey keys [key...],对一个或多个key逻辑并,结果保存到newKey。
     * bitop=>or newKey keys [key...],对一个或多个key逻辑或,结果保存到newKey
     * bitop=>xor newKey keys [key...],对一个或多个key逻辑异或,结果保存到newKey
     * bitop=>not newKey key 对一个或多个key逻辑非,结果保存到newKey
     * <p>
     *
     * @param op      元操作类型;
     * @param newKey 元操作后将结果保存到newKey所在的结构中。
     * @param desKey  需要进行元操作的类型。
     * @return 1:返回元操作值。
     */
    Long bGetOp(RedisCommands.BitOperation op, String newKey, String... desKey);

    /**
     * 对一个或多个保存二进制的字符串key进行元操作,并将结果保存到saveKey上,并返回统计之后的结果。
     *
     * @param op      元操作类型;
     * @param newKey 元操作后将结果保存到newKey所在的结构中。
     * @param desKey  需要进行元操作的类型。
     * @return 返回saveKey结构上value=1的所有数量值。
     */
    Long bGetOpResult(RedisCommands.BitOperation op, String newKey, String... desKey);

    /**
     * 计算用户的 组,片,和对应的 offset
     * 如果要用userId计算日活等数据,使用此方法获取BitMapKey
     *
     * @param userId
     * @return
     */
    BitMapKey bGetBitMapKey(long userId);

4.7.1 防止热key 分组分片

import java.util.StringJoiner;
public final class BitMapKey {
    /**
     * 组 0开始
     */
    private int groupIndex;
    /**
     * 组中分片 0开始
     */
    private int shardIndex;
    /**
     * (组=>分片)下的offset位置 0开始
     */
    private int offset;

    public BitMapKey(int groupIndex, int shardIndex, int offset) {
        this.groupIndex = groupIndex;
        this.shardIndex = shardIndex;
        this.offset = offset;
    }
    public String getRedisKey(String key) {
        return String.join(":", key, groupIndex + "", shardIndex + "");
    }
    //getter...
    //setter...

4.8 HyperLogLog 记录数据数量,而不记录数据本身

/**
     * 添加
     * @param key
     * @param values
     * @return 至少有一个添加成功true,否则false
     */
    Boolean hllSet(String key, Object... values);

    /**
     * 总共有多少个元素
     * @param keys 可以多个组合查
     * @return
     */
    Long hllSize(String... keys);

    /**
     * 将所有keys合并到desKey中
     * @param desKey
     * @param keys
     * @return
     */
    Long hllUnion(String desKey,String... keys);

    /**
     * 删除
     * @param keys
     */
    void hllDelete(String... keys);

4.7 Geospatial 经纬度

/**
     * 添加经纬度坐标
     * <p>两极无法直接添加,一般会下载城市数据,直接通过Java程序一次性导入。</p>
     * <p>有效的经度从-180度到180度。有效的纬度从-85.05112878度到85.05112878度。</p>
     * <p>当坐标位置超出指定范围时,该命令将会返回一个错误。</p>
     * <p>已经添加的数据,是无法再次往里面添加的。</p>
     *
     * @param key 健
     * @param x 经度
     * @param y 维度
     * @param member 名称
     * @return  key的元素数量
     */
    Long gpsSet(String key, double x,double y, String member);

    /**
     * 批量添加
     * @param key
     * @param map
     * @return
     */
    Long gpsSet(String key,Map<Object,Point> map);

    /**
     * 获得一个或多个指定地区的坐标值
     * @param key
     * @param members 名称
     * @return
     */
    List<Point> gpsGet(String key, String... members);

    /**
     * 获取两点之间的直线距离
     * @param key
     * @param member1
     * @param member2
     * @return 公里
     */
    Double gpsGetDistance(String key,String member1,String member2);

    /**
     * 获取附近的城市
     * @param key 健
     * @param member 中心
     * @param km 公里
     * @return
     */
    GeoResults<RedisGeoCommands.GeoLocation<Object>> gpsGetNearby(String key, String member, double km);


    /**
     * 获取附近的城市
     * @param key 健
     * @param x 坐标x
     * @param y 坐标y
     * @param km 公里
     * @return
     */
    GeoResults<RedisGeoCommands.GeoLocation<Object>> gpsGetNearby(String key, double x, double y, double km);

    /**
     * 删除
     * @param key
     * @param member
     * @return  删除的元素数量
     */
    Long gpsDel(String key,Object... member);

5. 实现类

package com.adleading.mediadatabase.util.redis;

import com.google.common.hash.Funnels;
import com.google.common.hash.Hashing;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisCommands;
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.Component;
import org.springframework.util.StringUtils;

import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class RedisTemplateUtilsImpl implements RedisTemplateUtils {

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    /**
     * 每个组中有多少
     *
     * 如果userId < 100亿,则会分到7000个分组里
     * 1 << 20  = 1048576
     */
    private static final int ONE_BITMAP_SIZE = 1 << 20;

    /**
     * 同一个分组里的的useId划分到20个bitmap里
     * 避免出现范围用户太多导致查询时出现热key
      */
    private static final int SHARD_COUNT = 20;


    public static int getOneBitmapSize() {
        return ONE_BITMAP_SIZE;
    }

    public static int getShardCount() {
        return SHARD_COUNT;
    }

    @Override
    public Boolean expire(String key, long time) {
        try {
            if(time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

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

    @Override
    public Boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public void del(String... keys) {
        if(keys != null && keys.length>0){
            if(keys.length == 1) {
                redisTemplate.delete(keys[0]);
            }else{
                redisTemplate.delete(Arrays.asList(keys));
            }
        }
    }

//------------------------ String --------------------------------------
    @Override
    public Object get(String key) {
        return StringUtils.hasText(key) ? redisTemplate.opsForValue().get(key) : null;
    }

    @Override
    public <T> T get(String key, Class<T> clazz) {
        Object obj = StringUtils.hasText(key) ? redisTemplate.opsForValue().get(key) : null;
        if (clazz.isInstance(obj)){
            return clazz.cast(obj);
        }
        return null;
    }

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

    @Override
    public Boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        }catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public Boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public Boolean setOrUpdate(Map<String, Object> map, boolean isAtom) {
        try {
            if(isAtom){
                return redisTemplate.opsForValue().multiSetIfAbsent(map);
            }
            redisTemplate.opsForValue().multiSet(map);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public List<Object> getList(String... keys) {
        return redisTemplate.opsForValue().multiGet(Arrays.asList(keys));
    }

    @Override
    public Boolean setNx(String key, Object value) {
        return redisTemplate.opsForValue().setIfAbsent(key, value);
    }

    @Override
    public Boolean setNx(String key, Object value, long time) {
        if(time > 0) {
            return redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
        }
        return setNx(key,value);

    }

    @Override
    public Boolean setXx(String key, Object value) {
        return redisTemplate.opsForValue().setIfPresent(key, value);
    }

    @Override
    public Boolean setXx(String key, Object value, long time) {
        if(time > 0){
            return redisTemplate.opsForValue().setIfPresent(key, value, time, TimeUnit.SECONDS);
        }
        return setXx(key,value);
    }

    @Override
    public Double incr(String key, double delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    @Override
    public Double decr(String key, double delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }

// ================================Map=================================

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

    @Override
    public Map<Object, Object> hmGet(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    @Override
    public List<Object> hGetMulti(String key, String... hashKey) {
        return redisTemplate.opsForHash().multiGet(key, Arrays.asList(hashKey));
    }

    @Override
    public Set hGetItemKeys(String key) {
        return redisTemplate.opsForHash().keys(key);
    }

    @Override
    public List<Object> hGetItemValues(String key) {
        return redisTemplate.opsForHash().values(key);
    }

    @Override
    public Boolean hmSet(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public Boolean hmSet(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public Boolean hSet(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public Boolean hSetIfAbsent(String key, String hKey, Object hValue) {
        try {
            return redisTemplate.opsForHash().putIfAbsent(key, hKey, hValue);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public Boolean hSet(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public Long hSize(String key) {
        return redisTemplate.opsForHash().size(key);
    }

    @Override
    public Long hLengthOfValue(String key, String hKey) {
        return redisTemplate.opsForHash().lengthOfValue(key,hKey);
    }

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

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

    @Override
    public Double hIncr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    @Override
    public Double hDecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

// ============================set=============================

    @Override
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public Long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Long sSetAndTime(String key, long time, Object... values) {
        try{
            Long count = redisTemplate.opsForSet().add(key, values);
            expire(key,time);
            return count;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Long sDel(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().remove(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Set<Object> sGetUnion(String... keys){
        try {
            if(null != keys && keys.length == 1){
                return sGet(keys[0]);
            }else if(null != keys && keys.length > 0){
                return redisTemplate.opsForSet().union(Arrays.asList(keys));
            }
            return null;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Long sGetUnionAndStore(String destKey, String... otherKeys){
            return redisTemplate.opsForSet().unionAndStore(Arrays.asList(otherKeys), destKey);
    }

    @Override
    public Set<Object> sGetDifference(String... keys){
        try {
            if(null != keys && keys.length == 1){
                return sGet(keys[0]);
            }else if(null != keys && keys.length > 0){
                return redisTemplate.opsForSet().difference(Arrays.asList(keys));
            }
            return null;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Long sGetDifference(String destKey, String... otherKeys) {
        return redisTemplate.opsForSet().differenceAndStore(Arrays.asList(otherKeys), destKey);
    }

    @Override
    public Set<Object> sGetIntersect(String... keys) {
        try {
            if(null != keys && keys.length == 1){
                return sGet(keys[0]);
            }else if(null != keys && keys.length > 0){
                return redisTemplate.opsForSet().intersect(Arrays.asList(keys));
            }
            return null;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Long sGetIntersect(String destKey, String... otherKeys) {
        return redisTemplate.opsForSet().intersectAndStore(Arrays.asList(otherKeys), destKey);
    }

    @Override
    public Boolean sMove(String key1, Object value, String key2) {
        return redisTemplate.opsForSet().move(key1, value, key2);
    }

    @Override
    public Object sPop(String key, Long count) {
        if(count < 0){
            throw new RuntimeException("pop的数量不能为负数!");
        }
        if(count == 0){
            return redisTemplate.opsForSet().pop(key);
        }
        return redisTemplate.opsForSet().pop(key,count);
    }

    @Override
    public List<Object> sRandomMembers(String key, Long count, boolean isDistinct) {
        count = count == null ? 10 : count;
        if(isDistinct){
            return new ArrayList(Objects.requireNonNull(redisTemplate.opsForSet().distinctRandomMembers(key, count)));
        }
        return redisTemplate.opsForSet().randomMembers(key, count);
    }




// ===============================list=================================


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

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

    @Override
    public Object lGet(String key, Object object) {
        return redisTemplate.opsForList().indexOf(key, object);
    }


    @Override
    public void lAppend(String key, Object value, boolean isRight) {
        try {
            if(isRight){
                redisTemplate.opsForList().rightPush(key, value);
                return;
            }
            redisTemplate.opsForList().leftPush(key, value);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void lAppendPivot(String key, Object pivot, Object newValue, boolean isRight) {
        try {
            if(isRight){
                redisTemplate.opsForList().rightPush(key, pivot, newValue);
                return;
            }
            redisTemplate.opsForList().leftPush(key, pivot, newValue);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void lAppendAll(String key, long time, boolean isRight, Object... values) {
        try {
            if (isRight) {
                redisTemplate.opsForList().rightPushAll(key, values);
            }else {
                redisTemplate.opsForList().leftPushAll(key, values);
            }
            expire(key,time);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void lSetIfPresent(String key, String value,boolean isRight) {
        try{
            if(isRight){
                redisTemplate.opsForList().rightPushIfPresent(key,value);
            }else {
                redisTemplate.opsForList().leftPushIfPresent(key, value);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public Object lLeftPop(String key,long time) {
        try {
            if(time > 0){
                return redisTemplate.opsForList().leftPop(key, time, TimeUnit.SECONDS);
            }
            return redisTemplate.opsForList().leftPop(key);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Object lRightPop(String key,long time) {
        try {
            if (time > 0){
                return redisTemplate.opsForList().rightPop(key, time, TimeUnit.SECONDS);
            }
            return redisTemplate.opsForList().rightPop(key);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Object lRightPopAndLeftPush(String key1, String key2, long timeout) {
        try {
            if (timeout > 0) {
                return redisTemplate.opsForList().rightPopAndLeftPush(key1, key2);
            }
            return redisTemplate.opsForList().rightPopAndLeftPush(key1, key2, timeout, TimeUnit.SECONDS);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public void lReplace(String key, Object value, long index) {
        redisTemplate.opsForList().set(key,index,value);
    }

    @Override
    public void lTrim(String key, long index1, long index2) {
        if(index1 < 0){
            throw new RuntimeException("开始下标不能为负数!");
        }
        redisTemplate.opsForList().trim("rightList",index1,index2);
    }

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

    @Override
    public void lRemove(String key, int count, Object value) {
        redisTemplate.opsForList().remove(key,count,value);
    }

// =============================== zSet 有序集合 根据 score =================================

    @Override
    public Boolean zSet(String key, Object value, double score) {
        return redisTemplate.opsForZSet().add(key, value, score);
    }


    @Override
    public Boolean zSet(String key, Set<ZSetOperations.TypedTuple<Object>> tuples) {
        try {
            redisTemplate.opsForZSet().add(key, tuples);
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public Double zIncrScore(String key, Object value, double delta) {
        return redisTemplate.opsForZSet().incrementScore(key, value, delta);
    }

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

    @Override
    public Long zSize(String key) {
        return redisTemplate.opsForZSet().size(key);
    }

    @Override
    public Long zCount(String key, double min, double max) {
        return redisTemplate.opsForZSet().count(key,min,max);
    }

    @Override
    public Set<Object> zGet(String key, long start, long end, boolean isDesc) {
        if(isDesc) {
            return redisTemplate.opsForZSet().reverseRange(key, start, end);
        }
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    @Override
    public Set<Object> zGet(String key, double min, double max) {
        return redisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

    @Override
    public Set<Object> zGet(String key, double min, double max, long offset, long count) {
        return redisTemplate.opsForZSet().rangeByScore(key, min, max, offset, count);
    }

    @Override
    public Long zGetRank(String key, Object value, boolean isDesc) {
        if(isDesc){
            return redisTemplate.opsForZSet().reverseRank(key,value);
        }
        return redisTemplate.opsForZSet().rank(key,value);
    }

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

    @Override
    public Long zDel(String key, long start, long end) {
        return redisTemplate.opsForZSet().removeRange(key, start, end);
    }

    @Override
    public Long zDel(String key, double min, double max) {
        return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
    }



//==============================bitmaps===================================



    @Override
    public Boolean bSet(String key, String param, boolean value) {
        return bSet(key,hash(param),value);
    }

    @Override
    public Boolean bSet(String key, long offset, boolean value) {
        return redisTemplate.opsForValue().setBit(key,offset,value);
    }

    @Override
    public Boolean bGet(String key, String param) {
        return bGet(key, hash(param));
    }

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

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

    @Override
    public Long bCount(String key, long start, long end) {

        return redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes(), start, end));
    }

    @Override
    public Long bGetOp(RedisCommands.BitOperation op, String newKey, String... desKey) {
        byte[][] bytes = new byte[desKey.length][];
        for (int i = 0; i < desKey.length; i++) {
            bytes[i] = desKey[i].getBytes();
        }
        return redisTemplate.execute((RedisCallback<Long>) con -> con.bitOp(op, newKey.getBytes(), bytes));
    }

    @Override
    public Long bGetOpResult(RedisCommands.BitOperation op, String newKey, String... desKey) {
        bGetOp(op, newKey, desKey);
        return bCount(newKey);
    }

    @Override
    public BitMapKey bGetBitMapKey(long userId){
        //获取组
        long groupIndex = userId / ONE_BITMAP_SIZE;
        //获取分片
        int shardIndex = Math.abs((int) (hash(userId+"") % SHARD_COUNT));
        //获取(组-分片)下的offset位置
        int offset = (int) (userId - groupIndex * ONE_BITMAP_SIZE);
        return new BitMapKey((int)groupIndex,shardIndex,offset);
    }

    /**
     * guava依赖获取hash值。
     */
    private static long hash(String key) {
        Charset charset = Charset.forName("UTF-8");
        return Math.abs(Hashing.murmur3_128().hashObject(key, Funnels.stringFunnel(charset)).asInt());
    }

//============================== HyperLogLog ===================================


    @Override
    public Boolean hllSet(String key, Object... values) {
        try {
            Long add = redisTemplate.opsForHyperLogLog().add(key, values);
            return 1 == add;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }

    }

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

    @Override
    public Long hllUnion(String desKey, String... keys) {
        try {
            return redisTemplate.opsForHyperLogLog().union(desKey,keys);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public void hllDelete(String... keys) {
        try {
            for (String key : keys) {
                redisTemplate.opsForHyperLogLog().delete(key);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
//============================== Geospatial 经纬度===================================

    @Override
    public Long gpsSet(String key, double x,double y, String member){
        Point point = new Point(x, y);
        try {
            return redisTemplate.opsForGeo().add(key, point, member);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public Long gpsSet(String key,Map<Object,Point> map){
        try {
            return redisTemplate.opsForGeo().add(key,map);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

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

    @Override
    public Double gpsGetDistance(String key, String member1, String member2) {
        Distance distance = redisTemplate.opsForGeo().distance(key, member1, member2, Metrics.KILOMETERS);
        return distance.getValue();
    }

    @Override
    public GeoResults<RedisGeoCommands.GeoLocation<Object>> gpsGetNearby(String key, String member, double km){
        return redisTemplate.opsForGeo().radius(key,member,new Distance(km,Metrics.KILOMETERS));
//        GeoResults<RedisGeoCommands.GeoLocation<Object>> radius = redisTemplate.opsForGeo().radius(key, member, new Distance(km, Metrics.KILOMETERS));
//        for (GeoResult<RedisGeoCommands.GeoLocation<Object>> geoLocationGeoResult : radius) {
//            //地点名
//            geoLocationGeoResult.getContent().getName();
//            //地点坐标
//            geoLocationGeoResult.getContent().getPoint();
//            //地址计算器
//            geoLocationGeoResult.getDistance();
//        }
    }

    @Override
    public GeoResults<RedisGeoCommands.GeoLocation<Object>> gpsGetNearby(String key, double x, double y, double km) {
        return redisTemplate.opsForGeo().radius(key,new Point(x,y),new Distance(km,Metrics.KILOMETERS));
    }

    @Override
    public Long gpsDel(String key, Object... members) {
        try {
            return redisTemplate.opsForGeo().remove(key, members);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }


}