This commit is contained in:
Your Name
2026-03-23 18:02:16 +08:00
parent 18fee2e8f8
commit 250d173c2f
525 changed files with 10659 additions and 793 deletions
+251
View File
@@ -0,0 +1,251 @@
# 预约列表查询性能优化 - 索引方案
## 问题分析
预约列表查询涉及多表关联和复杂条件筛选,主要性能瓶颈:
1. 多表 LEFT JOIN(预约表、诊断表、管理员表)
2. 多条件筛选(状态、日期范围、患者姓名、医生姓名)
3. 权限过滤(医生角色、医助角色)
4. 复杂排序(状态 + 日期 + 时间)
5. 子查询(确认诊单、开方状态)
## 索引优化方案
### 1. 预约表 (zyt_appointment)
#### 单列索引
```sql
-- 医生ID索引(医生角色查询)
ALTER TABLE `zyt_appointment` ADD INDEX `idx_doctor_id` (`doctor_id`);
-- 患者ID索引(关联查询)
ALTER TABLE `zyt_appointment` ADD INDEX `idx_patient_id` (`patient_id`);
-- 状态索引(状态筛选)
ALTER TABLE `zyt_appointment` ADD INDEX `idx_status` (`status`);
-- 预约日期索引(日期范围查询)
ALTER TABLE `zyt_appointment` ADD INDEX `idx_appointment_date` (`appointment_date`);
```
#### 组合索引(重点优化)
```sql
-- 状态+日期+时间(用于排序,覆盖 ORDER BY)
ALTER TABLE `zyt_appointment` ADD INDEX `idx_status_date_time` (`status`, `appointment_date`, `appointment_time`);
-- 医生+状态+日期(医生角色常用查询)
ALTER TABLE `zyt_appointment` ADD INDEX `idx_doctor_status_date` (`doctor_id`, `status`, `appointment_date`);
```
**优化效果**
- `idx_status_date_time` 可以直接用于排序,避免 filesort
- `idx_doctor_status_date` 可以快速过滤医生的预约记录
### 2. 诊断表 (zyt_tcm_diagnosis)
```sql
-- 患者姓名索引(模糊搜索,前缀索引)
ALTER TABLE `zyt_tcm_diagnosis` ADD INDEX `idx_patient_name` (`patient_name`(20));
-- 医助ID索引(医助角色查询)
ALTER TABLE `zyt_tcm_diagnosis` ADD INDEX `idx_assistant_id` (`assistant_id`);
```
**说明**
- `patient_name` 使用前缀索引(20),节省空间且支持 LIKE 'xxx%' 查询
- 不支持 LIKE '%xxx%' 的优化,但可以减少扫描行数
### 3. 管理员表 (zyt_admin)
```sql
-- 管理员姓名索引(医生姓名搜索)
ALTER TABLE `zyt_admin` ADD INDEX `idx_name` (`name`(20));
```
### 4. 诊断查看记录表 (zyt_diagnosis_view_records)
```sql
-- 诊断ID+确认状态+删除时间(用于确认诊单查询)
ALTER TABLE `zyt_diagnosis_view_records` ADD INDEX `idx_diagnosis_confirmed` (`diagnosis_id`, `is_confirmed`, `delete_time`);
```
**优化效果**
- 加速 `WHERE diagnosis_id IN (...) AND is_confirmed = 1 AND delete_time IS NULL` 查询
- 覆盖索引,无需回表
### 5. 处方表 (zyt_prescription)
```sql
-- 诊断ID+删除时间(用于开方状态查询)
ALTER TABLE `zyt_prescription` ADD INDEX `idx_diagnosis_delete` (`diagnosis_id`, `delete_time`);
```
**优化效果**
- 加速 `WHERE diagnosis_id IN (...) AND delete_time IS NULL` 查询
### 6. 管理员角色表 (zyt_admin_role)
```sql
-- 管理员ID+角色ID(用于权限判断)
ALTER TABLE `zyt_admin_role` ADD INDEX `idx_admin_role` (`admin_id`, `role_id`);
```
**优化效果**
- 加速角色查询,快速判断用户权限
## 执行步骤
### 方式一:直接执行(推荐)
```bash
# 连接数据库
mysql -h 39.97.232.35 -u cs_zyt -p cs_zyt
# 执行索引创建脚本
source optimize_appointment_indexes.sql
```
### 方式二:安全执行(检查后添加)
```bash
# 执行安全脚本(会先检查索引是否存在)
source check_and_add_indexes.sql
```
### 方式三:phpMyAdmin
1. 登录 phpMyAdmin
2. 选择数据库 `cs_zyt`
3. 点击 SQL 标签
4. 复制 `optimize_appointment_indexes.sql` 内容
5. 点击执行
## 预期效果
### 优化前
- 查询时间:500ms - 2000ms
- 扫描行数:全表扫描
- 使用索引:PRIMARY 或无
### 优化后
- 查询时间:50ms - 200ms(提升 5-10 倍)
- 扫描行数:索引范围扫描
- 使用索引:组合索引
## 验证方法
### 1. 查看执行计划
```sql
EXPLAIN SELECT
a.*,
u.patient_name,
u.phone as patient_phone,
ad.name as doctor_name
FROM zyt_appointment a
LEFT JOIN zyt_tcm_diagnosis u ON a.patient_id = u.id
LEFT JOIN zyt_admin ad ON a.doctor_id = ad.id
WHERE a.doctor_id = 1
AND a.status = 1
AND a.appointment_date >= '2024-03-01'
ORDER BY a.status ASC, a.appointment_date ASC, a.appointment_time ASC
LIMIT 20;
```
**期望结果**
- `type`: ref 或 range(不是 ALL
- `key`: idx_doctor_status_date 或 idx_status_date_time
- `rows`: 较小的数字(不是全表行数)
### 2. 查看索引使用情况
```sql
-- 查看索引统计
SHOW INDEX FROM zyt_appointment;
-- 查看索引使用情况(需要开启慢查询日志)
SELECT * FROM sys.schema_unused_indexes WHERE object_schema = 'cs_zyt';
```
## 注意事项
1. **索引维护成本**:索引会增加 INSERT/UPDATE/DELETE 的开销,但对于查询为主的场景,收益远大于成本
2. **索引选择性**
- 高选择性字段(如 ID、日期)适合建索引
- 低选择性字段(如性别、状态)单独建索引效果不明显,但可以作为组合索引的一部分
3. **前缀索引**
- 对于长字符串字段(如姓名),使用前缀索引可以节省空间
- 前缀长度选择:能区分大部分数据即可(通常 10-20 个字符)
4. **组合索引顺序**
- 遵循"最左前缀"原则
- 将选择性高的字段放在前面
- 考虑查询条件的组合频率
5. **定期维护**
```sql
-- 优化表(重建索引)
OPTIMIZE TABLE zyt_appointment;
OPTIMIZE TABLE zyt_tcm_diagnosis;
```
## 进一步优化建议
### 1. 查询优化
```php
// 避免 SELECT *,只查询需要的字段
$query->field('a.id, a.patient_id, a.doctor_id, a.status, ...');
// 使用 whereIn 代替多个 OR
$query->whereIn('a.status', [1, 3]);
```
### 2. 分页优化
```php
// 使用延迟关联优化深分页
$subQuery = Appointment::where($conditions)
->field('id')
->limit($offset, $limit)
->buildSql();
$list = Appointment::alias('a')
->join([$subQuery => 'sub'], 'a.id = sub.id')
->with(['patient', 'doctor'])
->select();
```
### 3. 缓存优化
```php
// 缓存医生列表、角色信息等不常变化的数据
$roleIds = Cache::remember('admin_role_' . $adminId, function() {
return AdminRole::where('admin_id', $adminId)->column('role_id');
}, 3600);
```
## 监控指标
定期检查以下指标:
1. 慢查询日志(> 1s 的查询)
2. 索引使用率
3. 表大小和索引大小
4. 查询响应时间
```sql
-- 查看表和索引大小
SELECT
table_name,
ROUND(data_length / 1024 / 1024, 2) AS data_mb,
ROUND(index_length / 1024 / 1024, 2) AS index_mb,
ROUND((data_length + index_length) / 1024 / 1024, 2) AS total_mb
FROM information_schema.tables
WHERE table_schema = 'cs_zyt'
AND table_name IN ('zyt_appointment', 'zyt_tcm_diagnosis', 'zyt_admin')
ORDER BY (data_length + index_length) DESC;
```