目录
1. 我们为什么要使用Redis缓存数据库?
2. 关系型与非关系型数据库有哪些区别?
3. Redis中的缓存问题与解决方案:穿透、击穿、雪崩
4. 为什么要在一个项目中使用/管理16个数据库呢?
5. 使用16个数据库比使用1个数据库的好处在哪里?
6. 怎么将应用集成到项目开发中使用?
6.1 配置application.yml文件
6.2 Redis缓存数据库核心配置文件
6.3 Redis一站式服务治理核心配置文件
6.4 测试
1. 我们为什么要使用Redis缓存数据库?
-
快速的内存存储:Redis是一种基于内存的高性能键值存储系统,读取和写入都发生在内存中,这使得它具有极快的读写速度。相比于传统的磁盘存储系统(如关系型数据库),Redis可以提供更低的延迟和更高的吞吐量。
-
缓存加速:作为缓存层,Redis常用于提升应用程序的性能。它可以将经常访问的数据存储在内存中,从而避免了频繁地访问磁盘或数据库。这可以大大减少应用程序的响应时间,并减轻后端数据库的负载。
-
数据结构丰富:Redis支持多种数据结构,如字符串、哈希表、列表、集合和有序集合等。这使得Redis不仅可以作为简单的键值存储使用,还可以处理更复杂的数据操作,如计数器、排行榜、发布与订阅等。
-
高可用性和容错性:Redis提供了主从复制和Sentinel机制来实现高可用性和容错性。通过复制数据到多个副本,当主节点发生故障时,系统可以自动切换到一个备用节点。这确保了数据的可用性和系统的稳定性。
-
持久化选项:Redis支持不同的持久化选项,可以将内存中的数据持久化到磁盘上,以防止数据丢失。您可以选择在需要时将数据写入磁盘(RDB快照)或实时记录所有写操作(AOF日志)。这增加了数据的恢复能力和持久性。
-
分布式缓存:Redis还可以作为分布式缓存系统使用。多个Redis节点可以组成一个集群,数据可以在节点间进行分片和复制,提供了更高的可伸缩性和容量。
2. 关系型与非关系型数据库有哪些区别?
1. 数据模型:
- 关系型数据库:使用表格(二维表)的形式来组织和存储数据,数据之间通过键和关系进行连接和表示。使用SQL语言进行数据的查询和操作,具有固定的结构和严格的数据模式。
- 非关系型数据库:不使用传统的表格结构来组织数据,采用键值对、文档、列族或图形等方式来存储数据,数据模型更加灵活。非关系型数据库可以根据需要存储各种类型的数据,无需遵循固定的模式。
2. 可扩展性:
- 关系型数据库:通常采用垂直扩展(增加硬件资源)的方式来提高性能和容量,需要在单个节点上增加更多的计算和存储资源。
- 非关系型数据库:多数非关系型数据库采用水平扩展(横向扩展)的方式,通过添加更多的节点来分布和处理数据,从而实现更高的可扩展性和负载均衡。
3. 数据一致性:
- 关系型数据库:关系型数据库强调ACID事务的一致性,确保数据的完整性和可靠性。在写入和更新操作时提供强一致性。
- 非关系型数据库:非关系型数据库在一致性方面有不同的权衡。一些非关系型数据库提供强一致性,但通常会以性能损失为代价;而其他非关系型数据库(如分布式数据库)可能提供最终一致性或柔性一致性,允许数据在一段时间内存在不一致。
4. 应用场景:
- 关系型数据库:适用于需要严格数据模型和结构的应用场景,如金融系统、电子商务平台、管理系统等,对事务支持和数据一致性要求较高的应用。
- 非关系型数据库:适用于大规模数据存储和处理、需要高吞吐量和低延迟的应用,如社交网络、实时分析、缓存、日志处理等,对数据模型灵活性和可扩展性要求较高的应用。
3. Redis中的缓存问题与解决方案:穿透、击穿、雪崩
1. 缓存穿透(Cache Penetration):
- 问题描述:指恶意或非法的请求经过缓存层,直接访问数据库,导致数据库压力过大。
- 解决方案:使用布隆过滤器(Bloom Filter)等技术,对查询结果为空的请求进行过滤,避免对数据库的直接访问。另外,还可以将空结果缓存一段时间,避免频繁查询。
2. 缓存击穿(Cache Breakdown):
- 问题描述:指一个原本存在的key,在即将过期时被并发请求同时查询,导致多个请求同时访问数据库。
- 解决方案:使用互斥锁或分布式锁来保证只有一台服务器能够查询数据库,其他服务器在获取锁失败时等待查询结果。另外,在设置缓存时,可以给缓存设置一个随机的过期时间,避免缓存同时失效。
3. 缓存雪崩(Cache Avalanche):
- 问题描述:指在某个时间段内,大量的缓存同时失效,导致大量请求直接访问数据库,造成数据库负载过高。
- 解决方案:可以采用以下几种方式来应对缓存雪崩问题:
- 设置不同的过期时间:将缓存的过期时间分散开,避免同时失效。可以在原有的过期时间上增加一个随机值,使得每个缓存的失效时间不完全相同。
- 热点数据预加载:在缓存失效前,提前异步加载热点数据到缓存中,保证数据的可用性。
- 使用主从复制或者分片技术:通过部署多个缓存节点,将缓存的负载分散开,提高系统的可用性和容错性。
- 数据库限流:在缓存失效期间,对数据库查询进行限流,避免数据库压力过大。
4. 为什么要在一个项目中使用/管理16个数据库呢?
-
数据隔离:不同的数据库可以用来存储不同的业务数据,以实现数据的逻辑隔离。这样可以降低系统之间的耦合性,提高数据安全性和可维护性。
-
数据性能:通过将数据分散存储在多个数据库中,可以提高系统的并发处理能力和响应速度。每个数据库可以专注于处理特定类型或特定区域的数据,减少数据库的负载压力。
-
数据复制和备份:使用多个数据库可以方便进行数据的复制和备份。通过数据库间的数据同步和复制,可以实现数据的容灾和紧急恢复,提高系统的可用性和数据的安全性。
-
数据管理和扩展:对于大规模的项目,使用多个数据库可以更好地进行数据管理和扩展。不同的数据库可以部署在不同的服务器上,实现水平扩展和负载均衡。这样可以提高系统的灵活性和可扩展性。
-
数据安全:通过将敏感数据分别存储在不同的数据库中,可以实现数据的分级保护和访问控制。不同的数据库可以设置不同的权限和加密机制,提高数据的安全性。
5. 使用16个数据库比使用1个数据库的好处在哪里?
-
数据分区:使用多个数据库可以将数据进行更细粒度的分区。每个数据库负责存储特定范围或特定类型的数据,这样可以更有效地组织和查询数据。例如,可以按照地理位置、时间范围、用户类型等进行数据分区,提高数据的查询效率。
-
并行处理:通过将数据分散存储在多个数据库中,可以实现更高程度的并行处理。不同的数据库可以同时处理不同的查询或事务,提升系统的并发性能和吞吐量。这对于需要处理大量数据或高并发场景非常有利。
-
减少锁竞争:在单个数据库中,数据的读写操作可能会导致锁竞争,限制了并发性能。而使用多个数据库,可以将数据分散到不同的数据库中,减少锁竞争的可能性,进而提高系统的并发处理能力。
-
模块化开发:将系统的不同模块或功能存储在不同的数据库中,可以实现更好的模块化开发和维护。不同的团队或开发人员可以独立操作和管理各自负责的数据库,减少代码的耦合性,提高团队的协作效率。
-
水平扩展:使用多个数据库可以更容易地实现水平扩展。不同的数据库可以部署在不同的服务器上,通过添加更多的服务器节点来增加系统的处理能力。这样可以更灵活地满足增长需求,避免单点故障,并提供更好的可伸缩性和可用性。
-
多租户支持:如果您的应用程序是一个多租户系统,每个租户都有自己的数据集,使用多个数据库可以更好地实现租户数据的隔离和管理。每个租户可以拥有自己的数据库,这样可以更好地控制数据访问和管理。
6. 怎么将应用集成到项目开发中使用?
6.1 配置application.yml文件
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: 数据库驱动
url: 数据库链接
username: 数据库账号
password: 数据库密码
hikari:
connection-timeout: 100000
validation-timeout: 30000
idle-timeout: 60000
login-timeout: 5
maximum-pool-size: 20
max-lifetime: 60000
minimum-idle: 5
read-only: false
redis:
# Redis数据库索引(默认为0)
database: 10
# Redis服务器地址
host: 127.0.0.1
# Redis服务器连接端口
port: 6379
# 密码(默认为空)
password: 1234
# 连接超时时间 单位 ms(毫秒)
timeout: 6000
# 过期时间是30分钟30*60
expire-time: 1800
lettuce:
pool:
# 连接池最大连接数(使用负值表示没有限制) 默认 8,建议设置为CPU核数的两倍
max-active: 6000
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
max-wait: -1
# 连接池中的最大空闲连接 默认 8,建议设置为CPU核数的两倍
max-idle: 100
# 连接池中的最小空闲连接 默认 0
min-idle: 50
enabled: true
#--------------------------------Redis自定义一台服务配置操作多个库
redis:
database:
# 默认库
database0: 0
database1: 1
database2: 2
database3: 3
database4: 4
database5: 5
database6: 6
database7: 7
database8: 8
database9: 9
database10: 10
database11: 11
database12: 12
database13: 13
database14: 14
database15: 15
6.2 Redis缓存数据库核心配置文件
package com.jmh.demo03.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import javax.annotation.Resource;
/**
* @author 蒋明辉
* @data 2022/10/1 17:08
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
//实例化一个redis模板
RedisTemplate<String,Object> redisTemplate=new RedisTemplate<>();
//设置连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
//针对string类型的key和value进行序列化
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//针对has类型的key和value进行序列化
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
//将上诉代码启用
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
6.3 Redis一站式服务治理核心配置文件
package com.jmh.demo03.config;
import io.lettuce.core.resource.DefaultClientResources;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.ObjectUtils;
import java.time.Duration;
@Configuration
public class RedisConfigMultipleUnits {
/**
* 自定义redis配置获取
*/
@Value("${redis.database.database0}")
private int database0;
@Value("${redis.database.database1}")
private int database1;
@Value("${redis.database.database2}")
private int database2;
@Value("${redis.database.database3}")
private int database3;
@Value("${redis.database.database4}")
private int database4;
@Value("${redis.database.database5}")
private int database5;
@Value("${redis.database.database6}")
private int database6;
@Value("${redis.database.database7}")
private int database7;
@Value("${redis.database.database8}")
private int database8;
@Value("${redis.database.database9}")
private int database9;
@Value("${redis.database.database10}")
private int database10;
@Value("${redis.database.database11}")
private int database11;
@Value("${redis.database.database12}")
private int database12;
@Value("${redis.database.database13}")
private int database13;
@Value("${redis.database.database14}")
private int database14;
@Value("${redis.database.database15}")
private int database15;
/**
* spring继承redis配置获取
*/
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.lettuce.pool.max-active}")
private int maxActive;
@Value("${spring.redis.lettuce.pool.max-wait}")
private int maxWait;
@Value("${spring.redis.lettuce.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.lettuce.pool.min-idle}")
private int minIdle;
@Value("${spring.redis.timeout}")
private long timeout;
/**
* 配置redis连接池
*/
@Bean
public GenericObjectPoolConfig poolConfig() {
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxTotal(maxActive);
poolConfig.setMaxIdle(maxIdle);
poolConfig.setMinIdle(minIdle);
poolConfig.setMaxWaitMillis(maxWait);
return poolConfig;
}
/**
* 配置默认客户端资源
*/
@Bean("defaultClientResources")
public DefaultClientResources getDefaultClientResources(){
return DefaultClientResources.create();
}
private StringRedisTemplate getStringRedisTemplate(int database, GenericObjectPoolConfig poolConfig, DefaultClientResources defaultClientResources) {
StringRedisTemplate redisTemplate=new StringRedisTemplate();
// 构建工厂对象
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(host);
config.setPort(port);
if (!ObjectUtils.isEmpty(password)) {
RedisPassword redisPassword = RedisPassword.of(password);
config.setPassword(redisPassword);
}
LettucePoolingClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
.commandTimeout(Duration.ofSeconds(timeout))
.poolConfig(poolConfig)
.clientResources(defaultClientResources)
.build();
LettuceConnectionFactory factory = new LettuceConnectionFactory(config, clientConfig);
// 设置使用的redis数据库
factory.setDatabase(database);
// 重新初始化工厂
factory.afterPropertiesSet();
//设置连接工厂
redisTemplate.setConnectionFactory(factory);
//针对string类型的key和value进行序列化
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//针对has类型的key和value进行序列化
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
//将上诉代码启用
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean(name="redisTemplateDataBase0")
public StringRedisTemplate redisTemplateDataBase0(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database0, poolConfig,defaultClientResources);
}
@Bean(name="redisTemplateDataBase1")
public StringRedisTemplate redisTemplateDataBase1(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database1, poolConfig,defaultClientResources);
}
@Bean(name="redisTemplateDataBase2")
public StringRedisTemplate redisTemplateDataBase2(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database2, poolConfig,defaultClientResources);
}
@Bean(name="redisTemplateDataBase3")
public StringRedisTemplate redisTemplateDataBase3(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database3, poolConfig,defaultClientResources);
}
@Bean(name="redisTemplateDataBase4")
public StringRedisTemplate redisTemplateDataBase4(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database4, poolConfig,defaultClientResources);
}
@Bean(name="redisTemplateDataBase5")
public StringRedisTemplate redisTemplateDataBase5(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database5, poolConfig,defaultClientResources);
}
@Bean(name="redisTemplateDataBase6")
public StringRedisTemplate redisTemplateDataBase6(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database6, poolConfig,defaultClientResources);
}
@Bean(name="redisTemplateDataBase7")
public StringRedisTemplate redisTemplateDataBase7(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database7, poolConfig,defaultClientResources);
}
@Bean(name="redisTemplateDataBase8")
public StringRedisTemplate redisTemplateDataBase8(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database8, poolConfig,defaultClientResources);
}
@Bean(name="redisTemplateDataBase9")
public StringRedisTemplate redisTemplateDataBase9(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database9, poolConfig,defaultClientResources);
}
@Bean(name="redisTemplateDataBase10")
public StringRedisTemplate redisTemplateDataBase10(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database10, poolConfig,defaultClientResources);
}
@Bean(name="redisTemplateDataBase11")
public StringRedisTemplate redisTemplateDataBase11(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database11, poolConfig,defaultClientResources);
}
@Bean(name="redisTemplateDataBase12")
public StringRedisTemplate redisTemplateDataBase12(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database12, poolConfig,defaultClientResources);
}
@Bean(name="redisTemplateDataBase13")
public StringRedisTemplate redisTemplateDataBase13(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database13, poolConfig,defaultClientResources);
}
@Bean(name="redisTemplateDataBase14")
public StringRedisTemplate redisTemplateDataBase14(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database14, poolConfig,defaultClientResources);
}
@Bean(name="redisTemplateDataBase15")
public StringRedisTemplate redisTemplateDataBase15(GenericObjectPoolConfig poolConfig,
@Qualifier("defaultClientResources") DefaultClientResources defaultClientResources) {
return getStringRedisTemplate(database15, poolConfig,defaultClientResources);
}
}
6.4 测试
package com.jmh.demo03;
import com.alibaba.fastjson2.JSONObject;
import com.jmh.demo03.utils.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.ContentType;
import org.ehcache.xml.model.TimeUnit;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author 蒋明辉
* @data 2023/5/27 0:24
*/
@SpringBootTest
@Slf4j
public class demo13 {
@Resource(name = "redisTemplateDataBase0")
private StringRedisTemplate stringRedisTemplate0;
@Resource(name = "redisTemplateDataBase1")
private StringRedisTemplate stringRedisTemplate1;
@Resource(name = "redisTemplateDataBase2")
private StringRedisTemplate stringRedisTemplate2;
@Resource(name = "redisTemplateDataBase3")
private StringRedisTemplate stringRedisTemplate3;
@Resource(name = "redisTemplateDataBase4")
private StringRedisTemplate stringRedisTemplate4;
@Resource(name = "redisTemplateDataBase5")
private StringRedisTemplate stringRedisTemplate5;
@Resource(name = "redisTemplateDataBase6")
private StringRedisTemplate stringRedisTemplate6;
@Resource(name = "redisTemplateDataBase7")
private StringRedisTemplate stringRedisTemplate7;
@Resource(name = "redisTemplateDataBase8")
private StringRedisTemplate stringRedisTemplate8;
@Resource(name = "redisTemplateDataBase9")
private StringRedisTemplate stringRedisTemplate9;
@Resource(name = "redisTemplateDataBase10")
private StringRedisTemplate stringRedisTemplate10;
@Resource(name = "redisTemplateDataBase11")
private StringRedisTemplate stringRedisTemplate11;
@Resource(name = "redisTemplateDataBase12")
private StringRedisTemplate stringRedisTemplate12;
@Resource(name = "redisTemplateDataBase13")
private StringRedisTemplate stringRedisTemplate13;
@Resource(name = "redisTemplateDataBase14")
private StringRedisTemplate stringRedisTemplate14;
@Resource(name = "redisTemplateDataBase15")
private StringRedisTemplate stringRedisTemplate15;
@Test
public void demo04(){
stringRedisTemplate0.opsForValue().set("0","我是第1个数据库【redis】");
stringRedisTemplate1.opsForValue().set("1","我是第2个数据库【redis】");
stringRedisTemplate2.opsForValue().set("2","我是第3个数据库【redis】");
stringRedisTemplate3.opsForValue().set("3","我是第4个数据库【redis】");
stringRedisTemplate4.opsForValue().set("4","我是第5个数据库【redis】");
stringRedisTemplate5.opsForValue().set("5","我是第6个数据库【redis】");
stringRedisTemplate6.opsForValue().set("6","我是第7个数据库【redis】");
stringRedisTemplate7.opsForValue().set("7","我是第8个数据库【redis】");
stringRedisTemplate8.opsForValue().set("8","我是第9个数据库【redis】");
stringRedisTemplate9.opsForValue().set("9","我是第10个数据库【redis】");
stringRedisTemplate10.opsForValue().set("10","我是第11个数据库【redis】");
stringRedisTemplate11.opsForValue().set("11","我是第12个数据库【redis】");
stringRedisTemplate12.opsForValue().set("12","我是第13个数据库【redis】");
stringRedisTemplate13.opsForValue().set("13","我是第14个数据库【redis】");
stringRedisTemplate14.opsForValue().set("14","我是第15个数据库【redis】");
stringRedisTemplate15.opsForValue().set("15","我是第16个数据库【redis】");
}
}
- 测试结果如图