一个redis实例,默认包含16个库,序号从0到15。在redis命令行中,可以用select 序号来切换。我最近在做的一个项目中,需要使用redis的2个库。一个是由其他子系统写入,web后端(java)只读取;数据读出来,处理后,再写入另一个redis库,供WEB前端请求。
同时,对Redis的操作支持哈希表。即运行过程中,可以修改哈希类型的键值。比如该值是一个Hash<String,Object>类型,赋值的时候,如果不存在指定元素,则添加;否则更新。这样做的好处是,该键值的元素,可由不同的步骤产生。比如写入的值,有些是同步产生的,有些是异步产生的,有先有后。
具体如下:
一、配置文件中设置redis信息
redis:
db:
db1:
host: 10.0.1.8
port: 6379
database: 5
timeout: 6000 # 单位是毫秒
db2:
host: 10.0.1.8
port: 6379
database: 6
timeout: 6000 # 单位是毫秒
二、Configuration
1、创建一个RedisProperties类来读取配置文件信息
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
@ConfigurationProperties(prefix = "redis")
public class RedisProperties {
private Map<String, DatabaseProperties> db = new HashMap<>();
public Map<String, DatabaseProperties> getDb() {
return db;
}
public void setDb(Map<String, DatabaseProperties> db) {
this.db = db;
}
public static class DatabaseProperties {
private String host;
private int port;
private int database;
private int timeout;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public int getDatabase() {
return database;
}
public void setDatabase(int database) {
this.database = database;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
}
}
2、Redis.config
里面声明了2个工厂,可供产生2个redisTemplate,分别对应不同的redis数据库。
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {
@Bean(name = "redisConnectionFactory1")
@Primary
public RedisConnectionFactory redisConnectionFactory1(RedisProperties redisProperties) {
return createConnectionFactory(redisProperties.getDb().get("db1"));
}
@Bean(name = "redisConnectionFactory2")
public RedisConnectionFactory redisConnectionFactory2(RedisProperties redisProperties) {
return createConnectionFactory(redisProperties.getDb().get("db2"));
}
private RedisConnectionFactory createConnectionFactory(RedisProperties.DatabaseProperties properties) {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(properties.getHost());
config.setPort(properties.getPort());
config.setDatabase(properties.getDatabase());
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.commandTimeout(Duration.ofMillis(properties.getTimeout()))
.build();
return new LettuceConnectionFactory(config, clientConfig);
}
@Bean(name = "redisTemplate1")
@Primary
public RedisTemplate<String, Object> redisTemplate1(
@Qualifier("redisConnectionFactory1") RedisConnectionFactory factory) {
return createRedisTemplate(factory);
}
@Bean(name = "redisTemplate2")
public RedisTemplate<String, Object> redisTemplate2(
@Qualifier("redisConnectionFactory2") RedisConnectionFactory factory) {
return createRedisTemplate(factory);
}
private RedisTemplate<String, Object> createRedisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 配置具体的序列化方式
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
}
三、如何使用两个 redis库?
思路是:
1)创建一个接口RedisService,声明各种对redis的操作
2)针对该接口,实现redisService1和redisService2,分别使用redisTemplate1和redisTemplate2
3)使用的时候,redisService1就是对应库1,redisService2对应库2
具体如下:
1、接口RedisService
import java.util.Map;
import java.util.Set;
public interface RedisService {
Set<String> keys(String key);
Set<String> keysAll();
String get(String key);
String get(String key, String hashKey);
void set(String key,String value);
void upsertArrayElements(String hashKey, Map<String, Object> newElements);
Map<Object, Object> getArrayElements(String hashKey);
boolean exists(String key);
boolean hasKey(String key, String hashKey);
}
2、实现类1RedisService1Impl
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.Set;
@Service("redisService1")
public class RedisService1Impl implements RedisService{
private final RedisHandler redisHandler;
@Autowired
public RedisService1Impl(@Qualifier("redisTemplate1") RedisTemplate<String, Object> redisTemplate) {
this.redisHandler = new RedisHandler(redisTemplate);
}
@Override
public Set<String> keys(String key){
return redisHandler.keys(key);
}
@Override
public Set<String> keysAll(){
return redisHandler.keysAll();
}
@Override
public String get(String key){
return redisHandler.get(key);
}
@Override
public String get(String key, String hashKey){
return redisHandler.get(key,hashKey);
}
@Override
public void set(String key,String value){
redisHandler.set(key,value);
}
@Override
public void upsertArrayElements(String hashKey, Map<String, Object> newElements) {
// 对于每个新元素,如果它不存在于哈希表中,则添加它;如果它已经存在,则覆盖它。
redisHandler.upsertArrayElements(hashKey,newElements);
}
@Override
public Map<Object, Object> getArrayElements(String hashKey) {
return redisHandler.getArrayElements(hashKey);
}
@Override
public boolean exists(String key){
return redisHandler.exists(key);
}
@Override
public boolean hasKey(String key, String hashKey){
return redisHandler.hasKey(key,hashKey);
}
}
注意代码中,声明使用redisTemplate1
@Autowired
public RedisService1Impl(@Qualifier("redisTemplate1") RedisTemplate<String, Object> redisTemplate) {
this.redisHandler = new RedisHandler(redisTemplate);
}
3、实现类2RedisService2Impl
基本上与RedisService1Impl没有什么区别,只是对应的redisTemplate不一样。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.Set;
@Service("redisService2")
public class RedisService2Impl implements RedisService{
private final RedisHandler redisHandler;
@Autowired
public RedisService2Impl(@Qualifier("redisTemplate2") RedisTemplate<String, Object> redisTemplate) {
this.redisHandler = new RedisHandler(redisTemplate);
}
@Override
public Set<String> keys(String key){
return redisHandler.keys(key);
}
@Override
public Set<String> keysAll(){
return redisHandler.keysAll();
}
@Override
public String get(String key){
return redisHandler.get(key);
}
@Override
public String get(String key, String hashKey){
return redisHandler.get(key,hashKey);
}
@Override
public void set(String key,String value){
redisHandler.set(key,value);
}
@Override
public void upsertArrayElements(String hashKey, Map<String, Object> newElements) {
// 对于每个新元素,如果它不存在于哈希表中,则添加它;如果它已经存在,则覆盖它。
redisHandler.upsertArrayElements(hashKey,newElements);
}
@Override
public Map<Object, Object> getArrayElements(String hashKey) {
return redisHandler.getArrayElements(hashKey);
}
@Override
public boolean exists(String key){
return redisHandler.exists(key);
}
@Override
public boolean hasKey(String key, String hashKey){
return redisHandler.hasKey(key,hashKey);
}
}
4、公共Redis操作类RedisHandler
import com.landtool.server.modules.utils.JsonUtil;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.Set;
@Component
public class RedisHandler {
private RedisTemplate<String, Object> redisTemplate;
public RedisHandler(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 是否存在指定key
*/
public boolean exists(String key) {
return redisTemplate.hasKey(key);
}
/**
* 是否存在键
*/
public boolean hasKey(String key, String hashKey) {
assert key != null && hashKey != null;
HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
return hashOperations.hasKey(key, hashKey);
}
/**
* 获取所有key
*/
public Set<String> keys(String key) {
assert key != null;
HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
return hashOperations.keys(key);
}
public Set<String> keysAll() {
return redisTemplate.keys("*");
}
public String get(String key) {
Object value = redisTemplate.opsForValue().get(key);
return value != null ? value.toString() : null;
}
public String get(String key, String hashKey) {
assert key != null && hashKey != null;
HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
String json = null;
try {
json = JsonUtil.toJsonString(hashOperations.get(hashKey, key));
} catch (Exception e) {
e.printStackTrace();
}
return json;
}
public void set(String key, String value) {
redisTemplate.opsForValue().set(key, value);
}
public void upsertArrayElements(String hashKey, Map<String, Object> newElements) {
// 对于每个新元素,如果它不存在于哈希表中,则添加它;如果它已经存在,则覆盖它。
redisTemplate.opsForHash().putAll(hashKey, newElements);
}
public Map<Object, Object> getArrayElements(String hashKey) {
return redisTemplate.opsForHash().entries(hashKey);
}
/**
* 删除数据
*/
public Long delete(String key, String... hashKeys) {
assert key != null && hashKeys != null;
HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
return hashOperations.delete(key, hashKeys);
}
}
四、调用RedisService
@Autowired
@Qualifier("redisService1")
RedisService redisService;
@Autowired
@Qualifier("redisService2")
RedisService redisService2;
redisService.***,使用库1
redisService2.***,使用库2
五、操作哈希表
private static final String REDIS_CHECK_LINK = "check_link";
public void freshCheckLink() {
Map<String, Object> links = new HashMap<>();
//nce
getNce();
//outer-api
links.put("outerApi",某值);
redisService2.upsertArrayElements(REDIS_CHECK_LINK,links);
}
private void getNce() {
AtomicLong myCount = new AtomicLong();
CompletableFuture.supplyAsync(() -> {
。。。
}).thenAccept(count -> {
Map<String, Object> links = new HashMap<>();
links.put("nce",myCount.get() >= 0);
redisService2.upsertArrayElements(REDIS_CHECK_LINK,links);
});
}
private void setNceStatus(boolean status){
}
这样子,键名为“check_link”的结构如下:
六、小结
Redis使用多个库有现实意义。在我这个项目中,是为了避免冲突。库1由其他子系统频繁写入,而库2则是将数据从库1读取、分析、整理后产生的结果。web前端向后端请求数据,后端直接从结果从库2读出,避免了每个请求都分析、整理。