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
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数据库进行缓存。
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
此优化指南由自动化工具生成,建议根据实际情况进行调整和测试。
|