文章目录
视频链接
官网: https://redis.io/
中文官网: http://redis.cn/
Ⅰ、jedis简单使用
java 操作 jedis ,jedis 是 redis 官方推荐的 java 连接开发工具!使用 Java 操作 redis 中间件
一、导入依赖
新建 maven 项目,导入 jedis 依赖
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
</dependencies>
- 连接数据库
- 操作命令
- 断开连接
二、解决错误
如果遇到如下错误,查看我另一篇博客
https://blog.csdn.net/qq_41538097/article/details/118226758
Exception in thread "main" redis.clients.jedis.exceptions.JedisDataException: DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the loopback interface.
If you want to connect from external computers to Redis you may adopt one of the following solutions:
1) Just disable protected mode sending the command 'CONFIG SET protected-mode no' from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent.
2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to 'no', and then restarting the server.
3) If you started the server manually just for testing, restart it with the '--protected-mode no' option.
4) Setup a bind address or an authentication password.
NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside.
线程“main”中的异常 redis.clients.jedis.exceptions.JedisDataException:DENIED Redis 正在保护模式下运行,因为已启用保护模式,未指定绑定地址,未向客户端请求身份验证密码。在此模式下,仅接受来自环回接口的连接。
如果您想从外部计算机连接到 Redis,您可以采用以下解决方案之一:
1) 只需禁用保护模式,通过从与服务器相同的主机连接到 Redis,从环回接口发送命令“CONFIG SET protected-mode no”正在运行,但是如果这样做,请确保 Redis 不能从 Internet 公开访问。使用 CONFIG REWRITE 使此更改永久化。
2) 或者,您可以通过编辑 Redis 配置文件并将保护模式选项设置为“no”,然后重新启动服务器来禁用保护模式。
3) 如果您手动启动服务器只是为了测试,请使用“--protected-mode no”选项重新启动它。
4) 设置绑定地址或认证密码。
注意:您只需要执行上述操作之一,服务器就可以开始接受来自外部的连接。
三、使用 API 测试连接
java 测试 redis string 类型
// 1、 new 一个 redis 对象
Jedis jedis = new Jedis("1.117.165.107", 6378);
jedis.auth("******");
jedis.set("name", "tom");
jedis.set("age", 18 + "");
jedis.get("name"); // tom
byte[] bytes = jedis.get(new byte[]{'a', 'g', 'e'});
new String(bytes); // 18
jedis.exists("name", "111"); // 1 返回的是一个 long 类型
Set<String> keys = jedis.keys("*"); // [name, age]
keys.toString();
jedis.type("name"); // string
jedis.randomKey(); // 随机返回一个 key
jedis.rename("name", "username"); // 重命名 成功返回 OK,否则 报错
jedis.get("username");
jedis.del("age23"); // 删除 :返回 long 类型, 0
jedis.select(2); // 切换数据库,返回 string 类型, OK --- 一共 16 个库 0~15,默认 0
jedis.flushDB(); // 清空当前数据库,返回 string 类型, OK
jedis.flushAll(); // 清空所有数据库
从上可以看出,使用方法和命令一摸一样,返回结果也相同,其他几种基本类型类似,可以直接参考命令使用
四、事务管理
和命令行类似
1、正常->事务
Jedis jedis = new Jedis("1.117.165.107", 6378);
jedis.auth("******");
jedis.flushAll();
JSONObject jsonObject = new JSONObject();
jsonObject.put("userName","sweet");
jsonObject.put("age",18);
Transaction multi = jedis.multi(); // 开启事务
try {
multi.set("user",jsonObject.toJSONString());
multi.set("user2",jsonObject.toJSONString());
multi.exec(); // 执行事务
} catch (Exception e) {
multi.discard(); // 放弃事务
e.printStackTrace();
} finally {
System.out.println(jedis.get("user")); // 打印 {"userName":"sweet","age":18}
System.out.println(jedis.get("user2")); // 打印 {"userName":"sweet","age":18}
jedis.close();
}
2、异常->事务
Jedis jedis = new Jedis("1.117.165.107", 6378);
jedis.auth("******");
jedis.flushAll();
JSONObject jsonObject = new JSONObject();
jsonObject.put("userName","sweet");
jsonObject.put("age",18);
Transaction multi = jedis.multi(); // 开启事务
try {
multi.set("user",jsonObject.toJSONString());
multi.set("user2",jsonObject.toJSONString());
System.out.println(1/0);
multi.exec(); // 执行事务
} catch (Exception e) {
multi.discard(); // 放弃事务
e.printStackTrace();
} finally {
System.out.println(jedis.get("user")); // 发生异常,打印 null
System.out.println(jedis.get("user2")); // 发生异常,打印 null
jedis.close();
}
5、乐观锁
开启两个客户端利用延时操作测试
- client 1
Jedis jedis = new Jedis("1.117.165.107", 6378);
jedis.auth("******");
jedis.incrBy("age",10);
System.out.println(jedis.get("age")); // 28
- client 2
Jedis jedis = new Jedis("1.117.165.107", 6378);
jedis.auth("******");
jedis.set("age", "18");
jedis.watch("age");
Transaction multi = jedis.multi();
Thread.sleep(20000);
multi.decrBy("age", 20);
multi.exec();
System.out.println(jedis.get("age")); // 28
先运行 client2 ,再运行 client1,得到的结果都是 28(因为 client2 监听了年龄,在延时期间, client1 对其进行了修改,则二失败)
Ⅱ、springboot 整合
一、认识 spring-boot-starter-data-redis
- springboot 操作数据:springboot jpa jdbc mongodb redis !
- springdata 也是和 springboot 齐名,操作数据导包都是以 spring-boot-starter-data- 开始
说明:在SpringBoot2.x之后,原来使用的jedis 被替换为了lettuce?
- jedis :采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool连接池!更像BIO模式
- lettuce :采用netty,实例可以再多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据了,更像NIO模式
二、配置 redis
1、源码分析
①、导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
②、包说明
<!--当前项目 依赖springboot 的 redis 包 spring-boot-starter-data-redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring-boot-starter-data-redis 包依赖了 spring-data-redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.5.2</version>
<scope>compile</scope>
</dependency>
<!-- spring-data-redis 包依赖了 jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis}</version>
<optional>true</optional>
</dependency>
③、redis 自动配置类 RedisAutoConfiguration
④、自动配置类绑定 properties 配置文件 RedisProperties
private int database = 0;
private String url;
private String host = "localhost";
private String username;
private String password;
private int port = 6379;
private boolean ssl;
private Duration timeout;
private Duration connectTimeout;
private String clientName;
private ClientType clientType;
private Sentinel sentinel;
private Cluster cluster;
⑤、RedisAutoConfiguration 说明
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
// 默认的 RedisTemplate 没有过多的设置,redis 对象都是需要序列化的!
// 两个泛型都是 Object 类型,后续使用需要强制类型转换
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
// string 是 最长使用的一个类类型,所以单独有一个 bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
2、连接池配置
连接池配置 2.x 版本使用 lettuce
不确定支持哪个版本,可以查看RedisConnectionFactory
查看 RedisConnectionFactory 的实现类
发现 JedisConnectionFactory 有好多类找不到
查看 LettuceConnectionFactory 类正常
小结:
- 综上所述, 2.x 版本默认使用 LettuceConnectionFactory (因为2.x以后,spring-boot-stater-data-redis 包中导入了 lettuce 包,没有导入jedis 客户端的包),如果需要使用 JedisConnectionFactory 需要导入 jedis.client
application.yml 配置
JedisConnectionFactory
spring:
redis:
database: 0
host: 1.117.165.107
password: 111111
port: 6378
client-type: jedis # 客户端类型,主要为了这块导入的 jedis.client 包
jedis:
pool:
max-active: 8 #最大连接数据库连接数,设 0 为没有限制
max-idle: 8 #最大等待连接中的数量,设 0 为没有限制
max-wait: -1ms #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
min-idle: 0 #最小等待连接中的数量,设 0 为没有限制
# url: redis://111111@111.111.111.111:6378 # url 配置和上面 host、password、port 相同,两种配置一种即可
导包
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
LettuceConnectionFactory
------------可以使用
spring:
redis:
database: 0
host: 1.117.165.107
password: 111111
port: 6378
client-type: lettuce
lettuce:
pool:
max-active: 8 #最大连接数据库连接数,设 0 为没有限制
max-idle: 8 #最大等待连接中的数量,设 0 为没有限制
max-wait: -1ms #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
min-idle: 0 #最小等待连接中的数量,设 0 为没有限制
# url: redis://user:password@example.com:6379
可以配置指定不同数据库连接池
@Autowired
RedisConnectionFactory redisConnectionFactory;
System.out.println(redisConnectionFactory.getClass());
Lettuce 连接池
class org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
Jredis 连接池
class org.springframework.data.redis.connection.jedis.JedisConnectionFactory
注意: 要使用连接池必须有 commons-pool2 包,或者直接导入 jedis 包,该包中包含 commons-pool2包
导入 jedis 包
或者导入commons-pool2 包
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
- 运行测试
`使用 redisTemplate 下面 api 可以对 redis 五大基本类型,三个特殊类型增查`
// 操作字符串 string,其他类型类似
redisTemplate.opsForValue().set("name","tom");
redisTemplate.opsForValue().get("name");
redisTemplate.opsForValue().increment("age",20);
// 操作 list
redisTemplate.opsForList();
// 操作 set
redisTemplate.opsForSet();
// 操作 hash
redisTemplate.opsForHash();
// 操作 Geo 地理位置
redisTemplate.opsForGeo();
// 操作 HyperLogLog 基数统计
redisTemplate.opsForHyperLogLog();
// 操作有序集合 set
redisTemplate.opsForZSet();
`使用 redisTemplate 下面 api 可以对 redis 事务过期时间等做修改`
redisTemplate.delete("user");
redisTemplate.multi();
redisTemplate.exec();
redisTemplate.expire("", Duration.ofDays(1000*60));
redisTemplate.expireAt("", Instant.ofEpochSecond(1000*60));
redisTemplate.type("");
redisTemplate.discard();
`连接操作`
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.flushDb();
connection.flushAll();
connection.close();
三、序列化
值的序列化
默认使用 JDK 序列化
1、简单测试
存储对象
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private String userName;
private int age;
}
@Test
void test(){
User user = new User("法外狂徒", 18);
redisTemplate.opsForValue().set("user",user);
System.out.println(redisTemplate.opsForValue().get("user"));
}
}
如果直接使用实体类进行序列化,存取正常,但是用客户端连接结果如下图,建议自定义实现 RedisTemplate,选择合适的序列化
存储对象必须 implements Serializable 将实体类进行实例化,否则会报错 ,默认使用 JDK 序列化
2、实现自己的 RedisTemplate
如下,可以看出序列化方式,默认为 JDK
RedisConfig 配置类,该文件必须与启动类同级或者下级
RedisConfig.java
@Configuration
public class RedisConfig {
// 编写自己的 RedisTemplate
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
// 为了开发方便,将原来的泛型从 <Object, Object> 变为 <String, Object>
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// JSON 序列化配置
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// String 序列化配置
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key 采用 string 的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash 的 key 也采用 string 的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value 采用 JSON 序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash 的 value 采用 JSON 序列化
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
3、测试
实体类不去实现序列化使用自己的 RedisTemplate 测试
@Autowired
@Qualifier("redisTemplate") // 实现 RedisTemplate 有好多,需要使用 @Qualifier 注解表明使用哪个
private RedisTemplate redisTemplate;
@Test
void test(){
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.flushAll();
User user = new User("法外狂徒", 18);
redisTemplate.opsForValue().set("user",user);
redisTemplate.expireAt("user", new Date(System.currentTimeMillis() + 1000 * 60 * 60));
System.out.println(redisTemplate.opsForValue().get("user"));
}
测试结果
4、RedisUtil 工具类
工具类可以简化部分代码,每次都 redisTemplate.opsForValue() 挺麻烦,可以对其进行封装,参考
https://www.cnblogs.com/zhzhlong/p/11434284.html 博客实现自己的工具类
测试
RedisUtil.java
@Autowired
@Qualifier("redisTemplate")
private RedisTemplate redisTemplate;
/**
* 设置指定 key 的值
* @param key
* @param value
*/
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* 获取指定 key 的值
* @param key
* @return
*/
public Object get(String key) {
return redisTemplate.opsForValue().get(key);
}
SpringBootRedisApplicationTests.java
@Autowired
RedisUtil redisUtil;
@Test
void testRedisUtil(){
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.flushAll();
User user = new User("法外狂徒", 18);
redisUtil.set("user", user);
redisUtil.expireAt("user", new Date(System.currentTimeMillis() + 1000 * 60 * 60));
System.out.println(redisUtil.get("user"));
}
运行结果