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.

280 lines
9.0 KiB

3 months ago
# 数据库性能优化指南
## 问题分析
根据对Mapper XML文件的检查,发现以下可能导致数据返回慢的主要问题:
### 1. 使用 SELECT * 查询所有字段
- **问题**:`SELECT *` 查询会返回表中的所有列,增加网络传输量和处理时间
- **影响文件**:ManagersMapper.xml中的多个查询
### 2. 复杂的JOIN和嵌套WHERE条件
- **问题**:复杂的JOIN操作和多层嵌套条件会增加数据库解析和执行时间
- **影响文件**:UsersMapper.xml中的`getAuthorizedCustomers`和`getAuthorizedUserIds`查询
### 3. 可能缺少必要的索引
- **问题**:频繁用于查询条件的字段可能缺少索引,导致全表扫描
- **影响字段**:userId, phoneNumber, type, level, managerId等
### 4. 未使用分页查询
- **问题**:查询大量数据时未使用分页,一次性返回过多数据
- **影响文件**:selectAllManagers等查询
## 优化方案
### 1. 优化SQL查询语句
#### 1.1 替换 SELECT * 为具体字段
**修改前** (ManagersMapper.xml):
```xml
<select id="selectAllManagers" resultMap="managersMap">
SELECT * FROM managers ORDER BY userName
</select>
```
**修改后**:
```xml
<select id="selectAllManagers" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers ORDER BY userName
</select>
```
对其他使用 `SELECT *` 的查询也进行类似修改。
#### 1.2 优化复杂的JOIN查询
**修改建议** (UsersMapper.xml):
- 将复杂的嵌套条件拆分为更简单的部分
- 使用子查询代替部分复杂JOIN
- 考虑使用临时表缓存中间结果
#### 1.3 添加分页查询
**修改前**:
```xml
<select id="selectAllManagers" resultMap="managersMap">
SELECT * FROM managers ORDER BY userName
</select>
```
**修改后**:
```xml
<select id="selectAllManagersWithPagination" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers
ORDER BY userName
LIMIT #{limit} OFFSET #{offset}
</select>
```
同时在Mapper接口中添加对应的方法。
### 2. 添加必要的数据库索引
建议在以下字段上添加索引:
```sql
-- 在users表上添加索引
CREATE INDEX idx_users_userId ON users(userId);
CREATE INDEX idx_users_phoneNumber ON users(phoneNumber);
CREATE INDEX idx_users_type ON users(type);
CREATE INDEX idx_users_level ON users(level);
CREATE INDEX idx_users_created_at ON users(created_at);
-- 在managers表上添加索引
CREATE INDEX idx_managers_id ON managers(id);
CREATE INDEX idx_managers_userName ON managers(userName);
CREATE INDEX idx_managers_managerId ON managers(managerId);
-- 在usermanagements表上添加索引
CREATE INDEX idx_usermanagements_userId ON usermanagements(userId);
CREATE INDEX idx_usermanagements_managerId ON usermanagements(managerId);
CREATE INDEX idx_usermanagements_userName ON usermanagements(userName);
CREATE INDEX idx_usermanagements_organization ON usermanagements(organization);
CREATE INDEX idx_usermanagements_managerdepartment ON usermanagements(managerdepartment);
```
### 3. 添加缓存机制
#### 3.1 在MyBatis中添加二级缓存
在Mapper XML文件中添加缓存配置:
```xml
<mapper namespace="com.example.web.mapper.ManagersMapper">
<!-- 添加二级缓存 -->
<cache
eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>
<!-- 现有映射内容 -->
<!-- ... -->
</mapper>
```
#### 3.2 在Service层添加本地缓存
在Service类中使用Guava Cache或Caffeine等本地缓存库。
### 4. 优化结果集映射
#### 4.1 优化ResultMap定义
确保ResultMap只映射必要的字段,避免不必要的映射。
#### 4.2 使用Constructor Args映射
对于频繁使用的DTO,可以考虑使用构造函数参数映射,提高性能。
## 具体优化实施
### 优化ManagersMapper.xml
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.web.mapper.ManagersMapper">
<!-- 添加二级缓存 -->
<cache
eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>
<resultMap id="managersMap" type="com.example.web.entity.Managers">
<id column="manager_id" property="manager_id"/>
<result column="id" property="id"/>
<result column="managerId" property="managerId"/>
<result column="managercompany" property="managercompany"/>
<result column="managerdepartment" property="managerdepartment"/>
<result column="organization" property="organization"/>
<result column="role" property="role"/>
<result column="root" property="root"/>
<result column="created_at" property="created_at"/>
<result column="updated_at" property="updated_at"/>
<result column="userName" property="userName"/>
<result column="assistant" property="assistant"/>
</resultMap>
<!-- 在 ManagersMapper.xml 中添加 -->
<select id="selectByUserNameAndManagerId" parameterType="map" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers
WHERE userName = #{userName} AND managerId = #{managerId}
</select>
<!-- 根据企业ID查询负责人信息 -->
<select id="selectManagersByEnterpriseId" parameterType="String" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers WHERE id = #{id}
</select>
<!-- 查询所有负责人信息 -->
<select id="selectAllManagers" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers ORDER BY userName
</select>
<!-- 分页查询负责人信息 -->
<select id="selectAllManagersWithPagination" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers
ORDER BY userName
LIMIT #{limit} OFFSET #{offset}
</select>
<!-- 根据负责人姓名查询 -->
<select id="selectByUserName" parameterType="String" resultMap="managersMap">
SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers WHERE userName = #{userName}
</select>
<!-- 插入负责人信息 -->
<insert id="insertManagers" parameterType="com.example.web.entity.Managers">
INSERT INTO managers (
id, managerId, managercompany, managerdepartment,
organization, role, root, created_at, updated_at,
userName, assistant
) VALUES (
#{id}, #{managerId}, #{managercompany}, #{managerdepartment},
#{organization}, #{role}, #{root}, #{created_at}, #{updated_at},
#{userName}, #{assistant}
)
</insert>
<!-- 更新负责人信息 -->
<update id="updateManagers" parameterType="com.example.web.entity.Managers">
UPDATE managers
SET
managercompany = #{managercompany},
managerdepartment = #{managerdepartment},
organization = #{organization},
role = #{role},
userName = #{userName},
assistant = #{assistant},
updated_at = #{updated_at}
WHERE id = #{id}
</update>
</mapper>
```
### 优化UsersMapper接口添加分页方法
在UsersMapper.java中添加分页查询方法:
```java
// 分页查询授权客户
List<UserProductCartDTO> getAuthorizedCustomersWithPagination(Map<String, Object> params);
// 获取授权客户总数
int getAuthorizedCustomersCount(Map<String, Object> params);
```
## 验证优化效果
优化后,建议通过以下方式验证性能改进:
1. **使用EXPLAIN分析SQL执行计划**
```sql
EXPLAIN SELECT manager_id, id, managerId, managercompany, managerdepartment,
organization, role, root, userName, assistant
FROM managers WHERE userName = 'someName';
```
2. **添加性能监控日志**
在Service层添加方法执行时间记录。
3. **压力测试**
使用JMeter等工具进行压力测试,比较优化前后的响应时间。
## 额外建议
1. **考虑读写分离**
对于高并发场景,考虑实现数据库读写分离。
2. **使用数据库连接池**
确保使用高效的连接池管理数据库连接。
3. **定期清理和优化数据库**
定期执行VACUUM(PostgreSQL)或OPTIMIZE TABLE(MySQL)等操作。
4. **考虑使用NoSQL缓存热点数据**
对于频繁访问的数据,可以考虑使用Redis等NoSQL数据库进行缓存。
---
此优化指南由自动化工具生成,建议根据实际情况进行调整和测试。