常见的客户端
在Redis官网中提供了各种语言的客户端地址,其中Java客户端
也包含很多(黄色五角星标记的就是推荐使用的)
Lettuce
: 也提供了Redis命令对应的API并且是线程安全的,且基于Netty实现支持同步/异步和响应式编程方式, 支持Redis的哨兵模式、集群模式和管道模式Redisson
: 在Redis基础上实现了分布式的可伸缩的java数据结构(如Map等), 支持跨进程的同步机制(Lock、Semaphore等待),适合用来实现特殊功能需求SpringDataRedis
: 对Jedis和Lettuce做了抽象和封装,在Spring Boot项目中还提供了对应的Starterspring-boot-starter-data-redis
使用Jdeis操作Redis
第一步: 创建一个Maven工程: 引入jedis依赖
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.7.0version>
dependency>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiterartifactId>
<version>5.7.0version>
<scope>testscope>
dependency>
第二步: 指定ip,端口号,密码
创建连接Redis的连接对象Jedis
第三步: 使用连接对象提供的方法(方法名与Redis命令一致)操作Redis实现增删改查操作
第四步: 关闭连接对象,释放资源
@SpringBootTest
class RedisTestApplicationTests {
private Jedis jedis;
// 在每个@Test方法之前执行的方法
@BeforeEach
void setUp() {
// 通过直连的方式手动创建Jedis对象
jedis = new Jedis("192.168.150.101", 6379);
// 从JedisPool连接池中获取Jedis对象
jedis = JedisConnectionFactory.getJedis();
// 输入Redis数据库的密码
jedis.auth("123456");
// 选择库(默认选择的是0号库)
jedis.select(0);
}
// 测试String类型
@Test
void testString() {
// 存入数据
String result = jedis.set("name", "虎哥");
System.out.println("result = " + result);
// 获取数据
String name = jedis.get("name");
System.out.println("name = " + name);
}
// 测试Hash类型
@Test
void testHash() {
// 插入hash数据
jedis.hset("user:1", "name", "Jack");
jedis.hset("user:1", "age", "21");
// 获取
Map<String, String> map = jedis.hgetAll("user:1");
System.out.println(map);
}
// 在每个@Test方法之后执行的方法
@BeforeEach
@AfterEach
void tearDown() {
if (jedis != null) {
// close方法内判断如果使用了数据库连接池是吧Jedis对象归还而不是关闭
jedis.close();
}
}
}
使用Jedis连接池
Jedis本身是线程不安全的,并且频繁的创建和销毁连接对象会有性能损耗
,建议从Jedis连接池中获取Jedis连接对象(这些连接对象都是提前创建的)
第一步: 配置JedisConnectionFactory
的相关配置,如连接池配置、服务端ip、服务端端口、超时时间、Redis的密码
import redis.clients.jedis.*;
public class JedisConnectionFactory {
// JedisPool是Jedis官方提供的连接池对象
private static JedisPool jedisPool;
static {
// 配置JedisConnectionFactory
JedisPoolConfig poolConfig = new JedisPoolConfig();
// 设置最大连接数
poolConfig.setMaxTotal(8);
// 设置最大空闲连接
poolConfig.setMaxIdle(8);
// 设置最小空闲连接
poolConfig.setMinIdle(0);
// 设置获取Jedis对象的最大等待时间
poolConfig.setMaxWaitMillis(1000);
// 创建连接池对象,参数:连接池配置、服务端ip、服务端端口、超时时间、密码
jedisPool = new JedisPool(poolConfig, "192.168.150.101", 6379, 1000, "123321");
}
// 从连接池中获取Jedis连接对象(这些对象都提前创建好了)
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
第二步: 从JedisConnectionFactory连接池中获取Jedis连接对象
@SpringBootTest
class RedisTestApplicationTests {
private Jedis jedis = JedisConnectionFactory.getJedis();
@Test
void testString(){
jedis.set("name","Kyle");
String name = jedis.get("name");
System.out.println("name = " + name);
}
@AfterEach
void tearDown(){
if (jedis != null){
jedis.close();
}
}
}
RedisTemplate操作redis
SpringData是Spring提供的数据操作的模块,该模块对各种数据库进行了集成,其中对Redis的集成模块就叫做SpringDataRedis(Reids的Java客户端依赖)
Lettuce和Jedis
进行了整合(默认采用Lettuce)RedisTemplate
工具类用来统一执行的API发布订阅模型
Redis哨兵和Redis集群
基于Lettuce的响应式编程
JDKCollection实现
SpringDataRedis中提供了RedisTemplate工具类
, 其中封装了各种对Redis的操作的API, 并且将不同数据类型的操作API封装到了不同的返回值类型中
第一步: 新建一个maven项目然后引入相关依赖, SpringBoot已经提供了SpringDataRedis的stater即spring-boot-starter-data-redis
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.5.7version>
<relativePath/>
parent>
<groupId>com.heimagroupId>
<artifactId>redis-demoartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>redis-demoname>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
project>
第二步: 编写application.yml
文件配置Redis的参数信息(IP,端口,连接池等)
spring:
redis:
host: 主机IP地址
port: 6379
password: 123456
database: 0 #操作的是0号数据库
# 连接池的配置(spring默认使用的就是lettuce,使用jedis需要手动设置)
lettuce:
pool:
max-active: 8 #最大连接数
max-idle: 8 #连接池中的最大空闲连接
min-idle: 0 #连接池中的最小空闲连接
max-wait: 100ms #连接池最大阻塞等待时间
自定义序列化器
RedisTemplate
可以接收任意Object作为值写入Redis, 只不过写入前会把Object对象序列化为字节形式
, 默认是采用JDK序列化(可读性差 , 内存占用较大)
@SpringBootTest
class RedisStringTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void testString() {
// 此时name和"虎哥"都当成Object然后采用JDK序列化的方式以字节的形式存入Reids
redisTemplate.opsForValue().set("name", "虎哥");
// 查询String数据时会反序列化输出到控制台
Object name = stringRedisTemplate.opsForValue().get("name");
System.out.println("name = " + name);// name = 虎哥
}
}
在config
包下编写对应的配置类RedisConfig
将RedisTemplate的序列化器由默认的JDK序列化方式换成JSON序列化方式
key或者hashkey
一般情况下都是字符串,所以可以采用StringRedisSerializer
的序列化方式value或者hashvalue
一般情况下可能是对象采用GenericJackson2JsonRedisSerializer
的序列化方式所以需要引入Jackson的依赖
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
dependency>
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){
// 创建RedisTemplate对象
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 设置连接工厂
template.setConnectionFactory(connectionFactory);
// 创建JSON序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// 设置Key和HashKey的序列化方式
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
//redisTemplate.setKeySerializer(new StringRedisSerializer());
//redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// 设置Value和HashValue的序列化方式
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);
// 最终返回对象将其注册到容器中
return template;
}
}
测试GenericJackson2JsonRedisSerializer
序列化工具
优点
: 可以将Java对象自动的序列化为JSON字符串存入Redis,查询时又会自动把JSON反序列化为Java对象,提升可读性缺点
:得到的JSON字符串记录了序列化时对应的class名称
(目的是为了查询时实现自动反序列化), 但这也会带来额外的内存开销@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private Integer age;
}
@Test
void stringTest(){
redisTemplate.opsForValue().set("user:100",new User("虎哥",21));
}
StringRedisTemplate
我们可以不使用JSON序列化器来处理value而是统一使用StringRedisSerializer
的序列化方式处理value
只能存储String类型的key和value
, 当需要存储Java对象时就要手动完成对象的序列化和反序列化
(这样class信息就不会写入Redis)SpringDataRedis就提供了RedisTemplate的子类StringRedisTemplate
, 它的key和value的序列化方式默认是StringRedisSerializer,省去了自定义序列化方式的步骤
public class StringRedisTemplate extends RedisTemplate<String, String> {
public StringRedisTemplate() {
this.setKeySerializer(RedisSerializer.string());
this.setValueSerializer(RedisSerializer.string());
this.setHashKeySerializer(RedisSerializer.string());
this.setHashValueSerializer(RedisSerializer.string());
}
@SpringBootTest
class RedisStringRedisTemplateTests {
// StringRedisTemplate的key和value的序列化方式默认就是StringRedisSerializer
@Autowired
private StringRedisTemplate stringRedisTemplate;
// SpringMvc默认使用的JSON序列化工具,也可以使用fastjson
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testSaveUser() throws JsonProcessingException {
// 创建对象
User user = new User("虎哥", 21);
// 手动序列化为json字符串
String json = mapper.writeValueAsString(user);
// 将json字符串写入Redis
stringRedisTemplate.opsForValue().set("user:200", json);
// 获取数据得到的也是json字符串
String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
// 手动将json字符串反序列化为java对象
User user1 = mapper.readValue(jsonUser, User.class);
System.out.println("user1 = " + user1);
}
}
最终存入Redis中
的数据
{
"name": "虎哥",
"age": 21
}
操作其他数据类型
String
类型数据操作(自定义序列化器或注入StringRedisTemplate): redisTemplate.opsForValue()
方法名 | 功能 |
---|---|
void set(Stirng key,String value) | 向数据库中存入一个键值对 |
set(key,value, 超时时间, 时间单位) | 设置键值对的有效时间,时间单位如TimeUnit.SECONDS/MINUTES 等 |
boolean setIfAbsent() | 当key不存在的时候才会添键值对,如果key存在不执行任何操作并返回false |
String get(String value) | 获取数据库中某个key对应的value |
@Test
void stringTest() {
//获取对象
ValueOperations valueOperations = redisTemplate.opsForValue();
//设置name为Hades
valueOperations.set("name","Hades");
String name = (String) valueOperations.get("name");
//Hades
System.out.println(name);
//设置age为9527且有效时间10秒
valueOperations.set("age", "9527", 10, TimeUnit.SECONDS);
String age = (String) valueOperations.get("age");
//9527,10秒过后再去redis中获取name的值则输出`nil`表示不存在
System.out.println(age);
//如果不存在则设置name为Kyle,由于name已经存在所以最后返回false
Boolean aBoolean = valueOperations.setIfAbsent("name", "Kyle");
System.out.println(aBoolean);
}
Hash
类型数据操作: redisTemplate.opsForHash()
方法名 | 功能 |
---|---|
void put(Stirng key,String failed,Sting value) | 向数据库中存入一个key,值是字段名和字段值 |
Object get(String key,Sting failed) | 获取数据库中某个key中某个字段的字段值 |
Set< String> keys(String key) | 获取数据库中某个key下的所有字段 |
List< String> keys(String key) | 获取数据库中某个key下的所有字段值 |
Map |
获取数据库中某个key下所有的字段及其字段值,返回值是个Map集合 |
@Test
void hashTest() {
// 存值
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.put("100", "name", "Hades");
hashOperations.put("100", "age", "18");
hashOperations.put("100", "hobby", "Apex");
// 获取某个key中name字段的值
hashOperations.get("100","name");
// 获取hash结构中某个key下所有的字段和字段值,返回值是个Mapj集合
Map map = hashOperations.entries("100");
// 获取Map集合中所有的key(字段名)
Set keySet = map.keySet();
for (String hashKey : keySet) {
// 获取集合中具体字段对应的字段值
System.out.println(hashKey + ":" + map.get(hashKey));
}
System.out.println("====================");
// 获取hash结构中某个key下所有的字段
Set keys = hashOperations.keys("100");
for (String key : keys) {
System.out.println(key);
}
System.out.println("====================");
//获取hash结构中某个key所有的字段值
List values = hashOperations.values("100");
for (String value : values) {
System.out.println(value);
}
}
List
类型数据操作: redisTemplate.opsForList()
方法名 | 功能 |
---|---|
void leftPush(String key,String element) | 从某个key链表的左侧插入一个元素 |
void leftPushAll(String key,String element…) | 从某个key链表的左侧插入多个元素 |
List< String> range(String key, start, stop) | 获取某个key链表中指定下标范围内的元素 |
Long size(String key) | 获取某个key链表的长度(用于遍历) |
Object leftPop(String key) | 从左侧开始弹出某个链表的第一个元素,返回弹出的元素 |
Object rightPop(String key) | 从右侧开始弹出某个链表的第一个元素,返回弹出的元素 |
@Test
void listTest() {
ListOperations listOperations = redisTemplate.opsForList();
//存数据
listOperations.leftPush("testData", "A");
//一次存多个数据
listOperations.leftPushAll("testData", "B", "C", "D");
//获取某个key链表的所有元素,(0,-1)表示查询所有元素
List testDatas = listOperations.range("testData", 0, -1);
for (String tableData : testDatas) {
System.out.print(tableData + " ");
}
System.out.println();
//获取当前list长度,用于遍历
Long size = listOperations.size("testData");
int value = size.intValue();
//遍历链表并弹出链表的元素
for (int i = 0; i < value; i++) {
System.out.print(listOperations.leftPop("testData") + " ");
}
//最后输出一下当前list长度
System.out.println(listOperations.size("testData"));
}
Set
类型(无须的Set集合)数据操作: redisTemplate.opsForSet()
方法名 | 功能 |
---|---|
add(String key,String element…) | 从某个key链表的左侧插入一个或多个元素(元素不可重复,重复会覆盖) |
Set< String> members(String key) | 获取某个key的Set集合中的所有元素 |
void remove(String key,String element…) | 删除某个key的Set集合中的一个或多个元素 |
@Test
void setTest() {
SetOperations setOperations = redisTemplate.opsForSet();
//存数据,这里存了两个a但元素会覆盖
setOperations.add("tmp", "a", "b", "c", "d", "a");
//遍历输出
Set tmpData = setOperations.members("tmp");
for (String value : tmpData) {
System.out.print(value + " ");
}
System.out.println("=================");
//删除元素
setOperations.remove("tmp", "b", "c");
//再次遍历输出
tmpData = setOperations.members("tmp");
for (String value : tmpData) {
System.out.print(value + " ");
}
}
ZSet
类型(有序的Set集合)数据操作: redisTemplate.opsForZSet()
方法名 | 功能 |
---|---|
add(String key,String value,double score) | 从某个key链表的左侧插入一个或多个元素(元素之间会根据分数排序) |
Set< String> range(String key, start, stop) | 获取某个key的Set集合中指定下标范围内的元素 |
incrementScore(String key,String value,double,score) | 修改某个key的Set集合中某个元素的分数 |
Set< String> members(String key) | 获取某个key的Set集合中的所有元素 |
void remove(String key,String value…) | 删除某个key的Set集合中的一个或多个元素 |
@Test
void zsetTest() {
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
//存值(元素不可重复)
zSetOperations.add("myZset", "a", 0.0);
zSetOperations.add("myZset", "b", 1.0);
zSetOperations.add("myZset", "c", 2.0);
zSetOperations.add("myZset", "a", 3.0);
//取出所有元素
Set myZset = zSetOperations.range("myZset", 0, -1);
for (String s : myZset) {
System.out.println(s);
}
//修改分数
zSetOperations.incrementScore("myZset", "b", 4.0);
//取出所有元素
myZset = zSetOperations.range("myZset", 0, -1);
for (String s : myZset) {
System.out.println(s);
}
//删除成员
zSetOperations.remove("myZset", "a", "b");
//取出所有元素
myZset = zSetOperations.range("myZset", 0, -1);
for (String s : myZset) {
System.out.println(s);
}
}
通用的数据类型操作: redisTemplate.api
方法名 | 功能 |
---|---|
Set< String> keys(String 通配符) | * 表示获取所有的key |
boolean hasKey(String key) | 查看指定的key是否存在 |
void delete(String key) | 删除指定key |
DateType type(String key) | 获取某个key的类型 |
@Test
void commonTest() {
//查看所有key
Set keys = redisTemplate.keys("*");
for (String key : keys) {
System.out.println(key);
}
//查看是否存在指定key
System.out.println(redisTemplate.hasKey("Random"));
//删除指定key
redisTemplate.delete("myZset");
//输出指定key的类型
Datetype datatype = redisTemplate.type("tmp")
System.out.println(datetype.name);
}