达梦数据库慢SQL排查与优化实战:从定位到解决的全流程
title: “达梦数据库慢SQL排查与优化实战:从定位到解决的全流程”
tags:
- 达梦数据库
- DM8
- 慢SQL
- SQL优化
- 数据库性能
categories: - 数据库
description: “达梦数据库慢SQL怎么查?本文从动态视图实时监控、慢SQL日志配置、EXPLAIN执行计划分析、到具体优化手段,手把手教你完整的排查与优化流程,适合达梦DBA和开发人员实战参考。”
导读:做过达梦数据库运维的同学应该都遇到过——系统突然变慢,应用频繁超时,最后定位下来就是几条 SQL 在搞鬼。这篇文章把我日常排查达梦慢 SQL 的完整流程整理出来,从"怎么发现慢 SQL"到"怎么优化慢 SQL",尽量覆盖实际工作中的常见场景。
一、先说我的排查思路
我碰到达梦性能问题,一般按这个顺序来:
- 实时监控:用动态视图看看当前有没有正在执行的慢 SQL
- 日志分析:开启慢 SQL 日志,把历史慢 SQL 记录下来
- 执行计划分析:拿到慢 SQL 后用 EXPLAIN 看执行计划,找到瓶颈
- 针对性优化:根据执行计划的结果,加索引、改写 SQL、调参数
下面按这个顺序一个个讲。
⚠️ 本文基于 DM8 版本,DM7 大部分操作也类似,但个别参数可能有差异。
二、方法一:动态视图实时排查
这是最快的方式,不需要额外配置,连上数据库就能查。
2.1 看看当前有多少活动会话
SELECT COUNT(*) FROM V$SESSIONS WHERE STATE='ACTIVE';
如果这个数字突然飙升,说明系统压力变大,大概率有慢 SQL 在捣乱。
2.2 查正在执行的慢 SQL
这个查询我经常用,找出已经执行超过 2 秒的 SQL:
SELECT * FROM (
SELECT
SESS_ID,
SQL_TEXT,
DATEDIFF(SS, LAST_RECV_TIME, SYSDATE) Y_EXETIME,
SF_GET_SESSION_SQL(SESS_ID) FULLSQL,
CLNT_IP
FROM V$SESSIONS
WHERE STATE='ACTIVE'
) WHERE Y_EXETIME >= 2;
返回字段说明:
| 字段 | 含义 |
|---|---|
| SESS_ID | 会话 ID,杀会话时要用 |
| SQL_TEXT | 正在执行的 SQL 片段 |
| FULLSQL | 完整的 SQL 语句 |
| Y_EXETIME | 已执行时间(秒) |
| CLNT_IP | 客户端 IP |
💡 如果某个 SQL 卡死了需要紧急处理,可以用
SP_CLOSE_SESSION(SESS_ID)杀掉会话。生产环境慎用,确认没有重要事务再操作。
2.3 SQL_TEXT 显示问号(?)看不到实际参数怎么办
这是达梦 DBA 最头疼的问题之一。因为应用端大多用 PreparedStatement 绑定变量的方式执行 SQL,V$SESSIONS 里的 SQL_TEXT 和 SF_GET_SESSION_SQL() 返回的都是预编译 SQL,参数全是 ?,比如:
SELECT * FROM EMPLOYEE WHERE DEPT_ID = ? AND SALARY > ?
你看不到实际传了什么值,排查问题时就很抓瞎。我总结了几种解决办法:
方法一:开启 SQL 跟踪日志(最靠谱)
达梦的 dmsql 日志文件会记录完整的 SQL 语句和参数信息。开启后日志里能看到带实际参数值的 SQL,而不是占位符。具体配置方法见本文第三章"开启慢 SQL 日志"部分,关键是配置好 sqllog.ini 中的 ITEMS 参数(设为 0 表示记录所有列)。
日志开启后,在 dmsql 日志文件中搜索对应的 SESS 或 TRXID 即可找到完整 SQL:
grep "SESS: 0x7f8a2c001234" /home/dmdba/dmdbms/log/dmsql_DMSERVER_*.log
⚠️ 注意:开启 SQL 日志对性能有影响,排查完记得关掉。
方法二:用 dbms_lob.substr 获取完整 SQL
有时候 SF_GET_SESSION_SQL 返回的 CLOB 被截断了,用 dbms_lob.substr 可以获取完整内容:
SELECT
DATEDIFF(SS, LAST_RECV_TIME, SYSDATE) SS,
DBMS_LOB.SUBSTR(SF_GET_SESSION_SQL(SESS_ID)) FULLSQL,
SQL_ID,
CURR_SCH,
SESS_ID,
TRX_ID,
CLNT_IP
FROM V$SESSIONS
WHERE STATE = 'ACTIVE'
ORDER BY 1 DESC;
注意:这个方法能拿到完整的 SQL 模板,但参数仍然是
?,因为绑定变量的值在协议层,动态视图里拿不到。
方法三:通过 V$SQL_HISTORY 查执行记录
SELECT
SQL_TEXT,
EXEC_TIME,
START_TIME,
SEQNO,
SESS
FROM V$SQL_HISTORY
WHERE SQL_TEXT LIKE '%EMPLOYEE%'
ORDER BY EXEC_TIME DESC;
V$SQL_HISTORY 中的 SQL_TEXT 同样是预编译的,但可以结合执行时间、客户端 IP 等信息缩小范围,然后去应用日志中根据时间点反查具体参数。
方法四:结合应用日志定位
这是最务实的方法。先用动态视图定位到慢 SQL 的 SQL_TEXT 模板和执行时间,然后去应用的业务日志里根据时间窗口和 SQL 模式反查传入的参数值。
方法五:开启审计功能(终极手段)
如果以上方法都搞不定,可以开启达梦的审计功能,它会记录完整的 SQL 操作信息:
SP_SET_ENABLE_AUDIT(1);
审计记录会写入 V$AUDITRECORDS 视图,包含更详细的执行信息。不过审计对性能影响更大,一般只在万不得已的时候用。
我的建议: 日常排查用方法二 + 方法三快速定位 SQL 模板和执行时间,然后去应用日志反查参数。如果应用日志不够详细,再临时开 SQL 跟踪日志(方法一)抓完整信息。
2.4 查看历史 SQL 执行记录
达梦提供了 V$SQL_HISTORY 视图,可以查看历史执行过的 SQL:
SELECT
SQL_TEXT,
EXEC_TIME,
START_TIME,
FINISH_TIME
FROM V$SQL_HISTORY
ORDER BY EXEC_TIME DESC
LIMIT 20;
这个视图在排查"昨天那个时段到底哪条 SQL 慢"时特别有用。
2.4 排查锁和阻塞
有时候 SQL 慢不是因为本身执行效率低,而是被别的会话阻塞了。
查锁:
SELECT O.NAME, L.*
FROM V$LOCK L, SYSOBJECTS O
WHERE L.TABLE_ID = O.ID AND BLOCKED = 1;
查阻塞详情(能看到谁阻塞了谁):
SELECT
DS.SESS_ID "被阻塞的会话ID",
DS.SQL_TEXT "被阻塞的SQL",
DS.TRX_ID "被阻塞的事务ID",
(CASE L.LTYPE
WHEN 'OBJECT' THEN '对象锁'
WHEN 'TID' THEN '事务锁'
END CASE) "被阻塞的锁类型",
DS.CREATE_TIME "开始阻塞时间",
SS.SESS_ID "占用锁的会话ID",
SS.SQL_TEXT "占用锁的SQL",
SS.CLNT_IP "占用锁的IP",
L.TID "占用锁的事务ID"
FROM V$SESSIONS DS
INNER JOIN V$LOCK L ON DS.TRX_ID = L.TRX_ID
LEFT JOIN V$SESSIONS SS ON SS.TRX_ID = L.TID
WHERE L.BLOCKED = 1;
找到阻塞源后,如果确认可以杀,执行:
SP_CLOSE_SESSION(占用锁的会话ID);
三、方法二:开启慢 SQL 日志
动态视图只能看当前的,如果要做系统性的慢 SQL 排查,还是得开慢 SQL 日志。
3.1 开启慢 SQL 日志
在 disql 中依次执行以下语句:
-- 设置记录的 SQL 类型(2:3:22:25:28 是常用组合)
SF_SET_SYSTEM_PARA_VALUE('SQL_TRACE_MASK', '2:3:22:25:28', 0, 1);
-- 开启异步刷盘(对性能影响更小)
SF_SET_SYSTEM_PARA_VALUE('SVR_LOG_ASYNC_FLUSH', 1, 0, 1);
-- 设置最小执行时间阈值(单位毫秒,200 表示只记录执行超过 200ms 的 SQL)
SF_SET_SYSTEM_PARA_VALUE('SVR_LOG_MIN_EXEC_TIME', 200, 0, 1);
-- 正式开启慢日志
SP_SET_PARA_VALUE(1, 'SVR_LOG', 1);
3.2 确认配置是否生效
SELECT * FROM V$DM_INI WHERE para_name = 'SVR_LOG';
SELECT * FROM V$DM_INI WHERE para_name = 'SQL_TRACE_MASK';
SELECT * FROM V$DM_INI WHERE para_name = 'SVR_LOG_MIN_EXEC_TIME';
SELECT * FROM V$DM_INI WHERE para_name = 'SVR_LOG_ASYNC_FLUSH';
确认 SVR_LOG 的值为 1 就说明已经开启。
3.3 查看慢日志文件
慢 SQL 日志文件默认在达梦安装目录的 log 子目录下,文件名格式为:
dmsql_实例名_日期_时间.log
例如:
ls -ltr /home/dmdba/dmdbms/log/dmsql*.log
直接用 grep 过滤慢 SQL:
grep -n "select" /home/dmdba/dmdbms/log/dmsql_DMSERVER_20260520_140043.log
3.4 用 DMLOG 工具分析
达梦官方提供了一个日志分析工具 DMLOG,可以把慢 SQL 日志导入后生成可视化的统计报表,包括:
- SQL 执行时间排行
- SQL 执行次数统计
- 时间分布趋势图
如果你的慢 SQL 日志量比较大,手动 grep 效率太低,建议用这个工具。
3.5 用完记得关
慢 SQL 日志对系统性能有影响,排查完之后一定要关掉:
SP_SET_PARA_VALUE(1, 'SVR_LOG', 0);
⚠️ 生产环境不建议长期开启慢 SQL 日志。排查问题时临时开,排查完马上关。
3.6 sqllog.ini 进阶配置
如果你对日志的粒度有更高要求,可以编辑 sqllog.ini 文件(位于数据库实例目录下):
BUF_TOTAL_SIZE = 10240
BUF_SIZE = 1024
BUF_KEEP_CNT = 6
[SLOG_ALL]
FILE_PATH = ../log
PART_STOR = 0
SWITCH_MODE = 2
SWITCH_LIMIT = 128
ASYNC_FLUSH = 1
FILE_NUM = 5
ITEMS = 0
SQL_TRACE_MASK = 1
MIN_EXEC_TIME = 0
USER_MODE = 0
几个关键参数解释:
| 参数 | 含义 | 建议值 |
|---|---|---|
| SWITCH_MODE | 日志切换模式,2 表示按大小切换 | 2 |
| SWITCH_LIMIT | 日志文件大小上限(MB) | 128 |
| ASYNC_FLUSH | 异步刷盘,1 开启 | 1 |
| FILE_NUM | 保留的日志文件数量 | 5 |
| MIN_EXEC_TIME | 最小执行时间阈值(ms) | 按需设置 |
四、拿到慢 SQL 后怎么看执行计划
找到慢 SQL 之后,下一步就是分析执行计划,看它到底慢在哪。
4.1 EXPLAIN 命令
最简单的方式,在 SQL 前面加上 EXPLAIN:
EXPLAIN SELECT * FROM EMPLOYEE WHERE DEPT_ID = 10 AND SALARY > 5000;
输出类似这样:
1 #NSET2: [1, 100, 156]
2 #PRJT2: [1, 100, 156]; exp_num(8), is_atom(FALSE)
3 #BLKUP2: [1, 100, 156]; IDX_EMP_DEPT(EMPLOYEE)
4 #SSEK2: [1, 100, 156]; scan_type(ASC), IDX_EMP_DEPT(EMPLOYEE),
scan_range[(10,min),(10,max)]
4.2 看懂执行计划中的操作符
这是我整理的达梦常见操作符速查表:
| 操作符 | 含义 | 慢的典型原因 |
|---|---|---|
| CSCN2 | 全表扫描 | 没有合适的索引 |
| SSEK2 | 二级索引扫描 | 正常,关注回表代价 |
| CSEK2 | 聚集索引扫描 | 范围太大 |
| BLKUP2 | 回表操作 | 索引覆盖不够 |
| NEST LOOP | 嵌套循环连接 | 驱动表数据量太大 |
| HASH2 INNER JOIN | 哈希连接 | 一般是合理选择 |
| HAGR2 | 哈希聚合 | 分组字段没有索引 |
| SORT | 排序 | 排序字段没有索引 |
| PRJT2 | 投影 | 通常不慢 |
| SLCT | 选择过滤 | 过滤条件没走索引 |
4.3 管理工具查看执行计划
如果你用的是达梦自带的管理工具(DM Manager),可以选中 SQL 语句后按 F9 直接查看图形化的执行计划,比纯文本直观很多。
4.4 用 AUTOTRACE 看实际执行统计
在 disql 中可以开启 AUTOTRACE:
SET AUTOTRACE ON;
SELECT * FROM EMPLOYEE WHERE DEPT_ID = 10 AND SALARY > 5000;
这样会同时显示 SQL 的执行结果和执行计划。如果只想看计划不看结果:
SET AUTOTRACE TRACE;
用完关闭:
SET AUTOTRACE OFF;
4.5 用 ET 看每个操作符的实际耗时
EXPLAIN 显示的是预估计划,如果你想看每个操作符实际执行了多久,用 ET:
-- 先确保开启了监控参数
SP_SET_PARA_VALUE(1, 'ENABLE_MONITOR', 1);
SP_SET_PARA_VALUE(1, 'MONITOR_SQL_EXEC', 1);
-- 执行 SQL(注意执行号)
SELECT * FROM EMPLOYEE WHERE DEPT_ID = 10 AND SALARY > 5000;
-- 用执行号查看各操作符耗时
ET 140007513786848;
ET 会显示每个操作符的实际执行时间、处理行数等信息,这对定位瓶颈非常关键。
五、慢 SQL 优化的实战手段
看完执行计划,基本就知道问题在哪了。下面是我常用的几种优化手段。
5.1 加索引——最直接的优化
场景: 执行计划里出现了全表扫描(CSCN2),而且 WHERE 条件中有明确的过滤字段。
CREATE INDEX IDX_EMP_DEPT_SALARY ON EMPLOYEE(DEPT_ID, SALARY);
几个加索引的经验:
- 把区分度高的字段放前面(比如 DEPT_ID 比 STATUS 区分度高)
- 如果查询只涉及索引字段,数据库会走索引覆盖,不需要回表(BLKUP2)
- 不要盲目加索引,索引太多会影响 INSERT/UPDATE 性能
- 一个表的索引数量我一般控制在 5 个以内
5.2 更新统计信息
场景: 执行计划看起来很离谱,比如预估行数跟实际差了好几个数量级。
达梦的 CBO(基于代价的优化器)依赖统计信息来选择执行计划。如果统计信息过期,优化器就可能做出错误的判断。
-- 收集指定表的统计信息
DBMS_STATS.GATHER_TABLE_STATS('SYSDBA', 'EMPLOYEE');
-- 收集指定索引的统计信息
DBMS_STATS.GATHER_INDEX_STATS('SYSDBA', 'IDX_EMP_DEPT');
-- 收集整个模式的统计信息
DBMS_STATS.GATHER_SCHEMA_STATS('SYSDBA');
💡 我的建议是,在大量数据导入或大批量删除之后,手动收集一次统计信息。日常可以设置定时任务定期收集。
5.3 用 Hint 强制指定执行计划
有时候优化器的选择确实不对,你可以用 Hint 手动引导:
SELECT /*+ INDEX(EMPLOYEE IDX_EMP_DEPT_SALARY) */ *
FROM EMPLOYEE
WHERE DEPT_ID = 10;
SELECT /*+ USE_HASH(E, D) */ *
FROM EMPLOYEE E
JOIN DEPARTMENT D ON E.DEPT_ID = D.ID;
SELECT /*+ NO_INDEX(EMPLOYEE) */ *
FROM EMPLOYEE
WHERE DEPT_ID = 10;
常用的 Hint:
| Hint | 作用 |
|---|---|
/*+ INDEX(表名 索引名) */ |
强制使用指定索引 |
/*+ NO_INDEX(表名) */ |
禁止使用索引 |
/*+ USE_HASH(表1, 表2) */ |
强制使用哈希连接 |
/*+ USE_NL(表1, 表2) */ |
强制使用嵌套循环连接 |
/*+ USE_MERGE(表1, 表2) */ |
强制使用合并连接 |
5.4 SQL 改写优化
有些 SQL 写法本身就有效率问题,改写一下效果立竿见影。
**1. 避免 SELECT ***
❌ 低效:
SELECT * FROM EMPLOYEE WHERE DEPT_ID = 10;
✅ 改为只查需要的列:
SELECT ID, NAME, SALARY FROM EMPLOYEE WHERE DEPT_ID = 10;
只查需要的列可以减少数据传输量,还可能走索引覆盖。
2. 用 EXISTS 替代 IN(大数据量子查询时)
❌ 当子查询结果集很大时低效:
SELECT * FROM DEPARTMENT D
WHERE D.ID IN (SELECT DEPT_ID FROM EMPLOYEE WHERE SALARY > 10000);
✅ 改为 EXISTS:
SELECT * FROM DEPARTMENT D
WHERE EXISTS (
SELECT 1 FROM EMPLOYEE E
WHERE E.DEPT_ID = D.ID AND E.SALARY > 10000
);
3. 避免在索引列上做函数运算
❌ 索引失效:
SELECT * FROM EMPLOYEE WHERE SUBSTR(NAME, 1, 3) = '张三';
✅ 改写:
SELECT * FROM EMPLOYEE WHERE NAME LIKE '张三%';
4. 避免隐式类型转换
CREATE INDEX IDX_EMP_CODE ON EMPLOYEE(CODE);
❌ CODE 是 VARCHAR 类型,但传了数字,隐式转换导致索引失效:
SELECT * FROM EMPLOYEE WHERE CODE = 10086;
✅ 类型一致:
SELECT * FROM EMPLOYEE WHERE CODE = '10086';
5. 大 IN 列表改为临时表 JOIN
❌ IN 里几百个值,执行计划会出现 CONST VALUE LIST,效率很差:
SELECT * FROM EMPLOYEE WHERE DEPT_ID IN (
'D001', 'D002', 'D003', ... /* 几百个值 */
);
✅ 用临时表:
CREATE TEMPORARY TABLE TMP_DEPT_IDS (DEPT_ID VARCHAR(20));
INSERT INTO TMP_DEPT_IDS VALUES ('D001');
INSERT INTO TMP_DEPT_IDS VALUES ('D002');
-- 批量插入
SELECT E.* FROM EMPLOYEE E
INNER JOIN TMP_DEPT_IDS T ON E.DEPT_ID = T.DEPT_ID;
5.5 调整数据库参数
如果 SQL 本身没问题,但系统整体还是慢,可能要调整数据库参数。几个常见的:
-- 增大缓冲区(设置为物理内存的 60%-70%)
SP_SET_PARA_VALUE(2, 'BUFFER', 8192);
-- 增大排序区(大量排序场景)
SP_SET_PARA_VALUE(2, 'SORT_BUF_SIZE', 128);
-- 增大共享内存池
SP_SET_PARA_VALUE(2, 'MEMORY_POOL', 1024);
-- 减少缓冲区并发冲突
SP_SET_PARA_VALUE(2, 'BUFFER_POOLS', 19);
| 参数 | 含义 | 调优建议 |
|---|---|---|
| BUFFER | 系统缓冲区大小(MB) | 数据量小于内存就设为数据量大小,否则设为内存的 2/3 |
| BUFFER_POOLS | 缓冲区分区数 | 高并发时调大,减少冲突 |
| MEMORY_POOL | 共享内存池(MB) | 高并发时调大 |
| SORT_BUF_SIZE | 排序区大小(MB) | 大量排序时调大 |
| RECYCLE | 回收缓冲区(MB) | 大量临时表、WITH 子句时调大 |
⚠️ 参数修改后部分需要重启数据库才能生效。修改前做好备份,在测试环境验证后再上生产。
六、一个完整的排查优化案例
假设某天系统告警,查询超时。我来走一遍完整流程。
第一步:实时查看
SELECT * FROM (
SELECT SESS_ID, SQL_TEXT,
DATEDIFF(SS, LAST_RECV_TIME, SYSDATE) Y_EXETIME,
SF_GET_SESSION_SQL(SESS_ID) FULLSQL,
CLNT_IP
FROM V$SESSIONS WHERE STATE='ACTIVE'
) WHERE Y_EXETIME >= 5;
发现有一条 SQL 已经执行了 30 多秒:
SELECT * FROM ORDER_INFO WHERE CREATE_TIME >= '2026-01-01'
AND STATUS = 'PENDING' AND AMOUNT > 1000;
第二步:看执行计划
EXPLAIN SELECT * FROM ORDER_INFO
WHERE CREATE_TIME >= '2026-01-01'
AND STATUS = 'PENDING' AND AMOUNT > 1000;
发现执行计划是 CSCN2(全表扫描),ORDER_INFO 表有 500 万行数据。
第三步:检查索引
SELECT * FROM DBA_INDEXES WHERE TABLE_NAME = 'ORDER_INFO';
发现只有一个主键索引,没有业务字段的索引。
第四步:创建组合索引
CREATE INDEX IDX_ORDER_STATUS_TIME_AMT
ON ORDER_INFO(STATUS, CREATE_TIME, AMOUNT);
选择 STATUS 放第一列,因为它的区分度最高(PENDING 只占少量数据)。
第五步:更新统计信息
DBMS_STATS.GATHER_TABLE_STATS('SYSDBA', 'ORDER_INFO');
第六步:验证
再次执行 EXPLAIN,确认走了索引扫描 SSEK2。实际执行时间从 30 秒降到 50 毫秒。
七、速查表
常用排查 SQL 速查
| 场景 | SQL |
|---|---|
| 查活动会话数 | SELECT COUNT(*) FROM V$SESSIONS WHERE STATE='ACTIVE'; |
| 查慢 SQL(>2秒) | 查上面 2.2 节的完整 SQL |
| 查历史 SQL | SELECT * FROM V$SQL_HISTORY ORDER BY EXEC_TIME DESC; |
| 查锁 | SELECT O.NAME,L.* FROM V$LOCK L,SYSOBJECTS O WHERE L.TABLE_ID=O.ID AND BLOCKED=1; |
| 查执行计划 | EXPLAIN <你的SQL>; |
| 收集统计信息 | DBMS_STATS.GATHER_TABLE_STATS('模式名','表名'); |
| 杀会话 | SP_CLOSE_SESSION(会话ID); |
| 开慢日志 | SP_SET_PARA_VALUE(1,'SVR_LOG',1); |
| 关慢日志 | SP_SET_PARA_VALUE(1,'SVR_LOG',0); |
执行计划操作符速查
| 操作符 | 含义 | 出现时关注 |
|---|---|---|
| CSCN2 | 全表扫描 | 是否缺少索引 |
| SSEK2 | 二级索引扫描 | 正常,关注回表 |
| BLKUP2 | 回表 | 考虑覆盖索引 |
| NEST LOOP | 嵌套循环 | 驱动表是否太大 |
| HASH JOIN | 哈希连接 | 大表连接首选 |
| HAGR2 | 哈希聚合 | 分组字段有无索引 |
| SORT | 排序 | 排序字段有无索引 |
八、常见问题
Q:开启慢 SQL 日志对性能影响有多大?
影响是有的,尤其是高并发场景下。建议开启异步刷盘(SVR_LOG_ASYNC_FLUSH=1),并设置合理的最小执行时间阈值(比如 500ms),只记录真正慢的 SQL。排查完及时关闭。
Q:EXPLAIN 和实际执行时间差异很大怎么办?
EXPLAIN 是预估的执行计划,如果统计信息不准,预估可能和实际差很多。用 ET 执行号 查看每个操作符的实际耗时,或者用 SET AUTOTRACE ON 看实际的执行统计。如果差异很大,先收集统计信息。
Q:加了索引但还是走全表扫描?
几个可能的原因:
- 统计信息过期——收集一下统计信息
- WHERE 条件上有函数或隐式类型转换——检查 SQL 写法
- 数据量太小,优化器认为全表扫描更快——这是正常的
- 查询条件不满足索引最左前缀原则——调整索引列顺序
Q:怎么判断是该加索引还是改 SQL?
我的经验是:先看执行计划,如果是全表扫描且 WHERE 条件明确,优先加索引。如果已经走了索引但还是慢,那就要考虑改写 SQL(减少回表、优化 JOIN 方式等)。如果 SQL 本身没问题,考虑调整数据库参数。
参考链接
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)