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.

314 lines
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依赖
```xml
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
```
#### 1.2 配置Redis连接
```yaml
# application.yaml
spring:
redis:
host: localhost
port: 6379
password:
database: 0
```
#### 1.3 实现缓存服务
```java
// 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 实现数据预加载服务
```java
// 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 修改服务层代码,使用缓存
```java
// 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 实现数据变更监听
```java
// 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 前端预加载逻辑
```javascript
// 在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 前端缓存读取逻辑
```javascript
// 获取客户数据,优先从缓存读取
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. 用户体验改善
- **页面加载更快**:减少用户等待时间
- **数据响应更迅速**:提升系统交互流畅度
- **减少页面卡顿**:优化数据加载机制
- **支持更多并发用户**:提高系统吞吐量
## 五、实施步骤
1. **安装并配置Redis服务器**
2. **添加Redis依赖和配置**
3. **实现缓存服务和预加载服务**
4. **修改现有服务层代码,使用缓存**
5. **实现数据变更监听**
6. **修改前端代码,实现预加载和缓存**
7. **测试和优化**
## 六、注意事项
1. **数据一致性**:确保缓存数据与数据库数据的一致性
2. **缓存过期策略**:根据数据更新频率设置合理的过期时间
3. **内存管理**:监控Redis内存使用情况,避免内存溢出
4. **错误处理**:实现缓存失效时的降级策略
5. **性能监控**:添加性能监控,持续优化系统性能
## 七、总结
本预加载解决方案通过引入缓存机制和预加载策略,将有效解决当前系统数据加载慢的问题。通过后端预加载热点数据到Redis缓存,前端预加载和缓存数据,以及实现数据变更监听,系统的数据加载速度将得到显著提升,同时降低数据库压力和网络延迟,改善用户体验。
该方案具有良好的扩展性和可维护性,可以根据系统的实际运行情况进行调整和优化,为系统的长期稳定运行提供保障。