You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
10 KiB
10 KiB
客户数据预加载解决方案
一、当前系统性能问题分析
1. 数据加载模式
- 请求-响应模式:当前系统使用传统的请求-响应模式,每次前端请求数据时,后端才会查询数据库并返回结果
- 多次数据库查询:单个客户数据请求涉及多个数据库查询(基本信息、联系人、购物车项、负责人等)
- 缺少缓存机制:系统中没有实现任何缓存机制,所有数据都直接从数据库查询
- 前端频繁请求:作为客户关系管理系统,前端需要频繁请求客户数据
2. 性能瓶颈
- 数据库查询开销大:每次请求都需要执行多次数据库查询
- 网络延迟:频繁的HTTP请求导致网络延迟累积
- 数据重复传输:相同数据可能被多次请求和传输
- 缺少数据预加载:没有在用户需要之前预先加载数据
二、预加载解决方案设计
1. 后端预加载策略
1.1 添加缓存层
- 使用Redis作为缓存存储:高性能、高可用的键值存储
- 实现数据预加载服务:定期将热点数据加载到缓存中
- 合理设置过期时间:根据数据类型和更新频率设置不同的过期时间
1.2 优化数据库查询
- 实现数据预聚合:减少查询次数,提高查询效率
- 使用JOIN查询:替代多次单表查询,减少数据库交互次数
- 添加索引优化:为频繁查询的字段添加索引
1.3 实现数据预加载服务
- 定时任务预加载:定期预加载热点数据
- 数据变更监听:当数据发生变化时及时更新缓存
- 分层预加载:为不同类型的用户预加载不同的数据
2. 前端预加载策略
2.1 实现前端缓存
- 使用localStorage/sessionStorage:缓存不经常变化的数据
- 数据预加载机制:在用户访问页面之前加载数据
- 数据过期机制:定期更新缓存,确保数据新鲜度
2.2 优化请求策略
- 请求合并:减少HTTP请求次数
- 请求预加载:在用户可能访问的数据之前加载
- 懒加载:只加载当前需要的数据,减少初始加载时间
2.3 WebSocket实时更新
- 建立WebSocket连接:实现数据实时更新
- 后端主动推送:当数据发生变化时,主动推送给前端
- 减少轮询请求:降低服务器压力,提高响应速度
三、具体实施方案
1. 后端实现
1.1 添加Redis依赖
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
1.2 配置Redis连接
# application.yaml
spring:
redis:
host: localhost
port: 6379
password:
database: 0
1.3 实现缓存服务
// CacheService.java
@Service
public class CacheService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 缓存数据
public void cacheData(String key, Object data, long expireTime, TimeUnit timeUnit) {
redisTemplate.opsForValue().set(key, data, expireTime, timeUnit);
}
// 获取缓存数据
public <T> T getCachedData(String key, Class<T> clazz) {
Object data = redisTemplate.opsForValue().get(key);
return clazz.cast(data);
}
// 删除缓存数据
public void deleteCachedData(String key) {
redisTemplate.delete(key);
}
// 检查缓存是否存在
public boolean isCached(String key) {
return redisTemplate.hasKey(key);
}
}
1.4 实现数据预加载服务
// PreloadService.java
@Service
public class PreloadService {
@Autowired
private UsersMapper usersMapper;
@Autowired
private CacheService cacheService;
// 预加载热点客户数据 - 每5分钟执行一次
@Scheduled(fixedRate = 300000)
public void preloadHotCustomers() {
List<UserProductCartDTO> hotCustomers = usersMapper.selectHotCustomers();
for (UserProductCartDTO customer : hotCustomers) {
String key = "customer:" + customer.getUserId();
cacheService.cacheData(key, customer, 1, TimeUnit.HOURS);
}
}
// 预加载客户列表数据 - 每10分钟执行一次
@Scheduled(fixedRate = 600000)
public void preloadCustomerList() {
List<UserProductCartDTO> customerList = usersMapper.selectAllCustomers();
cacheService.cacheData("customer:list", customerList, 30, TimeUnit.MINUTES);
}
}
1.5 修改服务层代码,使用缓存
// CustomerService.java
@Service
public class CustomerService {
@Autowired
private UsersMapper usersMapper;
@Autowired
private CacheService cacheService;
// 获取客户数据,优先从缓存获取
public UserProductCartDTO getCustomerById(String userId) {
String cacheKey = "customer:" + userId;
// 尝试从缓存获取
UserProductCartDTO customer = cacheService.getCachedData(cacheKey, UserProductCartDTO.class);
if (customer != null) {
return customer;
}
// 缓存未命中,从数据库查询
customer = usersMapper.selectById(userId);
// 将查询结果存入缓存
if (customer != null) {
cacheService.cacheData(cacheKey, customer, 1, TimeUnit.HOURS);
}
return customer;
}
}
1.6 实现数据变更监听
// DataChangeAspect.java
@Aspect
@Component
public class DataChangeAspect {
@Autowired
private CacheService cacheService;
// 监听数据更新方法,清除相关缓存
@AfterReturning(pointcut = "execution(* com.example.web.service.*Service.update*(..))")
public void afterUpdate(JoinPoint joinPoint) {
// 清除相关缓存
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
if (arg instanceof UserProductCartDTO) {
UserProductCartDTO customer = (UserProductCartDTO) arg;
String cacheKey = "customer:" + customer.getUserId();
cacheService.deleteCachedData(cacheKey);
}
// 清除客户列表缓存
cacheService.deleteCachedData("customer:list");
}
}
}
2. 前端实现
2.1 前端预加载逻辑
// 在mainapp-sells.html和mainapp-supplys.html中添加
// 预加载客户数据
function preloadCustomerData() {
// 预加载客户列表
fetch('/api/customers/list')
.then(response => response.json())
.then(data => {
if (data.success) {
// 将数据存入localStorage
localStorage.setItem('customerList', JSON.stringify(data.data));
localStorage.setItem('customerListExpire', Date.now() + 30 * 60 * 1000); // 30分钟过期
}
});
// 预加载热点客户数据
fetch('/api/customers/hot')
.then(response => response.json())
.then(data => {
if (data.success) {
// 将数据存入localStorage
localStorage.setItem('hotCustomers', JSON.stringify(data.data));
localStorage.setItem('hotCustomersExpire', Date.now() + 60 * 60 * 1000); // 1小时过期
}
});
}
// 页面加载完成后预加载数据
window.addEventListener('load', preloadCustomerData);
// 在用户可能访问的页面之前预加载数据
document.addEventListener('click', function(e) {
if (e.target.matches('[data-preload]')) {
const preloadUrl = e.target.getAttribute('data-preload');
fetch(preloadUrl)
.then(response => response.json())
.then(data => {
// 将数据存入sessionStorage
sessionStorage.setItem(preloadUrl, JSON.stringify(data));
});
}
});
2.2 前端缓存读取逻辑
// 获取客户数据,优先从缓存读取
function getCustomerData(userId) {
// 尝试从localStorage获取
const cachedData = localStorage.getItem('customerList');
const expireTime = localStorage.getItem('customerListExpire');
if (cachedData && expireTime && Date.now() < parseInt(expireTime)) {
const customerList = JSON.parse(cachedData);
const customer = customerList.find(c => c.userId === userId);
if (customer) {
return Promise.resolve(customer);
}
}
// 缓存未命中,从服务器获取
return fetch(`/api/customers/${userId}`)
.then(response => response.json())
.then(data => {
if (data.success) {
return data.data;
}
throw new Error(data.message);
});
}
四、预期效果
1. 性能提升
- 数据加载速度:预计提升50%-90%
- 数据库查询次数:预计减少60%-80%
- 前端响应时间:预计提升40%-70%
- 服务器压力:预计降低50%-70%
2. 用户体验改善
- 页面加载更快:减少用户等待时间
- 数据响应更迅速:提升系统交互流畅度
- 减少页面卡顿:优化数据加载机制
- 支持更多并发用户:提高系统吞吐量
五、实施步骤
- 安装并配置Redis服务器
- 添加Redis依赖和配置
- 实现缓存服务和预加载服务
- 修改现有服务层代码,使用缓存
- 实现数据变更监听
- 修改前端代码,实现预加载和缓存
- 测试和优化
六、注意事项
- 数据一致性:确保缓存数据与数据库数据的一致性
- 缓存过期策略:根据数据更新频率设置合理的过期时间
- 内存管理:监控Redis内存使用情况,避免内存溢出
- 错误处理:实现缓存失效时的降级策略
- 性能监控:添加性能监控,持续优化系统性能
七、总结
本预加载解决方案通过引入缓存机制和预加载策略,将有效解决当前系统数据加载慢的问题。通过后端预加载热点数据到Redis缓存,前端预加载和缓存数据,以及实现数据变更监听,系统的数据加载速度将得到显著提升,同时降低数据库压力和网络延迟,改善用户体验。
该方案具有良好的扩展性和可维护性,可以根据系统的实际运行情况进行调整和优化,为系统的长期稳定运行提供保障。