Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库。是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以用作数据库、缓存和消息中间件,可以说是实现网站高并发不可或缺的一部分。它支持多种类型的数据结构,如字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets)。Redis 使用C语言开发,支持的客户端语言也非常丰富,如C、C#、C++、Object-C、PHP、Python、 Java、Perl、Lua等。Redis 支持master-slave模式应用,支持数据的持久化,可以将内存中的数据存储在硬盘中,重启、断电的时候并不会丢失数据。至于Redis的基本操作命令,请参考Redis命令参考。
通过上述介绍,对Redis有个最基础的概念了解——Redis是一个有着高速读取的内存数据库,而本篇文章也着重介绍一下,Redis作为存储的使用方法。Redis的各种语言客户端列表,请参见Redis Client。其中Java客户端在github上start最高的是Jedis和Redisson。Jedis提供了完整Redis命令,而Redisson有更多分布式的容器实现,本文使用的客户端是Jedis。Jedis不是线程安全的,故不应该在多线程环境中共用一个Jedis实例。同时,也应该避免直接创建多个Jedis实例,因为这种做法会导致创建过多的socket连接,性能不高。 要保证线程安全且获得较好的性能,可以使用JedisPool。JedisPool是一个连接池,既可以保证线程安全,又可以保证了较高的效率。本文基于JedisPool实现了一个自定义的RedisTemplate,通过RedisTemplate实现字符串、散列、列表、集合、有序集合的存储。
1. 项目结构
| pom.xml
| springboot-12-redis.iml
|
+---redis-client
| | pom.xml
| | redis-client.iml
| |
| +---src
| | +---main
| | | +---java
| | | | \---com
| | | | \---zhuoli
| | | | \---service
| | | | \---redis
| | | | \---client
| | | | +---action
| | | | | GedisAction.java
| | | | |
| | | | \---template
| | | | | GedisTemplate.java
| | | | |
| | | | \---redis
| | | | RedisTemplate.java
| | | |
| | | \---resources
| | \---test
| | \---java
\---redis-repository
| pom.xml
| redis-repository.iml
|
+---src
| +---main
| | +---java
| | | \---com
| | | \---zhuoli
| | | \---service
| | | \---springboot
| | | \---redis
| | | \---repository
| | | | SpringBootRedisApplicationContext.java
| | | |
| | | +---config
| | | | RedisConfig.java
| | | |
| | | +---controller
| | | | RedisController.java
| | | |
| | | +---core
| | | | | RedisControllerService.java
| | | | |
| | | | \---impl
| | | | RedisControllerServiceImpl.java
| | | |
| | | \---repository
| | | RedisHashRepository.java
| | | RedisSetRepository.java
| | | RedisStringRepository.java
| | | RedisZSetRepository.java
| | |
| | \---resources
| | application.properties
| |
| \---test
| \---java
- redis-client:封装自定义RedisTemplate,用于实现Redis基本数据结构的操作
- redis-repository:Redis基本数据结构操作应用
2. pom.xml
2.1 redis-client pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot-12-redis</artifactId>
<groupId>com.zhuoli.service</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>redis-client</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
</project>
2.2 redis-repository pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot-12-redis</artifactId>
<groupId>com.zhuoli.service</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>redis-repository</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8</version>
</dependency>
<!--redis-client module依赖-->
<dependency>
<groupId>com.zhuoli.service</groupId>
<artifactId>redis-client</artifactId>
</dependency>
</dependencies>
</project>
2.3 springboot-12-redis pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zhuoli.service</groupId>
<artifactId>springboot-12-redis</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>redis-client</module>
<module>redis-repository</module>
</modules>
<!-- 继承说明:这里继承SpringBoot提供的父工程 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.7.RELEASE</version>
<relativePath/>
</parent>
<!-- 版本说明:这里统一管理依赖的版本号 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.zhuoli.service</groupId>
<artifactId>redis-client</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
3. redis-client定义
3.1 GedisAction定义
GedisAction是一个函数式接口,主要功能是,通过一个Redis Java客户端实例jedis,返回jedis操作Redis后的结果,比如String的存取结果、Set的随机结果等等,如下:
/**
* 函数式接口,用于通过Jedis实例获取Redis查询结果
* @author: zhuoli
* @date: 2018/8/24 11:08
*/
public interface GedisAction<T> {
T doAction(Jedis jedis);
}
3.2 GedisTemplate定义
GedisTemplate定义一个模板接口,所有客户端模板都要继承此接口。接口提供一个excute方法,接收一个GedisAction函数式接口参数,返回一个泛型结果,如下:
/**
* Template接口,所有客户端模板都要继承该接口
* @author: zhuoli
* @date: 2018/8/24 11:11
*/
public interface GedisTemplate {
<T> T execute(GedisAction<T> action) throws Exception;
}
3.3 RedisTemplate定义
RedisTemplate是Redis存取操作的客户端,内部持有一个JedisPool实例。通过GedisAction的doAction方法,实现Redis具体数据结构的存取操作,如下:
@Slf4j
public class RedisTemplate implements GedisTemplate {
private JedisPool jedisPool;
public RedisTemplate(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
public <T> T execute(GedisAction<T> action) {
T result = null;
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
result = action.doAction(jedis);
} catch (Exception e) {
log.error("Call redis failed.", e);
} finally {
assert jedis != null;
jedis.close();
}
return result;
}
}
4. redis-repository定义
4.1 application.properties定义Redis数据源信息
##数据源配置
zhuoli.redis.url=redis://127.0.0.1:6379/0
zhuoli.redis.timeout=2000
zhuoli.redis.size=2
4.2 RedisConfig定义
主要是为了定义一个RedisTemplate Bean,其中JedisPoolConfig的参数是我参考一些资料自己定义的,也可以使用默认JedisPoolConfig或者按照自己的实际需求进行修改。
@Configuration
@Slf4j
public class RedisConfig {
@Value("${zhuoli.redis.url}")
private String redisUri;
@Value("${zhuoli.redis.timeout}")
private Integer timeOut;
@Value("${zhuoli.redis.size}")
private Integer size;
@Bean
public RedisTemplate redisTemplate(){
JedisPool jedisPool = null;
try {
jedisPool = new JedisPool(jedisPoolConfig(size), new URI(redisUri), timeOut);
} catch (URISyntaxException e) {
log.info("Jedis Pool Init Exception, message is {}", e);
}
return new RedisTemplate(jedisPool);
}
private JedisPoolConfig jedisPoolConfig(int size) {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMinIdle(size);
jedisPoolConfig.setMaxIdle(2 * size);
jedisPoolConfig.setMaxTotal(2 * size);
jedisPoolConfig.setMaxWaitMillis(3000L);
jedisPoolConfig.setTestOnBorrow(true);
jedisPoolConfig.setTestOnReturn(true);
jedisPoolConfig.setTestWhileIdle(true);
jedisPoolConfig.setTimeBetweenEvictionRunsMillis(1000);
return jedisPoolConfig;
}
}
4.3 repository定义
在repository包中,我定义了String、Set、Sorted Set、Hash四种数据结构的常用方法,这里贴出String的常用方法。其实原理很简单,每个方法都试通过jedis的方法实现的,目的是为了隐藏jedis操作的具体细节,方便使用。
@Component
@AllArgsConstructor
@Slf4j
public class RedisStringRepository {
private RedisTemplate redisTemplate;
/*通过key获取对应地value*/
public String getString(String key) {
return redisTemplate.execute(jedis -> jedis.get(key));
}
/*set key-value对*/
public String setKey(String key, String value) {
return redisTemplate.execute(jedis -> jedis.set(key, value));
}
/*为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除*/
public Long setKeyExpire(String key, Integer expireTime) {
return redisTemplate.execute(jedis -> jedis.expire(key, expireTime));
}
/*set key-value对,同时设置过期时间*/
public String setKeyWithExpire(String key, Integer ddl, String value){
return redisTemplate.execute(jedis -> jedis.setex(key, ddl, value));
}
/*将key中储存的数字值加1*/
public Long incr(String key) {
return redisTemplate.execute(jedis -> jedis.incr(key));
}
/*将key中储存的数字值减1*/
public Long decr(String key) {
return redisTemplate.execute(jedis -> jedis.decr(key));
}
/*从当前数据库中随机返回(不删除)一个key*/
public String randomKey() {
return redisTemplate.execute(Jedis::randomKey);
}
/*删除给定的key*/
public Long delete(String key) {
return redisTemplate.execute(jedis -> jedis.del(key));
}
}
所有jedis的方法都有相应的Redis原生操作命令对应,需要了解Redis操作命令的同学,请参考Redis 命令参考
4.4 ControllerService定义
public interface RedisControllerService {
String insertKV(String key, String value);
String getValue(String key);
}
@Service
@AllArgsConstructor
@Slf4j
public class RedisControllerServiceImpl implements RedisControllerService {
private RedisStringRepository redisStringRepository;
@Override
public String insertKV(String key, String value) {
return redisStringRepository.setKey(key, value);
}
@Override
public String getValue(String key) {
return redisStringRepository.getString(key);
}
}
4.5 Controller定义
@RestController
@AllArgsConstructor
@RequestMapping(value = "/redis")
@Slf4j
public class RedisController {
private RedisControllerService redisControllerService;
@RequestMapping(value = "/set_kv", method = RequestMethod.POST)
public ResponseEntity setKV(@RequestParam(value = "key") String key,
@RequestParam(value = "value") String value) {
return ResponseEntity.status(HttpStatus.OK).body(redisControllerService.insertKV(key, value));
}
@RequestMapping(value = "/get_value", method = RequestMethod.POST)
public ResponseEntity getValue(@RequestParam(value = "key") String key) {
return ResponseEntity.status(HttpStatus.OK).body(redisControllerService.getValue(key));
}
}
5. 测试
除了本文提供的实现方式外,SpringBoot其实自己也基于Jedis实现了一个Redis访问框架Spring Data Redis,使用起来也挺方便的,场景也比较丰富,有兴趣的同学可以参考如何使用RedisTemplate访问Redis数据结构