官方文档地址
https://docs.spring.io/spring-data/data-redis/docs/
https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#reference
spring 配置
1,配置连接工厂
@Bean("RedisConnectionFactory")
public RedisConnectionFactory getJedisConnectionFactory() {
//集群
// RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration();
//单机redis
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration("localhost", 6379);
config.setDatabase(1);
//连接池设置
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//最大连接数
jedisPoolConfig.setMaxTotal(100);
//最大空闲数
jedisPoolConfig.setMaxIdle(100);
//最小空闲连接数
jedisPoolConfig.setMinIdle(10);
//最长等待时间
jedisPoolConfig.setMaxWaitMillis(2000);
JedisClientConfiguration clientConfiguration = JedisClientConfiguration.builder()
.usePooling()
.poolConfig(jedisPoolConfig)
.build();
//连接超时2s,读取超时2s,等待超时
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(config, clientConfiguration);
return jedisConnectionFactory;
}
2,配置 redistempleat
@Bean("RedisTemplate")
public RedisTemplate getRedisTemplate(@Qualifier("RedisConnectionFactory") RedisConnectionFactory connectionFactory) {
/**
* redis 缓存
*/
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//设置连接工厂
redisTemplate.setConnectionFactory(connectionFactory);
//开启事务 默认为开启状态,若使用 redisRepository 需要关闭事务
redisTemplate.setEnableTransactionSupport(true);
//redisTemplate.setEnableTransactionSupport(false);
//使用 jackson 序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//将类名称序列化到json串中
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//设置输入时忽略JSON字符串中存在而Java对象实际没有的属性
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//Use String RedisSerializer to serialize and deserialize the key value of redis
RedisSerializer redisSerializer = new StringRedisSerializer();
//key
redisTemplate.setKeySerializer(redisSerializer);
redisTemplate.setHashKeySerializer(redisSerializer);
//value
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
3,添加配置类注解
@Configuration
@ComponentScan("com.springdataredis")
//声明式事务注解
@EnableTransactionManagement
//redis repositories 注解,需要关闭事务才可以
@EnableRedisRepositories(basePackages = {"com.springdataredis.dao"})
4,使用
@Autowired
private RedisTemplate redisTemplate;
spring boot 配置
1,配置 RedisTemplate ,修改序列化器为 jackson 的序列化器
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//Use Jackson 2Json RedisSerializer to serialize and deserialize the value of redis (default JDK serialization)
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//将类名称序列化到json串中
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//设置输入时忽略JSON字符串中存在而Java对象实际没有的属性
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//Use String RedisSerializer to serialize and deserialize the key value of redis
RedisSerializer redisSerializer = new StringRedisSerializer();
//key
redisTemplate.setKeySerializer(redisSerializer);
redisTemplate.setHashKeySerializer(redisSerializer);
//value
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
2,添加 配置类注解
@SuppressWarnings("all")
@Configuration
3,在配置文件中添加配置
spring:
redis:
host: localhost
port: 6379
database: 1
timeout: 2000ms # 连接超时时间(毫秒)默认是2000ms
lettuce:
pool:
max-active: 200 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 100 # 连接池中的最大空闲连接
min-idle: 50 # 连接池中的最小空闲连接
shutdown-timeout: 100ms
4,spring 默认选择 lettuce 为 redis 实现,修改 jedis 实现需进行以下操作
1,pom.xml 中排除 lettuce 依赖
<!-- spirngboot redis 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<!-- 排除 lettuce 依赖-->
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
2,导入 jedis 依赖为 redis 实现
<!-- redis 驱动依赖-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>
3,配置文件中配置修改为 jedis 配置
spring.redis.jedis.pool.enabled=true
#连接池最大数量
spring.redis.jedis.pool.max-active=8
#最大等待时间 -1 表示没有限制
spring.redis.jedis.pool.max-wait=-1ms
#最小空闲连接
spring.redis.jedis.pool.min-idle=50
#最大空闲连接
spring.redis.jedis.pool.max-idle=100
redis 整合 repository
1,配置类上添加 指定注解
//redis repositories 注解,需要关闭 RedisTemplate 事务才可以
//实测可以不关闭事务也可以使用
@EnableRedisRepositories(basePackages = {"com.springdataredis.dao"})
2,编写实体类
@Data
//声明在 redis 中存储的 key 名
//redis 存储的key会以 @RedisHash指定的值加上:和@Id声明字段的值
//例子 user:3
@RedisHash("user")
@Accessors(chain = true)
public class User implements Serializable {
//必须声明 @Id
@Id
private Integer uId;
private String uName;
}
3,编写 repositorie 接口
import com.springdataredis.entity.User;
import org.springframework.data.repository.PagingAndSortingRepository;
/**
* 继承 CrudRepository 或 PagingAndSortingRepository 接口
*/
public interface RedisUserDao extends PagingAndSortingRepository<User,Integer> {
}
4,使用
@Autowired
private RedisUserDao redisUserDao;
/**
* 直接使用 dao 接口操作 redis 对实体进行各种操作
*/
@Test
public void insert() {
User user = new User()
.setUId(2)
.setUName("历史的");
User save = redisUserDao.save(user);
}
发布订阅
1,创建消息监听容器
只需要订阅者服务创建
/**
* 消息监听器容器
* 负责管理线程池,消息分发给对应的管道和订阅者
*/
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(MessageListener messageListenerAdapter){
//创建消息监听容器
RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
//设置连接工厂
redisMessageListenerContainer.setConnectionFactory(getJedisConnectionFactory());
//创建需要监听的管道集合
List<Topic> list = new ArrayList<>();
list.add(new PatternTopic("log"));
//设置监听者绑定的管道
//监听者<-->监听者监听的管道
redisMessageListenerContainer.addMessageListener(messageListenerAdapter,list);
//返回容器交给 spring 管理
return redisMessageListenerContainer;
}
2,创建发布者和订阅者
发布者
@Autowired
private RedisTemplate redisTemplate;
/**
* 发布者
*/
@Test
public void producerTest() {
redisTemplate.convertAndSend("log", "管道测试\n");
redisTemplate.convertAndSend("msg", "测试消息\n");
}
订阅者
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
/**
* @author ChengQiuBo
* @version 1.0
* @date 2022/3/4 11:04
* @description 订阅者
* 实现 MessageListener 接口并重写 onMessage 方法
* 在 onMessage 方法中进行业务处理
*/
@Component
@SuppressWarnings("all")
public class RedisConsumer implements MessageListener {
@Autowired
private RedisTemplate redisTemplate;
@Override
public void onMessage(Message message, byte[] pattern) {
//获取管道名称的 byte 数组
byte[] channel = message.getChannel();
//获取消息体的 byte 数组
byte[] body = message.getBody();
//获取 RedisTemplate 的序列化器反序列化管道名称和消息体
Object channelInfo = redisTemplate.getKeySerializer().deserialize(channel);
Object bodyInfo = redisTemplate.getValueSerializer().deserialize(body);
//业务处理
System.out.println("消息管道-->"+channelInfo);
System.out.println("消息体-->"+bodyInfo);
}
}
事务
编程式事务
1,RedisTemplate 配置类中设置 开启事务
//开启事务支持
redisTemplate.setEnableTransactionSupport(true);
2,使用事务
@Autowired
private RedisTemplate redisTemplate;
@Test
public void executeTest() {
//出现异常会中断线程,不会执行后续操作,非异步操作
redisTemplate.execute((RedisCallback) item -> {
//监测指定的 key 若在执行该代码块时其他线程对该 key 发生了修改操作则中断此代码块,不会抛出异常
item.watch("str".getBytes(StandardCharsets.UTF_8));
//开启事务
item.multi();
//执行业务
redisTemplate.opsForValue().set("uiui", "test");
//为了保证数据的隔离性,事务没有提交前是无法查到数据的
Object str = redisTemplate.opsForValue().get("uiui");
System.out.println("str--->" + str);
//抛出异常
int a = 1 / 0;
//提交事务
return item.exec();
});
System.out.println("后续操作");
}
声明式事务
//使用 spring 的 @Transactional 控制事务
常用方法
主要使用 key 绑定 (bound) 方式而非 opsfor 方式
String 字符串
新增和修改
BoundValueOperations ops = redisTemplate.boundValueOps("wangwu");
//设置 value
ops.set("zhagnsna");
//在最后追加
ops.append("+++");
//设置 value 和过期时间
ops.set(1,10, TimeUnit.SECONDS);
//如果不存在则设置 返回是否设置成功
Boolean ifa = ops.setIfAbsent(2, 10, TimeUnit.SECONDS);
//如果存在则设置 返回是否设置成功
Boolean ifp = ops.setIfPresent(3, 10, TimeUnit.SECONDS);
//修改键名
ops.rename("password");
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date parse = simpleDateFormat.parse("2022-03-03 10:45:55");
//设置什么时候过期 data
Boolean aBoolean = ops.expireAt(parse);
//设置多长时间过期 过期时间 单位
Boolean expire = ops.expire(30, TimeUnit.SECONDS);
//自增 可指定每次自增值
Long increment = ops.increment();
Long increment1 = ops.increment(10);
//自减 可指定每次自减值
Long decrement = ops.decrement();
Long decrement1 = ops.decrement(10);
查询
BoundValueOperations ops = redisTemplate.boundValueOps("jj");
//获取 value
String s = (String) ops.get();
//获取 type
DataType type = ops.getType();
//获取 key 的 value 并重设过期时间
Object andExpire = ops.getAndExpire(100, TimeUnit.SECONDS);
//获取 key 的 value 并移除过期时间
Object andPersist = ops.getAndPersist();
//获取 key 的 value 并删除
Object andDelete = ops.getAndDelete();
//获取当前 key 的 value 并设置新值
Object wangwu = ops.getAndSet("wangwu");
删除
//删除
redisTemplate.delete("password");
//获取 key 的 value 并删除
Object andDelete = ops.getAndDelete();
List 列表
/**
* 队列
*/
新增和修改
BoundListOperations ops = redisTemplate.boundListOps("list");
for (int i = 0; i < 10; i++) {
//头部插入
ops.leftPush(-i - 1);
//尾部插入
ops.rightPush(i);
}
//往指定索引位置(10)添加元素(99),若该位置存在值,则覆盖
ops.set(10,99);
//头部全部插入 返回受影响的行数
ops.leftPushAll(1,2,3);
//尾部全部插入
ops.rightPushAll(1,2,3);
//如果集合存在则在头部插入
ops.leftPushIfPresent(-100);
//如果集合存在则在尾部插入
ops.rightPushIfPresent(100);
//如果 key 存在则移除过期随时间,返回是否是否持久化成功
Boolean persist = ops.persist();
查询
//范围获取
List range = ops.range(0, ops.size());
//获取指定下标位置
Object index = ops.index(2);
//获取list中的元素个数
ops.size();
删除
//从头部删除 3 个,返回前3个元素
ops.leftPop(3);
//从尾部删除 3 个,返回后3个元素
ops.rightPop(3);
//删除特定值的
//删除值为 10 的值,删除两个,返回删除成功的个数
ops.remove(2,10);
//保留指定索引区间(2-20)的,删除剩余所有,裁剪
ops.trim(2,20);
Set 集合
无序不重复
/**
* 共同关注
* 可能认识的人
* 我关注的人关注了他
* 抽奖
*/
新增和修改
BoundSetOperations ops1 = redisTemplate.boundSetOps("set1");
BoundSetOperations ops2 = redisTemplate.boundSetOps("set2");
for (int i = 0; i < 10; i++) {
ops1.add(i);
ops2.add(i+5);
}
查询
BoundSetOperations ops = redisTemplate.boundSetOps("set");
//获取 set 中的元素个数
Long size = ops.size();
//获取所有
Set members = ops.members();
//判断 set 中是否包含指定值
Boolean member = ops.isMember(3);
//从 set 中随机获取一个或多个元素,会重复
Object o = ops.randomMember();
List list = ops.randomMembers(3);
//随机获取一个元素并删除
Object pop = ops.pop();
//随机获取多个元素并从set中删除
List set = redisTemplate.opsForSet().pop("set", 3);
删除
//随机获取一个元素并删除
Object pop = ops.pop();
//随机获取多个元素并从set中删除
List set = redisTemplate.opsForSet().pop("set", 3);
//删除指定值的元素,并返回删除成功的个数
Long remove = ops.remove(2, 4);
/**
* 删除整个 set
*/
Boolean set1 = redisTemplate.delete("set1");
Boolean set2= redisTemplate.delete("set2");
交集
/**
* 交集
*/
@Test
public void test02(){
BoundSetOperations ops1 = redisTemplate.boundSetOps("set1");
//获取交集 参数为另一个 set 的 key
Set intersect = ops1.intersect("set2");
print(intersect);
//获取交集并放入新的 set
ops1.intersectAndStore("set2","set3");
print(redisTemplate.opsForSet().members("set3"));
}
并集
/**
* 并集
*/
@Test
public void test03(){
BoundSetOperations ops1 = redisTemplate.boundSetOps("set1");
//计算并集 参数为另一个 set 的 key
Set union = ops1.union("set2");
//计算并集并放入新的set
ops1.unionAndStore("set2","set4");
print(redisTemplate.opsForSet().members("set4"));
}
差集
/**
* 差集
*/
@Test
public void test04(){
BoundSetOperations ops1 = redisTemplate.boundSetOps("set1");
//计算差集 参数为另一个 set 的 key
//set1 去掉 set1和set2 中都有的元素
//相当于 (set1) - (set1 和 set2 的交集)
Set diff = ops1.diff("set2");
//计算差集并放入新的set
ops1.diffAndStore("set2","set5");
print(redisTemplate.opsForSet().members("set5"));
}
Hash 哈希
新增和修改
BoundHashOperations ops = redisTemplate.boundHashOps("shoppingCar");
ops.put("name","鸡翅");
//新增一对 key-val
ops.put("number",20);
//新增全部
ops.putAll(new HashMap());
//如果不存在则设置 返回是否设置成功
Boolean test = ops.putIfAbsent("test", "123");
//对shoppingCar中的某个key进行自增或自减
ops.increment("number",-1);
查询
//获取所有
Map entries = ops.entries();
//获取 map 中 key 为指定值的 value
Object size2 = ops.get("size");
//获取map中元素的个数
Long size1 = ops.size();
//检查 map 中是否存在 key 为指定值的元素,返回存在结果
Boolean key1 = ops.hasKey("key1");
删除
//删除 map 中 key 为指定值的元素
Long delete = ops.delete("key1", "key2");
/**
* 删除 map
*/
redisTemplate.delete("shoppingCar");
Zset 有序不重复集合
/**
* 热搜
* 排行榜
*/
新增和修改
BoundZSetOperations zset = redisTemplate.boundZSetOps("zset");
/**
* 单个插入
*/
//添加单个元素返回插入结果
Boolean zhangsan = zset.add("zhangsan", 19);
//如果不存在则添加
zset.addIfAbsent("lisi", 10D);
/**
* 批量插入
*/
//创建 set
HashSet<ZSetOperations.TypedTuple> typedTuples = new HashSet<>();
//set 赋值
typedTuples.add(ZSetOperations.TypedTuple.of(1,23D));
typedTuples.add(ZSetOperations.TypedTuple.of(3,15D));
typedTuples.add(ZSetOperations.TypedTuple.of(2,45D));
//执行插入 返回受影响的行数
Long add = zset.add(typedTuples);
//如果不存在则添加多个 返回受影响的行数
Long aLong = zset.addIfAbsent(typedTuples);
查询
BoundZSetOperations zset = redisTemplate.boundZSetOps("zset");
//根据元素获取分数
Double score = zset.score("张三");
//获取元素个数
Long size = zset.size();
//按照索引顺序获取元素集合(升序)
Set range = zset.range(0, 10);
//获取指定索引范围的元素,返回元素和分数集合(升序)
Set withScores = zset.rangeWithScores(1, 10);
//按照分数获取元素集合(升序)
Set rangeByScore = zset.rangeByScore(2D, 10D);
//获取指定分数范围的元素,返回元素和分数集合(升序)
Set scoreWithScores = zset.rangeByScoreWithScores(10D, 20D);
//按照索引顺序获取元素集合(降序)
Set reverseRange = zset.reverseRange(1, 10);
//获取指定索引范围的元素,返回元素和分数集合(降序)
Set rangeWithScores = zset.reverseRangeWithScores(1, 10);
//按照分数获取元素集合(降序)
Set reverseRangeByScore = zset.reverseRangeByScore(2D, 10D);
//获取指定分数范围的元素,返回元素和分数集合(降序)
Set byScoreWithScores = zset.reverseRangeByScoreWithScores(10, 20);
//分数原子添加并返回添加后的分数 自增
Double increment = zset.incrementScore("张三", 1);
删除
BoundZSetOperations zset = redisTemplate.boundZSetOps("zset");
//删除特定值的元素,返回受影响的行数
Long remove = zset.remove("张三");
//删除指定索引范围的元素,返回受影响的行数
Long range = zset.removeRange(1, 10);
//删除指定分数范围的元素,返回受影响的行数
zset.removeRangeByScore(1, 10);
交集-并集-差集
/**
* 交集intersect,并集union,差集diff
*/
public void test01() {
BoundZSetOperations zset = redisTemplate.boundZSetOps("zset");
//交集
Set intersect = zset.intersect("zset1");
//并集
Set union = zset.union("zset1");
//差集
Set diff = zset.difference("zset1");
}
Geospatial 地理坐标 Geo
需要 redis 3.2.0 以上
使用场景举例:
1,附近的人
2,两地距离
3,坐标半径内有多少人
......
新增和修改
//先经度,后维度
BoundGeoOperations ops = redisTemplate.boundGeoOps("china:city");
//返回受影响的行数
Long add = ops.add(new Point(11.12, 13.4), "无锡");
查询
@Test
public void queryTest(){
BoundGeoOperations ops = redisTemplate.boundGeoOps("china:city");
//获取位置信息
List list = ops.geoPos("无锡", "江苏");
/**
* m 米 METERS
* km 千米 KILOMETERS
* mi 英里 MILES
* ft 英尺 FEET
*/
Distance distance = ops.geoDist("江苏", "无锡", Metrics.KILOMETERS);
//获取两地之间的距离
double value = distance.getValue();
//获取指定位置的 hash
List geoHash = ops.geoHash("江苏", "无锡");
//获取位置的坐标
List geoPos = ops.geoPos("江苏", "无锡");
//获取指定位置范围内的城市
Point jiangsu = new Point(10.23, 14.11);
Circle circle = new Circle(jiangsu,1000000);
GeoResults geoResults = ops.geoRadius(circle);
//获得结果集
//距离withdist 经纬度weithcoord
List<GeoResult> content = geoResults.getContent();
for (GeoResult geoResult : content) {
System.out.println("城市信息-->"+geoResult.getContent());
}
}
删除
@Test
public void deleteTest(){
BoundGeoOperations ops = redisTemplate.boundGeoOps("china:city");
//删除指定坐标
Long remove = ops.remove("无锡");
System.out.println("--->"+remove);
}
Hyperloglog 基数统计
//需要 redis 2.8.9 以上,无元素删除方法
/**
* 统计信息 占用内存少 稳定占用 12kb 但存在一定误差,只有在对精度要求不是很高的场景下使用
*/
新增和修改
@Test
public void insertTest() {
HyperLogLogOperations ops = redisTemplate.opsForHyperLogLog();
//添加
Long key1 = ops.add("key1", 1, 2, 3, 4, 5, 6);
Long key2 = ops.add("key2", 1, 2, 3, 4, 7, 8);
//合并放入 key3
Long key3 = ops.union("key3", key1, key2);
}
查询
@Test
public void queryTest(){
HyperLogLogOperations ops = redisTemplate.opsForHyperLogLog();
Long key2 = ops.add("key4", 1, 2, 3,3,3, 4, 7, 8);
//返回 key4 中元素的基数,允许存在重复元素,但重复元素只会被计算一次
Long size = ops.size("key4");
System.out.println("size--->"+size);
}
删除
@Test
public void deleteTest(){
HyperLogLogOperations ops = redisTemplate.opsForHyperLogLog();
//删除指定的 key
//等于 redisTemplate.delete("key3");
ops.delete("key3");
}
BitMap 位图
BitMap 无元素删除方法
/**
* 使用场景
* 记录只有两种状态的信息
* 例如:
* 签到
* 打卡
* ......
*/
新增和修改
@Test
public void insertTest(){
ValueOperations ops = redisTemplate.opsForValue();
ops.setBit("punch",0,true);
ops.setBit("punch",1,false);
ops.setBit("punch",2,true);
ops.setBit("punch",3,false);
ops.setBit("punch",4,false);
ops.setBit("punch",5,false);
//位图赋值,赋值成功返回 true 失败返回 false
Boolean punch = ops.setBit("punch", 6, false);
//再次赋值会覆盖之前的值,覆盖成功返回 true 失败返回 false
Boolean punch1 = ops.setBit("punch", 6, false);
}
查询
@Test
public void queryTest(){
ValueOperations ops = redisTemplate.opsForValue();
//获取所有
for (int i = 0; i < 6; i++) {
print(i);
//获取指定位置的位,返回值类型为 boolean
boolean bit = ops.getBit("punch",i);
print(bit);
}
}